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 }