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  }