github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/provider/ec2/image_test.go (about)

     1  // Copyright 2011, 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package ec2
     5  
     6  import (
     7  	jc "github.com/juju/testing/checkers"
     8  	gc "gopkg.in/check.v1"
     9  
    10  	corebase "github.com/juju/juju/core/base"
    11  	"github.com/juju/juju/core/constraints"
    12  	"github.com/juju/juju/environs/imagemetadata"
    13  	"github.com/juju/juju/environs/instances"
    14  	"github.com/juju/juju/testing"
    15  	"github.com/juju/juju/version"
    16  )
    17  
    18  var _ = gc.Suite(&specSuite{})
    19  
    20  type specSuite struct {
    21  	testing.BaseSuite
    22  }
    23  
    24  var paravirtual = "pv"
    25  
    26  var testInstanceTypes = []instances.InstanceType{{
    27  	Name:     "t3a.xlarge",
    28  	CpuCores: 4,
    29  	CpuPower: instances.CpuPower(1400),
    30  	Mem:      16384,
    31  	Arch:     "amd64",
    32  	Cost:     172,
    33  }, {
    34  	Name:     "t3a.nano",
    35  	CpuCores: 2,
    36  	CpuPower: instances.CpuPower(700),
    37  	Mem:      512,
    38  	Arch:     "amd64",
    39  	Cost:     5,
    40  }, {
    41  	Name:     "t3a.micro",
    42  	CpuCores: 2,
    43  	CpuPower: instances.CpuPower(700),
    44  	Mem:      1024,
    45  	Arch:     "amd64",
    46  	Cost:     10,
    47  }, {
    48  	Name:     "t3a.small",
    49  	CpuCores: 2,
    50  	CpuPower: instances.CpuPower(700),
    51  	Mem:      2048,
    52  	Arch:     "amd64",
    53  	Cost:     21,
    54  }, {
    55  	Name:     "t3a.medium",
    56  	CpuCores: 2,
    57  	CpuPower: instances.CpuPower(700),
    58  	Mem:      4096,
    59  	Arch:     "amd64",
    60  	Cost:     43,
    61  }, {
    62  	Name:     "m1.medium",
    63  	CpuCores: 1,
    64  	CpuPower: instances.CpuPower(100),
    65  	Mem:      3840,
    66  	Arch:     "amd64",
    67  	VirtType: &paravirtual,
    68  	Cost:     117,
    69  }, {
    70  	Name:     "a1.xlarge",
    71  	CpuCores: 4,
    72  	CpuPower: instances.CpuPower(1288),
    73  	Mem:      8192,
    74  	Arch:     "arm64",
    75  }, {
    76  	Name:     "c4.large",
    77  	CpuCores: 2,
    78  	CpuPower: instances.CpuPower(811),
    79  	Mem:      3840,
    80  	Arch:     "amd64",
    81  	Cost:     114,
    82  }, {
    83  	Name:     "t2.medium",
    84  	CpuCores: 2,
    85  	CpuPower: instances.CpuPower(40),
    86  	Mem:      4096,
    87  	Arch:     "amd64",
    88  	Cost:     46,
    89  }, {
    90  	Name:     "c1.medium",
    91  	CpuCores: 2,
    92  	CpuPower: instances.CpuPower(200),
    93  	Mem:      1741,
    94  	Arch:     "amd64",
    95  	Cost:     164,
    96  }, {
    97  	Name:     "cc2.8xlarge",
    98  	CpuCores: 32,
    99  	CpuPower: instances.CpuPower(11647),
   100  	Mem:      61952,
   101  	Arch:     "amd64",
   102  	Cost:     2250,
   103  }, {
   104  	Name:     "r5a.large",
   105  	CpuCores: 2,
   106  	CpuPower: instances.CpuPower(700),
   107  	Mem:      16384,
   108  	Arch:     "amd64",
   109  	Cost:     137,
   110  }}
   111  
   112  var findInstanceSpecTests = []struct {
   113  	// LTS-dependent requires new or updated entries upon a new LTS release.
   114  	version string
   115  	arch    string
   116  	cons    string
   117  	itype   string
   118  	image   string
   119  	storage []string
   120  }{
   121  	{
   122  		version: "18.04",
   123  		arch:    "amd64",
   124  		itype:   "t3a.small",
   125  		image:   "ami-00001133",
   126  	}, {
   127  		version: "18.04",
   128  		arch:    "amd64",
   129  		cons:    "cores=4",
   130  		itype:   "t3a.xlarge",
   131  		image:   "ami-00001133",
   132  	}, {
   133  		version: "20.04",
   134  		arch:    "arm64",
   135  		cons:    "cores=4",
   136  		itype:   "a1.xlarge",
   137  		image:   "ami-02004133",
   138  	}, {
   139  		version: "18.04",
   140  		arch:    "amd64",
   141  		cons:    "mem=10G",
   142  		itype:   "r5a.large",
   143  		image:   "ami-00001133",
   144  	}, {
   145  		version: "18.04",
   146  		arch:    "amd64",
   147  		cons:    "mem=",
   148  		itype:   "t3a.small",
   149  		image:   "ami-00001133",
   150  	}, {
   151  		version: "18.04",
   152  		arch:    "amd64",
   153  		cons:    "cpu-power=",
   154  		itype:   "t3a.small",
   155  		image:   "ami-00001133",
   156  	}, {
   157  		version: "18.04",
   158  		arch:    "amd64",
   159  		cons:    "cpu-power=800",
   160  		itype:   "c4.large",
   161  		image:   "ami-00001133",
   162  	}, {
   163  		version: "18.04",
   164  		arch:    "amd64",
   165  		cons:    "instance-type=m1.medium cpu-power=100",
   166  		itype:   "m1.medium",
   167  		image:   "ami-00001135",
   168  	}, {
   169  		version: "18.04",
   170  		arch:    "amd64",
   171  		cons:    "mem=2G root-disk=16384M",
   172  		itype:   "t3a.small",
   173  		image:   "ami-00001133",
   174  	}, {
   175  		version: "18.04",
   176  		arch:    "amd64",
   177  		cons:    "mem=4G root-disk=16384M",
   178  		itype:   "t3a.medium",
   179  		storage: []string{"ssd", "ebs"},
   180  		image:   "ami-00001133",
   181  	}, {
   182  		version: "18.04",
   183  		arch:    "amd64",
   184  		cons:    "mem=4G root-disk=16384M",
   185  		itype:   "t3a.medium",
   186  		storage: []string{"ebs", "ssd"},
   187  		image:   "ami-00001139",
   188  	}, {
   189  		version: "18.04",
   190  		arch:    "amd64",
   191  		cons:    "mem=4G root-disk=16384M",
   192  		itype:   "t3a.medium",
   193  		storage: []string{"ebs"},
   194  		image:   "ami-00001139",
   195  	}, {
   196  		version: "22.04",
   197  		arch:    "amd64",
   198  		itype:   "t3a.small",
   199  		image:   "ami-02204133",
   200  	}, {
   201  		version: "20.04",
   202  		arch:    "amd64",
   203  		cons:    "arch=amd64",
   204  		itype:   "t3a.small",
   205  		image:   "ami-02004133",
   206  	}, {
   207  		version: "20.04",
   208  		arch:    "amd64",
   209  		cons:    "instance-type=cc2.8xlarge",
   210  		itype:   "cc2.8xlarge",
   211  		image:   "ami-02004133",
   212  	},
   213  }
   214  
   215  func (s *specSuite) TestFindInstanceSpec(c *gc.C) {
   216  	size := len(findInstanceSpecTests)
   217  	for i, test := range findInstanceSpecTests {
   218  		c.Logf("\ntest %d of %d: %q; %q; %q; %q; %q; %v", i+1, size,
   219  			test.version, test.arch, test.cons, test.itype, test.image,
   220  			test.storage)
   221  		stor := test.storage
   222  		if len(stor) == 0 {
   223  			stor = []string{ssdStorage, ebsStorage}
   224  		}
   225  		// We need to filter the image metadata to the test's
   226  		// arches and series; the provisioner and bootstrap
   227  		// code will do this.
   228  		imageMetadata := filterImageMetadata(
   229  			c, TestImageMetadata, test.version, test.arch,
   230  		)
   231  		base := corebase.MakeDefaultBase("ubuntu", test.version)
   232  		spec, err := findInstanceSpec(
   233  			false, // non-controller
   234  			imageMetadata,
   235  			testInstanceTypes,
   236  			&instances.InstanceConstraint{
   237  				Region:      "test",
   238  				Base:        base,
   239  				Arch:        test.arch,
   240  				Constraints: constraints.MustParse(test.cons),
   241  				Storage:     stor,
   242  			})
   243  		c.Assert(err, jc.ErrorIsNil)
   244  		c.Check(spec.InstanceType.Name, gc.Equals, test.itype)
   245  		c.Check(spec.Image.Id, gc.Equals, test.image)
   246  	}
   247  }
   248  
   249  func (s *specSuite) TestFindInstanceSpecNotSetCpuPowerWhenInstanceTypeSet(c *gc.C) {
   250  	instanceConstraint := &instances.InstanceConstraint{
   251  		Region:      "test",
   252  		Base:        version.DefaultSupportedLTSBase(),
   253  		Constraints: constraints.MustParse("instance-type=t2.medium"),
   254  	}
   255  
   256  	c.Check(instanceConstraint.Constraints.CpuPower, gc.IsNil)
   257  	_, err := findInstanceSpec(
   258  		false, // non-controller
   259  		TestImageMetadata,
   260  		testInstanceTypes,
   261  		instanceConstraint,
   262  	)
   263  	c.Assert(err, jc.ErrorIsNil)
   264  
   265  	c.Check(instanceConstraint.Constraints.CpuPower, gc.IsNil)
   266  }
   267  
   268  var findInstanceSpecErrorTests = []struct {
   269  	base corebase.Base
   270  	arch string
   271  	cons string
   272  	err  string
   273  }{
   274  	{
   275  		base: version.DefaultSupportedLTSBase(),
   276  		arch: "arm",
   277  		err:  `no metadata for "ubuntu@22.04" images in test with arch arm`,
   278  	}, {
   279  		base: corebase.MakeDefaultBase("ubuntu", "15.04"),
   280  		arch: "amd64",
   281  		cons: "mem=4G",
   282  		err:  `no metadata for \"ubuntu@15.04\" images in test with arch amd64`,
   283  	}, {
   284  		base: version.DefaultSupportedLTSBase(),
   285  		arch: "amd64",
   286  		cons: "instance-type=m1.small mem=4G",
   287  		err:  `no instance types in test matching constraints "arch=amd64 instance-type=m1.small mem=4096M"`,
   288  	},
   289  }
   290  
   291  func (s *specSuite) TestFindInstanceSpecErrors(c *gc.C) {
   292  	for i, t := range findInstanceSpecErrorTests {
   293  		c.Logf("test %d", i)
   294  		// We need to filter the image metadata to the test's
   295  		// arches and series; the provisioner and bootstrap
   296  		// code will do this.
   297  		imageMetadata := filterImageMetadata(
   298  			c, TestImageMetadata, t.base.Channel.Track, t.arch,
   299  		)
   300  		_, err := findInstanceSpec(
   301  			false, // non-controller
   302  			imageMetadata,
   303  			testInstanceTypes,
   304  			&instances.InstanceConstraint{
   305  				Region:      "test",
   306  				Base:        t.base,
   307  				Arch:        t.arch,
   308  				Constraints: constraints.MustParse(t.cons),
   309  			})
   310  		c.Check(err, gc.ErrorMatches, t.err)
   311  	}
   312  }
   313  
   314  func filterImageMetadata(
   315  	c *gc.C,
   316  	in []*imagemetadata.ImageMetadata,
   317  	filterVersion string, filterArch string,
   318  ) []*imagemetadata.ImageMetadata {
   319  	var imageMetadata []*imagemetadata.ImageMetadata
   320  	for _, im := range in {
   321  		if im.Version != filterVersion {
   322  			continue
   323  		}
   324  		if im.Arch == filterArch {
   325  			imageMetadata = append(imageMetadata, im)
   326  		}
   327  	}
   328  	return imageMetadata
   329  }
   330  
   331  func (*specSuite) TestFilterImagesAcceptsNil(c *gc.C) {
   332  	c.Check(filterImages(nil, nil), gc.HasLen, 0)
   333  }
   334  
   335  func (*specSuite) TestFilterImagesReturnsSelectively(c *gc.C) {
   336  	good := imagemetadata.ImageMetadata{Id: "good", Storage: "ebs"}
   337  	bad := imagemetadata.ImageMetadata{Id: "bad", Storage: "ftp"}
   338  	input := []*imagemetadata.ImageMetadata{&good, &bad}
   339  	expectation := []*imagemetadata.ImageMetadata{&good}
   340  
   341  	ic := &instances.InstanceConstraint{Storage: []string{"ebs"}}
   342  	c.Check(filterImages(input, ic), gc.DeepEquals, expectation)
   343  }
   344  
   345  func (*specSuite) TestFilterImagesMaintainsOrdering(c *gc.C) {
   346  	input := []*imagemetadata.ImageMetadata{
   347  		{Id: "one", Storage: "ebs"},
   348  		{Id: "two", Storage: "ebs"},
   349  		{Id: "three", Storage: "ebs"},
   350  	}
   351  	ic := &instances.InstanceConstraint{Storage: []string{"ebs"}}
   352  	c.Check(filterImages(input, ic), gc.DeepEquals, input)
   353  }