github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/environs/simplestreams/simplestreams_test.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package simplestreams_test 5 6 import ( 7 "strings" 8 "testing" 9 10 jc "github.com/juju/testing/checkers" 11 "github.com/juju/utils" 12 gc "gopkg.in/check.v1" 13 14 "github.com/juju/juju/environs/simplestreams" 15 sstesting "github.com/juju/juju/environs/simplestreams/testing" 16 ) 17 18 func Test(t *testing.T) { 19 registerSimpleStreamsTests() 20 gc.Suite(&jsonSuite{}) 21 gc.TestingT(t) 22 } 23 24 func registerSimpleStreamsTests() { 25 gc.Suite(&simplestreamsSuite{ 26 LocalLiveSimplestreamsSuite: sstesting.LocalLiveSimplestreamsSuite{ 27 Source: simplestreams.NewURLDataSource("test", "test:", utils.VerifySSLHostnames, simplestreams.DEFAULT_CLOUD_DATA, false), 28 RequireSigned: false, 29 DataType: "image-ids", 30 StreamsVersion: "v1", 31 ValidConstraint: sstesting.NewTestConstraint(simplestreams.LookupParams{ 32 CloudSpec: simplestreams.CloudSpec{ 33 Region: "us-east-1", 34 Endpoint: "https://ec2.us-east-1.amazonaws.com", 35 }, 36 Series: []string{"precise"}, 37 Arches: []string{"amd64", "arm"}, 38 }), 39 }, 40 }) 41 } 42 43 type simplestreamsSuite struct { 44 sstesting.TestDataSuite 45 sstesting.LocalLiveSimplestreamsSuite 46 } 47 48 func (s *simplestreamsSuite) SetUpSuite(c *gc.C) { 49 s.LocalLiveSimplestreamsSuite.SetUpSuite(c) 50 s.TestDataSuite.SetUpSuite(c) 51 } 52 53 func (s *simplestreamsSuite) TearDownSuite(c *gc.C) { 54 s.TestDataSuite.TearDownSuite(c) 55 s.LocalLiveSimplestreamsSuite.TearDownSuite(c) 56 } 57 58 func (s *simplestreamsSuite) TestGetProductsPath(c *gc.C) { 59 indexRef, err := s.GetIndexRef(sstesting.Index_v1) 60 c.Assert(err, jc.ErrorIsNil) 61 path, err := indexRef.GetProductsPath(s.ValidConstraint) 62 c.Assert(err, jc.ErrorIsNil) 63 c.Assert(path, gc.Equals, "streams/v1/image_metadata.json") 64 } 65 66 func (*simplestreamsSuite) TestExtractCatalogsForProductsAcceptsNil(c *gc.C) { 67 empty := simplestreams.CloudMetadata{} 68 c.Check(simplestreams.ExtractCatalogsForProducts(empty, nil), gc.HasLen, 0) 69 } 70 71 func (*simplestreamsSuite) TestExtractCatalogsForProductsReturnsMatch(c *gc.C) { 72 metadata := simplestreams.CloudMetadata{ 73 Products: map[string]simplestreams.MetadataCatalog{ 74 "foo": {}, 75 }, 76 } 77 c.Check( 78 simplestreams.ExtractCatalogsForProducts(metadata, []string{"foo"}), 79 gc.DeepEquals, 80 []simplestreams.MetadataCatalog{metadata.Products["foo"]}) 81 } 82 83 func (*simplestreamsSuite) TestExtractCatalogsForProductsIgnoresNonMatches(c *gc.C) { 84 metadata := simplestreams.CloudMetadata{ 85 Products: map[string]simplestreams.MetadataCatalog{ 86 "one-product": {}, 87 }, 88 } 89 absentProducts := []string{"another-product"} 90 c.Check(simplestreams.ExtractCatalogsForProducts(metadata, absentProducts), gc.HasLen, 0) 91 } 92 93 func (*simplestreamsSuite) TestExtractCatalogsForProductsPreservesOrder(c *gc.C) { 94 products := map[string]simplestreams.MetadataCatalog{ 95 "1": {}, 96 "2": {}, 97 "3": {}, 98 "4": {}, 99 } 100 101 metadata := simplestreams.CloudMetadata{Products: products} 102 103 c.Check( 104 simplestreams.ExtractCatalogsForProducts(metadata, []string{"1", "3", "4", "2"}), 105 gc.DeepEquals, 106 []simplestreams.MetadataCatalog{ 107 products["1"], 108 products["3"], 109 products["4"], 110 products["2"], 111 }) 112 } 113 114 func (*simplestreamsSuite) TestExtractIndexesAcceptsEmpty(c *gc.C) { 115 ind := simplestreams.Indices{} 116 c.Check(simplestreams.ExtractIndexes(ind, nil), gc.HasLen, 0) 117 } 118 119 func (*simplestreamsSuite) TestExtractIndexesReturnsIndex(c *gc.C) { 120 metadata := simplestreams.IndexMetadata{} 121 ind := simplestreams.Indices{Indexes: map[string]*simplestreams.IndexMetadata{"foo": &metadata}} 122 c.Check(simplestreams.ExtractIndexes(ind, nil), gc.DeepEquals, simplestreams.IndexMetadataSlice{&metadata}) 123 } 124 125 func (*simplestreamsSuite) TestExtractIndexesReturnsAllIndexes(c *gc.C) { 126 ind := simplestreams.Indices{ 127 Indexes: map[string]*simplestreams.IndexMetadata{ 128 "foo": {}, 129 "bar": {}, 130 }, 131 } 132 133 array := simplestreams.ExtractIndexes(ind, nil) 134 135 c.Assert(array, gc.HasLen, len(ind.Indexes)) 136 c.Check(array[0], gc.NotNil) 137 c.Check(array[1], gc.NotNil) 138 c.Check(array[0], gc.Not(gc.Equals), array[1]) 139 c.Check( 140 (array[0] == ind.Indexes["foo"]), 141 gc.Not(gc.Equals), 142 (array[1] == ind.Indexes["foo"])) 143 c.Check( 144 (array[0] == ind.Indexes["bar"]), 145 gc.Not(gc.Equals), 146 (array[1] == ind.Indexes["bar"])) 147 } 148 149 func (*simplestreamsSuite) TestExtractIndexesReturnsSpecifiedIndexes(c *gc.C) { 150 ind := simplestreams.Indices{ 151 Indexes: map[string]*simplestreams.IndexMetadata{ 152 "foo": {}, 153 "bar": {}, 154 "foobar": {}, 155 }, 156 } 157 158 array := simplestreams.ExtractIndexes(ind, []string{"foobar"}) 159 c.Assert(array, gc.HasLen, 1) 160 c.Assert(array[0], gc.Equals, ind.Indexes["foobar"]) 161 } 162 163 func (*simplestreamsSuite) TestHasCloudAcceptsNil(c *gc.C) { 164 metadata := simplestreams.IndexMetadata{Clouds: nil} 165 c.Check(simplestreams.HasCloud(metadata, simplestreams.CloudSpec{}), jc.IsTrue) 166 } 167 168 func (*simplestreamsSuite) TestHasCloudFindsMatch(c *gc.C) { 169 metadata := simplestreams.IndexMetadata{ 170 Clouds: []simplestreams.CloudSpec{ 171 {Region: "r1", Endpoint: "http://e1"}, 172 {Region: "r2", Endpoint: "http://e2"}, 173 }, 174 } 175 c.Check(simplestreams.HasCloud(metadata, metadata.Clouds[1]), jc.IsTrue) 176 } 177 178 func (*simplestreamsSuite) TestHasCloudFindsMatchWithTrailingSlash(c *gc.C) { 179 metadata := simplestreams.IndexMetadata{ 180 Clouds: []simplestreams.CloudSpec{ 181 {Region: "r1", Endpoint: "http://e1/"}, 182 {Region: "r2", Endpoint: "http://e2"}, 183 }, 184 } 185 spec := simplestreams.CloudSpec{Region: "r1", Endpoint: "http://e1"} 186 c.Check(simplestreams.HasCloud(metadata, spec), jc.IsTrue) 187 spec = simplestreams.CloudSpec{Region: "r1", Endpoint: "http://e1/"} 188 c.Check(simplestreams.HasCloud(metadata, spec), jc.IsTrue) 189 spec = simplestreams.CloudSpec{Region: "r2", Endpoint: "http://e2/"} 190 c.Check(simplestreams.HasCloud(metadata, spec), jc.IsTrue) 191 } 192 193 func (*simplestreamsSuite) TestHasCloudReturnsFalseIfCloudsDoNotMatch(c *gc.C) { 194 metadata := simplestreams.IndexMetadata{ 195 Clouds: []simplestreams.CloudSpec{ 196 {Region: "r1", Endpoint: "http://e1"}, 197 {Region: "r2", Endpoint: "http://e2"}, 198 }, 199 } 200 otherCloud := simplestreams.CloudSpec{Region: "r9", Endpoint: "http://e9"} 201 c.Check(simplestreams.HasCloud(metadata, otherCloud), jc.IsFalse) 202 } 203 204 func (*simplestreamsSuite) TestHasCloudRequiresIdenticalRegion(c *gc.C) { 205 metadata := simplestreams.IndexMetadata{ 206 Clouds: []simplestreams.CloudSpec{ 207 {Region: "around", Endpoint: "http://nearby"}, 208 }, 209 } 210 similarCloud := metadata.Clouds[0] 211 similarCloud.Region = "elsewhere" 212 c.Assert(similarCloud, gc.Not(gc.Equals), metadata.Clouds[0]) 213 214 c.Check(simplestreams.HasCloud(metadata, similarCloud), jc.IsFalse) 215 } 216 217 func (*simplestreamsSuite) TestHasCloudRequiresIdenticalEndpoint(c *gc.C) { 218 metadata := simplestreams.IndexMetadata{ 219 Clouds: []simplestreams.CloudSpec{ 220 {Region: "around", Endpoint: "http://nearby"}, 221 }, 222 } 223 similarCloud := metadata.Clouds[0] 224 similarCloud.Endpoint = "http://far" 225 c.Assert(similarCloud, gc.Not(gc.Equals), metadata.Clouds[0]) 226 227 c.Check(simplestreams.HasCloud(metadata, similarCloud), jc.IsFalse) 228 } 229 230 func (*simplestreamsSuite) TestHasProductAcceptsNils(c *gc.C) { 231 metadata := simplestreams.IndexMetadata{} 232 c.Check(simplestreams.HasProduct(metadata, nil), jc.IsFalse) 233 } 234 235 func (*simplestreamsSuite) TestHasProductFindsMatchingProduct(c *gc.C) { 236 metadata := simplestreams.IndexMetadata{ProductIds: []string{"x", "y", "z"}} 237 c.Check( 238 simplestreams.HasProduct(metadata, []string{"a", "b", metadata.ProductIds[1]}), 239 gc.Equals, 240 true) 241 } 242 243 func (*simplestreamsSuite) TestHasProductReturnsFalseIfProductsDoNotMatch(c *gc.C) { 244 metadata := simplestreams.IndexMetadata{ProductIds: []string{"x", "y", "z"}} 245 c.Check(simplestreams.HasProduct(metadata, []string{"a", "b", "c"}), jc.IsFalse) 246 } 247 248 func (*simplestreamsSuite) TestFilterReturnsNothingForEmptyArray(c *gc.C) { 249 empty := simplestreams.IndexMetadataSlice{} 250 c.Check( 251 simplestreams.Filter(empty, func(*simplestreams.IndexMetadata) bool { return true }), 252 gc.HasLen, 253 0) 254 } 255 256 func (*simplestreamsSuite) TestFilterRemovesNonMatches(c *gc.C) { 257 array := simplestreams.IndexMetadataSlice{&simplestreams.IndexMetadata{}} 258 c.Check( 259 simplestreams.Filter(array, func(*simplestreams.IndexMetadata) bool { return false }), 260 gc.HasLen, 261 0) 262 } 263 264 func (*simplestreamsSuite) TestFilterIncludesMatches(c *gc.C) { 265 metadata := simplestreams.IndexMetadata{} 266 array := simplestreams.IndexMetadataSlice{&metadata} 267 c.Check( 268 simplestreams.Filter(array, func(*simplestreams.IndexMetadata) bool { return true }), 269 gc.DeepEquals, 270 simplestreams.IndexMetadataSlice{&metadata}) 271 } 272 273 func (*simplestreamsSuite) TestFilterLeavesOriginalUnchanged(c *gc.C) { 274 item1 := simplestreams.IndexMetadata{CloudName: "aws"} 275 item2 := simplestreams.IndexMetadata{CloudName: "openstack"} 276 array := simplestreams.IndexMetadataSlice{&item1, &item2} 277 278 result := simplestreams.Filter(array, func(metadata *simplestreams.IndexMetadata) bool { 279 return metadata.CloudName == "aws" 280 }) 281 // This exercises both the "leave out" and the "include" code paths. 282 c.Assert(result, gc.HasLen, 1) 283 284 // The original, however, has not changed. 285 c.Assert(array, gc.HasLen, 2) 286 c.Check(array, gc.DeepEquals, simplestreams.IndexMetadataSlice{&item1, &item2}) 287 } 288 289 func (*simplestreamsSuite) TestFilterPreservesOrder(c *gc.C) { 290 array := simplestreams.IndexMetadataSlice{ 291 &simplestreams.IndexMetadata{CloudName: "aws"}, 292 &simplestreams.IndexMetadata{CloudName: "maas"}, 293 &simplestreams.IndexMetadata{CloudName: "openstack"}, 294 } 295 296 c.Check( 297 simplestreams.Filter(array, func(metadata *simplestreams.IndexMetadata) bool { return true }), 298 gc.DeepEquals, 299 array) 300 } 301 302 func (*simplestreamsSuite) TestFilterCombinesMatchesAndNonMatches(c *gc.C) { 303 array := simplestreams.IndexMetadataSlice{ 304 &simplestreams.IndexMetadata{Format: "1.0"}, 305 &simplestreams.IndexMetadata{Format: "1.1"}, 306 &simplestreams.IndexMetadata{Format: "2.0"}, 307 &simplestreams.IndexMetadata{Format: "2.1"}, 308 } 309 310 dotOFormats := simplestreams.Filter(array, func(metadata *simplestreams.IndexMetadata) bool { 311 return strings.HasSuffix(metadata.Format, ".0") 312 }) 313 314 c.Check(dotOFormats, gc.DeepEquals, simplestreams.IndexMetadataSlice{array[0], array[2]}) 315 } 316 317 // countingSource is used to check that a DataSource has been queried. 318 type countingSource struct { 319 simplestreams.DataSource 320 count int 321 } 322 323 func (s *countingSource) URL(path string) (string, error) { 324 s.count++ 325 return s.DataSource.URL(path) 326 } 327 328 func (s *simplestreamsSuite) TestGetMetadataNoMatching(c *gc.C) { 329 source := &countingSource{ 330 DataSource: simplestreams.NewURLDataSource( 331 "test", 332 "test:/daily", 333 utils.VerifySSLHostnames, 334 simplestreams.DEFAULT_CLOUD_DATA, 335 false, 336 ), 337 } 338 sources := []simplestreams.DataSource{source, source, source} 339 constraint := sstesting.NewTestConstraint(simplestreams.LookupParams{ 340 CloudSpec: simplestreams.CloudSpec{ 341 Region: "us-east-1", 342 Endpoint: "https://ec2.us-east-1.amazonaws.com", 343 }, 344 Series: []string{"precise"}, 345 Arches: []string{"not-a-real-arch"}, // never matches 346 }) 347 params := simplestreams.GetMetadataParams{ 348 StreamsVersion: s.StreamsVersion, 349 LookupConstraint: constraint, 350 ValueParams: simplestreams.ValueParams{DataType: "image-ids"}, 351 } 352 353 items, resolveInfo, err := simplestreams.GetMetadata(sources, params) 354 c.Assert(err, jc.ErrorIsNil) 355 c.Assert(items, gc.HasLen, 0) 356 c.Assert(resolveInfo, gc.DeepEquals, &simplestreams.ResolveInfo{ 357 Source: "test", 358 Signed: false, 359 IndexURL: "test:/daily/streams/v1/index.json", 360 MirrorURL: "", 361 }) 362 363 // There should be 4 calls to each data-source: 364 // one for .sjson, one for .json, repeated for legacy vs new index files. 365 c.Assert(source.count, gc.Equals, 4*len(sources)) 366 } 367 368 func (s *simplestreamsSuite) TestMetadataCatalog(c *gc.C) { 369 metadata := s.AssertGetMetadata(c) 370 c.Check(len(metadata.Products), gc.Equals, 6) 371 c.Check(len(metadata.Aliases), gc.Equals, 1) 372 metadataCatalog := metadata.Products["com.ubuntu.cloud:server:12.04:amd64"] 373 c.Check(len(metadataCatalog.Items), gc.Equals, 2) 374 c.Check(metadataCatalog.Series, gc.Equals, "precise") 375 c.Check(metadataCatalog.Version, gc.Equals, "12.04") 376 c.Check(metadataCatalog.Arch, gc.Equals, "amd64") 377 c.Check(metadataCatalog.RegionName, gc.Equals, "au-east-1") 378 c.Check(metadataCatalog.Endpoint, gc.Equals, "https://somewhere") 379 } 380 381 func (s *simplestreamsSuite) TestItemCollection(c *gc.C) { 382 ic := s.AssertGetItemCollections(c, "20121218") 383 c.Check(ic.RegionName, gc.Equals, "au-east-2") 384 c.Check(ic.Endpoint, gc.Equals, "https://somewhere-else") 385 c.Assert(len(ic.Items) > 0, jc.IsTrue) 386 ti := ic.Items["usww2he"].(*sstesting.TestItem) 387 c.Check(ti.Id, gc.Equals, "ami-442ea674") 388 c.Check(ti.Storage, gc.Equals, "ebs") 389 c.Check(ti.VirtType, gc.Equals, "hvm") 390 c.Check(ti.RegionName, gc.Equals, "us-east-1") 391 c.Check(ti.Endpoint, gc.Equals, "https://ec2.us-east-1.amazonaws.com") 392 } 393 394 func (s *simplestreamsSuite) TestDenormalisationFromCollection(c *gc.C) { 395 ic := s.AssertGetItemCollections(c, "20121218") 396 ti := ic.Items["usww1pe"].(*sstesting.TestItem) 397 c.Check(ti.RegionName, gc.Equals, ic.RegionName) 398 c.Check(ti.Endpoint, gc.Equals, ic.Endpoint) 399 } 400 401 func (s *simplestreamsSuite) TestDenormalisationFromCatalog(c *gc.C) { 402 metadata := s.AssertGetMetadata(c) 403 metadataCatalog := metadata.Products["com.ubuntu.cloud:server:12.04:amd64"] 404 ic := metadataCatalog.Items["20111111"] 405 ti := ic.Items["usww3pe"].(*sstesting.TestItem) 406 c.Check(ti.RegionName, gc.Equals, metadataCatalog.RegionName) 407 c.Check(ti.Endpoint, gc.Equals, metadataCatalog.Endpoint) 408 } 409 410 func (s *simplestreamsSuite) TestDenormalisationFromTopLevel(c *gc.C) { 411 metadata := s.AssertGetMetadata(c) 412 metadataCatalog := metadata.Products["com.ubuntu.cloud:server:14.04:amd64"] 413 ic := metadataCatalog.Items["20140118"] 414 ti := ic.Items["nzww1pe"].(*sstesting.TestItem) 415 c.Check(ti.RegionName, gc.Equals, metadata.RegionName) 416 c.Check(ti.Endpoint, gc.Equals, metadata.Endpoint) 417 } 418 419 func (s *simplestreamsSuite) TestDealiasing(c *gc.C) { 420 metadata := s.AssertGetMetadata(c) 421 metadataCatalog := metadata.Products["com.ubuntu.cloud:server:12.04:amd64"] 422 ic := metadataCatalog.Items["20121218"] 423 ti := ic.Items["usww3he"].(*sstesting.TestItem) 424 c.Check(ti.RegionName, gc.Equals, "us-west-3") 425 c.Check(ti.Endpoint, gc.Equals, "https://ec2.us-west-3.amazonaws.com") 426 } 427 428 type storageVirtTest struct { 429 product, coll, item, storage, virt string 430 } 431 432 func (s *simplestreamsSuite) TestStorageVirtFromTopLevel(c *gc.C) { 433 s.assertImageMetadata(c, 434 storageVirtTest{"com.ubuntu.cloud:server:13.04:amd64", "20160318", "nzww1pe", "ebs", "pv"}, 435 ) 436 } 437 438 func (s *simplestreamsSuite) TestStorageVirtFromCatalog(c *gc.C) { 439 s.assertImageMetadata(c, 440 storageVirtTest{"com.ubuntu.cloud:server:14.10:amd64", "20160218", "nzww1pe", "ebs", "pv"}, 441 ) 442 } 443 444 func (s *simplestreamsSuite) TestStorageVirtFromCollection(c *gc.C) { 445 s.assertImageMetadata(c, 446 storageVirtTest{"com.ubuntu.cloud:server:12.10:amd64", "20160118", "nzww1pe", "ebs", "pv"}, 447 ) 448 } 449 450 func (s *simplestreamsSuite) TestStorageVirtFromItem(c *gc.C) { 451 s.assertImageMetadata(c, 452 storageVirtTest{"com.ubuntu.cloud:server:14.04:amd64", "20140118", "nzww1pe", "ssd", "hvm"}, 453 ) 454 } 455 456 func (s *simplestreamsSuite) assertImageMetadata(c *gc.C, one storageVirtTest) { 457 metadata := s.AssertGetMetadata(c) 458 metadataCatalog := metadata.Products[one.product] 459 ic := metadataCatalog.Items[one.coll] 460 ti := ic.Items[one.item].(*sstesting.TestItem) 461 c.Check(ti.Storage, gc.Equals, one.storage) 462 c.Check(ti.VirtType, gc.Equals, one.virt) 463 } 464 465 var getMirrorTests = []struct { 466 region string 467 endpoint string 468 err string 469 mirrorURL string 470 path string 471 }{{ 472 // defaults 473 mirrorURL: "http://some-mirror/", 474 path: "com.ubuntu.juju:download.json", 475 }, { 476 // default mirror index entry 477 region: "some-region", 478 endpoint: "https://some-endpoint.com", 479 mirrorURL: "http://big-mirror/", 480 path: "big:download.json", 481 }, { 482 // endpoint with trailing "/" 483 region: "some-region", 484 endpoint: "https://some-endpoint.com/", 485 mirrorURL: "http://big-mirror/", 486 path: "big:download.json", 487 }} 488 489 func (s *simplestreamsSuite) TestGetMirrorMetadata(c *gc.C) { 490 for i, t := range getMirrorTests { 491 c.Logf("test %d", i) 492 if t.region == "" { 493 t.region = "us-east-2" 494 } 495 if t.endpoint == "" { 496 t.endpoint = "https://ec2.us-east-2.amazonaws.com" 497 } 498 cloud := simplestreams.CloudSpec{t.region, t.endpoint} 499 params := simplestreams.ValueParams{ 500 DataType: "content-download", 501 MirrorContentId: "com.ubuntu.juju:released:tools", 502 } 503 indexRef, err := simplestreams.GetIndexWithFormat( 504 s.Source, s.IndexPath(), sstesting.Index_v1, 505 simplestreams.MirrorsPath("v1"), s.RequireSigned, cloud, params) 506 if !c.Check(err, jc.ErrorIsNil) { 507 continue 508 } 509 if t.err != "" { 510 c.Check(err, gc.ErrorMatches, t.err) 511 continue 512 } 513 if !c.Check(err, jc.ErrorIsNil) { 514 continue 515 } 516 mirrorURL, err := indexRef.Source.URL("") 517 if !c.Check(err, jc.ErrorIsNil) { 518 continue 519 } 520 c.Check(mirrorURL, gc.Equals, t.mirrorURL) 521 c.Check(indexRef.MirroredProductsPath, gc.Equals, t.path) 522 } 523 }