github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/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 Name: "dep.small", 102 Arches: []string{"amd64"}, 103 CpuCores: 1, 104 CpuPower: CpuPower(100), 105 Mem: 1740, 106 Cost: 60, 107 Deprecated: true, 108 }, { 109 Name: "dep.medium", 110 Arches: []string{"amd64"}, 111 CpuCores: 2, 112 CpuPower: CpuPower(200), 113 Mem: 4096, 114 Cost: 80, 115 Deprecated: true, 116 }, 117 } 118 119 var getInstanceTypesTest = []struct { 120 about string 121 cons string 122 itypesToUse []InstanceType 123 expectedItypes []string 124 arches []string 125 }{ 126 { 127 about: "cores", 128 cons: "cores=2", 129 expectedItypes: []string{ 130 "c1.medium", "m1.large", "m1.xlarge", "c1.xlarge", "cc1.4xlarge", 131 "cc2.8xlarge", 132 }, 133 }, { 134 about: "cpu-power", 135 cons: "cpu-power=2000", 136 expectedItypes: []string{"c1.xlarge", "cc1.4xlarge", "cc2.8xlarge"}, 137 }, { 138 about: "mem", 139 cons: "mem=4G", 140 expectedItypes: []string{ 141 "m1.large", "m1.xlarge", "c1.xlarge", "cc1.4xlarge", "cc2.8xlarge", 142 }, 143 }, { 144 about: "root-disk", 145 cons: "root-disk=16G", 146 expectedItypes: []string{ 147 "m1.medium", "m1.large", "m1.xlarge", "c1.xlarge", "cc1.4xlarge", "cc2.8xlarge", 148 }, 149 }, { 150 about: "arches filtered by constraint", 151 cons: "cpu-power=100 arch=armhf", 152 expectedItypes: []string{"m1.small", "m1.medium", "c1.medium"}, 153 arches: []string{"armhf"}, 154 }, 155 { 156 about: "enough memory for mongodb if mem not specified", 157 cons: "cores=4", 158 itypesToUse: []InstanceType{ 159 {Id: "5", Name: "it-5", Arches: []string{"amd64"}, Mem: 1024, CpuCores: 2}, 160 {Id: "4", Name: "it-4", Arches: []string{"amd64"}, Mem: 2048, CpuCores: 4}, 161 {Id: "3", Name: "it-3", Arches: []string{"amd64"}, Mem: 1024, CpuCores: 4}, 162 {Id: "2", Name: "it-2", Arches: []string{"amd64"}, Mem: 256, CpuCores: 4}, 163 {Id: "1", Name: "it-1", Arches: []string{"amd64"}, Mem: 512, CpuCores: 4}, 164 }, 165 expectedItypes: []string{"it-3", "it-4"}, 166 }, 167 { 168 about: "small mem specified, use that even though less than needed for mongodb", 169 cons: "mem=300M", 170 itypesToUse: []InstanceType{ 171 {Id: "3", Name: "it-3", Arches: []string{"amd64"}, Mem: 2048}, 172 {Id: "2", Name: "it-2", Arches: []string{"amd64"}, Mem: 256}, 173 {Id: "1", Name: "it-1", Arches: []string{"amd64"}, Mem: 512}, 174 }, 175 expectedItypes: []string{"it-1", "it-3"}, 176 }, 177 { 178 about: "mem specified and match found", 179 cons: "mem=4G arch=amd64", 180 itypesToUse: []InstanceType{ 181 {Id: "4", Name: "it-4", Arches: []string{"armhf"}, Mem: 8096}, 182 {Id: "3", Name: "it-3", Arches: []string{"amd64"}, Mem: 4096}, 183 {Id: "2", Name: "it-2", Arches: []string{"amd64"}, Mem: 2048}, 184 {Id: "1", Name: "it-1", Arches: []string{"amd64"}, Mem: 512}, 185 }, 186 expectedItypes: []string{"it-3"}, 187 }, 188 { 189 about: "instance-type specified and match found", 190 cons: "instance-type=it-3", 191 itypesToUse: []InstanceType{ 192 {Id: "4", Name: "it-4", Arches: []string{"amd64"}, Mem: 8096}, 193 {Id: "3", Name: "it-3", Arches: []string{"amd64"}, Mem: 4096}, 194 {Id: "2", Name: "it-2", Arches: []string{"amd64"}, Mem: 2048}, 195 {Id: "1", Name: "it-1", Arches: []string{"amd64"}, Mem: 512}, 196 }, 197 expectedItypes: []string{"it-3"}, 198 }, 199 { 200 about: "largest mem available matching other constraints if mem not specified", 201 cons: "cores=4", 202 itypesToUse: []InstanceType{ 203 {Id: "3", Name: "it-3", Arches: []string{"amd64"}, Mem: 1024, CpuCores: 2}, 204 {Id: "2", Name: "it-2", Arches: []string{"amd64"}, Mem: 256, CpuCores: 4}, 205 {Id: "1", Name: "it-1", Arches: []string{"amd64"}, Mem: 512, CpuCores: 4}, 206 }, 207 expectedItypes: []string{"it-1"}, 208 }, 209 { 210 about: "largest mem available matching other constraints if mem not specified, cost is tie breaker", 211 cons: "cores=4", 212 itypesToUse: []InstanceType{ 213 {Id: "4", Name: "it-4", Arches: []string{"amd64"}, Mem: 1024, CpuCores: 2}, 214 {Id: "3", Name: "it-3", Arches: []string{"amd64"}, Mem: 256, CpuCores: 4}, 215 {Id: "2", Name: "it-2", Arches: []string{"amd64"}, Mem: 512, CpuCores: 4, Cost: 50}, 216 {Id: "1", Name: "it-1", Arches: []string{"amd64"}, Mem: 512, CpuCores: 4, Cost: 100}, 217 }, 218 expectedItypes: []string{"it-2"}, 219 }, { 220 about: "virt-type filtered by constraint", 221 cons: "virt-type=hvm", 222 expectedItypes: []string{"cc1.4xlarge", "cc2.8xlarge"}, 223 itypesToUse: nil, 224 }, { 225 about: "deprecated image type requested by name", 226 cons: "instance-type=dep.small", 227 expectedItypes: []string{"dep.small"}, 228 }, { 229 about: "deprecated image type requested by name with constraints", 230 cons: "instance-type=dep.small cpu-power=100", 231 expectedItypes: []string{"dep.small"}, 232 }, 233 } 234 235 func (s *instanceTypeSuite) TestGetMatchingInstanceTypes(c *gc.C) { 236 for i, t := range getInstanceTypesTest { 237 c.Logf("test %d: %s", i, t.about) 238 itypesToUse := t.itypesToUse 239 if itypesToUse == nil { 240 itypesToUse = instanceTypes 241 } 242 itypes, err := MatchingInstanceTypes(itypesToUse, "test", constraints.MustParse(t.cons)) 243 c.Assert(err, jc.ErrorIsNil) 244 names := make([]string, len(itypes)) 245 for i, itype := range itypes { 246 if len(t.arches) > 0 { 247 c.Check(itype.Arches, gc.DeepEquals, filterArches(itype.Arches, t.arches)) 248 } else { 249 c.Check(len(itype.Arches) > 0, jc.IsTrue) 250 } 251 names[i] = itype.Name 252 } 253 c.Check(names, gc.DeepEquals, t.expectedItypes) 254 } 255 } 256 257 func (s *instanceTypeSuite) TestGetMatchingInstanceTypesErrors(c *gc.C) { 258 _, err := MatchingInstanceTypes(nil, "test", constraints.MustParse("cpu-power=9001")) 259 c.Check(err, gc.ErrorMatches, `no instance types in test matching constraints "cpu-power=9001"`) 260 261 _, err = MatchingInstanceTypes(instanceTypes, "test", constraints.MustParse("arch=i386 mem=8G")) 262 c.Check(err, gc.ErrorMatches, `no instance types in test matching constraints "arch=i386 mem=8192M"`) 263 264 _, err = MatchingInstanceTypes(instanceTypes, "test", constraints.MustParse("cores=9000")) 265 c.Check(err, gc.ErrorMatches, `no instance types in test matching constraints "cores=9000"`) 266 267 _, err = MatchingInstanceTypes(instanceTypes, "test", constraints.MustParse("mem=90000M")) 268 c.Check(err, gc.ErrorMatches, `no instance types in test matching constraints "mem=90000M"`) 269 270 _, err = MatchingInstanceTypes(instanceTypes, "test", constraints.MustParse("instance-type=dep.medium mem=8G")) 271 c.Check(err, gc.ErrorMatches, `no instance types in test matching constraints "instance-type=dep.medium mem=8192M"`) 272 } 273 274 var instanceTypeMatchTests = []struct { 275 cons string 276 itype string 277 arches []string 278 }{ 279 {"", "m1.small", []string{"amd64", "armhf"}}, 280 {"", "m1.large", []string{"amd64"}}, 281 {"cpu-power=100", "m1.small", []string{"amd64", "armhf"}}, 282 {"arch=amd64", "m1.small", []string{"amd64"}}, 283 {"cores=3", "m1.xlarge", []string{"amd64"}}, 284 {"cpu-power=", "t1.micro", []string{"amd64", "armhf"}}, 285 {"cpu-power=500", "c1.medium", []string{"amd64", "armhf"}}, 286 {"cpu-power=2000", "c1.xlarge", []string{"amd64"}}, 287 {"cpu-power=2001", "cc1.4xlarge", []string{"amd64"}}, 288 {"mem=2G", "m1.medium", []string{"amd64", "armhf"}}, 289 290 {"arch=i386", "m1.small", nil}, 291 {"cpu-power=100", "t1.micro", nil}, 292 {"cpu-power=9001", "cc2.8xlarge", nil}, 293 {"mem=1G", "t1.micro", nil}, 294 {"arch=armhf", "c1.xlarge", nil}, 295 } 296 297 func (s *instanceTypeSuite) TestMatch(c *gc.C) { 298 for i, t := range instanceTypeMatchTests { 299 c.Logf("test %d", i) 300 cons := constraints.MustParse(t.cons) 301 var itype InstanceType 302 for _, itype = range instanceTypes { 303 if itype.Name == t.itype { 304 break 305 } 306 } 307 c.Assert(itype.Name, gc.Not(gc.Equals), "") 308 itype, match := itype.match(cons) 309 if len(t.arches) > 0 { 310 c.Check(match, jc.IsTrue) 311 expect := itype 312 expect.Arches = t.arches 313 c.Check(itype, gc.DeepEquals, expect) 314 } else { 315 c.Check(match, jc.IsFalse) 316 c.Check(itype, gc.DeepEquals, InstanceType{}) 317 } 318 } 319 } 320 321 var byCostTests = []struct { 322 about string 323 itypesToUse []InstanceType 324 expectedItypes []string 325 }{ 326 { 327 about: "default to lowest cost", 328 itypesToUse: []InstanceType{ 329 {Id: "2", Name: "it-2", CpuCores: 2, Mem: 4096, Cost: 240}, 330 {Id: "1", Name: "it-1", CpuCores: 1, Mem: 2048, Cost: 241}, 331 }, 332 expectedItypes: []string{ 333 "it-2", "it-1", 334 }, 335 }, { 336 about: "when no cost associated, pick lowest ram", 337 itypesToUse: []InstanceType{ 338 {Id: "2", Name: "it-2", CpuCores: 2, Mem: 4096}, 339 {Id: "1", Name: "it-1", CpuCores: 1, Mem: 2048}, 340 }, 341 expectedItypes: []string{ 342 "it-1", "it-2", 343 }, 344 }, { 345 about: "when cost is the same, pick lowest ram", 346 itypesToUse: []InstanceType{ 347 {Id: "2", Name: "it-2", CpuCores: 2, Mem: 4096, Cost: 240}, 348 {Id: "1", Name: "it-1", CpuCores: 1, Mem: 2048, Cost: 240}, 349 }, 350 expectedItypes: []string{ 351 "it-1", "it-2", 352 }, 353 }, { 354 about: "when cost and ram is the same, pick lowest cpu power", 355 itypesToUse: []InstanceType{ 356 {Id: "2", Name: "it-2", CpuCores: 2, CpuPower: CpuPower(200)}, 357 {Id: "1", Name: "it-1", CpuCores: 1, CpuPower: CpuPower(100)}, 358 }, 359 expectedItypes: []string{ 360 "it-1", "it-2", 361 }, 362 }, { 363 about: "when cpu power is the same, pick the lowest cores", 364 itypesToUse: []InstanceType{ 365 {Id: "2", Name: "it-2", CpuCores: 2, CpuPower: CpuPower(200)}, 366 {Id: "1", Name: "it-1", CpuCores: 1, CpuPower: CpuPower(200)}, 367 }, 368 expectedItypes: []string{ 369 "it-1", "it-2", 370 }, 371 }, { 372 about: "when cpu power is missing in side a, pick the lowest cores", 373 itypesToUse: []InstanceType{ 374 {Id: "2", Name: "it-2", CpuCores: 2, CpuPower: CpuPower(200)}, 375 {Id: "1", Name: "it-1", CpuCores: 1}, 376 }, 377 expectedItypes: []string{ 378 "it-1", "it-2", 379 }, 380 }, { 381 about: "when cpu power is missing in side b, pick the lowest cores", 382 itypesToUse: []InstanceType{ 383 {Id: "2", Name: "it-2", CpuCores: 2}, 384 {Id: "1", Name: "it-1", CpuCores: 1, CpuPower: CpuPower(200)}, 385 }, 386 expectedItypes: []string{ 387 "it-1", "it-2", 388 }, 389 }, { 390 about: "when cpu cores is the same, pick the lowest root disk size", 391 itypesToUse: []InstanceType{ 392 {Id: "2", Name: "it-2", CpuCores: 1, RootDisk: 8192}, 393 {Id: "1", Name: "it-1", CpuCores: 1, RootDisk: 4096}, 394 }, 395 expectedItypes: []string{ 396 "it-1", "it-2", 397 }, 398 }, 399 } 400 401 func (s *instanceTypeSuite) TestSortByCost(c *gc.C) { 402 for i, t := range byCostTests { 403 c.Logf("test %d: %s", i, t.about) 404 sort.Sort(byCost(t.itypesToUse)) 405 names := make([]string, len(t.itypesToUse)) 406 for i, itype := range t.itypesToUse { 407 names[i] = itype.Name 408 } 409 c.Check(names, gc.DeepEquals, t.expectedItypes) 410 } 411 }