github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/provider/gce/testing_test.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package gce 5 6 import ( 7 "fmt" 8 "strings" 9 10 gitjujutesting "github.com/juju/testing" 11 jc "github.com/juju/testing/checkers" 12 "github.com/juju/utils/arch" 13 "github.com/juju/version" 14 gc "gopkg.in/check.v1" 15 16 "github.com/juju/juju/cloudconfig/instancecfg" 17 "github.com/juju/juju/cloudconfig/providerinit" 18 "github.com/juju/juju/constraints" 19 "github.com/juju/juju/environs" 20 "github.com/juju/juju/environs/config" 21 "github.com/juju/juju/environs/imagemetadata" 22 "github.com/juju/juju/environs/instances" 23 "github.com/juju/juju/environs/simplestreams" 24 "github.com/juju/juju/instance" 25 "github.com/juju/juju/network" 26 "github.com/juju/juju/provider/common" 27 "github.com/juju/juju/provider/gce/google" 28 "github.com/juju/juju/testing" 29 coretools "github.com/juju/juju/tools" 30 ) 31 32 // These values are fake GCE auth credentials for use in tests. 33 const ( 34 ClientName = "ba9876543210-0123456789abcdefghijklmnopqrstuv" 35 ClientID = ClientName + ".apps.googleusercontent.com" 36 ClientEmail = ClientName + "@developer.gserviceaccount.com" 37 PrivateKey = `-----BEGIN PRIVATE KEY----- 38 ... 39 ... 40 ... 41 ... 42 ... 43 ... 44 ... 45 ... 46 ... 47 ... 48 ... 49 ... 50 ... 51 ... 52 -----END PRIVATE KEY----- 53 ` 54 ) 55 56 // These are fake config values for use in tests. 57 var ( 58 AuthFile = fmt.Sprintf(`{ 59 "private_key_id": "abcdef0123456789abcdef0123456789abcdef01", 60 "private_key": "%s", 61 "client_email": "%s", 62 "client_id": "%s", 63 "type": "service_account" 64 }`, strings.Replace(PrivateKey, "\n", "\\n", -1), ClientEmail, ClientID) 65 66 ConfigAttrs = testing.FakeConfig().Merge(testing.Attrs{ 67 "type": "gce", 68 "private-key": PrivateKey, 69 "client-id": ClientID, 70 "client-email": ClientEmail, 71 "region": "home", 72 "project-id": "my-juju", 73 "image-endpoint": "https://www.googleapis.com", 74 "uuid": "2d02eeac-9dbb-11e4-89d3-123b93f75cba", 75 "controller-uuid": "bfef02f1-932a-425a-a102-62175dcabd1d", 76 }) 77 ) 78 79 type BaseSuiteUnpatched struct { 80 gitjujutesting.IsolationSuite 81 82 Config *config.Config 83 EnvConfig *environConfig 84 Env *environ 85 Prefix string 86 87 Addresses []network.Address 88 BaseInstance *google.Instance 89 BaseDisk *google.Disk 90 Instance *environInstance 91 InstName string 92 UbuntuMetadata map[string]string 93 WindowsMetadata map[string]string 94 StartInstArgs environs.StartInstanceParams 95 InstanceType instances.InstanceType 96 97 Ports []network.PortRange 98 } 99 100 var _ environs.Environ = (*environ)(nil) 101 var _ simplestreams.HasRegion = (*environ)(nil) 102 var _ instance.Instance = (*environInstance)(nil) 103 104 func (s *BaseSuiteUnpatched) SetUpTest(c *gc.C) { 105 s.IsolationSuite.SetUpTest(c) 106 107 s.initEnv(c) 108 s.initInst(c) 109 s.initNet(c) 110 } 111 112 func (s *BaseSuiteUnpatched) initEnv(c *gc.C) { 113 s.Env = &environ{ 114 name: "google", 115 } 116 cfg := s.NewConfig(c, nil) 117 s.setConfig(c, cfg) 118 } 119 120 func (s *BaseSuiteUnpatched) initInst(c *gc.C) { 121 tools := []*coretools.Tools{{ 122 Version: version.Binary{Arch: arch.AMD64, Series: "trusty"}, 123 URL: "https://example.org", 124 }} 125 126 cons := constraints.Value{InstanceType: &allInstanceTypes[0].Name} 127 128 instanceConfig, err := instancecfg.NewBootstrapInstanceConfig(cons, cons, "trusty", "") 129 c.Assert(err, jc.ErrorIsNil) 130 131 err = instanceConfig.SetTools(coretools.List(tools)) 132 c.Assert(err, jc.ErrorIsNil) 133 instanceConfig.AuthorizedKeys = s.Config.AuthorizedKeys() 134 135 userData, err := providerinit.ComposeUserData(instanceConfig, nil, GCERenderer{}) 136 c.Assert(err, jc.ErrorIsNil) 137 138 authKeys, err := google.FormatAuthorizedKeys(instanceConfig.AuthorizedKeys, "ubuntu") 139 c.Assert(err, jc.ErrorIsNil) 140 141 s.UbuntuMetadata = map[string]string{ 142 metadataKeyIsState: metadataValueTrue, 143 metadataKeyCloudInit: string(userData), 144 metadataKeyEncoding: "base64", 145 metadataKeySSHKeys: authKeys, 146 } 147 s.WindowsMetadata = map[string]string{ 148 metadataKeyWindowsUserdata: string(userData), 149 metadataKeyWindowsSysprep: fmt.Sprintf(winSetHostnameScript, "juju.*"), 150 } 151 s.Addresses = []network.Address{{ 152 Value: "10.0.0.1", 153 Type: network.IPv4Address, 154 Scope: network.ScopeCloudLocal, 155 }} 156 s.Instance = s.NewInstance(c, "spam") 157 s.BaseInstance = s.Instance.base 158 s.InstName = s.Prefix + "machine-spam" 159 160 s.StartInstArgs = environs.StartInstanceParams{ 161 InstanceConfig: instanceConfig, 162 Tools: tools, 163 Constraints: cons, 164 //Placement: "", 165 //DistributionGroup: nil, 166 } 167 168 s.InstanceType = allInstanceTypes[0] 169 170 // Storage 171 eUUID := s.Env.Config().UUID() 172 s.BaseDisk = &google.Disk{ 173 Id: 1234567, 174 Name: "home-zone--c930380d-8337-4bf5-b07a-9dbb5ae771e4", 175 Zone: "home-zone", 176 Status: google.StatusReady, 177 Size: 1024, 178 Description: eUUID, 179 } 180 } 181 182 func (s *BaseSuiteUnpatched) initNet(c *gc.C) { 183 s.Ports = []network.PortRange{{ 184 FromPort: 80, 185 ToPort: 80, 186 Protocol: "tcp", 187 }} 188 } 189 190 func (s *BaseSuiteUnpatched) setConfig(c *gc.C, cfg *config.Config) { 191 s.Config = cfg 192 ecfg, err := newValidConfig(cfg, configDefaults) 193 c.Assert(err, jc.ErrorIsNil) 194 s.EnvConfig = ecfg 195 uuid := cfg.UUID() 196 s.Env.uuid = uuid 197 s.Env.ecfg = s.EnvConfig 198 s.Prefix = "juju-" + uuid + "-" 199 } 200 201 func (s *BaseSuiteUnpatched) NewConfig(c *gc.C, updates testing.Attrs) *config.Config { 202 var err error 203 cfg := testing.ModelConfig(c) 204 cfg, err = cfg.Apply(ConfigAttrs) 205 c.Assert(err, jc.ErrorIsNil) 206 cfg, err = cfg.Apply(updates) 207 c.Assert(err, jc.ErrorIsNil) 208 return cfg 209 } 210 211 func (s *BaseSuiteUnpatched) UpdateConfig(c *gc.C, attrs map[string]interface{}) { 212 cfg, err := s.Config.Apply(attrs) 213 c.Assert(err, jc.ErrorIsNil) 214 s.setConfig(c, cfg) 215 } 216 217 func (s *BaseSuiteUnpatched) NewBaseInstance(c *gc.C, id string) *google.Instance { 218 diskSpec := google.DiskSpec{ 219 Series: "trusty", 220 SizeHintGB: 15, 221 ImageURL: "some/image/path", 222 Boot: true, 223 Scratch: false, 224 Readonly: false, 225 AutoDelete: true, 226 } 227 instanceSpec := google.InstanceSpec{ 228 ID: id, 229 Type: "mtype", 230 Disks: []google.DiskSpec{diskSpec}, 231 Network: google.NetworkSpec{Name: "somenetwork"}, 232 NetworkInterfaces: []string{"somenetif"}, 233 Metadata: s.UbuntuMetadata, 234 Tags: []string{id}, 235 } 236 summary := google.InstanceSummary{ 237 ID: id, 238 ZoneName: "home-zone", 239 Status: google.StatusRunning, 240 Metadata: s.UbuntuMetadata, 241 Addresses: s.Addresses, 242 } 243 return google.NewInstance(summary, &instanceSpec) 244 } 245 246 func (s *BaseSuiteUnpatched) NewInstance(c *gc.C, id string) *environInstance { 247 base := s.NewBaseInstance(c, id) 248 return newInstance(base, s.Env) 249 } 250 251 type BaseSuite struct { 252 BaseSuiteUnpatched 253 254 FakeConn *fakeConn 255 FakeCommon *fakeCommon 256 FakeEnviron *fakeEnviron 257 } 258 259 func (s *BaseSuite) SetUpTest(c *gc.C) { 260 s.BaseSuiteUnpatched.SetUpTest(c) 261 262 s.FakeConn = &fakeConn{} 263 s.FakeCommon = &fakeCommon{} 264 s.FakeEnviron = &fakeEnviron{} 265 266 // Patch out all expensive external deps. 267 s.Env.gce = s.FakeConn 268 s.PatchValue(&newConnection, func(*environConfig) (gceConnection, error) { 269 return s.FakeConn, nil 270 }) 271 s.PatchValue(&supportedArchitectures, s.FakeCommon.SupportedArchitectures) 272 s.PatchValue(&bootstrap, s.FakeCommon.Bootstrap) 273 s.PatchValue(&destroyEnv, s.FakeCommon.Destroy) 274 s.PatchValue(&availabilityZoneAllocations, s.FakeCommon.AvailabilityZoneAllocations) 275 s.PatchValue(&buildInstanceSpec, s.FakeEnviron.BuildInstanceSpec) 276 s.PatchValue(&getHardwareCharacteristics, s.FakeEnviron.GetHardwareCharacteristics) 277 s.PatchValue(&newRawInstance, s.FakeEnviron.NewRawInstance) 278 s.PatchValue(&findInstanceSpec, s.FakeEnviron.FindInstanceSpec) 279 s.PatchValue(&getInstances, s.FakeEnviron.GetInstances) 280 } 281 282 func (s *BaseSuite) CheckNoAPI(c *gc.C) { 283 c.Check(s.FakeConn.Calls, gc.HasLen, 0) 284 } 285 286 // TODO(ericsnow) Move fakeCallArgs, fakeCall, and fake to the testing repo? 287 288 type FakeCallArgs map[string]interface{} 289 290 type FakeCall struct { 291 FuncName string 292 Args FakeCallArgs 293 } 294 295 type fake struct { 296 calls []FakeCall 297 298 Err error 299 FailOnCall int 300 } 301 302 func (f *fake) err() error { 303 if len(f.calls) != f.FailOnCall+1 { 304 return nil 305 } 306 return f.Err 307 } 308 309 func (f *fake) addCall(funcName string, args FakeCallArgs) { 310 f.calls = append(f.calls, FakeCall{ 311 FuncName: funcName, 312 Args: args, 313 }) 314 } 315 316 func (f *fake) CheckCalls(c *gc.C, expected []FakeCall) { 317 c.Check(f.calls, jc.DeepEquals, expected) 318 } 319 320 type fakeCommon struct { 321 fake 322 323 Arches []string 324 Arch string 325 Series string 326 BSFinalizer environs.BootstrapFinalizer 327 AZInstances []common.AvailabilityZoneInstances 328 } 329 330 func (fc *fakeCommon) SupportedArchitectures(env environs.Environ, cons *imagemetadata.ImageConstraint) ([]string, error) { 331 fc.addCall("SupportedArchitectures", FakeCallArgs{ 332 "switch": env, 333 "cons": cons, 334 }) 335 return fc.Arches, fc.err() 336 } 337 338 func (fc *fakeCommon) Bootstrap(ctx environs.BootstrapContext, env environs.Environ, params environs.BootstrapParams) (*environs.BootstrapResult, error) { 339 fc.addCall("Bootstrap", FakeCallArgs{ 340 "ctx": ctx, 341 "switch": env, 342 "params": params, 343 }) 344 345 result := &environs.BootstrapResult{ 346 Arch: fc.Arch, 347 Series: fc.Series, 348 Finalize: fc.BSFinalizer, 349 } 350 return result, fc.err() 351 } 352 353 func (fc *fakeCommon) Destroy(env environs.Environ) error { 354 fc.addCall("Destroy", FakeCallArgs{ 355 "switch": env, 356 }) 357 return fc.err() 358 } 359 360 func (fc *fakeCommon) AvailabilityZoneAllocations(env common.ZonedEnviron, group []instance.Id) ([]common.AvailabilityZoneInstances, error) { 361 fc.addCall("AvailabilityZoneAllocations", FakeCallArgs{ 362 "switch": env, 363 "group": group, 364 }) 365 return fc.AZInstances, fc.err() 366 } 367 368 type fakeEnviron struct { 369 fake 370 371 Inst *google.Instance 372 Insts []instance.Instance 373 Hwc *instance.HardwareCharacteristics 374 Spec *instances.InstanceSpec 375 } 376 377 func (fe *fakeEnviron) GetInstances(env *environ) ([]instance.Instance, error) { 378 fe.addCall("GetInstances", FakeCallArgs{ 379 "switch": env, 380 }) 381 return fe.Insts, fe.err() 382 } 383 384 func (fe *fakeEnviron) BuildInstanceSpec(env *environ, args environs.StartInstanceParams) (*instances.InstanceSpec, error) { 385 fe.addCall("BuildInstanceSpec", FakeCallArgs{ 386 "switch": env, 387 "args": args, 388 }) 389 return fe.Spec, fe.err() 390 } 391 392 func (fe *fakeEnviron) GetHardwareCharacteristics(env *environ, spec *instances.InstanceSpec, inst *environInstance) *instance.HardwareCharacteristics { 393 fe.addCall("GetHardwareCharacteristics", FakeCallArgs{ 394 "switch": env, 395 "spec": spec, 396 "inst": inst, 397 }) 398 return fe.Hwc 399 } 400 401 func (fe *fakeEnviron) NewRawInstance(env *environ, args environs.StartInstanceParams, spec *instances.InstanceSpec) (*google.Instance, error) { 402 fe.addCall("NewRawInstance", FakeCallArgs{ 403 "switch": env, 404 "args": args, 405 "spec": spec, 406 }) 407 return fe.Inst, fe.err() 408 } 409 410 func (fe *fakeEnviron) FindInstanceSpec( 411 env *environ, 412 ic *instances.InstanceConstraint, 413 imageMetadata []*imagemetadata.ImageMetadata, 414 ) (*instances.InstanceSpec, error) { 415 fe.addCall("FindInstanceSpec", FakeCallArgs{ 416 "switch": env, 417 "ic": ic, 418 "imageMetadata": imageMetadata, 419 }) 420 return fe.Spec, fe.err() 421 } 422 423 // TODO(ericsnow) Refactor fakeConnCall and fakeConn to embed fakeCall and fake. 424 425 type fakeConnCall struct { 426 FuncName string 427 428 ID string 429 IDs []string 430 ZoneName string 431 ZoneNames []string 432 Prefix string 433 Statuses []string 434 InstanceSpec google.InstanceSpec 435 FirewallName string 436 PortRanges []network.PortRange 437 Region string 438 Disks []google.DiskSpec 439 VolumeName string 440 InstanceId string 441 Mode string 442 } 443 444 type fakeConn struct { 445 Calls []fakeConnCall 446 447 Inst *google.Instance 448 Insts []google.Instance 449 PortRanges []network.PortRange 450 Zones []google.AvailabilityZone 451 452 GoogleDisks []*google.Disk 453 GoogleDisk *google.Disk 454 AttachedDisk *google.AttachedDisk 455 AttachedDisks []*google.AttachedDisk 456 457 Err error 458 FailOnCall int 459 } 460 461 func (fc *fakeConn) err() error { 462 if len(fc.Calls) != fc.FailOnCall+1 { 463 return nil 464 } 465 return fc.Err 466 } 467 468 func (fc *fakeConn) VerifyCredentials() error { 469 fc.Calls = append(fc.Calls, fakeConnCall{ 470 FuncName: "", 471 }) 472 return fc.err() 473 } 474 475 func (fc *fakeConn) Instance(id, zone string) (google.Instance, error) { 476 fc.Calls = append(fc.Calls, fakeConnCall{ 477 FuncName: "Instance", 478 ID: id, 479 ZoneName: zone, 480 }) 481 return *fc.Inst, fc.err() 482 } 483 484 func (fc *fakeConn) Instances(prefix string, statuses ...string) ([]google.Instance, error) { 485 fc.Calls = append(fc.Calls, fakeConnCall{ 486 FuncName: "Instances", 487 Prefix: prefix, 488 Statuses: statuses, 489 }) 490 return fc.Insts, fc.err() 491 } 492 493 func (fc *fakeConn) AddInstance(spec google.InstanceSpec, zones ...string) (*google.Instance, error) { 494 fc.Calls = append(fc.Calls, fakeConnCall{ 495 FuncName: "AddInstance", 496 InstanceSpec: spec, 497 ZoneNames: zones, 498 }) 499 return fc.Inst, fc.err() 500 } 501 502 func (fc *fakeConn) RemoveInstances(prefix string, ids ...string) error { 503 fc.Calls = append(fc.Calls, fakeConnCall{ 504 FuncName: "RemoveInstances", 505 Prefix: prefix, 506 IDs: ids, 507 }) 508 return fc.err() 509 } 510 511 func (fc *fakeConn) Ports(fwname string) ([]network.PortRange, error) { 512 fc.Calls = append(fc.Calls, fakeConnCall{ 513 FuncName: "Ports", 514 FirewallName: fwname, 515 }) 516 return fc.PortRanges, fc.err() 517 } 518 519 func (fc *fakeConn) OpenPorts(fwname string, ports ...network.PortRange) error { 520 fc.Calls = append(fc.Calls, fakeConnCall{ 521 FuncName: "OpenPorts", 522 FirewallName: fwname, 523 PortRanges: ports, 524 }) 525 return fc.err() 526 } 527 528 func (fc *fakeConn) ClosePorts(fwname string, ports ...network.PortRange) error { 529 fc.Calls = append(fc.Calls, fakeConnCall{ 530 FuncName: "ClosePorts", 531 FirewallName: fwname, 532 PortRanges: ports, 533 }) 534 return fc.err() 535 } 536 537 func (fc *fakeConn) AvailabilityZones(region string) ([]google.AvailabilityZone, error) { 538 fc.Calls = append(fc.Calls, fakeConnCall{ 539 FuncName: "AvailabilityZones", 540 Region: region, 541 }) 542 return fc.Zones, fc.err() 543 } 544 545 func (fc *fakeConn) CreateDisks(zone string, disks []google.DiskSpec) ([]*google.Disk, error) { 546 fc.Calls = append(fc.Calls, fakeConnCall{ 547 FuncName: "CreateDisks", 548 ZoneName: zone, 549 Disks: disks, 550 }) 551 return fc.GoogleDisks, fc.err() 552 } 553 554 func (fc *fakeConn) Disks(zone string) ([]*google.Disk, error) { 555 fc.Calls = append(fc.Calls, fakeConnCall{ 556 FuncName: "Disks", 557 ZoneName: zone, 558 }) 559 return fc.GoogleDisks, fc.err() 560 } 561 562 func (fc *fakeConn) RemoveDisk(zone, id string) error { 563 fc.Calls = append(fc.Calls, fakeConnCall{ 564 FuncName: "RemoveDisk", 565 ZoneName: zone, 566 ID: id, 567 }) 568 return fc.err() 569 } 570 571 func (fc *fakeConn) Disk(zone, id string) (*google.Disk, error) { 572 fc.Calls = append(fc.Calls, fakeConnCall{ 573 FuncName: "Disk", 574 ZoneName: zone, 575 ID: id, 576 }) 577 return fc.GoogleDisk, fc.err() 578 } 579 580 func (fc *fakeConn) AttachDisk(zone, volumeName, instanceId string, mode google.DiskMode) (*google.AttachedDisk, error) { 581 fc.Calls = append(fc.Calls, fakeConnCall{ 582 FuncName: "AttachDisk", 583 ZoneName: zone, 584 VolumeName: volumeName, 585 InstanceId: instanceId, 586 Mode: string(mode), 587 }) 588 return fc.AttachedDisk, fc.err() 589 } 590 591 func (fc *fakeConn) DetachDisk(zone, instanceId, volumeName string) error { 592 fc.Calls = append(fc.Calls, fakeConnCall{ 593 FuncName: "DetachDisk", 594 ZoneName: zone, 595 InstanceId: instanceId, 596 VolumeName: volumeName, 597 }) 598 return fc.err() 599 } 600 601 func (fc *fakeConn) InstanceDisks(zone, instanceId string) ([]*google.AttachedDisk, error) { 602 fc.Calls = append(fc.Calls, fakeConnCall{ 603 FuncName: "InstanceDisks", 604 ZoneName: zone, 605 InstanceId: instanceId, 606 }) 607 return fc.AttachedDisks, fc.err() 608 } 609 610 func (fc *fakeConn) WasCalled(funcName string) (bool, []fakeConnCall) { 611 var calls []fakeConnCall 612 called := false 613 for _, call := range fc.Calls { 614 if call.FuncName == funcName { 615 called = true 616 calls = append(calls, call) 617 } 618 } 619 return called, calls 620 }