github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/provider/ec2/image_test.go (about) 1 // Copyright 2011, 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package ec2 5 6 import ( 7 jc "github.com/juju/testing/checkers" 8 gc "gopkg.in/check.v1" 9 10 corebase "github.com/juju/juju/core/base" 11 "github.com/juju/juju/core/constraints" 12 "github.com/juju/juju/environs/imagemetadata" 13 "github.com/juju/juju/environs/instances" 14 "github.com/juju/juju/testing" 15 "github.com/juju/juju/version" 16 ) 17 18 var _ = gc.Suite(&specSuite{}) 19 20 type specSuite struct { 21 testing.BaseSuite 22 } 23 24 var paravirtual = "pv" 25 26 var testInstanceTypes = []instances.InstanceType{{ 27 Name: "t3a.xlarge", 28 CpuCores: 4, 29 CpuPower: instances.CpuPower(1400), 30 Mem: 16384, 31 Arch: "amd64", 32 Cost: 172, 33 }, { 34 Name: "t3a.nano", 35 CpuCores: 2, 36 CpuPower: instances.CpuPower(700), 37 Mem: 512, 38 Arch: "amd64", 39 Cost: 5, 40 }, { 41 Name: "t3a.micro", 42 CpuCores: 2, 43 CpuPower: instances.CpuPower(700), 44 Mem: 1024, 45 Arch: "amd64", 46 Cost: 10, 47 }, { 48 Name: "t3a.small", 49 CpuCores: 2, 50 CpuPower: instances.CpuPower(700), 51 Mem: 2048, 52 Arch: "amd64", 53 Cost: 21, 54 }, { 55 Name: "t3a.medium", 56 CpuCores: 2, 57 CpuPower: instances.CpuPower(700), 58 Mem: 4096, 59 Arch: "amd64", 60 Cost: 43, 61 }, { 62 Name: "m1.medium", 63 CpuCores: 1, 64 CpuPower: instances.CpuPower(100), 65 Mem: 3840, 66 Arch: "amd64", 67 VirtType: ¶virtual, 68 Cost: 117, 69 }, { 70 Name: "a1.xlarge", 71 CpuCores: 4, 72 CpuPower: instances.CpuPower(1288), 73 Mem: 8192, 74 Arch: "arm64", 75 }, { 76 Name: "c4.large", 77 CpuCores: 2, 78 CpuPower: instances.CpuPower(811), 79 Mem: 3840, 80 Arch: "amd64", 81 Cost: 114, 82 }, { 83 Name: "t2.medium", 84 CpuCores: 2, 85 CpuPower: instances.CpuPower(40), 86 Mem: 4096, 87 Arch: "amd64", 88 Cost: 46, 89 }, { 90 Name: "c1.medium", 91 CpuCores: 2, 92 CpuPower: instances.CpuPower(200), 93 Mem: 1741, 94 Arch: "amd64", 95 Cost: 164, 96 }, { 97 Name: "cc2.8xlarge", 98 CpuCores: 32, 99 CpuPower: instances.CpuPower(11647), 100 Mem: 61952, 101 Arch: "amd64", 102 Cost: 2250, 103 }, { 104 Name: "r5a.large", 105 CpuCores: 2, 106 CpuPower: instances.CpuPower(700), 107 Mem: 16384, 108 Arch: "amd64", 109 Cost: 137, 110 }} 111 112 var findInstanceSpecTests = []struct { 113 // LTS-dependent requires new or updated entries upon a new LTS release. 114 version string 115 arch string 116 cons string 117 itype string 118 image string 119 storage []string 120 }{ 121 { 122 version: "18.04", 123 arch: "amd64", 124 itype: "t3a.small", 125 image: "ami-00001133", 126 }, { 127 version: "18.04", 128 arch: "amd64", 129 cons: "cores=4", 130 itype: "t3a.xlarge", 131 image: "ami-00001133", 132 }, { 133 version: "20.04", 134 arch: "arm64", 135 cons: "cores=4", 136 itype: "a1.xlarge", 137 image: "ami-02004133", 138 }, { 139 version: "18.04", 140 arch: "amd64", 141 cons: "mem=10G", 142 itype: "r5a.large", 143 image: "ami-00001133", 144 }, { 145 version: "18.04", 146 arch: "amd64", 147 cons: "mem=", 148 itype: "t3a.small", 149 image: "ami-00001133", 150 }, { 151 version: "18.04", 152 arch: "amd64", 153 cons: "cpu-power=", 154 itype: "t3a.small", 155 image: "ami-00001133", 156 }, { 157 version: "18.04", 158 arch: "amd64", 159 cons: "cpu-power=800", 160 itype: "c4.large", 161 image: "ami-00001133", 162 }, { 163 version: "18.04", 164 arch: "amd64", 165 cons: "instance-type=m1.medium cpu-power=100", 166 itype: "m1.medium", 167 image: "ami-00001135", 168 }, { 169 version: "18.04", 170 arch: "amd64", 171 cons: "mem=2G root-disk=16384M", 172 itype: "t3a.small", 173 image: "ami-00001133", 174 }, { 175 version: "18.04", 176 arch: "amd64", 177 cons: "mem=4G root-disk=16384M", 178 itype: "t3a.medium", 179 storage: []string{"ssd", "ebs"}, 180 image: "ami-00001133", 181 }, { 182 version: "18.04", 183 arch: "amd64", 184 cons: "mem=4G root-disk=16384M", 185 itype: "t3a.medium", 186 storage: []string{"ebs", "ssd"}, 187 image: "ami-00001139", 188 }, { 189 version: "18.04", 190 arch: "amd64", 191 cons: "mem=4G root-disk=16384M", 192 itype: "t3a.medium", 193 storage: []string{"ebs"}, 194 image: "ami-00001139", 195 }, { 196 version: "22.04", 197 arch: "amd64", 198 itype: "t3a.small", 199 image: "ami-02204133", 200 }, { 201 version: "20.04", 202 arch: "amd64", 203 cons: "arch=amd64", 204 itype: "t3a.small", 205 image: "ami-02004133", 206 }, { 207 version: "20.04", 208 arch: "amd64", 209 cons: "instance-type=cc2.8xlarge", 210 itype: "cc2.8xlarge", 211 image: "ami-02004133", 212 }, 213 } 214 215 func (s *specSuite) TestFindInstanceSpec(c *gc.C) { 216 size := len(findInstanceSpecTests) 217 for i, test := range findInstanceSpecTests { 218 c.Logf("\ntest %d of %d: %q; %q; %q; %q; %q; %v", i+1, size, 219 test.version, test.arch, test.cons, test.itype, test.image, 220 test.storage) 221 stor := test.storage 222 if len(stor) == 0 { 223 stor = []string{ssdStorage, ebsStorage} 224 } 225 // We need to filter the image metadata to the test's 226 // arches and series; the provisioner and bootstrap 227 // code will do this. 228 imageMetadata := filterImageMetadata( 229 c, TestImageMetadata, test.version, test.arch, 230 ) 231 base := corebase.MakeDefaultBase("ubuntu", test.version) 232 spec, err := findInstanceSpec( 233 false, // non-controller 234 imageMetadata, 235 testInstanceTypes, 236 &instances.InstanceConstraint{ 237 Region: "test", 238 Base: base, 239 Arch: test.arch, 240 Constraints: constraints.MustParse(test.cons), 241 Storage: stor, 242 }) 243 c.Assert(err, jc.ErrorIsNil) 244 c.Check(spec.InstanceType.Name, gc.Equals, test.itype) 245 c.Check(spec.Image.Id, gc.Equals, test.image) 246 } 247 } 248 249 func (s *specSuite) TestFindInstanceSpecNotSetCpuPowerWhenInstanceTypeSet(c *gc.C) { 250 instanceConstraint := &instances.InstanceConstraint{ 251 Region: "test", 252 Base: version.DefaultSupportedLTSBase(), 253 Constraints: constraints.MustParse("instance-type=t2.medium"), 254 } 255 256 c.Check(instanceConstraint.Constraints.CpuPower, gc.IsNil) 257 _, err := findInstanceSpec( 258 false, // non-controller 259 TestImageMetadata, 260 testInstanceTypes, 261 instanceConstraint, 262 ) 263 c.Assert(err, jc.ErrorIsNil) 264 265 c.Check(instanceConstraint.Constraints.CpuPower, gc.IsNil) 266 } 267 268 var findInstanceSpecErrorTests = []struct { 269 base corebase.Base 270 arch string 271 cons string 272 err string 273 }{ 274 { 275 base: version.DefaultSupportedLTSBase(), 276 arch: "arm", 277 err: `no metadata for "ubuntu@22.04" images in test with arch arm`, 278 }, { 279 base: corebase.MakeDefaultBase("ubuntu", "15.04"), 280 arch: "amd64", 281 cons: "mem=4G", 282 err: `no metadata for \"ubuntu@15.04\" images in test with arch amd64`, 283 }, { 284 base: version.DefaultSupportedLTSBase(), 285 arch: "amd64", 286 cons: "instance-type=m1.small mem=4G", 287 err: `no instance types in test matching constraints "arch=amd64 instance-type=m1.small mem=4096M"`, 288 }, 289 } 290 291 func (s *specSuite) TestFindInstanceSpecErrors(c *gc.C) { 292 for i, t := range findInstanceSpecErrorTests { 293 c.Logf("test %d", i) 294 // We need to filter the image metadata to the test's 295 // arches and series; the provisioner and bootstrap 296 // code will do this. 297 imageMetadata := filterImageMetadata( 298 c, TestImageMetadata, t.base.Channel.Track, t.arch, 299 ) 300 _, err := findInstanceSpec( 301 false, // non-controller 302 imageMetadata, 303 testInstanceTypes, 304 &instances.InstanceConstraint{ 305 Region: "test", 306 Base: t.base, 307 Arch: t.arch, 308 Constraints: constraints.MustParse(t.cons), 309 }) 310 c.Check(err, gc.ErrorMatches, t.err) 311 } 312 } 313 314 func filterImageMetadata( 315 c *gc.C, 316 in []*imagemetadata.ImageMetadata, 317 filterVersion string, filterArch string, 318 ) []*imagemetadata.ImageMetadata { 319 var imageMetadata []*imagemetadata.ImageMetadata 320 for _, im := range in { 321 if im.Version != filterVersion { 322 continue 323 } 324 if im.Arch == filterArch { 325 imageMetadata = append(imageMetadata, im) 326 } 327 } 328 return imageMetadata 329 } 330 331 func (*specSuite) TestFilterImagesAcceptsNil(c *gc.C) { 332 c.Check(filterImages(nil, nil), gc.HasLen, 0) 333 } 334 335 func (*specSuite) TestFilterImagesReturnsSelectively(c *gc.C) { 336 good := imagemetadata.ImageMetadata{Id: "good", Storage: "ebs"} 337 bad := imagemetadata.ImageMetadata{Id: "bad", Storage: "ftp"} 338 input := []*imagemetadata.ImageMetadata{&good, &bad} 339 expectation := []*imagemetadata.ImageMetadata{&good} 340 341 ic := &instances.InstanceConstraint{Storage: []string{"ebs"}} 342 c.Check(filterImages(input, ic), gc.DeepEquals, expectation) 343 } 344 345 func (*specSuite) TestFilterImagesMaintainsOrdering(c *gc.C) { 346 input := []*imagemetadata.ImageMetadata{ 347 {Id: "one", Storage: "ebs"}, 348 {Id: "two", Storage: "ebs"}, 349 {Id: "three", Storage: "ebs"}, 350 } 351 ic := &instances.InstanceConstraint{Storage: []string{"ebs"}} 352 c.Check(filterImages(input, ic), gc.DeepEquals, input) 353 }