github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/instance/placement.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package instance
     5  
     6  import (
     7  	"fmt"
     8  	"strings"
     9  
    10  	"github.com/juju/names"
    11  )
    12  
    13  const (
    14  	// MachineScope is a special scope name that is used
    15  	// for machine placement directives (e.g. --to 0).
    16  	MachineScope = "#"
    17  )
    18  
    19  var ErrPlacementScopeMissing = fmt.Errorf("placement scope missing")
    20  
    21  // Placement defines a placement directive, which has a scope
    22  // and a value that is scope-specific.
    23  type Placement struct {
    24  	// Scope is the scope of the placement directive. Scope may
    25  	// be a container type (lxc, kvm), instance.MachineScope, or
    26  	// an environment name.
    27  	//
    28  	// If Scope is empty, then it must be inferred from the context.
    29  	Scope string
    30  
    31  	// Directive is a scope-specific placement directive.
    32  	//
    33  	// For MachineScope or a container scope, this may be empty or
    34  	// the ID of an existing machine.
    35  	Directive string
    36  }
    37  
    38  func (p *Placement) String() string {
    39  	return fmt.Sprintf("%s:%s", p.Scope, p.Directive)
    40  }
    41  
    42  func isContainerType(s string) bool {
    43  	_, err := ParseContainerType(s)
    44  	return err == nil
    45  }
    46  
    47  // ParsePlacement attempts to parse the specified string and create a
    48  // corresponding Placement structure.
    49  //
    50  // If the placement directive is non-empty and missing a scope,
    51  // ErrPlacementScopeMissing will be returned as well as a Placement
    52  // with an empty Scope field.
    53  func ParsePlacement(directive string) (*Placement, error) {
    54  	if directive == "" {
    55  		return nil, nil
    56  	}
    57  	if colon := strings.IndexRune(directive, ':'); colon != -1 {
    58  		scope, directive := directive[:colon], directive[colon+1:]
    59  		if scope == "" {
    60  			return nil, ErrPlacementScopeMissing
    61  		}
    62  		// Sanity check: machine/container scopes require a machine ID as the value.
    63  		if (scope == MachineScope || isContainerType(scope)) && !names.IsValidMachine(directive) {
    64  			return nil, fmt.Errorf("invalid value %q for %q scope: expected machine-id", directive, scope)
    65  		}
    66  		return &Placement{Scope: scope, Directive: directive}, nil
    67  	}
    68  	if names.IsValidMachine(directive) {
    69  		return &Placement{Scope: MachineScope, Directive: directive}, nil
    70  	}
    71  	if isContainerType(directive) {
    72  		return &Placement{Scope: directive}, nil
    73  	}
    74  	return nil, ErrPlacementScopeMissing
    75  }
    76  
    77  // MustParsePlacement attempts to parse the specified string and create
    78  // a corresponding Placement structure, panicking if an error occurs.
    79  func MustParsePlacement(directive string) *Placement {
    80  	placement, err := ParsePlacement(directive)
    81  	if err != nil {
    82  		panic(err)
    83  	}
    84  	return placement
    85  }