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