github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/core/description/machine_test.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package description 5 6 import ( 7 jc "github.com/juju/testing/checkers" 8 "github.com/juju/version" 9 gc "gopkg.in/check.v1" 10 "gopkg.in/juju/names.v2" 11 "gopkg.in/yaml.v2" 12 ) 13 14 type MachineSerializationSuite struct { 15 SliceSerializationSuite 16 PortRangeCheck 17 StatusHistoryMixinSuite 18 } 19 20 var _ = gc.Suite(&MachineSerializationSuite{}) 21 22 func (s *MachineSerializationSuite) SetUpTest(c *gc.C) { 23 s.SliceSerializationSuite.SetUpTest(c) 24 s.importName = "machines" 25 s.sliceName = "machines" 26 s.importFunc = func(m map[string]interface{}) (interface{}, error) { 27 return importMachines(m) 28 } 29 s.testFields = func(m map[string]interface{}) { 30 m["machines"] = []interface{}{} 31 } 32 s.StatusHistoryMixinSuite.creator = func() HasStatusHistory { 33 return minimalMachine("1") 34 } 35 s.StatusHistoryMixinSuite.serializer = func(c *gc.C, initial interface{}) HasStatusHistory { 36 return s.exportImport(c, initial.(*machine)) 37 } 38 } 39 40 func minimalMachineMap(id string, containers ...interface{}) map[interface{}]interface{} { 41 return map[interface{}]interface{}{ 42 "id": id, 43 "nonce": "a-nonce", 44 "password-hash": "some-hash", 45 "instance": minimalCloudInstanceMap(), 46 "series": "zesty", 47 "tools": minimalAgentToolsMap(), 48 "jobs": []interface{}{"host-units"}, 49 "containers": containers, 50 "status": minimalStatusMap(), 51 "status-history": emptyStatusHistoryMap(), 52 "block-devices": emptyBlockDeviceMap(), 53 } 54 } 55 56 func minimalMachine(id string, containers ...*machine) *machine { 57 m := newMachine(MachineArgs{ 58 Id: names.NewMachineTag(id), 59 Nonce: "a-nonce", 60 PasswordHash: "some-hash", 61 Series: "zesty", 62 Jobs: []string{"host-units"}, 63 }) 64 m.Containers_ = containers 65 m.SetInstance(minimalCloudInstanceArgs()) 66 m.SetTools(minimalAgentToolsArgs()) 67 m.SetStatus(minimalStatusArgs()) 68 return m 69 } 70 71 func addMinimalMachine(model Model, id string) { 72 m := model.AddMachine(MachineArgs{ 73 Id: names.NewMachineTag(id), 74 Nonce: "a-nonce", 75 PasswordHash: "some-hash", 76 Series: "zesty", 77 Jobs: []string{"host-units"}, 78 }) 79 m.SetInstance(minimalCloudInstanceArgs()) 80 m.SetTools(minimalAgentToolsArgs()) 81 m.SetStatus(minimalStatusArgs()) 82 } 83 84 func (s *MachineSerializationSuite) machineArgs(id string) MachineArgs { 85 return MachineArgs{ 86 Id: names.NewMachineTag(id), 87 Nonce: "a nonce", 88 PasswordHash: "some-hash", 89 Placement: "placement", 90 Series: "zesty", 91 ContainerType: "magic", 92 Jobs: []string{"this", "that"}, 93 } 94 } 95 96 func (s *MachineSerializationSuite) TestNewMachine(c *gc.C) { 97 m := newMachine(s.machineArgs("42")) 98 c.Assert(m.Id(), gc.Equals, "42") 99 c.Assert(m.Tag(), gc.Equals, names.NewMachineTag("42")) 100 c.Assert(m.Nonce(), gc.Equals, "a nonce") 101 c.Assert(m.PasswordHash(), gc.Equals, "some-hash") 102 c.Assert(m.Placement(), gc.Equals, "placement") 103 c.Assert(m.Series(), gc.Equals, "zesty") 104 c.Assert(m.ContainerType(), gc.Equals, "magic") 105 c.Assert(m.Jobs(), jc.DeepEquals, []string{"this", "that"}) 106 supportedContainers, ok := m.SupportedContainers() 107 c.Assert(ok, jc.IsFalse) 108 c.Assert(supportedContainers, gc.IsNil) 109 } 110 111 func (s *MachineSerializationSuite) TestMinimalMachineValid(c *gc.C) { 112 m := minimalMachine("1") 113 c.Assert(m.Validate(), jc.ErrorIsNil) 114 } 115 116 func (s *MachineSerializationSuite) TestNewMachineWithSupportedContainers(c *gc.C) { 117 supported := []string{"lxd", "kvm"} 118 args := s.machineArgs("id") 119 args.SupportedContainers = &supported 120 m := newMachine(args) 121 supportedContainers, ok := m.SupportedContainers() 122 c.Assert(ok, jc.IsTrue) 123 c.Assert(supportedContainers, jc.DeepEquals, supported) 124 } 125 126 func (s *MachineSerializationSuite) TestNewMachineWithNoSupportedContainers(c *gc.C) { 127 supported := []string{} 128 args := s.machineArgs("id") 129 args.SupportedContainers = &supported 130 m := newMachine(args) 131 supportedContainers, ok := m.SupportedContainers() 132 c.Assert(ok, jc.IsTrue) 133 c.Assert(supportedContainers, gc.HasLen, 0) 134 } 135 136 func (s *MachineSerializationSuite) TestNewMachineWithNoSupportedContainersNil(c *gc.C) { 137 var supported []string 138 args := s.machineArgs("id") 139 args.SupportedContainers = &supported 140 m := newMachine(args) 141 supportedContainers, ok := m.SupportedContainers() 142 c.Assert(ok, jc.IsTrue) 143 c.Assert(supportedContainers, gc.HasLen, 0) 144 } 145 146 func (s *MachineSerializationSuite) TestMinimalMatches(c *gc.C) { 147 bytes, err := yaml.Marshal(minimalMachine("0")) 148 c.Assert(err, jc.ErrorIsNil) 149 150 var source map[interface{}]interface{} 151 err = yaml.Unmarshal(bytes, &source) 152 c.Assert(err, jc.ErrorIsNil) 153 c.Assert(source, jc.DeepEquals, minimalMachineMap("0")) 154 } 155 156 func (*MachineSerializationSuite) TestNestedParsing(c *gc.C) { 157 machines, err := importMachines(map[string]interface{}{ 158 "version": 1, 159 "machines": []interface{}{ 160 minimalMachineMap("0"), 161 minimalMachineMap("1", 162 minimalMachineMap("1/lxd/0"), 163 minimalMachineMap("1/lxd/1"), 164 ), 165 minimalMachineMap("2", 166 minimalMachineMap("2/kvm/0", 167 minimalMachineMap("2/kvm/0/lxd/0"), 168 minimalMachineMap("2/kvm/0/lxd/1"), 169 ), 170 ), 171 }}) 172 c.Assert(err, jc.ErrorIsNil) 173 expected := []*machine{ 174 minimalMachine("0"), 175 minimalMachine("1", 176 minimalMachine("1/lxd/0"), 177 minimalMachine("1/lxd/1"), 178 ), 179 minimalMachine("2", 180 minimalMachine("2/kvm/0", 181 minimalMachine("2/kvm/0/lxd/0"), 182 minimalMachine("2/kvm/0/lxd/1"), 183 ), 184 ), 185 } 186 c.Assert(machines, jc.DeepEquals, expected) 187 } 188 189 func (s *MachineSerializationSuite) addOpenedPorts(m Machine) []OpenedPortsArgs { 190 args := []OpenedPortsArgs{ 191 { 192 SubnetID: "0.1.2.0/24", 193 OpenedPorts: []PortRangeArgs{ 194 { 195 UnitName: "magic/0", 196 FromPort: 1234, 197 ToPort: 2345, 198 Protocol: "tcp", 199 }, 200 }, 201 }, { 202 SubnetID: "", 203 OpenedPorts: []PortRangeArgs{ 204 { 205 UnitName: "unicorn/0", 206 FromPort: 80, 207 ToPort: 80, 208 Protocol: "tcp", 209 }, 210 }, 211 }, 212 } 213 m.AddOpenedPorts(args[0]) 214 m.AddOpenedPorts(args[1]) 215 return args 216 } 217 218 func (s *MachineSerializationSuite) TestOpenedPorts(c *gc.C) { 219 m := newMachine(s.machineArgs("42")) 220 args := s.addOpenedPorts(m) 221 ports := m.OpenedPorts() 222 c.Assert(ports, gc.HasLen, 2) 223 withSubnet, withoutSubnet := ports[0], ports[1] 224 c.Assert(withSubnet.SubnetID(), gc.Equals, "0.1.2.0/24") 225 c.Assert(withoutSubnet.SubnetID(), gc.Equals, "") 226 opened := withSubnet.OpenPorts() 227 c.Assert(opened, gc.HasLen, 1) 228 s.AssertPortRange(c, opened[0], args[0].OpenedPorts[0]) 229 opened = withoutSubnet.OpenPorts() 230 c.Assert(opened, gc.HasLen, 1) 231 s.AssertPortRange(c, opened[0], args[1].OpenedPorts[0]) 232 } 233 234 func (s *MachineSerializationSuite) TestAnnotations(c *gc.C) { 235 initial := minimalMachine("42") 236 annotations := map[string]string{ 237 "string": "value", 238 "another": "one", 239 } 240 initial.SetAnnotations(annotations) 241 242 machine := s.exportImport(c, initial) 243 c.Assert(machine.Annotations(), jc.DeepEquals, annotations) 244 } 245 246 func (s *MachineSerializationSuite) TestConstraints(c *gc.C) { 247 initial := minimalMachine("42") 248 args := ConstraintsArgs{ 249 Architecture: "amd64", 250 Memory: 8 * gig, 251 RootDisk: 40 * gig, 252 } 253 initial.SetConstraints(args) 254 255 machine := s.exportImport(c, initial) 256 c.Assert(machine.Constraints(), jc.DeepEquals, newConstraints(args)) 257 } 258 259 func (s *MachineSerializationSuite) exportImport(c *gc.C, machine_ *machine) *machine { 260 initial := machines{ 261 Version: 1, 262 Machines_: []*machine{machine_}, 263 } 264 265 bytes, err := yaml.Marshal(initial) 266 c.Assert(err, jc.ErrorIsNil) 267 268 var source map[string]interface{} 269 err = yaml.Unmarshal(bytes, &source) 270 c.Assert(err, jc.ErrorIsNil) 271 272 machines, err := importMachines(source) 273 c.Assert(err, jc.ErrorIsNil) 274 c.Assert(machines, gc.HasLen, 1) 275 return machines[0] 276 } 277 278 func (s *MachineSerializationSuite) TestParsingSerializedData(c *gc.C) { 279 // TODO: need to fully specify a machine. 280 args := s.machineArgs("0") 281 supported := []string{"kvm", "lxd"} 282 args.SupportedContainers = &supported 283 m := newMachine(args) 284 m.SetTools(minimalAgentToolsArgs()) 285 m.SetStatus(minimalStatusArgs()) 286 m.SetInstance(minimalCloudInstanceArgs()) 287 m.AddBlockDevice(allBlockDeviceArgs()) 288 s.addOpenedPorts(m) 289 290 // Just use one set of address args for both machine and provider. 291 addrArgs := []AddressArgs{ 292 { 293 Value: "10.0.0.10", 294 Type: "special", 295 }, { 296 Value: "10.1.2.3", 297 Type: "other", 298 }, 299 } 300 m.SetAddresses(addrArgs, addrArgs) 301 m.SetPreferredAddresses(addrArgs[0], addrArgs[1]) 302 303 // Make sure the machine is valid. 304 c.Assert(m.Validate(), jc.ErrorIsNil) 305 306 machine := s.exportImport(c, m) 307 c.Assert(machine, jc.DeepEquals, m) 308 } 309 310 type CloudInstanceSerializationSuite struct { 311 SerializationSuite 312 } 313 314 var _ = gc.Suite(&CloudInstanceSerializationSuite{}) 315 316 func (s *CloudInstanceSerializationSuite) SetUpTest(c *gc.C) { 317 s.importName = "cloudInstance" 318 s.importFunc = func(m map[string]interface{}) (interface{}, error) { 319 return importCloudInstance(m) 320 } 321 } 322 323 func minimalCloudInstanceMap() map[interface{}]interface{} { 324 return map[interface{}]interface{}{ 325 "version": 1, 326 "instance-id": "instance id", 327 "status": "some status", 328 } 329 } 330 331 func minimalCloudInstance() *cloudInstance { 332 return newCloudInstance(minimalCloudInstanceArgs()) 333 } 334 335 func minimalCloudInstanceArgs() CloudInstanceArgs { 336 return CloudInstanceArgs{ 337 InstanceId: "instance id", 338 Status: "some status", 339 } 340 } 341 342 func (s *CloudInstanceSerializationSuite) TestNewCloudInstance(c *gc.C) { 343 // NOTE: using gig from package_test.go 344 args := CloudInstanceArgs{ 345 InstanceId: "instance id", 346 Status: "working", 347 Architecture: "amd64", 348 Memory: 16 * gig, 349 RootDisk: 200 * gig, 350 CpuCores: 8, 351 CpuPower: 4000, 352 Tags: []string{"much", "strong"}, 353 AvailabilityZone: "everywhere", 354 } 355 356 instance := newCloudInstance(args) 357 358 c.Assert(instance.InstanceId(), gc.Equals, args.InstanceId) 359 c.Assert(instance.Status(), gc.Equals, args.Status) 360 c.Assert(instance.Architecture(), gc.Equals, args.Architecture) 361 c.Assert(instance.Memory(), gc.Equals, args.Memory) 362 c.Assert(instance.RootDisk(), gc.Equals, args.RootDisk) 363 c.Assert(instance.CpuCores(), gc.Equals, args.CpuCores) 364 c.Assert(instance.CpuPower(), gc.Equals, args.CpuPower) 365 c.Assert(instance.AvailabilityZone(), gc.Equals, args.AvailabilityZone) 366 367 // Before we check tags, modify args to make sure that the instance ones 368 // don't change. 369 370 args.Tags[0] = "weird" 371 tags := instance.Tags() 372 c.Assert(tags, jc.DeepEquals, []string{"much", "strong"}) 373 374 // Also, changing the tags returned, doesn't modify the instance 375 tags[0] = "weird" 376 c.Assert(instance.Tags(), jc.DeepEquals, []string{"much", "strong"}) 377 } 378 379 func (s *CloudInstanceSerializationSuite) TestMinimalMatches(c *gc.C) { 380 bytes, err := yaml.Marshal(minimalCloudInstance()) 381 c.Assert(err, jc.ErrorIsNil) 382 383 var source map[interface{}]interface{} 384 err = yaml.Unmarshal(bytes, &source) 385 c.Assert(err, jc.ErrorIsNil) 386 c.Assert(source, jc.DeepEquals, minimalCloudInstanceMap()) 387 } 388 389 func (s *CloudInstanceSerializationSuite) TestParsingSerializedData(c *gc.C) { 390 const MaxUint64 = 1<<64 - 1 391 initial := newCloudInstance(CloudInstanceArgs{ 392 InstanceId: "instance id", 393 Status: "working", 394 Architecture: "amd64", 395 Memory: 16 * gig, 396 CpuPower: MaxUint64, 397 Tags: []string{"much", "strong"}, 398 }) 399 bytes, err := yaml.Marshal(initial) 400 c.Assert(err, jc.ErrorIsNil) 401 402 var source map[string]interface{} 403 err = yaml.Unmarshal(bytes, &source) 404 c.Assert(err, jc.ErrorIsNil) 405 406 instance, err := importCloudInstance(source) 407 c.Assert(err, jc.ErrorIsNil) 408 c.Assert(instance, jc.DeepEquals, initial) 409 } 410 411 type AgentToolsSerializationSuite struct { 412 SerializationSuite 413 } 414 415 var _ = gc.Suite(&AgentToolsSerializationSuite{}) 416 417 func (s *AgentToolsSerializationSuite) SetUpTest(c *gc.C) { 418 s.importName = "agentTools" 419 s.importFunc = func(m map[string]interface{}) (interface{}, error) { 420 return importAgentTools(m) 421 } 422 } 423 424 func (s *AgentToolsSerializationSuite) TestNewAgentTools(c *gc.C) { 425 args := AgentToolsArgs{ 426 Version: version.MustParseBinary("3.4.5-trusty-amd64"), 427 URL: "some-url", 428 SHA256: "long-hash", 429 Size: 123456789, 430 } 431 instance := newAgentTools(args) 432 433 c.Assert(instance.Version(), gc.Equals, args.Version) 434 c.Assert(instance.URL(), gc.Equals, args.URL) 435 c.Assert(instance.SHA256(), gc.Equals, args.SHA256) 436 c.Assert(instance.Size(), gc.Equals, args.Size) 437 } 438 439 func minimalAgentToolsMap() map[interface{}]interface{} { 440 return map[interface{}]interface{}{ 441 "version": 1, 442 "tools-version": "3.4.5-trusty-amd64", 443 "url": "some-url", 444 "sha256": "long-hash", 445 "size": 123456789, 446 } 447 } 448 449 func minimalAgentToolsArgs() AgentToolsArgs { 450 return AgentToolsArgs{ 451 Version: version.MustParseBinary("3.4.5-trusty-amd64"), 452 URL: "some-url", 453 SHA256: "long-hash", 454 Size: 123456789, 455 } 456 } 457 458 func minimalAgentTools() *agentTools { 459 return newAgentTools(minimalAgentToolsArgs()) 460 } 461 462 func (s *AgentToolsSerializationSuite) TestMinimalMatches(c *gc.C) { 463 bytes, err := yaml.Marshal(minimalAgentTools()) 464 c.Assert(err, jc.ErrorIsNil) 465 466 var source map[interface{}]interface{} 467 err = yaml.Unmarshal(bytes, &source) 468 c.Assert(err, jc.ErrorIsNil) 469 c.Assert(source, jc.DeepEquals, minimalAgentToolsMap()) 470 } 471 472 func (s *AgentToolsSerializationSuite) TestParsingSerializedData(c *gc.C) { 473 initial := newAgentTools(AgentToolsArgs{ 474 Version: version.MustParseBinary("2.0.4-trusty-amd64"), 475 URL: "some-url", 476 SHA256: "long-hash", 477 Size: 123456789, 478 }) 479 bytes, err := yaml.Marshal(initial) 480 c.Assert(err, jc.ErrorIsNil) 481 482 var source map[string]interface{} 483 err = yaml.Unmarshal(bytes, &source) 484 c.Assert(err, jc.ErrorIsNil) 485 486 instance, err := importAgentTools(source) 487 c.Assert(err, jc.ErrorIsNil) 488 c.Assert(instance, jc.DeepEquals, initial) 489 }