github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/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 "github.com/juju/utils" 11 gc "gopkg.in/check.v1" 12 13 "github.com/juju/juju/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": "precise", 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:armhf": { 95 "release": "precise", 96 "version": "12.04", 97 "arch": "armhf", 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-armhf-server-20121218", 121 "label": "release" 122 } 123 } 124 }, 125 "com.ubuntu.cloud:server:12.04:i386": { 126 "release": "precise", 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": "precise", 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": "precise", 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 arches []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.arches == nil { 220 p.arches = []string{"amd64", "armhf"} 221 } 222 if p.instanceTypes == nil { 223 p.instanceTypes = []InstanceType{{Id: "1", Name: "it-1", Arches: []string{"amd64", "armhf"}}} 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", Arches: []string{"amd64"}, VirtType: &pv, Mem: 512}, 237 }, 238 }, 239 { 240 desc: "prefer amd64 over i386", 241 region: "test", 242 imageId: "ami-00000033", 243 arches: []string{"amd64", "i386"}, 244 instanceTypes: []InstanceType{ 245 {Id: "1", Name: "it-1", Arches: []string{"i386", "amd64"}, VirtType: &pv, Mem: 512}, 246 }, 247 }, 248 { 249 desc: "prefer armhf over i386 (first alphabetical wins)", 250 region: "test", 251 imageId: "ami-00000034", 252 arches: []string{"armhf", "i386"}, 253 instanceTypes: []InstanceType{ 254 {Id: "1", Name: "it-1", Arches: []string{"armhf", "i386"}, VirtType: &pv, Mem: 512}, 255 }, 256 }, 257 { 258 desc: "prefer ppc64el over i386 (64-bit trumps 32-bit, regardless of alphabetical order)", 259 region: "test", 260 imageId: "ami-b79b09b9", 261 arches: []string{"ppc64el", "i386"}, 262 instanceTypes: []InstanceType{ 263 {Id: "1", Name: "it-1", Arches: []string{"i386", "ppc64el"}, VirtType: &pv, Mem: 512}, 264 }, 265 }, 266 { 267 desc: "prefer amd64 over arm64 (first 64-bit alphabetical wins)", 268 region: "test", 269 imageId: "ami-00000033", 270 arches: []string{"arm64", "amd64"}, 271 instanceTypes: []InstanceType{ 272 {Id: "1", Name: "it-1", Arches: []string{"arm64", "amd64"}, VirtType: &pv, Mem: 512}, 273 }, 274 }, 275 { 276 desc: "explicit release stream", 277 region: "test", 278 stream: "released", 279 imageId: "ami-00000035", 280 instanceTypes: []InstanceType{ 281 {Id: "1", Name: "it-1", Arches: []string{"amd64"}, VirtType: &hvm, Mem: 512, CpuCores: 2}, 282 }, 283 }, 284 { 285 desc: "non-release stream", 286 region: "test", 287 stream: "daily", 288 imageId: "ami-10000035", 289 instanceTypes: []InstanceType{ 290 {Id: "1", Name: "it-1", Arches: []string{"amd64"}, VirtType: &hvm, Mem: 512, CpuCores: 2}, 291 }, 292 }, 293 { 294 desc: "multiple images exists in metadata, use most recent", 295 region: "test", 296 imageId: "ami-00000035", 297 instanceTypes: []InstanceType{ 298 {Id: "1", Name: "it-1", Arches: []string{"amd64"}, VirtType: &hvm, Mem: 512, CpuCores: 2}, 299 }, 300 }, 301 { 302 desc: "empty instance type constraint", 303 region: "test", 304 constraints: "instance-type=", 305 imageId: "ami-00000033", 306 instanceTypes: []InstanceType{ 307 {Id: "1", Name: "it-1", Arches: []string{"amd64"}, VirtType: &pv, Mem: 512}, 308 }, 309 }, 310 { 311 desc: "use instance type constraint", 312 region: "test", 313 constraints: "instance-type=it-1", 314 imageId: "ami-00000035", 315 instanceTypes: []InstanceType{ 316 {Id: "1", Name: "it-1", Arches: []string{"amd64"}, VirtType: &hvm, Mem: 512, CpuCores: 2}, 317 {Id: "2", Name: "it-2", Arches: []string{"amd64"}, VirtType: &hvm, Mem: 1024, CpuCores: 2}, 318 }, 319 }, 320 { 321 desc: "instance type constraint, no matching instance types", 322 region: "test", 323 constraints: "instance-type=it-10", 324 instanceTypes: []InstanceType{ 325 {Id: "1", Name: "it-1", Arches: []string{"amd64"}, VirtType: &hvm, Mem: 512, CpuCores: 2}, 326 }, 327 err: `no instance types in test matching constraints "instance-type=it-10"`, 328 }, 329 { 330 desc: "no image exists in metadata", 331 region: "invalid-region", 332 err: `no "precise" images in invalid-region with arches \[amd64 armhf\]`, 333 }, 334 { 335 desc: "no valid instance types", 336 region: "test", 337 instanceTypes: []InstanceType{}, 338 err: `no instance types in test matching constraints ""`, 339 }, 340 { 341 desc: "no compatible instance types", 342 region: "arm-only", 343 instanceTypes: []InstanceType{{Id: "1", Name: "it-1", Arches: []string{"amd64"}, Mem: 2048}}, 344 err: `no "precise" images in arm-only matching instance types \[it-1\]`, 345 }, 346 } 347 348 func (s *imageSuite) TestFindInstanceSpec(c *gc.C) { 349 for _, t := range findInstanceSpecTests { 350 c.Logf("test: %v", t.desc) 351 t.init() 352 cons := imagemetadata.NewImageConstraint(simplestreams.LookupParams{ 353 CloudSpec: simplestreams.CloudSpec{t.region, "ep"}, 354 Series: []string{"precise"}, 355 Arches: t.arches, 356 Stream: t.stream, 357 }) 358 imageMeta, err := imagemetadata.GetLatestImageIdMetadata( 359 []byte(jsonImagesContent), 360 simplestreams.NewURLDataSource("test", "some-url", utils.VerifySSLHostnames, simplestreams.DEFAULT_CLOUD_DATA, false), cons) 361 c.Assert(err, jc.ErrorIsNil) 362 var images []Image 363 for _, imageMetadata := range imageMeta { 364 im := *imageMetadata 365 images = append(images, Image{ 366 Id: im.Id, 367 VirtType: im.VirtType, 368 Arch: im.Arch, 369 }) 370 } 371 imageCons := constraints.MustParse(t.constraints) 372 spec, err := FindInstanceSpec(images, &InstanceConstraint{ 373 Series: "precise", 374 Region: t.region, 375 Arches: t.arches, 376 Constraints: imageCons, 377 }, t.instanceTypes) 378 if t.err != "" { 379 c.Check(err, gc.ErrorMatches, t.err) 380 continue 381 } else { 382 if !c.Check(err, jc.ErrorIsNil) { 383 continue 384 } 385 c.Check(spec.Image.Id, gc.Equals, t.imageId) 386 if len(t.instanceTypes) == 1 { 387 c.Check(spec.InstanceType, gc.DeepEquals, t.instanceTypes[0]) 388 } 389 if imageCons.HasInstanceType() { 390 c.Assert(spec.InstanceType.Name, gc.Equals, *imageCons.InstanceType) 391 } 392 } 393 } 394 } 395 396 var imageMatchtests = []struct { 397 image Image 398 itype InstanceType 399 match imageMatch 400 }{ 401 { 402 image: Image{Arch: "amd64"}, 403 itype: InstanceType{Arches: []string{"amd64"}}, 404 match: exactMatch, 405 }, { 406 image: Image{Arch: "amd64"}, 407 itype: InstanceType{Arches: []string{"amd64", "armhf"}}, 408 match: exactMatch, 409 }, { 410 image: Image{Arch: "amd64", VirtType: hvm}, 411 itype: InstanceType{Arches: []string{"amd64"}, VirtType: &hvm}, 412 match: exactMatch, 413 }, { 414 image: Image{Arch: "armhf"}, 415 itype: InstanceType{Arches: []string{"amd64"}}, 416 }, { 417 image: Image{Arch: "amd64", VirtType: hvm}, 418 itype: InstanceType{Arches: []string{"amd64"}}, 419 match: exactMatch, 420 }, { 421 image: Image{Arch: "amd64"}, // no known virt type 422 itype: InstanceType{Arches: []string{"amd64"}, VirtType: &hvm}, 423 match: partialMatch, 424 }, { 425 image: Image{Arch: "amd64", VirtType: "pv"}, 426 itype: InstanceType{Arches: []string{"amd64"}, VirtType: &hvm}, 427 match: nonMatch, 428 }, 429 } 430 431 func (s *imageSuite) TestImageMatch(c *gc.C) { 432 for i, t := range imageMatchtests { 433 c.Logf("test %d", i) 434 c.Check(t.image.match(t.itype), gc.Equals, t.match) 435 } 436 } 437 438 func (*imageSuite) TestImageMetadataToImagesAcceptsNil(c *gc.C) { 439 c.Check(ImageMetadataToImages(nil), gc.HasLen, 0) 440 } 441 442 func (*imageSuite) TestImageMetadataToImagesConvertsSelectMetadata(c *gc.C) { 443 input := []*imagemetadata.ImageMetadata{ 444 { 445 Id: "id", 446 Storage: "storage-is-ignored", 447 VirtType: "vtype", 448 Arch: "arch", 449 RegionAlias: "region-alias-is-ignored", 450 RegionName: "region-name-is-ignored", 451 Endpoint: "endpoint-is-ignored", 452 }, 453 } 454 expectation := []Image{ 455 { 456 Id: "id", 457 VirtType: "vtype", 458 Arch: "arch", 459 }, 460 } 461 c.Check(ImageMetadataToImages(input), gc.DeepEquals, expectation) 462 } 463 464 func (*imageSuite) TestImageMetadataToImagesMaintainsOrdering(c *gc.C) { 465 input := []*imagemetadata.ImageMetadata{ 466 {Id: "one", Arch: "Z80"}, 467 {Id: "two", Arch: "i386"}, 468 {Id: "three", Arch: "amd64"}, 469 } 470 expectation := []Image{ 471 {Id: "one", Arch: "Z80"}, 472 {Id: "two", Arch: "i386"}, 473 {Id: "three", Arch: "amd64"}, 474 } 475 c.Check(ImageMetadataToImages(input), gc.DeepEquals, expectation) 476 } 477 478 func (*imageSuite) TestInstanceConstraintString(c *gc.C) { 479 imageCons := constraints.MustParse("mem=4G") 480 ic := &InstanceConstraint{ 481 Series: "precise", 482 Region: "region", 483 Arches: []string{"amd64", "arm64"}, 484 Constraints: imageCons, 485 } 486 c.Assert( 487 ic.String(), gc.Equals, 488 "{region: region, series: precise, arches: [amd64 arm64], constraints: mem=4096M, storage: []}") 489 490 ic.Storage = []string{"ebs", "ssd"} 491 c.Assert( 492 ic.String(), gc.Equals, 493 "{region: region, series: precise, arches: [amd64 arm64], constraints: mem=4096M, storage: [ebs ssd]}") 494 }