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