github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/environs/instances/instancetype_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  	"sort"
     8  
     9  	jc "github.com/juju/testing/checkers"
    10  	gc "gopkg.in/check.v1"
    11  
    12  	"github.com/juju/juju/constraints"
    13  	"github.com/juju/juju/testing"
    14  )
    15  
    16  type instanceTypeSuite struct {
    17  	testing.BaseSuite
    18  }
    19  
    20  var _ = gc.Suite(&instanceTypeSuite{})
    21  
    22  var hvm = "hvm"
    23  
    24  // The instance types below do not necessarily reflect reality and are just
    25  // defined here for ease of testing special cases.
    26  var instanceTypes = []InstanceType{
    27  	{
    28  		Name:     "m1.small",
    29  		Arches:   []string{"amd64", "armhf"},
    30  		CpuCores: 1,
    31  		CpuPower: CpuPower(100),
    32  		Mem:      1740,
    33  		Cost:     60,
    34  		RootDisk: 8192,
    35  	}, {
    36  		Name:     "m1.medium",
    37  		Arches:   []string{"amd64", "armhf"},
    38  		CpuCores: 1,
    39  		CpuPower: CpuPower(200),
    40  		Mem:      3840,
    41  		Cost:     120,
    42  		RootDisk: 16384,
    43  	}, {
    44  		Name:     "m1.large",
    45  		Arches:   []string{"amd64"},
    46  		CpuCores: 2,
    47  		CpuPower: CpuPower(400),
    48  		Mem:      7680,
    49  		Cost:     240,
    50  		RootDisk: 32768,
    51  	}, {
    52  		Name:     "m1.xlarge",
    53  		Arches:   []string{"amd64"},
    54  		CpuCores: 4,
    55  		CpuPower: CpuPower(800),
    56  		Mem:      15360,
    57  		Cost:     480,
    58  	},
    59  	{
    60  		Name:     "t1.micro",
    61  		Arches:   []string{"amd64", "armhf"},
    62  		CpuCores: 1,
    63  		CpuPower: CpuPower(20),
    64  		Mem:      613,
    65  		Cost:     20,
    66  		RootDisk: 4096,
    67  	},
    68  	{
    69  		Name:     "c1.medium",
    70  		Arches:   []string{"amd64", "armhf"},
    71  		CpuCores: 2,
    72  		CpuPower: CpuPower(500),
    73  		Mem:      1740,
    74  		Cost:     145,
    75  		RootDisk: 8192,
    76  	}, {
    77  		Name:     "c1.xlarge",
    78  		Arches:   []string{"amd64"},
    79  		CpuCores: 8,
    80  		CpuPower: CpuPower(2000),
    81  		Mem:      7168,
    82  		Cost:     580,
    83  	},
    84  	{
    85  		Name:     "cc1.4xlarge",
    86  		Arches:   []string{"amd64"},
    87  		CpuCores: 8,
    88  		CpuPower: CpuPower(3350),
    89  		Mem:      23552,
    90  		Cost:     1300,
    91  		VirtType: &hvm,
    92  	}, {
    93  		Name:     "cc2.8xlarge",
    94  		Arches:   []string{"amd64"},
    95  		CpuCores: 16,
    96  		CpuPower: CpuPower(8800),
    97  		Mem:      61952,
    98  		Cost:     2400,
    99  		VirtType: &hvm,
   100  	},
   101  }
   102  
   103  var getInstanceTypesTest = []struct {
   104  	about          string
   105  	cons           string
   106  	itypesToUse    []InstanceType
   107  	expectedItypes []string
   108  	arches         []string
   109  }{
   110  	{
   111  		about: "cpu-cores",
   112  		cons:  "cpu-cores=2",
   113  		expectedItypes: []string{
   114  			"c1.medium", "m1.large", "m1.xlarge", "c1.xlarge", "cc1.4xlarge",
   115  			"cc2.8xlarge",
   116  		},
   117  	}, {
   118  		about:          "cpu-power",
   119  		cons:           "cpu-power=2000",
   120  		expectedItypes: []string{"c1.xlarge", "cc1.4xlarge", "cc2.8xlarge"},
   121  	}, {
   122  		about: "mem",
   123  		cons:  "mem=4G",
   124  		expectedItypes: []string{
   125  			"m1.large", "m1.xlarge", "c1.xlarge", "cc1.4xlarge", "cc2.8xlarge",
   126  		},
   127  	}, {
   128  		about: "root-disk",
   129  		cons:  "root-disk=16G",
   130  		expectedItypes: []string{
   131  			"m1.medium", "m1.large", "m1.xlarge", "c1.xlarge", "cc1.4xlarge", "cc2.8xlarge",
   132  		},
   133  	}, {
   134  		about:          "arches filtered by constraint",
   135  		cons:           "cpu-power=100 arch=armhf",
   136  		expectedItypes: []string{"m1.small", "m1.medium", "c1.medium"},
   137  		arches:         []string{"armhf"},
   138  	},
   139  	{
   140  		about: "enough memory for mongodb if mem not specified",
   141  		cons:  "cpu-cores=4",
   142  		itypesToUse: []InstanceType{
   143  			{Id: "5", Name: "it-5", Arches: []string{"amd64"}, Mem: 1024, CpuCores: 2},
   144  			{Id: "4", Name: "it-4", Arches: []string{"amd64"}, Mem: 2048, CpuCores: 4},
   145  			{Id: "3", Name: "it-3", Arches: []string{"amd64"}, Mem: 1024, CpuCores: 4},
   146  			{Id: "2", Name: "it-2", Arches: []string{"amd64"}, Mem: 256, CpuCores: 4},
   147  			{Id: "1", Name: "it-1", Arches: []string{"amd64"}, Mem: 512, CpuCores: 4},
   148  		},
   149  		expectedItypes: []string{"it-3", "it-4"},
   150  	},
   151  	{
   152  		about: "small mem specified, use that even though less than needed for mongodb",
   153  		cons:  "mem=300M",
   154  		itypesToUse: []InstanceType{
   155  			{Id: "3", Name: "it-3", Arches: []string{"amd64"}, Mem: 2048},
   156  			{Id: "2", Name: "it-2", Arches: []string{"amd64"}, Mem: 256},
   157  			{Id: "1", Name: "it-1", Arches: []string{"amd64"}, Mem: 512},
   158  		},
   159  		expectedItypes: []string{"it-1", "it-3"},
   160  	},
   161  	{
   162  		about: "mem specified and match found",
   163  		cons:  "mem=4G arch=amd64",
   164  		itypesToUse: []InstanceType{
   165  			{Id: "4", Name: "it-4", Arches: []string{"armhf"}, Mem: 8096},
   166  			{Id: "3", Name: "it-3", Arches: []string{"amd64"}, Mem: 4096},
   167  			{Id: "2", Name: "it-2", Arches: []string{"amd64"}, Mem: 2048},
   168  			{Id: "1", Name: "it-1", Arches: []string{"amd64"}, Mem: 512},
   169  		},
   170  		expectedItypes: []string{"it-3"},
   171  	},
   172  	{
   173  		about: "instance-type specified and match found",
   174  		cons:  "instance-type=it-3",
   175  		itypesToUse: []InstanceType{
   176  			{Id: "4", Name: "it-4", Arches: []string{"amd64"}, Mem: 8096},
   177  			{Id: "3", Name: "it-3", Arches: []string{"amd64"}, Mem: 4096},
   178  			{Id: "2", Name: "it-2", Arches: []string{"amd64"}, Mem: 2048},
   179  			{Id: "1", Name: "it-1", Arches: []string{"amd64"}, Mem: 512},
   180  		},
   181  		expectedItypes: []string{"it-3"},
   182  	},
   183  	{
   184  		about: "largest mem available matching other constraints if mem not specified",
   185  		cons:  "cpu-cores=4",
   186  		itypesToUse: []InstanceType{
   187  			{Id: "3", Name: "it-3", Arches: []string{"amd64"}, Mem: 1024, CpuCores: 2},
   188  			{Id: "2", Name: "it-2", Arches: []string{"amd64"}, Mem: 256, CpuCores: 4},
   189  			{Id: "1", Name: "it-1", Arches: []string{"amd64"}, Mem: 512, CpuCores: 4},
   190  		},
   191  		expectedItypes: []string{"it-1"},
   192  	},
   193  	{
   194  		about: "largest mem available matching other constraints if mem not specified, cost is tie breaker",
   195  		cons:  "cpu-cores=4",
   196  		itypesToUse: []InstanceType{
   197  			{Id: "4", Name: "it-4", Arches: []string{"amd64"}, Mem: 1024, CpuCores: 2},
   198  			{Id: "3", Name: "it-3", Arches: []string{"amd64"}, Mem: 256, CpuCores: 4},
   199  			{Id: "2", Name: "it-2", Arches: []string{"amd64"}, Mem: 512, CpuCores: 4, Cost: 50},
   200  			{Id: "1", Name: "it-1", Arches: []string{"amd64"}, Mem: 512, CpuCores: 4, Cost: 100},
   201  		},
   202  		expectedItypes: []string{"it-2"},
   203  	},
   204  }
   205  
   206  func (s *instanceTypeSuite) TestGetMatchingInstanceTypes(c *gc.C) {
   207  	for i, t := range getInstanceTypesTest {
   208  		c.Logf("test %d: %s", i, t.about)
   209  		itypesToUse := t.itypesToUse
   210  		if itypesToUse == nil {
   211  			itypesToUse = instanceTypes
   212  		}
   213  		itypes, err := MatchingInstanceTypes(itypesToUse, "test", constraints.MustParse(t.cons))
   214  		c.Assert(err, jc.ErrorIsNil)
   215  		names := make([]string, len(itypes))
   216  		for i, itype := range itypes {
   217  			if len(t.arches) > 0 {
   218  				c.Check(itype.Arches, gc.DeepEquals, filterArches(itype.Arches, t.arches))
   219  			} else {
   220  				c.Check(len(itype.Arches) > 0, jc.IsTrue)
   221  			}
   222  			names[i] = itype.Name
   223  		}
   224  		c.Check(names, gc.DeepEquals, t.expectedItypes)
   225  	}
   226  }
   227  
   228  func (s *instanceTypeSuite) TestGetMatchingInstanceTypesErrors(c *gc.C) {
   229  	_, err := MatchingInstanceTypes(nil, "test", constraints.MustParse("cpu-power=9001"))
   230  	c.Check(err, gc.ErrorMatches, `no instance types in test matching constraints "cpu-power=9001"`)
   231  
   232  	_, err = MatchingInstanceTypes(instanceTypes, "test", constraints.MustParse("arch=i386 mem=8G"))
   233  	c.Check(err, gc.ErrorMatches, `no instance types in test matching constraints "arch=i386 mem=8192M"`)
   234  
   235  	_, err = MatchingInstanceTypes(instanceTypes, "test", constraints.MustParse("cpu-cores=9000"))
   236  	c.Check(err, gc.ErrorMatches, `no instance types in test matching constraints "cpu-cores=9000"`)
   237  
   238  	_, err = MatchingInstanceTypes(instanceTypes, "test", constraints.MustParse("mem=90000M"))
   239  	c.Check(err, gc.ErrorMatches, `no instance types in test matching constraints "mem=90000M"`)
   240  }
   241  
   242  var instanceTypeMatchTests = []struct {
   243  	cons   string
   244  	itype  string
   245  	arches []string
   246  }{
   247  	{"", "m1.small", []string{"amd64", "armhf"}},
   248  	{"", "m1.large", []string{"amd64"}},
   249  	{"cpu-power=100", "m1.small", []string{"amd64", "armhf"}},
   250  	{"arch=amd64", "m1.small", []string{"amd64"}},
   251  	{"cpu-cores=3", "m1.xlarge", []string{"amd64"}},
   252  	{"cpu-power=", "t1.micro", []string{"amd64", "armhf"}},
   253  	{"cpu-power=500", "c1.medium", []string{"amd64", "armhf"}},
   254  	{"cpu-power=2000", "c1.xlarge", []string{"amd64"}},
   255  	{"cpu-power=2001", "cc1.4xlarge", []string{"amd64"}},
   256  	{"mem=2G", "m1.medium", []string{"amd64", "armhf"}},
   257  
   258  	{"arch=i386", "m1.small", nil},
   259  	{"cpu-power=100", "t1.micro", nil},
   260  	{"cpu-power=9001", "cc2.8xlarge", nil},
   261  	{"mem=1G", "t1.micro", nil},
   262  	{"arch=armhf", "c1.xlarge", nil},
   263  }
   264  
   265  func (s *instanceTypeSuite) TestMatch(c *gc.C) {
   266  	for i, t := range instanceTypeMatchTests {
   267  		c.Logf("test %d", i)
   268  		cons := constraints.MustParse(t.cons)
   269  		var itype InstanceType
   270  		for _, itype = range instanceTypes {
   271  			if itype.Name == t.itype {
   272  				break
   273  			}
   274  		}
   275  		c.Assert(itype.Name, gc.Not(gc.Equals), "")
   276  		itype, match := itype.match(cons)
   277  		if len(t.arches) > 0 {
   278  			c.Check(match, jc.IsTrue)
   279  			expect := itype
   280  			expect.Arches = t.arches
   281  			c.Check(itype, gc.DeepEquals, expect)
   282  		} else {
   283  			c.Check(match, jc.IsFalse)
   284  			c.Check(itype, gc.DeepEquals, InstanceType{})
   285  		}
   286  	}
   287  }
   288  
   289  var byCostTests = []struct {
   290  	about          string
   291  	itypesToUse    []InstanceType
   292  	expectedItypes []string
   293  }{
   294  	{
   295  		about: "default to lowest cost",
   296  		itypesToUse: []InstanceType{
   297  			{Id: "2", Name: "it-2", CpuCores: 2, Mem: 4096, Cost: 240},
   298  			{Id: "1", Name: "it-1", CpuCores: 1, Mem: 2048, Cost: 241},
   299  		},
   300  		expectedItypes: []string{
   301  			"it-2", "it-1",
   302  		},
   303  	}, {
   304  		about: "when no cost associated, pick lowest ram",
   305  		itypesToUse: []InstanceType{
   306  			{Id: "2", Name: "it-2", CpuCores: 2, Mem: 4096},
   307  			{Id: "1", Name: "it-1", CpuCores: 1, Mem: 2048},
   308  		},
   309  		expectedItypes: []string{
   310  			"it-1", "it-2",
   311  		},
   312  	}, {
   313  		about: "when cost is the same, pick lowest ram",
   314  		itypesToUse: []InstanceType{
   315  			{Id: "2", Name: "it-2", CpuCores: 2, Mem: 4096, Cost: 240},
   316  			{Id: "1", Name: "it-1", CpuCores: 1, Mem: 2048, Cost: 240},
   317  		},
   318  		expectedItypes: []string{
   319  			"it-1", "it-2",
   320  		},
   321  	}, {
   322  		about: "when cost and ram is the same, pick lowest cpu power",
   323  		itypesToUse: []InstanceType{
   324  			{Id: "2", Name: "it-2", CpuCores: 2, CpuPower: CpuPower(200)},
   325  			{Id: "1", Name: "it-1", CpuCores: 1, CpuPower: CpuPower(100)},
   326  		},
   327  		expectedItypes: []string{
   328  			"it-1", "it-2",
   329  		},
   330  	}, {
   331  		about: "when cpu power is the same, pick the lowest cores",
   332  		itypesToUse: []InstanceType{
   333  			{Id: "2", Name: "it-2", CpuCores: 2, CpuPower: CpuPower(200)},
   334  			{Id: "1", Name: "it-1", CpuCores: 1, CpuPower: CpuPower(200)},
   335  		},
   336  		expectedItypes: []string{
   337  			"it-1", "it-2",
   338  		},
   339  	}, {
   340  		about: "when cpu power is missing in side a, pick the lowest cores",
   341  		itypesToUse: []InstanceType{
   342  			{Id: "2", Name: "it-2", CpuCores: 2, CpuPower: CpuPower(200)},
   343  			{Id: "1", Name: "it-1", CpuCores: 1},
   344  		},
   345  		expectedItypes: []string{
   346  			"it-1", "it-2",
   347  		},
   348  	}, {
   349  		about: "when cpu power is missing in side b, pick the lowest cores",
   350  		itypesToUse: []InstanceType{
   351  			{Id: "2", Name: "it-2", CpuCores: 2},
   352  			{Id: "1", Name: "it-1", CpuCores: 1, CpuPower: CpuPower(200)},
   353  		},
   354  		expectedItypes: []string{
   355  			"it-1", "it-2",
   356  		},
   357  	}, {
   358  		about: "when cpu cores is the same, pick the lowest root disk size",
   359  		itypesToUse: []InstanceType{
   360  			{Id: "2", Name: "it-2", CpuCores: 1, RootDisk: 8192},
   361  			{Id: "1", Name: "it-1", CpuCores: 1, RootDisk: 4096},
   362  		},
   363  		expectedItypes: []string{
   364  			"it-1", "it-2",
   365  		},
   366  	},
   367  }
   368  
   369  func (s *instanceTypeSuite) TestSortByCost(c *gc.C) {
   370  	for i, t := range byCostTests {
   371  		c.Logf("test %d: %s", i, t.about)
   372  		sort.Sort(byCost(t.itypesToUse))
   373  		names := make([]string, len(t.itypesToUse))
   374  		for i, itype := range t.itypesToUse {
   375  			names[i] = itype.Name
   376  		}
   377  		c.Check(names, gc.DeepEquals, t.expectedItypes)
   378  	}
   379  }