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  }