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 }