github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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/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": "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  }