github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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 "net/url" 9 "strings" 10 11 "github.com/juju/errors" 12 gitjujutesting "github.com/juju/testing" 13 jc "github.com/juju/testing/checkers" 14 "github.com/juju/utils/arch" 15 "github.com/juju/version" 16 "google.golang.org/api/compute/v1" 17 gc "gopkg.in/check.v1" 18 19 "github.com/juju/juju/cloud" 20 "github.com/juju/juju/cloudconfig/instancecfg" 21 "github.com/juju/juju/cloudconfig/providerinit" 22 "github.com/juju/juju/core/constraints" 23 "github.com/juju/juju/core/instance" 24 "github.com/juju/juju/environs" 25 "github.com/juju/juju/environs/config" 26 "github.com/juju/juju/environs/context" 27 "github.com/juju/juju/environs/imagemetadata" 28 "github.com/juju/juju/environs/instances" 29 "github.com/juju/juju/environs/simplestreams" 30 "github.com/juju/juju/environs/tags" 31 "github.com/juju/juju/network" 32 "github.com/juju/juju/provider/common" 33 "github.com/juju/juju/provider/gce/google" 34 "github.com/juju/juju/testing" 35 coretools "github.com/juju/juju/tools" 36 ) 37 38 // Ensure GCE provider supports the expected interfaces. 39 var ( 40 _ config.ConfigSchemaSource = (*environProvider)(nil) 41 ) 42 43 // These values are fake GCE auth credentials for use in tests. 44 const ( 45 ClientName = "ba9876543210-0123456789abcdefghijklmnopqrstuv" 46 ClientID = ClientName + ".apps.googleusercontent.com" 47 ClientEmail = ClientName + "@developer.gserviceaccount.com" 48 ProjectID = "my-juju" 49 PrivateKey = `-----BEGIN PRIVATE KEY----- 50 ... 51 ... 52 ... 53 ... 54 ... 55 ... 56 ... 57 ... 58 ... 59 ... 60 ... 61 ... 62 ... 63 ... 64 -----END PRIVATE KEY----- 65 ` 66 ) 67 68 // These are fake config values for use in tests. 69 var ( 70 AuthFile = fmt.Sprintf(`{ 71 "private_key_id": "abcdef0123456789abcdef0123456789abcdef01", 72 "private_key": "%s", 73 "client_email": "%s", 74 "client_id": "%s", 75 "type": "service_account" 76 }`, strings.Replace(PrivateKey, "\n", "\\n", -1), ClientEmail, ClientID) 77 78 ConfigAttrs = testing.FakeConfig().Merge(testing.Attrs{ 79 "type": "gce", 80 "uuid": "2d02eeac-9dbb-11e4-89d3-123b93f75cba", 81 "controller-uuid": "bfef02f1-932a-425a-a102-62175dcabd1d", 82 }) 83 ) 84 85 func MakeTestCloudSpec() environs.CloudSpec { 86 cred := MakeTestCredential() 87 return environs.CloudSpec{ 88 Type: "gce", 89 Name: "google", 90 Region: "us-east1", 91 Endpoint: "https://www.googleapis.com", 92 Credential: &cred, 93 } 94 } 95 96 func MakeTestCredential() cloud.Credential { 97 return cloud.NewCredential( 98 cloud.OAuth2AuthType, 99 map[string]string{ 100 "project-id": ProjectID, 101 "client-id": ClientID, 102 "client-email": ClientEmail, 103 "private-key": PrivateKey, 104 }, 105 ) 106 } 107 108 type BaseSuiteUnpatched struct { 109 gitjujutesting.IsolationSuite 110 111 ControllerUUID string 112 Config *config.Config 113 EnvConfig *environConfig 114 Env *environ 115 116 Addresses []network.Address 117 BaseInstance *google.Instance 118 BaseDisk *google.Disk 119 Instance *environInstance 120 InstName string 121 UbuntuMetadata map[string]string 122 WindowsMetadata map[string]string 123 StartInstArgs environs.StartInstanceParams 124 InstanceType instances.InstanceType 125 126 Rules []network.IngressRule 127 } 128 129 var _ environs.Environ = (*environ)(nil) 130 var _ simplestreams.HasRegion = (*environ)(nil) 131 var _ instances.Instance = (*environInstance)(nil) 132 133 func (s *BaseSuiteUnpatched) SetUpTest(c *gc.C) { 134 s.IsolationSuite.SetUpTest(c) 135 136 s.ControllerUUID = testing.FakeControllerConfig().ControllerUUID() 137 s.initEnv(c) 138 s.initInst(c) 139 s.initNet(c) 140 } 141 142 func (s *BaseSuiteUnpatched) Prefix() string { 143 return s.Env.namespace.Prefix() 144 } 145 146 func (s *BaseSuiteUnpatched) initEnv(c *gc.C) { 147 s.Env = &environ{ 148 name: "google", 149 cloud: MakeTestCloudSpec(), 150 } 151 cfg := s.NewConfig(c, nil) 152 s.setConfig(c, cfg) 153 } 154 155 func (s *BaseSuiteUnpatched) initInst(c *gc.C) { 156 tools := []*coretools.Tools{{ 157 Version: version.Binary{Arch: arch.AMD64, Series: "trusty"}, 158 URL: "https://example.org", 159 }} 160 161 cons := constraints.Value{InstanceType: &allInstanceTypes[0].Name} 162 163 instanceConfig, err := instancecfg.NewBootstrapInstanceConfig(testing.FakeControllerConfig(), cons, cons, "trusty", "") 164 c.Assert(err, jc.ErrorIsNil) 165 166 err = instanceConfig.SetTools(coretools.List(tools)) 167 c.Assert(err, jc.ErrorIsNil) 168 instanceConfig.AuthorizedKeys = s.Config.AuthorizedKeys() 169 170 userData, err := providerinit.ComposeUserData(instanceConfig, nil, GCERenderer{}) 171 c.Assert(err, jc.ErrorIsNil) 172 173 s.UbuntuMetadata = map[string]string{ 174 tags.JujuIsController: "true", 175 tags.JujuController: s.ControllerUUID, 176 metadataKeyCloudInit: string(userData), 177 metadataKeyEncoding: "base64", 178 } 179 instanceConfig.Tags = map[string]string{ 180 tags.JujuIsController: "true", 181 tags.JujuController: s.ControllerUUID, 182 } 183 s.WindowsMetadata = map[string]string{ 184 metadataKeyWindowsUserdata: string(userData), 185 metadataKeyWindowsSysprep: fmt.Sprintf(winSetHostnameScript, "juju.*"), 186 } 187 s.Addresses = []network.Address{{ 188 Value: "10.0.0.1", 189 Type: network.IPv4Address, 190 Scope: network.ScopeCloudLocal, 191 }} 192 s.Instance = s.NewInstance(c, "spam") 193 s.BaseInstance = s.Instance.base 194 s.InstName, err = s.Env.namespace.Hostname("42") 195 c.Assert(err, jc.ErrorIsNil) 196 197 s.StartInstArgs = environs.StartInstanceParams{ 198 ControllerUUID: s.ControllerUUID, 199 InstanceConfig: instanceConfig, 200 Tools: tools, 201 Constraints: cons, 202 //Placement: "", 203 } 204 205 s.InstanceType = allInstanceTypes[0] 206 207 // Storage 208 eUUID := s.Env.Config().UUID() 209 s.BaseDisk = &google.Disk{ 210 Id: 1234567, 211 Name: "home-zone--c930380d-8337-4bf5-b07a-9dbb5ae771e4", 212 Zone: "home-zone", 213 Status: google.StatusReady, 214 Size: 1024, 215 Description: eUUID, 216 LabelFingerprint: "foo", 217 Labels: map[string]string{ 218 "yodel": "eh", 219 "juju-model-uuid": eUUID, 220 "juju-controller-uuid": s.ControllerUUID, 221 }, 222 } 223 } 224 225 func (s *BaseSuiteUnpatched) initNet(c *gc.C) { 226 s.Rules = []network.IngressRule{network.MustNewIngressRule("tcp", 80, 80)} 227 } 228 229 func (s *BaseSuiteUnpatched) setConfig(c *gc.C, cfg *config.Config) { 230 s.Config = cfg 231 ecfg, err := newConfig(cfg, nil) 232 c.Assert(err, jc.ErrorIsNil) 233 s.EnvConfig = ecfg 234 uuid := cfg.UUID() 235 s.Env.uuid = uuid 236 s.Env.ecfg = s.EnvConfig 237 namespace, err := instance.NewNamespace(uuid) 238 c.Assert(err, jc.ErrorIsNil) 239 s.Env.namespace = namespace 240 } 241 242 func (s *BaseSuiteUnpatched) NewConfig(c *gc.C, updates testing.Attrs) *config.Config { 243 var err error 244 cfg := testing.ModelConfig(c) 245 cfg, err = cfg.Apply(ConfigAttrs) 246 c.Assert(err, jc.ErrorIsNil) 247 cfg, err = cfg.Apply(updates) 248 c.Assert(err, jc.ErrorIsNil) 249 return cfg 250 } 251 252 func (s *BaseSuiteUnpatched) UpdateConfig(c *gc.C, attrs map[string]interface{}) { 253 cfg, err := s.Config.Apply(attrs) 254 c.Assert(err, jc.ErrorIsNil) 255 s.setConfig(c, cfg) 256 } 257 258 func (s *BaseSuiteUnpatched) NewBaseInstance(c *gc.C, id string) *google.Instance { 259 diskSpec := google.DiskSpec{ 260 Series: "trusty", 261 SizeHintGB: 15, 262 ImageURL: "some/image/path", 263 Boot: true, 264 Scratch: false, 265 Readonly: false, 266 AutoDelete: true, 267 } 268 instanceSpec := google.InstanceSpec{ 269 ID: id, 270 Type: "mtype", 271 Disks: []google.DiskSpec{diskSpec}, 272 Network: google.NetworkSpec{Name: "somenetwork"}, 273 NetworkInterfaces: []string{"somenetif"}, 274 Metadata: s.UbuntuMetadata, 275 Tags: []string{id}, 276 } 277 summary := google.InstanceSummary{ 278 ID: id, 279 ZoneName: "home-zone", 280 Status: google.StatusRunning, 281 Metadata: s.UbuntuMetadata, 282 Addresses: s.Addresses, 283 NetworkInterfaces: []*compute.NetworkInterface{{ 284 Name: "somenetif", 285 NetworkIP: "10.0.10.3", 286 Network: "https://www.googleapis.com/compute/v1/projects/sonic-youth/global/networks/go-team", 287 Subnetwork: "https://www.googleapis.com/compute/v1/projects/sonic-youth/regions/asia-east1/subnetworks/go-team", 288 AccessConfigs: []*compute.AccessConfig{{ 289 Type: "ONE_TO_ONE_NAT", 290 Name: "ExternalNAT", 291 NatIP: "25.185.142.226", 292 }}, 293 }}, 294 } 295 return google.NewInstance(summary, &instanceSpec) 296 } 297 298 func (s *BaseSuiteUnpatched) NewInstance(c *gc.C, id string) *environInstance { 299 base := s.NewBaseInstance(c, id) 300 return newInstance(base, s.Env) 301 } 302 303 func (s *BaseSuiteUnpatched) NewInstanceFromBase(base *google.Instance) *environInstance { 304 return newInstance(base, s.Env) 305 } 306 307 type BaseSuite struct { 308 BaseSuiteUnpatched 309 310 FakeConn *fakeConn 311 FakeCommon *fakeCommon 312 FakeEnviron *fakeEnviron 313 314 CallCtx *context.CloudCallContext 315 InvalidatedCredentials bool 316 } 317 318 func (s *BaseSuite) SetUpTest(c *gc.C) { 319 s.BaseSuiteUnpatched.SetUpTest(c) 320 321 s.FakeConn = &fakeConn{} 322 s.FakeCommon = &fakeCommon{} 323 s.FakeEnviron = &fakeEnviron{} 324 325 // Patch out all expensive external deps. 326 s.Env.gce = s.FakeConn 327 s.PatchValue(&newConnection, func(google.ConnectionConfig, *google.Credentials) (gceConnection, error) { 328 return s.FakeConn, nil 329 }) 330 s.PatchValue(&bootstrap, s.FakeCommon.Bootstrap) 331 s.PatchValue(&destroyEnv, s.FakeCommon.Destroy) 332 s.PatchValue(&availabilityZoneAllocations, s.FakeCommon.AvailabilityZoneAllocations) 333 s.PatchValue(&buildInstanceSpec, s.FakeEnviron.BuildInstanceSpec) 334 s.PatchValue(&getHardwareCharacteristics, s.FakeEnviron.GetHardwareCharacteristics) 335 s.PatchValue(&newRawInstance, s.FakeEnviron.NewRawInstance) 336 s.PatchValue(&findInstanceSpec, s.FakeEnviron.FindInstanceSpec) 337 s.PatchValue(&getInstances, s.FakeEnviron.GetInstances) 338 339 s.CallCtx = &context.CloudCallContext{ 340 InvalidateCredentialFunc: func(string) error { 341 s.InvalidatedCredentials = true 342 return nil 343 }, 344 } 345 } 346 347 func (s *BaseSuite) TearDownTest(c *gc.C) { 348 s.BaseSuiteUnpatched.TearDownTest(c) 349 s.InvalidatedCredentials = false 350 } 351 352 func (s *BaseSuite) CheckNoAPI(c *gc.C) { 353 c.Check(s.FakeConn.Calls, gc.HasLen, 0) 354 } 355 356 // TODO(ericsnow) Move fakeCallArgs, fakeCall, and fake to the testing repo? 357 358 type FakeCallArgs map[string]interface{} 359 360 type FakeCall struct { 361 FuncName string 362 Args FakeCallArgs 363 } 364 365 type fake struct { 366 calls []FakeCall 367 368 Err error 369 FailOnCall int 370 } 371 372 func (f *fake) err() error { 373 if len(f.calls) != f.FailOnCall+1 { 374 return nil 375 } 376 return f.Err 377 } 378 379 func (f *fake) addCall(funcName string, args FakeCallArgs) { 380 f.calls = append(f.calls, FakeCall{ 381 FuncName: funcName, 382 Args: args, 383 }) 384 } 385 386 func (f *fake) CheckCalls(c *gc.C, expected []FakeCall) { 387 c.Check(f.calls, jc.DeepEquals, expected) 388 } 389 390 type fakeCommon struct { 391 fake 392 393 Arch string 394 Series string 395 BSFinalizer environs.CloudBootstrapFinalizer 396 AZInstances []common.AvailabilityZoneInstances 397 } 398 399 func (fc *fakeCommon) Bootstrap(ctx environs.BootstrapContext, env environs.Environ, callCtx context.ProviderCallContext, params environs.BootstrapParams) (*environs.BootstrapResult, error) { 400 fc.addCall("Bootstrap", FakeCallArgs{ 401 "ctx": ctx, 402 "switch": env, 403 "params": params, 404 }) 405 406 result := &environs.BootstrapResult{ 407 Arch: fc.Arch, 408 Series: fc.Series, 409 CloudBootstrapFinalizer: fc.BSFinalizer, 410 } 411 return result, fc.err() 412 } 413 414 func (fc *fakeCommon) Destroy(env environs.Environ, ctx context.ProviderCallContext) error { 415 fc.addCall("Destroy", FakeCallArgs{ 416 "switch": env, 417 }) 418 return fc.err() 419 } 420 421 func (fc *fakeCommon) AvailabilityZoneAllocations(env common.ZonedEnviron, ctx context.ProviderCallContext, group []instance.Id) ([]common.AvailabilityZoneInstances, error) { 422 fc.addCall("AvailabilityZoneAllocations", FakeCallArgs{ 423 "switch": env, 424 "group": group, 425 }) 426 return fc.AZInstances, fc.err() 427 } 428 429 type fakeEnviron struct { 430 fake 431 432 Inst *google.Instance 433 Insts []instances.Instance 434 Hwc *instance.HardwareCharacteristics 435 Spec *instances.InstanceSpec 436 } 437 438 func (fe *fakeEnviron) GetInstances(env *environ, ctx context.ProviderCallContext) ([]instances.Instance, error) { 439 fe.addCall("GetInstances", FakeCallArgs{ 440 "switch": env, 441 }) 442 return fe.Insts, fe.err() 443 } 444 445 func (fe *fakeEnviron) BuildInstanceSpec(env *environ, args environs.StartInstanceParams) (*instances.InstanceSpec, error) { 446 fe.addCall("BuildInstanceSpec", FakeCallArgs{ 447 "switch": env, 448 "args": args, 449 }) 450 return fe.Spec, fe.err() 451 } 452 453 func (fe *fakeEnviron) GetHardwareCharacteristics(env *environ, spec *instances.InstanceSpec, inst *environInstance) *instance.HardwareCharacteristics { 454 fe.addCall("GetHardwareCharacteristics", FakeCallArgs{ 455 "switch": env, 456 "spec": spec, 457 "inst": inst, 458 }) 459 return fe.Hwc 460 } 461 462 func (fe *fakeEnviron) NewRawInstance(env *environ, ctx context.ProviderCallContext, args environs.StartInstanceParams, spec *instances.InstanceSpec) (*google.Instance, error) { 463 fe.addCall("NewRawInstance", FakeCallArgs{ 464 "switch": env, 465 "args": args, 466 "spec": spec, 467 }) 468 return fe.Inst, fe.err() 469 } 470 471 func (fe *fakeEnviron) FindInstanceSpec( 472 env *environ, 473 ic *instances.InstanceConstraint, 474 imageMetadata []*imagemetadata.ImageMetadata, 475 ) (*instances.InstanceSpec, error) { 476 fe.addCall("FindInstanceSpec", FakeCallArgs{ 477 "switch": env, 478 "ic": ic, 479 "imageMetadata": imageMetadata, 480 }) 481 return fe.Spec, fe.err() 482 } 483 484 // TODO(ericsnow) Refactor fakeConnCall and fakeConn to embed fakeCall and fake. 485 486 type fakeConnCall struct { 487 FuncName string 488 489 ID string 490 IDs []string 491 ZoneName string 492 Prefix string 493 Statuses []string 494 InstanceSpec google.InstanceSpec 495 FirewallName string 496 Rules []network.IngressRule 497 Region string 498 Disks []google.DiskSpec 499 VolumeName string 500 InstanceId string 501 Mode string 502 Key string 503 Value string 504 LabelFingerprint string 505 Labels map[string]string 506 } 507 508 type fakeConn struct { 509 Calls []fakeConnCall 510 511 Inst *google.Instance 512 Insts []google.Instance 513 Rules []network.IngressRule 514 Zones []google.AvailabilityZone 515 Subnets []*compute.Subnetwork 516 Networks_ []*compute.Network 517 518 GoogleDisks []*google.Disk 519 GoogleDisk *google.Disk 520 AttachedDisk *google.AttachedDisk 521 AttachedDisks []*google.AttachedDisk 522 523 Err error 524 FailOnCall int 525 } 526 527 func (fc *fakeConn) err() error { 528 if len(fc.Calls) != fc.FailOnCall+1 { 529 return nil 530 } 531 return fc.Err 532 } 533 534 func (fc *fakeConn) VerifyCredentials() error { 535 fc.Calls = append(fc.Calls, fakeConnCall{ 536 FuncName: "", 537 }) 538 return fc.err() 539 } 540 541 func (fc *fakeConn) Instance(id, zone string) (google.Instance, error) { 542 fc.Calls = append(fc.Calls, fakeConnCall{ 543 FuncName: "Instance", 544 ID: id, 545 ZoneName: zone, 546 }) 547 return *fc.Inst, fc.err() 548 } 549 550 func (fc *fakeConn) Instances(prefix string, statuses ...string) ([]google.Instance, error) { 551 fc.Calls = append(fc.Calls, fakeConnCall{ 552 FuncName: "Instances", 553 Prefix: prefix, 554 Statuses: statuses, 555 }) 556 return fc.Insts, fc.err() 557 } 558 559 func (fc *fakeConn) AddInstance(spec google.InstanceSpec) (*google.Instance, error) { 560 fc.Calls = append(fc.Calls, fakeConnCall{ 561 FuncName: "AddInstance", 562 InstanceSpec: spec, 563 }) 564 return fc.Inst, fc.err() 565 } 566 567 func (fc *fakeConn) RemoveInstances(prefix string, ids ...string) error { 568 fc.Calls = append(fc.Calls, fakeConnCall{ 569 FuncName: "RemoveInstances", 570 Prefix: prefix, 571 IDs: ids, 572 }) 573 return fc.err() 574 } 575 576 func (fc *fakeConn) UpdateMetadata(key, value string, ids ...string) error { 577 fc.Calls = append(fc.Calls, fakeConnCall{ 578 FuncName: "UpdateMetadata", 579 Key: key, 580 Value: value, 581 IDs: ids, 582 }) 583 return fc.err() 584 } 585 586 func (fc *fakeConn) IngressRules(fwname string) ([]network.IngressRule, error) { 587 fc.Calls = append(fc.Calls, fakeConnCall{ 588 FuncName: "Ports", 589 FirewallName: fwname, 590 }) 591 return fc.Rules, fc.err() 592 } 593 594 func (fc *fakeConn) OpenPorts(fwname string, rules ...network.IngressRule) error { 595 fc.Calls = append(fc.Calls, fakeConnCall{ 596 FuncName: "OpenPorts", 597 FirewallName: fwname, 598 Rules: rules, 599 }) 600 return fc.err() 601 } 602 603 func (fc *fakeConn) ClosePorts(fwname string, rules ...network.IngressRule) error { 604 fc.Calls = append(fc.Calls, fakeConnCall{ 605 FuncName: "ClosePorts", 606 FirewallName: fwname, 607 Rules: rules, 608 }) 609 return fc.err() 610 } 611 612 func (fc *fakeConn) AvailabilityZones(region string) ([]google.AvailabilityZone, error) { 613 fc.Calls = append(fc.Calls, fakeConnCall{ 614 FuncName: "AvailabilityZones", 615 Region: region, 616 }) 617 return fc.Zones, fc.err() 618 } 619 620 func (fc *fakeConn) Subnetworks(region string) ([]*compute.Subnetwork, error) { 621 fc.Calls = append(fc.Calls, fakeConnCall{ 622 FuncName: "Subnetworks", 623 Region: region, 624 }) 625 return fc.Subnets, fc.err() 626 } 627 628 func (fc *fakeConn) Networks() ([]*compute.Network, error) { 629 fc.Calls = append(fc.Calls, fakeConnCall{ 630 FuncName: "Networks", 631 }) 632 return fc.Networks_, fc.err() 633 } 634 635 func (fc *fakeConn) CreateDisks(zone string, disks []google.DiskSpec) ([]*google.Disk, error) { 636 fc.Calls = append(fc.Calls, fakeConnCall{ 637 FuncName: "CreateDisks", 638 ZoneName: zone, 639 Disks: disks, 640 }) 641 return fc.GoogleDisks, fc.err() 642 } 643 644 func (fc *fakeConn) Disks() ([]*google.Disk, error) { 645 fc.Calls = append(fc.Calls, fakeConnCall{ 646 FuncName: "Disks", 647 }) 648 return fc.GoogleDisks, fc.err() 649 } 650 651 func (fc *fakeConn) RemoveDisk(zone, id string) error { 652 fc.Calls = append(fc.Calls, fakeConnCall{ 653 FuncName: "RemoveDisk", 654 ZoneName: zone, 655 ID: id, 656 }) 657 return fc.err() 658 } 659 660 func (fc *fakeConn) Disk(zone, id string) (*google.Disk, error) { 661 fc.Calls = append(fc.Calls, fakeConnCall{ 662 FuncName: "Disk", 663 ZoneName: zone, 664 ID: id, 665 }) 666 return fc.GoogleDisk, fc.err() 667 } 668 669 func (fc *fakeConn) SetDiskLabels(zone, id, labelFingerprint string, labels map[string]string) error { 670 fc.Calls = append(fc.Calls, fakeConnCall{ 671 FuncName: "SetDiskLabels", 672 ZoneName: zone, 673 ID: id, 674 LabelFingerprint: labelFingerprint, 675 Labels: labels, 676 }) 677 return fc.err() 678 } 679 680 func (fc *fakeConn) AttachDisk(zone, volumeName, instanceId string, mode google.DiskMode) (*google.AttachedDisk, error) { 681 fc.Calls = append(fc.Calls, fakeConnCall{ 682 FuncName: "AttachDisk", 683 ZoneName: zone, 684 VolumeName: volumeName, 685 InstanceId: instanceId, 686 Mode: string(mode), 687 }) 688 return fc.AttachedDisk, fc.err() 689 } 690 691 func (fc *fakeConn) DetachDisk(zone, instanceId, volumeName string) error { 692 fc.Calls = append(fc.Calls, fakeConnCall{ 693 FuncName: "DetachDisk", 694 ZoneName: zone, 695 InstanceId: instanceId, 696 VolumeName: volumeName, 697 }) 698 return fc.err() 699 } 700 701 func (fc *fakeConn) InstanceDisks(zone, instanceId string) ([]*google.AttachedDisk, error) { 702 fc.Calls = append(fc.Calls, fakeConnCall{ 703 FuncName: "InstanceDisks", 704 ZoneName: zone, 705 InstanceId: instanceId, 706 }) 707 return fc.AttachedDisks, fc.err() 708 } 709 710 func (fc *fakeConn) WasCalled(funcName string) (bool, []fakeConnCall) { 711 var calls []fakeConnCall 712 called := false 713 for _, call := range fc.Calls { 714 if call.FuncName == funcName { 715 called = true 716 calls = append(calls, call) 717 } 718 } 719 return called, calls 720 } 721 722 func (fc *fakeConn) ListMachineTypes(zone string) ([]google.MachineType, error) { 723 call := fakeConnCall{ 724 FuncName: "ListMachineTypes", 725 ZoneName: zone, 726 } 727 fc.Calls = append(fc.Calls, call) 728 729 return []google.MachineType{ 730 {Name: "type-1", MemoryMb: 1024}, 731 {Name: "type-2", MemoryMb: 2048}, 732 }, nil 733 } 734 735 var InvalidCredentialError = &url.Error{"Get", "testbad.com", errors.New("400 Bad Request")}