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