github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/environs/instances/image_test.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 "testing" 8 9 jc "github.com/juju/testing/checkers" 10 gc "gopkg.in/check.v1" 11 12 corebase "github.com/juju/juju/core/base" 13 "github.com/juju/juju/core/constraints" 14 "github.com/juju/juju/environs/imagemetadata" 15 "github.com/juju/juju/environs/simplestreams" 16 coretesting "github.com/juju/juju/testing" 17 ) 18 19 type imageSuite struct { 20 coretesting.BaseSuite 21 } 22 23 func Test(t *testing.T) { 24 gc.TestingT(t) 25 } 26 27 var _ = gc.Suite(&imageSuite{}) 28 29 var jsonImagesContent = ` 30 { 31 "content_id": "com.ubuntu.cloud:released:aws", 32 "products": { 33 "com.ubuntu.cloud:server:12.04:amd64": { 34 "release": "12.04", 35 "version": "12.04", 36 "arch": "amd64", 37 "versions": { 38 "20121218": { 39 "items": { 40 "usee1pi": { 41 "root_store": "instance", 42 "virt": "pv", 43 "region": "us-east-1", 44 "id": "ami-00000011" 45 }, 46 "usww1pe": { 47 "root_store": "ebs", 48 "virt": "pv", 49 "region": "us-west-1", 50 "id": "ami-00000016" 51 }, 52 "apne1pe": { 53 "root_store": "ebs", 54 "virt": "pv", 55 "region": "ap-northeast-1", 56 "id": "ami-00000026" 57 }, 58 "test1pe": { 59 "root_store": "ebs", 60 "virt": "pv", 61 "region": "test", 62 "id": "ami-00000033" 63 }, 64 "test1he": { 65 "root_store": "ebs", 66 "virt": "hvm", 67 "region": "test", 68 "id": "ami-00000035" 69 } 70 }, 71 "pubname": "ubuntu-precise-12.04-amd64-server-20121218", 72 "label": "release" 73 }, 74 "20121118": { 75 "items": { 76 "apne1pe": { 77 "root_store": "ebs", 78 "virt": "pv", 79 "region": "ap-northeast-1", 80 "id": "ami-00000008" 81 }, 82 "test2he": { 83 "root_store": "ebs", 84 "virt": "hvm", 85 "region": "test", 86 "id": "ami-00000036" 87 } 88 }, 89 "pubname": "ubuntu-precise-12.04-amd64-server-20121118", 90 "label": "release" 91 } 92 } 93 }, 94 "com.ubuntu.cloud:server:12.04:arm64": { 95 "release": "12.04", 96 "version": "12.04", 97 "arch": "arm64", 98 "versions": { 99 "20121218": { 100 "items": { 101 "apne1pe": { 102 "root_store": "ebs", 103 "virt": "pv", 104 "region": "ap-northeast-1", 105 "id": "ami-00000023" 106 }, 107 "test1pe": { 108 "root_store": "ebs", 109 "virt": "pv", 110 "region": "test", 111 "id": "ami-00000034" 112 }, 113 "armo1pe": { 114 "root_store": "ebs", 115 "virt": "pv", 116 "region": "arm-only", 117 "id": "ami-00000036" 118 } 119 }, 120 "pubname": "ubuntu-precise-12.04-arm64-server-20121218", 121 "label": "release" 122 } 123 } 124 }, 125 "com.ubuntu.cloud:server:12.04:i386": { 126 "release": "12.04", 127 "version": "12.04", 128 "arch": "i386", 129 "versions": { 130 "20121218": { 131 "items": { 132 "apne1pe": { 133 "root_store": "ebs", 134 "virt": "pv", 135 "region": "ap-northeast-1", 136 "id": "ami-b79b09b6" 137 }, 138 "test1pe": { 139 "root_store": "ebs", 140 "virt": "pv", 141 "region": "test", 142 "id": "ami-b79b09b7" 143 } 144 }, 145 "pubname": "ubuntu-precise-12.04-i386-server-20121218", 146 "label": "release" 147 } 148 } 149 }, 150 "com.ubuntu.cloud:server:12.04:ppc64el": { 151 "release": "12.04", 152 "version": "12.04", 153 "arch": "ppc64el", 154 "versions": { 155 "20121218": { 156 "items": { 157 "apne1pe": { 158 "root_store": "ebs", 159 "virt": "pv", 160 "region": "ap-northeast-1", 161 "id": "ami-b79b09b8" 162 }, 163 "test1pe": { 164 "root_store": "ebs", 165 "virt": "pv", 166 "region": "test", 167 "id": "ami-b79b09b9" 168 } 169 }, 170 "pubname": "ubuntu-precise-12.04-ppc64el-server-20121218", 171 "label": "release" 172 } 173 } 174 }, 175 "com.ubuntu.cloud.daily:server:12.04:amd64": { 176 "release": "12.04", 177 "version": "12.04", 178 "arch": "amd64", 179 "versions": { 180 "20121218": { 181 "items": { 182 "apne1pe": { 183 "root_store": "ebs", 184 "virt": "pv", 185 "region": "ap-northeast-1", 186 "id": "ami-10000026" 187 }, 188 "test1pe": { 189 "root_store": "ebs", 190 "virt": "hvm", 191 "region": "test", 192 "id": "ami-10000035" 193 } 194 }, 195 "pubname": "ubuntu-precise-12.04-amd64-daily-20121218", 196 "label": "release" 197 } 198 } 199 } 200 }, 201 "format": "products:1.0" 202 } 203 ` 204 205 type instanceSpecTestParams struct { 206 desc string 207 region string 208 arch string 209 stream string 210 constraints string 211 instanceTypes []InstanceType 212 imageId string 213 instanceTypeId string 214 instanceTypeName string 215 err string 216 } 217 218 func (p *instanceSpecTestParams) init() { 219 if p.arch == "" { 220 p.arch = "amd64" 221 } 222 if p.instanceTypes == nil { 223 p.instanceTypes = []InstanceType{{Id: "1", Name: "it-1", Arch: "amd64"}} 224 p.instanceTypeId = "1" 225 p.instanceTypeName = "it-1" 226 } 227 } 228 229 var pv = "pv" 230 var findInstanceSpecTests = []instanceSpecTestParams{ 231 { 232 desc: "image exists in metadata", 233 region: "test", 234 imageId: "ami-00000033", 235 instanceTypes: []InstanceType{ 236 {Id: "1", Name: "it-1", Arch: "amd64", VirtType: &pv, Mem: 512}, 237 }, 238 }, 239 { 240 desc: "explicit release stream", 241 region: "test", 242 stream: "released", 243 imageId: "ami-00000035", 244 instanceTypes: []InstanceType{ 245 {Id: "1", Name: "it-1", Arch: "amd64", VirtType: &hvm, Mem: 512, CpuCores: 2}, 246 }, 247 }, 248 { 249 desc: "non-release stream", 250 region: "test", 251 stream: "daily", 252 imageId: "ami-10000035", 253 instanceTypes: []InstanceType{ 254 {Id: "1", Name: "it-1", Arch: "amd64", VirtType: &hvm, Mem: 512, CpuCores: 2}, 255 }, 256 }, 257 { 258 desc: "multiple images exists in metadata, use most recent", 259 region: "test", 260 imageId: "ami-00000035", 261 instanceTypes: []InstanceType{ 262 {Id: "1", Name: "it-1", Arch: "amd64", VirtType: &hvm, Mem: 512, CpuCores: 2}, 263 }, 264 }, 265 { 266 desc: "empty instance type constraint", 267 region: "test", 268 constraints: "instance-type=", 269 imageId: "ami-00000033", 270 instanceTypes: []InstanceType{ 271 {Id: "1", Name: "it-1", Arch: "amd64", VirtType: &pv, Mem: 512}, 272 }, 273 }, 274 { 275 desc: "use instance type constraint", 276 region: "test", 277 constraints: "instance-type=it-1", 278 imageId: "ami-00000035", 279 instanceTypes: []InstanceType{ 280 {Id: "1", Name: "it-1", Arch: "amd64", VirtType: &hvm, Mem: 512, CpuCores: 2}, 281 {Id: "2", Name: "it-2", Arch: "amd64", VirtType: &hvm, Mem: 1024, CpuCores: 2}, 282 }, 283 }, 284 { 285 desc: "use instance type non amd64 constraint", 286 region: "test", 287 arch: "ppc64el", 288 imageId: "ami-b79b09b9", 289 instanceTypes: []InstanceType{ 290 {Id: "1", Name: "it-1", Arch: "amd64", VirtType: &pv, Mem: 4096, CpuCores: 2, Cost: 1}, 291 {Id: "2", Name: "it-2", Arch: "ppc64el", VirtType: &pv, Mem: 4096, CpuCores: 2, Cost: 2}, 292 }, 293 }, 294 { 295 desc: "instance type constraint, no matching instance types", 296 region: "test", 297 constraints: "instance-type=it-10", 298 instanceTypes: []InstanceType{ 299 {Id: "1", Name: "it-1", Arch: "amd64", VirtType: &hvm, Mem: 512, CpuCores: 2}, 300 }, 301 err: `no instance types in test matching constraints "arch=amd64 instance-type=it-10"`, 302 }, 303 { 304 desc: "no image exists in metadata", 305 region: "invalid-region", 306 err: `no metadata for "ubuntu@12.04" images in invalid-region with arch amd64`, 307 }, 308 { 309 desc: "no valid instance types", 310 region: "test", 311 instanceTypes: []InstanceType{}, 312 err: `no instance types in test matching constraints "arch=amd64"`, 313 }, 314 { 315 desc: "no compatible instance types", 316 region: "arm-only", 317 instanceTypes: []InstanceType{{Id: "1", Name: "it-1", Arch: "amd64", Mem: 2048}}, 318 err: `no "ubuntu@12.04" images in arm-only matching instance types \[it-1\]`, 319 }, 320 } 321 322 func (s *imageSuite) TestFindInstanceSpec(c *gc.C) { 323 for _, t := range findInstanceSpecTests { 324 c.Logf("test: %v", t.desc) 325 t.init() 326 cons, err := imagemetadata.NewImageConstraint(simplestreams.LookupParams{ 327 CloudSpec: simplestreams.CloudSpec{t.region, "ep"}, 328 Releases: []string{"12.04"}, 329 Stream: t.stream, 330 }) 331 c.Assert(err, jc.ErrorIsNil) 332 dataSource := simplestreams.NewDataSource(simplestreams.Config{ 333 Description: "test", 334 BaseURL: "some-url", 335 HostnameVerification: true, 336 Priority: simplestreams.DEFAULT_CLOUD_DATA, 337 }) 338 imageMeta, err := imagemetadata.GetLatestImageIdMetadata([]byte(jsonImagesContent), dataSource, cons) 339 c.Assert(err, jc.ErrorIsNil) 340 var images []Image 341 for _, imageMetadata := range imageMeta { 342 im := *imageMetadata 343 images = append(images, Image{ 344 Id: im.Id, 345 VirtType: im.VirtType, 346 Arch: im.Arch, 347 }) 348 } 349 imageCons := constraints.MustParse(t.constraints) 350 spec, err := FindInstanceSpec(images, &InstanceConstraint{ 351 Base: corebase.MakeDefaultBase("ubuntu", "12.04"), 352 Region: t.region, 353 Arch: t.arch, 354 Constraints: imageCons, 355 }, t.instanceTypes) 356 if t.err != "" { 357 c.Check(err, gc.ErrorMatches, t.err) 358 continue 359 } else { 360 if !c.Check(err, jc.ErrorIsNil) { 361 continue 362 } 363 c.Check(spec.Image.Id, gc.Equals, t.imageId) 364 if len(t.instanceTypes) == 1 { 365 c.Check(spec.InstanceType, gc.DeepEquals, t.instanceTypes[0]) 366 } 367 if imageCons.HasInstanceType() { 368 c.Assert(spec.InstanceType.Name, gc.Equals, *imageCons.InstanceType) 369 } 370 } 371 } 372 } 373 374 var imageMatchtests = []struct { 375 image Image 376 itype InstanceType 377 match imageMatch 378 }{ 379 { 380 image: Image{Arch: "amd64"}, 381 itype: InstanceType{Arch: "amd64"}, 382 match: exactMatch, 383 }, { 384 image: Image{Arch: "amd64"}, 385 itype: InstanceType{Arch: "amd64"}, 386 match: exactMatch, 387 }, { 388 image: Image{Arch: "amd64", VirtType: hvm}, 389 itype: InstanceType{Arch: "amd64", VirtType: &hvm}, 390 match: exactMatch, 391 }, { 392 image: Image{Arch: "arm64"}, 393 itype: InstanceType{Arch: "amd64"}, 394 }, { 395 image: Image{Arch: "amd64", VirtType: hvm}, 396 itype: InstanceType{Arch: "amd64"}, 397 match: exactMatch, 398 }, { 399 image: Image{Arch: "amd64"}, // no known virt type 400 itype: InstanceType{Arch: "amd64", VirtType: &hvm}, 401 match: partialMatch, 402 }, { 403 image: Image{Arch: "amd64", VirtType: "pv"}, 404 itype: InstanceType{Arch: "amd64", VirtType: &hvm}, 405 match: nonMatch, 406 }, 407 } 408 409 func (s *imageSuite) TestImageMatch(c *gc.C) { 410 for i, t := range imageMatchtests { 411 c.Logf("test %d", i) 412 c.Check(t.image.match(t.itype), gc.Equals, t.match) 413 } 414 } 415 416 func (*imageSuite) TestImageMetadataToImagesAcceptsNil(c *gc.C) { 417 c.Check(ImageMetadataToImages(nil), gc.HasLen, 0) 418 } 419 420 func (*imageSuite) TestImageMetadataToImagesConvertsSelectMetadata(c *gc.C) { 421 input := []*imagemetadata.ImageMetadata{ 422 { 423 Id: "id", 424 Storage: "storage-is-ignored", 425 VirtType: "vtype", 426 Arch: "arch", 427 RegionAlias: "region-alias-is-ignored", 428 RegionName: "region-name-is-ignored", 429 Endpoint: "endpoint-is-ignored", 430 }, 431 } 432 expectation := []Image{ 433 { 434 Id: "id", 435 VirtType: "vtype", 436 Arch: "arch", 437 }, 438 } 439 c.Check(ImageMetadataToImages(input), gc.DeepEquals, expectation) 440 } 441 442 func (*imageSuite) TestImageMetadataToImagesMaintainsOrdering(c *gc.C) { 443 input := []*imagemetadata.ImageMetadata{ 444 {Id: "one", Arch: "Z80"}, 445 {Id: "two", Arch: "i386"}, 446 {Id: "three", Arch: "amd64"}, 447 } 448 expectation := []Image{ 449 {Id: "one", Arch: "Z80"}, 450 {Id: "two", Arch: "i386"}, 451 {Id: "three", Arch: "amd64"}, 452 } 453 c.Check(ImageMetadataToImages(input), gc.DeepEquals, expectation) 454 } 455 456 func (*imageSuite) TestInstanceConstraintString(c *gc.C) { 457 imageCons := constraints.MustParse("mem=4G") 458 ic := &InstanceConstraint{ 459 Base: corebase.MakeDefaultBase("ubuntu", "12.04"), 460 Region: "region", 461 Arch: "amd64", 462 Constraints: imageCons, 463 } 464 c.Assert( 465 ic.String(), gc.Equals, 466 "{region: region, base: ubuntu@12.04, arch: amd64, constraints: mem=4096M, storage: []}") 467 468 ic.Storage = []string{"ebs", "ssd"} 469 c.Assert( 470 ic.String(), gc.Equals, 471 "{region: region, base: ubuntu@12.04, arch: amd64, constraints: mem=4096M, storage: [ebs ssd]}") 472 }