1
// Copyright 2014 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
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"
16
"github.com/juju/juju/cloud"
17
"github.com/juju/juju/cloudconfig/instancecfg"
18
"github.com/juju/juju/cloudconfig/providerinit"
19
"github.com/juju/juju/constraints"
20
"github.com/juju/juju/environs"
21
"github.com/juju/juju/environs/config"
22
"github.com/juju/juju/environs/imagemetadata"
23
"github.com/juju/juju/environs/instances"
24
"github.com/juju/juju/environs/simplestreams"
25
"github.com/juju/juju/environs/tags"
26
"github.com/juju/juju/instance"
27
"github.com/juju/juju/network"
28
"github.com/juju/juju/provider/common"
29
"github.com/juju/juju/provider/gce/google"
30
"github.com/juju/juju/testing"
31
coretools "github.com/juju/juju/tools"
34
// These values are fake GCE auth credentials for use in tests.
36
ClientName = "ba9876543210-0123456789abcdefghijklmnopqrstuv"
37
ClientID = ClientName + ".apps.googleusercontent.com"
38
ClientEmail = ClientName + "@developer.gserviceaccount.com"
40
PrivateKey = `-----BEGIN PRIVATE KEY-----
55
-----END PRIVATE KEY-----
59
// These are fake config values for use in tests.
61
AuthFile = fmt.Sprintf(`{
62
"private_key_id": "abcdef0123456789abcdef0123456789abcdef01",
66
"type": "service_account"
67
}`, strings.Replace(PrivateKey, "\n", "\\n", -1), ClientEmail, ClientID)
69
ConfigAttrs = testing.FakeConfig().Merge(testing.Attrs{
71
"uuid": "2d02eeac-9dbb-11e4-89d3-123b93f75cba",
72
"controller-uuid": "bfef02f1-932a-425a-a102-62175dcabd1d",
76
func MakeTestCloudSpec() environs.CloudSpec {
77
cred := MakeTestCredential()
78
return environs.CloudSpec{
82
Endpoint: "https://www.googleapis.com",
87
func MakeTestCredential() cloud.Credential {
88
return cloud.NewCredential(
91
"project-id": ProjectID,
92
"client-id": ClientID,
93
"client-email": ClientEmail,
94
"private-key": PrivateKey,
99
type BaseSuiteUnpatched struct {
100
gitjujutesting.IsolationSuite
102
ControllerUUID string
103
Config *config.Config
104
EnvConfig *environConfig
107
Addresses []network.Address
108
BaseInstance *google.Instance
109
BaseDisk *google.Disk
110
Instance *environInstance
112
UbuntuMetadata map[string]string
113
WindowsMetadata map[string]string
114
StartInstArgs environs.StartInstanceParams
115
InstanceType instances.InstanceType
117
Ports []network.PortRange
120
var _ environs.Environ = (*environ)(nil)
121
var _ simplestreams.HasRegion = (*environ)(nil)
122
var _ instance.Instance = (*environInstance)(nil)
124
func (s *BaseSuiteUnpatched) SetUpTest(c *gc.C) {
125
s.IsolationSuite.SetUpTest(c)
127
s.ControllerUUID = testing.FakeControllerConfig().ControllerUUID()
133
func (s *BaseSuiteUnpatched) Prefix() string {
134
return s.Env.namespace.Prefix()
137
func (s *BaseSuiteUnpatched) initEnv(c *gc.C) {
140
cloud: MakeTestCloudSpec(),
142
cfg := s.NewConfig(c, nil)
146
func (s *BaseSuiteUnpatched) initInst(c *gc.C) {
147
tools := []*coretools.Tools{{
148
Version: version.Binary{Arch: arch.AMD64, Series: "trusty"},
149
URL: "https://example.org",
152
cons := constraints.Value{InstanceType: &allInstanceTypes[0].Name}
154
instanceConfig, err := instancecfg.NewBootstrapInstanceConfig(testing.FakeControllerConfig(), cons, cons, "trusty", "")
155
c.Assert(err, jc.ErrorIsNil)
157
err = instanceConfig.SetTools(coretools.List(tools))
158
c.Assert(err, jc.ErrorIsNil)
159
instanceConfig.AuthorizedKeys = s.Config.AuthorizedKeys()
161
userData, err := providerinit.ComposeUserData(instanceConfig, nil, GCERenderer{})
162
c.Assert(err, jc.ErrorIsNil)
164
authKeys, err := google.FormatAuthorizedKeys(instanceConfig.AuthorizedKeys, "ubuntu")
165
c.Assert(err, jc.ErrorIsNil)
167
s.UbuntuMetadata = map[string]string{
168
tags.JujuIsController: "true",
169
tags.JujuController: s.ControllerUUID,
170
metadataKeyCloudInit: string(userData),
171
metadataKeyEncoding: "base64",
172
metadataKeySSHKeys: authKeys,
174
instanceConfig.Tags = map[string]string{
175
tags.JujuIsController: "true",
176
tags.JujuController: s.ControllerUUID,
178
s.WindowsMetadata = map[string]string{
179
metadataKeyWindowsUserdata: string(userData),
180
metadataKeyWindowsSysprep: fmt.Sprintf(winSetHostnameScript, "juju.*"),
182
s.Addresses = []network.Address{{
184
Type: network.IPv4Address,
185
Scope: network.ScopeCloudLocal,
187
s.Instance = s.NewInstance(c, "spam")
188
s.BaseInstance = s.Instance.base
189
s.InstName, err = s.Env.namespace.Hostname("42")
190
c.Assert(err, jc.ErrorIsNil)
192
s.StartInstArgs = environs.StartInstanceParams{
193
ControllerUUID: s.ControllerUUID,
194
InstanceConfig: instanceConfig,
198
//DistributionGroup: nil,
201
s.InstanceType = allInstanceTypes[0]
204
eUUID := s.Env.Config().UUID()
205
s.BaseDisk = &google.Disk{
207
Name: "home-zone--c930380d-8337-4bf5-b07a-9dbb5ae771e4",
209
Status: google.StatusReady,
215
func (s *BaseSuiteUnpatched) initNet(c *gc.C) {
216
s.Ports = []network.PortRange{{
223
func (s *BaseSuiteUnpatched) setConfig(c *gc.C, cfg *config.Config) {
225
ecfg, err := newConfig(cfg, nil)
226
c.Assert(err, jc.ErrorIsNil)
230
s.Env.ecfg = s.EnvConfig
231
namespace, err := instance.NewNamespace(uuid)
232
c.Assert(err, jc.ErrorIsNil)
233
s.Env.namespace = namespace
236
func (s *BaseSuiteUnpatched) NewConfig(c *gc.C, updates testing.Attrs) *config.Config {
238
cfg := testing.ModelConfig(c)
239
cfg, err = cfg.Apply(ConfigAttrs)
240
c.Assert(err, jc.ErrorIsNil)
241
cfg, err = cfg.Apply(updates)
242
c.Assert(err, jc.ErrorIsNil)
246
func (s *BaseSuiteUnpatched) UpdateConfig(c *gc.C, attrs map[string]interface{}) {
247
cfg, err := s.Config.Apply(attrs)
248
c.Assert(err, jc.ErrorIsNil)
252
func (s *BaseSuiteUnpatched) NewBaseInstance(c *gc.C, id string) *google.Instance {
253
diskSpec := google.DiskSpec{
256
ImageURL: "some/image/path",
262
instanceSpec := google.InstanceSpec{
265
Disks: []google.DiskSpec{diskSpec},
266
Network: google.NetworkSpec{Name: "somenetwork"},
267
NetworkInterfaces: []string{"somenetif"},
268
Metadata: s.UbuntuMetadata,
271
summary := google.InstanceSummary{
273
ZoneName: "home-zone",
274
Status: google.StatusRunning,
275
Metadata: s.UbuntuMetadata,
276
Addresses: s.Addresses,
278
return google.NewInstance(summary, &instanceSpec)
281
func (s *BaseSuiteUnpatched) NewInstance(c *gc.C, id string) *environInstance {
282
base := s.NewBaseInstance(c, id)
283
return newInstance(base, s.Env)
286
type BaseSuite struct {
290
FakeCommon *fakeCommon
291
FakeEnviron *fakeEnviron
294
func (s *BaseSuite) SetUpTest(c *gc.C) {
295
s.BaseSuiteUnpatched.SetUpTest(c)
297
s.FakeConn = &fakeConn{}
298
s.FakeCommon = &fakeCommon{}
299
s.FakeEnviron = &fakeEnviron{}
301
// Patch out all expensive external deps.
302
s.Env.gce = s.FakeConn
303
s.PatchValue(&newConnection, func(google.ConnectionConfig, *google.Credentials) (gceConnection, error) {
304
return s.FakeConn, nil
306
s.PatchValue(&supportedArchitectures, s.FakeCommon.SupportedArchitectures)
307
s.PatchValue(&bootstrap, s.FakeCommon.Bootstrap)
308
s.PatchValue(&destroyEnv, s.FakeCommon.Destroy)
309
s.PatchValue(&availabilityZoneAllocations, s.FakeCommon.AvailabilityZoneAllocations)
310
s.PatchValue(&buildInstanceSpec, s.FakeEnviron.BuildInstanceSpec)
311
s.PatchValue(&getHardwareCharacteristics, s.FakeEnviron.GetHardwareCharacteristics)
312
s.PatchValue(&newRawInstance, s.FakeEnviron.NewRawInstance)
313
s.PatchValue(&findInstanceSpec, s.FakeEnviron.FindInstanceSpec)
314
s.PatchValue(&getInstances, s.FakeEnviron.GetInstances)
317
func (s *BaseSuite) CheckNoAPI(c *gc.C) {
318
c.Check(s.FakeConn.Calls, gc.HasLen, 0)
321
// TODO(ericsnow) Move fakeCallArgs, fakeCall, and fake to the testing repo?
323
type FakeCallArgs map[string]interface{}
325
type FakeCall struct {
337
func (f *fake) err() error {
338
if len(f.calls) != f.FailOnCall+1 {
344
func (f *fake) addCall(funcName string, args FakeCallArgs) {
345
f.calls = append(f.calls, FakeCall{
351
func (f *fake) CheckCalls(c *gc.C, expected []FakeCall) {
352
c.Check(f.calls, jc.DeepEquals, expected)
355
type fakeCommon struct {
361
BSFinalizer environs.BootstrapFinalizer
362
AZInstances []common.AvailabilityZoneInstances
365
func (fc *fakeCommon) SupportedArchitectures(env environs.Environ, cons *imagemetadata.ImageConstraint) ([]string, error) {
366
fc.addCall("SupportedArchitectures", FakeCallArgs{
370
return fc.Arches, fc.err()
373
func (fc *fakeCommon) Bootstrap(ctx environs.BootstrapContext, env environs.Environ, params environs.BootstrapParams) (*environs.BootstrapResult, error) {
374
fc.addCall("Bootstrap", FakeCallArgs{
380
result := &environs.BootstrapResult{
383
Finalize: fc.BSFinalizer,
385
return result, fc.err()
388
func (fc *fakeCommon) Destroy(env environs.Environ) error {
389
fc.addCall("Destroy", FakeCallArgs{
395
func (fc *fakeCommon) AvailabilityZoneAllocations(env common.ZonedEnviron, group []instance.Id) ([]common.AvailabilityZoneInstances, error) {
396
fc.addCall("AvailabilityZoneAllocations", FakeCallArgs{
400
return fc.AZInstances, fc.err()
403
type fakeEnviron struct {
406
Inst *google.Instance
407
Insts []instance.Instance
408
Hwc *instance.HardwareCharacteristics
409
Spec *instances.InstanceSpec
412
func (fe *fakeEnviron) GetInstances(env *environ) ([]instance.Instance, error) {
413
fe.addCall("GetInstances", FakeCallArgs{
416
return fe.Insts, fe.err()
419
func (fe *fakeEnviron) BuildInstanceSpec(env *environ, args environs.StartInstanceParams) (*instances.InstanceSpec, error) {
420
fe.addCall("BuildInstanceSpec", FakeCallArgs{
424
return fe.Spec, fe.err()
427
func (fe *fakeEnviron) GetHardwareCharacteristics(env *environ, spec *instances.InstanceSpec, inst *environInstance) *instance.HardwareCharacteristics {
428
fe.addCall("GetHardwareCharacteristics", FakeCallArgs{
436
func (fe *fakeEnviron) NewRawInstance(env *environ, args environs.StartInstanceParams, spec *instances.InstanceSpec) (*google.Instance, error) {
437
fe.addCall("NewRawInstance", FakeCallArgs{
442
return fe.Inst, fe.err()
445
func (fe *fakeEnviron) FindInstanceSpec(
447
ic *instances.InstanceConstraint,
448
imageMetadata []*imagemetadata.ImageMetadata,
449
) (*instances.InstanceSpec, error) {
450
fe.addCall("FindInstanceSpec", FakeCallArgs{
453
"imageMetadata": imageMetadata,
455
return fe.Spec, fe.err()
458
// TODO(ericsnow) Refactor fakeConnCall and fakeConn to embed fakeCall and fake.
460
type fakeConnCall struct {
469
InstanceSpec google.InstanceSpec
471
PortRanges []network.PortRange
473
Disks []google.DiskSpec
479
type fakeConn struct {
482
Inst *google.Instance
483
Insts []google.Instance
484
PortRanges []network.PortRange
485
Zones []google.AvailabilityZone
487
GoogleDisks []*google.Disk
488
GoogleDisk *google.Disk
489
AttachedDisk *google.AttachedDisk
490
AttachedDisks []*google.AttachedDisk
496
func (fc *fakeConn) err() error {
497
if len(fc.Calls) != fc.FailOnCall+1 {
503
func (fc *fakeConn) VerifyCredentials() error {
504
fc.Calls = append(fc.Calls, fakeConnCall{
510
func (fc *fakeConn) Instance(id, zone string) (google.Instance, error) {
511
fc.Calls = append(fc.Calls, fakeConnCall{
512
FuncName: "Instance",
516
return *fc.Inst, fc.err()
519
func (fc *fakeConn) Instances(prefix string, statuses ...string) ([]google.Instance, error) {
520
fc.Calls = append(fc.Calls, fakeConnCall{
521
FuncName: "Instances",
525
return fc.Insts, fc.err()
528
func (fc *fakeConn) AddInstance(spec google.InstanceSpec, zones ...string) (*google.Instance, error) {
529
fc.Calls = append(fc.Calls, fakeConnCall{
530
FuncName: "AddInstance",
534
return fc.Inst, fc.err()
537
func (fc *fakeConn) RemoveInstances(prefix string, ids ...string) error {
538
fc.Calls = append(fc.Calls, fakeConnCall{
539
FuncName: "RemoveInstances",
546
func (fc *fakeConn) Ports(fwname string) ([]network.PortRange, error) {
547
fc.Calls = append(fc.Calls, fakeConnCall{
549
FirewallName: fwname,
551
return fc.PortRanges, fc.err()
554
func (fc *fakeConn) OpenPorts(fwname string, ports ...network.PortRange) error {
555
fc.Calls = append(fc.Calls, fakeConnCall{
556
FuncName: "OpenPorts",
557
FirewallName: fwname,
563
func (fc *fakeConn) ClosePorts(fwname string, ports ...network.PortRange) error {
564
fc.Calls = append(fc.Calls, fakeConnCall{
565
FuncName: "ClosePorts",
566
FirewallName: fwname,
572
func (fc *fakeConn) AvailabilityZones(region string) ([]google.AvailabilityZone, error) {
573
fc.Calls = append(fc.Calls, fakeConnCall{
574
FuncName: "AvailabilityZones",
577
return fc.Zones, fc.err()
580
func (fc *fakeConn) CreateDisks(zone string, disks []google.DiskSpec) ([]*google.Disk, error) {
581
fc.Calls = append(fc.Calls, fakeConnCall{
582
FuncName: "CreateDisks",
586
return fc.GoogleDisks, fc.err()
589
func (fc *fakeConn) Disks(zone string) ([]*google.Disk, error) {
590
fc.Calls = append(fc.Calls, fakeConnCall{
594
return fc.GoogleDisks, fc.err()
597
func (fc *fakeConn) RemoveDisk(zone, id string) error {
598
fc.Calls = append(fc.Calls, fakeConnCall{
599
FuncName: "RemoveDisk",
606
func (fc *fakeConn) Disk(zone, id string) (*google.Disk, error) {
607
fc.Calls = append(fc.Calls, fakeConnCall{
612
return fc.GoogleDisk, fc.err()
615
func (fc *fakeConn) AttachDisk(zone, volumeName, instanceId string, mode google.DiskMode) (*google.AttachedDisk, error) {
616
fc.Calls = append(fc.Calls, fakeConnCall{
617
FuncName: "AttachDisk",
619
VolumeName: volumeName,
620
InstanceId: instanceId,
623
return fc.AttachedDisk, fc.err()
626
func (fc *fakeConn) DetachDisk(zone, instanceId, volumeName string) error {
627
fc.Calls = append(fc.Calls, fakeConnCall{
628
FuncName: "DetachDisk",
630
InstanceId: instanceId,
631
VolumeName: volumeName,
636
func (fc *fakeConn) InstanceDisks(zone, instanceId string) ([]*google.AttachedDisk, error) {
637
fc.Calls = append(fc.Calls, fakeConnCall{
638
FuncName: "InstanceDisks",
640
InstanceId: instanceId,
642
return fc.AttachedDisks, fc.err()
645
func (fc *fakeConn) WasCalled(funcName string) (bool, []fakeConnCall) {
646
var calls []fakeConnCall
648
for _, call := range fc.Calls {
649
if call.FuncName == funcName {
651
calls = append(calls, call)