github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/instance/instance.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package instance
     5  
     6  import (
     7  	"fmt"
     8  	"math"
     9  	"strconv"
    10  	"strings"
    11  
    12  	"github.com/juju/utils/arch"
    13  
    14  	"github.com/juju/juju/network"
    15  	"github.com/juju/juju/status"
    16  )
    17  
    18  // An instance Id is a provider-specific identifier associated with an
    19  // instance (physical or virtual machine allocated in the provider).
    20  type Id string
    21  
    22  // InstanceStatus represents the status for a provider instance.
    23  type InstanceStatus struct {
    24  	Status  status.Status
    25  	Message string
    26  }
    27  
    28  // UnknownId can be used to explicitly specify the instance ID does not matter.
    29  const UnknownId Id = ""
    30  
    31  // Instance represents the the realization of a machine in state.
    32  type Instance interface {
    33  	// Id returns a provider-generated identifier for the Instance.
    34  	Id() Id
    35  
    36  	// Status returns the provider-specific status for the instance.
    37  	Status() InstanceStatus
    38  
    39  	// Addresses returns a list of hostnames or ip addresses
    40  	// associated with the instance.
    41  	Addresses() ([]network.Address, error)
    42  
    43  	// OpenPorts opens the given port ranges on the instance, which
    44  	// should have been started with the given machine id.
    45  	OpenPorts(machineId string, ports []network.PortRange) error
    46  
    47  	// ClosePorts closes the given port ranges on the instance, which
    48  	// should have been started with the given machine id.
    49  	ClosePorts(machineId string, ports []network.PortRange) error
    50  
    51  	// Ports returns the set of port ranges open on the instance,
    52  	// which should have been started with the given machine id. The
    53  	// port ranges are returned as sorted by network.SortPortRanges().
    54  	Ports(machineId string) ([]network.PortRange, error)
    55  }
    56  
    57  // HardwareCharacteristics represents the characteristics of the instance (if known).
    58  // Attributes that are nil are unknown or not supported.
    59  type HardwareCharacteristics struct {
    60  	Arch     *string   `json:",omitempty" yaml:"arch,omitempty"`
    61  	Mem      *uint64   `json:",omitempty" yaml:"mem,omitempty"`
    62  	RootDisk *uint64   `json:",omitempty" yaml:"rootdisk,omitempty"`
    63  	CpuCores *uint64   `json:",omitempty" yaml:"cpucores,omitempty"`
    64  	CpuPower *uint64   `json:",omitempty" yaml:"cpupower,omitempty"`
    65  	Tags     *[]string `json:",omitempty" yaml:"tags,omitempty"`
    66  
    67  	AvailabilityZone *string `json:",omitempty" yaml:"availabilityzone,omitempty"`
    68  }
    69  
    70  // An error reporting that an error has occurred during instance creation
    71  // (e.g. due to a failed container from on of previous deploys) and
    72  // that it is safe to restart instance creation
    73  type RetryableCreationError struct {
    74  	message string
    75  }
    76  
    77  // Returns the error message
    78  func (e RetryableCreationError) Error() string { return e.message }
    79  
    80  func NewRetryableCreationError(errorMessage string) *RetryableCreationError {
    81  	return &RetryableCreationError{errorMessage}
    82  }
    83  
    84  // IsRetryableCreationError returns true if the given error is
    85  // RetryableCreationError
    86  func IsRetryableCreationError(err error) bool {
    87  	_, ok := err.(*RetryableCreationError)
    88  	return ok
    89  }
    90  
    91  func (hc HardwareCharacteristics) String() string {
    92  	var strs []string
    93  	if hc.Arch != nil {
    94  		strs = append(strs, fmt.Sprintf("arch=%s", *hc.Arch))
    95  	}
    96  	if hc.CpuCores != nil {
    97  		strs = append(strs, fmt.Sprintf("cpu-cores=%d", *hc.CpuCores))
    98  	}
    99  	if hc.CpuPower != nil {
   100  		strs = append(strs, fmt.Sprintf("cpu-power=%d", *hc.CpuPower))
   101  	}
   102  	if hc.Mem != nil {
   103  		strs = append(strs, fmt.Sprintf("mem=%dM", *hc.Mem))
   104  	}
   105  	if hc.RootDisk != nil {
   106  		strs = append(strs, fmt.Sprintf("root-disk=%dM", *hc.RootDisk))
   107  	}
   108  	if hc.Tags != nil && len(*hc.Tags) > 0 {
   109  		strs = append(strs, fmt.Sprintf("tags=%s", strings.Join(*hc.Tags, ",")))
   110  	}
   111  	if hc.AvailabilityZone != nil && *hc.AvailabilityZone != "" {
   112  		strs = append(strs, fmt.Sprintf("availability-zone=%s", *hc.AvailabilityZone))
   113  	}
   114  	return strings.Join(strs, " ")
   115  }
   116  
   117  // Implement gnuflag.Value
   118  func (hc *HardwareCharacteristics) Set(s string) error {
   119  	parsed, err := ParseHardware(s)
   120  	if err != nil {
   121  		return err
   122  	}
   123  	*hc = parsed
   124  	return nil
   125  }
   126  
   127  // MustParseHardware constructs a HardwareCharacteristics from the supplied arguments,
   128  // as Parse, but panics on failure.
   129  func MustParseHardware(args ...string) HardwareCharacteristics {
   130  	hc, err := ParseHardware(args...)
   131  	if err != nil {
   132  		panic(err)
   133  	}
   134  	return hc
   135  }
   136  
   137  // ParseHardware constructs a HardwareCharacteristics from the supplied arguments,
   138  // each of which must contain only spaces and name=value pairs. If any
   139  // name is specified more than once, an error is returned.
   140  func ParseHardware(args ...string) (HardwareCharacteristics, error) {
   141  	hc := HardwareCharacteristics{}
   142  	for _, arg := range args {
   143  		raws := strings.Split(strings.TrimSpace(arg), " ")
   144  		for _, raw := range raws {
   145  			if raw == "" {
   146  				continue
   147  			}
   148  			if err := hc.setRaw(raw); err != nil {
   149  				return HardwareCharacteristics{}, err
   150  			}
   151  		}
   152  	}
   153  	return hc, nil
   154  }
   155  
   156  // setRaw interprets a name=value string and sets the supplied value.
   157  func (hc *HardwareCharacteristics) setRaw(raw string) error {
   158  	eq := strings.Index(raw, "=")
   159  	if eq <= 0 {
   160  		return fmt.Errorf("malformed characteristic %q", raw)
   161  	}
   162  	name, str := raw[:eq], raw[eq+1:]
   163  	var err error
   164  	switch name {
   165  	case "arch":
   166  		err = hc.setArch(str)
   167  	case "cpu-cores":
   168  		err = hc.setCpuCores(str)
   169  	case "cpu-power":
   170  		err = hc.setCpuPower(str)
   171  	case "mem":
   172  		err = hc.setMem(str)
   173  	case "root-disk":
   174  		err = hc.setRootDisk(str)
   175  	case "tags":
   176  		err = hc.setTags(str)
   177  	case "availability-zone":
   178  		err = hc.setAvailabilityZone(str)
   179  	default:
   180  		return fmt.Errorf("unknown characteristic %q", name)
   181  	}
   182  	if err != nil {
   183  		return fmt.Errorf("bad %q characteristic: %v", name, err)
   184  	}
   185  	return nil
   186  }
   187  
   188  func (hc *HardwareCharacteristics) setArch(str string) error {
   189  	if hc.Arch != nil {
   190  		return fmt.Errorf("already set")
   191  	}
   192  	if str != "" && !arch.IsSupportedArch(str) {
   193  		return fmt.Errorf("%q not recognized", str)
   194  	}
   195  	hc.Arch = &str
   196  	return nil
   197  }
   198  
   199  func (hc *HardwareCharacteristics) setCpuCores(str string) (err error) {
   200  	if hc.CpuCores != nil {
   201  		return fmt.Errorf("already set")
   202  	}
   203  	hc.CpuCores, err = parseUint64(str)
   204  	return
   205  }
   206  
   207  func (hc *HardwareCharacteristics) setCpuPower(str string) (err error) {
   208  	if hc.CpuPower != nil {
   209  		return fmt.Errorf("already set")
   210  	}
   211  	hc.CpuPower, err = parseUint64(str)
   212  	return
   213  }
   214  
   215  func (hc *HardwareCharacteristics) setMem(str string) (err error) {
   216  	if hc.Mem != nil {
   217  		return fmt.Errorf("already set")
   218  	}
   219  	hc.Mem, err = parseSize(str)
   220  	return
   221  }
   222  
   223  func (hc *HardwareCharacteristics) setRootDisk(str string) (err error) {
   224  	if hc.RootDisk != nil {
   225  		return fmt.Errorf("already set")
   226  	}
   227  	hc.RootDisk, err = parseSize(str)
   228  	return
   229  }
   230  
   231  func (hc *HardwareCharacteristics) setTags(str string) (err error) {
   232  	if hc.Tags != nil {
   233  		return fmt.Errorf("already set")
   234  	}
   235  	hc.Tags = parseTags(str)
   236  	return
   237  }
   238  
   239  func (hc *HardwareCharacteristics) setAvailabilityZone(str string) error {
   240  	if hc.AvailabilityZone != nil {
   241  		return fmt.Errorf("already set")
   242  	}
   243  	if str != "" {
   244  		hc.AvailabilityZone = &str
   245  	}
   246  	return nil
   247  }
   248  
   249  // parseTags returns the tags in the value s
   250  func parseTags(s string) *[]string {
   251  	if s == "" {
   252  		return &[]string{}
   253  	}
   254  	tags := strings.Split(s, ",")
   255  	return &tags
   256  }
   257  
   258  func parseUint64(str string) (*uint64, error) {
   259  	var value uint64
   260  	if str != "" {
   261  		if val, err := strconv.ParseUint(str, 10, 64); err != nil {
   262  			return nil, fmt.Errorf("must be a non-negative integer")
   263  		} else {
   264  			value = uint64(val)
   265  		}
   266  	}
   267  	return &value, nil
   268  }
   269  
   270  func parseSize(str string) (*uint64, error) {
   271  	var value uint64
   272  	if str != "" {
   273  		mult := 1.0
   274  		if m, ok := mbSuffixes[str[len(str)-1:]]; ok {
   275  			str = str[:len(str)-1]
   276  			mult = m
   277  		}
   278  		val, err := strconv.ParseFloat(str, 64)
   279  		if err != nil || val < 0 {
   280  			return nil, fmt.Errorf("must be a non-negative float with optional M/G/T/P suffix")
   281  		}
   282  		val *= mult
   283  		value = uint64(math.Ceil(val))
   284  	}
   285  	return &value, nil
   286  }
   287  
   288  var mbSuffixes = map[string]float64{
   289  	"M": 1,
   290  	"G": 1024,
   291  	"T": 1024 * 1024,
   292  	"P": 1024 * 1024 * 1024,
   293  }