github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/environs/instances/image.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package instances
     5  
     6  import (
     7  	"fmt"
     8  	"sort"
     9  
    10  	"github.com/juju/juju/constraints"
    11  	"github.com/juju/juju/environs/imagemetadata"
    12  	"github.com/juju/juju/juju/arch"
    13  )
    14  
    15  // InstanceConstraint constrains the possible instances that may be
    16  // chosen by the environment provider.
    17  type InstanceConstraint struct {
    18  	Region      string
    19  	Series      string
    20  	Arches      []string
    21  	Constraints constraints.Value
    22  	// Optional filtering criteria not supported by all providers. These attributes are not specified
    23  	// by the user as a constraint but rather passed in by the provider implementation to restrict the
    24  	// choice of available images.
    25  	Storage *string
    26  }
    27  
    28  // String returns a human readable form of this InstanceConstaint.
    29  func (ic *InstanceConstraint) String() string {
    30  	storage := "none"
    31  	if ic.Storage != nil {
    32  		storage = *ic.Storage
    33  	}
    34  	return fmt.Sprintf(
    35  		"{region: %s, series: %s, arches: %s, constraints: %s, storage: %s}",
    36  		ic.Region,
    37  		ic.Series,
    38  		ic.Arches,
    39  		ic.Constraints,
    40  		storage,
    41  	)
    42  }
    43  
    44  // InstanceSpec holds an instance type name and the chosen image info.
    45  type InstanceSpec struct {
    46  	InstanceType InstanceType
    47  	Image        Image
    48  	// order is used to sort InstanceSpec based on the input InstanceTypes.
    49  	order int
    50  }
    51  
    52  // FindInstanceSpec returns an InstanceSpec satisfying the supplied InstanceConstraint.
    53  // possibleImages contains a list of images matching the InstanceConstraint.
    54  // allInstanceTypes provides information on every known available instance type (name, memory, cpu cores etc) on
    55  // which instances can be run. The InstanceConstraint is used to filter allInstanceTypes and then a suitable image
    56  // compatible with the matching instance types is returned.
    57  func FindInstanceSpec(possibleImages []Image, ic *InstanceConstraint, allInstanceTypes []InstanceType) (*InstanceSpec, error) {
    58  	if len(possibleImages) == 0 {
    59  		return nil, fmt.Errorf("no %q images in %s with arches %s",
    60  			ic.Series, ic.Region, ic.Arches)
    61  	}
    62  
    63  	var matchingTypes []InstanceType
    64  	if ic.Constraints.HasInstanceType() {
    65  		for _, itype := range allInstanceTypes {
    66  			if itype.Name == *ic.Constraints.InstanceType {
    67  				matchingTypes = append(matchingTypes, itype)
    68  				break
    69  			}
    70  		}
    71  		if len(matchingTypes) == 0 {
    72  			return nil, fmt.Errorf("invalid instance type %q", *ic.Constraints.InstanceType)
    73  		}
    74  	} else {
    75  		var err error
    76  		matchingTypes, err = getMatchingInstanceTypes(ic, allInstanceTypes)
    77  		if err != nil {
    78  			return nil, err
    79  		}
    80  	}
    81  	if len(matchingTypes) == 0 {
    82  		return nil, fmt.Errorf("no instance types found matching constraint: %s", ic)
    83  	}
    84  
    85  	specs := []*InstanceSpec{}
    86  	for _, itype := range matchingTypes {
    87  		for _, image := range possibleImages {
    88  			if image.match(itype) {
    89  				specs = append(specs, &InstanceSpec{
    90  					InstanceType: itype,
    91  					Image:        image,
    92  					order:        len(specs),
    93  				})
    94  			}
    95  		}
    96  	}
    97  	if len(specs) > 0 {
    98  		sort.Sort(byArch(specs))
    99  		return specs[0], nil
   100  	}
   101  
   102  	names := make([]string, len(matchingTypes))
   103  	for i, itype := range matchingTypes {
   104  		names[i] = itype.Name
   105  	}
   106  	return nil, fmt.Errorf("no %q images in %s matching instance types %v", ic.Series, ic.Region, names)
   107  }
   108  
   109  // byArch sorts InstanceSpecs first by descending word-size, then
   110  // alphabetically by name, and choose the first spec in the sequence.
   111  type byArch []*InstanceSpec
   112  
   113  func (a byArch) Len() int {
   114  	return len(a)
   115  }
   116  
   117  func (a byArch) Less(i, j int) bool {
   118  	iArchName := a[i].Image.Arch
   119  	jArchName := a[j].Image.Arch
   120  	iArch := arch.Info[iArchName]
   121  	jArch := arch.Info[jArchName]
   122  	// Wider word-size first.
   123  	switch {
   124  	case iArch.WordSize > jArch.WordSize:
   125  		return true
   126  	case iArch.WordSize < jArch.WordSize:
   127  		return false
   128  	}
   129  	// Alphabetically by arch name.
   130  	switch {
   131  	case iArchName < jArchName:
   132  		return true
   133  	case iArchName > jArchName:
   134  		return false
   135  	}
   136  	// If word-size and name the same, keep stable.
   137  	return a[i].order < a[j].order
   138  }
   139  
   140  func (a byArch) Swap(i, j int) {
   141  	a[i], a[j] = a[j], a[i]
   142  }
   143  
   144  // Image holds the attributes that vary amongst relevant images for
   145  // a given series in a given region.
   146  type Image struct {
   147  	Id   string
   148  	Arch string
   149  	// The type of virtualisation supported by this image.
   150  	VirtType string
   151  }
   152  
   153  // match returns true if the image can run on the supplied instance type.
   154  func (image Image) match(itype InstanceType) bool {
   155  	// The virtualisation type is optional.
   156  	if itype.VirtType != nil && image.VirtType != *itype.VirtType {
   157  		return false
   158  	}
   159  	for _, arch := range itype.Arches {
   160  		if arch == image.Arch {
   161  			return true
   162  		}
   163  	}
   164  	return false
   165  }
   166  
   167  // ImageMetadataToImages converts an array of ImageMetadata pointers (as
   168  // returned by imagemetadata.Fetch) to an array of Image objects (as required
   169  // by instances.FindInstanceSpec).
   170  func ImageMetadataToImages(inputs []*imagemetadata.ImageMetadata) []Image {
   171  	result := make([]Image, len(inputs))
   172  	for index, input := range inputs {
   173  		result[index] = Image{
   174  			Id:       input.Id,
   175  			VirtType: input.VirtType,
   176  			Arch:     input.Arch,
   177  		}
   178  	}
   179  	return result
   180  }