github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/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  	"encoding/base64"
     8  	"fmt"
     9  	"strings"
    10  
    11  	gitjujutesting "github.com/juju/testing"
    12  	jc "github.com/juju/testing/checkers"
    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/juju/arch"
    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  	"github.com/juju/juju/tools"
    30  	"github.com/juju/juju/version"
    31  )
    32  
    33  // These values are fake GCE auth credentials for use in tests.
    34  const (
    35  	ClientName  = "ba9876543210-0123456789abcdefghijklmnopqrstuv"
    36  	ClientID    = ClientName + ".apps.googleusercontent.com"
    37  	ClientEmail = ClientName + "@developer.gserviceaccount.com"
    38  	PrivateKey  = `-----BEGIN PRIVATE KEY-----
    39  ...
    40  ...
    41  ...
    42  ...
    43  ...
    44  ...
    45  ...
    46  ...
    47  ...
    48  ...
    49  ...
    50  ...
    51  ...
    52  ...
    53  -----END PRIVATE KEY-----
    54  `
    55  )
    56  
    57  // These are fake config values for use in tests.
    58  var (
    59  	AuthFile = fmt.Sprintf(`{
    60    "private_key_id": "abcdef0123456789abcdef0123456789abcdef01",
    61    "private_key": "%s",
    62    "client_email": "%s",
    63    "client_id": "%s",
    64    "type": "service_account"
    65  }`, strings.Replace(PrivateKey, "\n", "\\n", -1), ClientEmail, ClientID)
    66  
    67  	ConfigAttrs = testing.FakeConfig().Merge(testing.Attrs{
    68  		"type":           "gce",
    69  		"auth-file":      "",
    70  		"private-key":    PrivateKey,
    71  		"client-id":      ClientID,
    72  		"client-email":   ClientEmail,
    73  		"region":         "home",
    74  		"project-id":     "my-juju",
    75  		"image-endpoint": "https://www.googleapis.com",
    76  		"uuid":           "2d02eeac-9dbb-11e4-89d3-123b93f75cba",
    77  	})
    78  )
    79  
    80  type BaseSuiteUnpatched struct {
    81  	gitjujutesting.IsolationSuite
    82  
    83  	Config    *config.Config
    84  	EnvConfig *environConfig
    85  	Env       *environ
    86  	Prefix    string
    87  
    88  	Addresses     []network.Address
    89  	BaseInstance  *google.Instance
    90  	Instance      *environInstance
    91  	InstName      string
    92  	Metadata      map[string]string
    93  	StartInstArgs environs.StartInstanceParams
    94  	InstanceType  instances.InstanceType
    95  
    96  	Ports []network.PortRange
    97  }
    98  
    99  var _ environs.Environ = (*environ)(nil)
   100  var _ simplestreams.HasRegion = (*environ)(nil)
   101  var _ instance.Instance = (*environInstance)(nil)
   102  
   103  func (s *BaseSuiteUnpatched) SetUpTest(c *gc.C) {
   104  	s.IsolationSuite.SetUpTest(c)
   105  
   106  	s.initEnv(c)
   107  	s.initInst(c)
   108  	s.initNet(c)
   109  }
   110  
   111  func (s *BaseSuiteUnpatched) initEnv(c *gc.C) {
   112  	s.Env = &environ{
   113  		name: "google",
   114  	}
   115  	cfg := s.NewConfig(c, nil)
   116  	s.setConfig(c, cfg)
   117  }
   118  
   119  func (s *BaseSuiteUnpatched) initInst(c *gc.C) {
   120  	tools := []*tools.Tools{{
   121  		Version: version.Binary{Arch: arch.AMD64, Series: "trusty"},
   122  		URL:     "https://example.org",
   123  	}}
   124  
   125  	cons := constraints.Value{InstanceType: &allInstanceTypes[0].Name}
   126  
   127  	instanceConfig, err := instancecfg.NewBootstrapInstanceConfig(cons, "trusty")
   128  	c.Assert(err, jc.ErrorIsNil)
   129  
   130  	instanceConfig.Tools = tools[0]
   131  	instanceConfig.AuthorizedKeys = s.Config.AuthorizedKeys()
   132  
   133  	userData, err := providerinit.ComposeUserData(instanceConfig, nil)
   134  	c.Assert(err, jc.ErrorIsNil)
   135  	b64UserData := base64.StdEncoding.EncodeToString([]byte(userData))
   136  
   137  	authKeys, err := google.FormatAuthorizedKeys(instanceConfig.AuthorizedKeys, "ubuntu")
   138  	c.Assert(err, jc.ErrorIsNil)
   139  
   140  	s.Metadata = map[string]string{
   141  		metadataKeyIsState:   metadataValueTrue,
   142  		metadataKeyCloudInit: b64UserData,
   143  		metadataKeyEncoding:  "base64",
   144  		metadataKeySSHKeys:   authKeys,
   145  	}
   146  	s.Addresses = []network.Address{{
   147  		Value: "10.0.0.1",
   148  		Type:  network.IPv4Address,
   149  		Scope: network.ScopeCloudLocal,
   150  	}}
   151  	s.Instance = s.NewInstance(c, "spam")
   152  	s.BaseInstance = s.Instance.base
   153  	s.InstName = s.Prefix + "machine-spam"
   154  
   155  	s.StartInstArgs = environs.StartInstanceParams{
   156  		InstanceConfig: instanceConfig,
   157  		Tools:          tools,
   158  		Constraints:    cons,
   159  		//Placement: "",
   160  		//DistributionGroup: nil,
   161  	}
   162  
   163  	s.InstanceType = allInstanceTypes[0]
   164  }
   165  
   166  func (s *BaseSuiteUnpatched) initNet(c *gc.C) {
   167  	s.Ports = []network.PortRange{{
   168  		FromPort: 80,
   169  		ToPort:   80,
   170  		Protocol: "tcp",
   171  	}}
   172  }
   173  
   174  func (s *BaseSuiteUnpatched) setConfig(c *gc.C, cfg *config.Config) {
   175  	s.Config = cfg
   176  	ecfg, err := newValidConfig(cfg, configDefaults)
   177  	c.Assert(err, jc.ErrorIsNil)
   178  	s.EnvConfig = ecfg
   179  	uuid, _ := cfg.UUID()
   180  	s.Env.uuid = uuid
   181  	s.Env.ecfg = s.EnvConfig
   182  	s.Prefix = "juju-" + uuid + "-"
   183  }
   184  
   185  func (s *BaseSuiteUnpatched) NewConfig(c *gc.C, updates testing.Attrs) *config.Config {
   186  	var err error
   187  	cfg := testing.EnvironConfig(c)
   188  	cfg, err = cfg.Apply(ConfigAttrs)
   189  	c.Assert(err, jc.ErrorIsNil)
   190  	cfg, err = cfg.Apply(updates)
   191  	c.Assert(err, jc.ErrorIsNil)
   192  	return cfg
   193  }
   194  
   195  func (s *BaseSuiteUnpatched) UpdateConfig(c *gc.C, attrs map[string]interface{}) {
   196  	cfg, err := s.Config.Apply(attrs)
   197  	c.Assert(err, jc.ErrorIsNil)
   198  	s.setConfig(c, cfg)
   199  }
   200  
   201  func (s *BaseSuiteUnpatched) NewBaseInstance(c *gc.C, id string) *google.Instance {
   202  	diskSpec := google.DiskSpec{
   203  		SizeHintGB: 15,
   204  		ImageURL:   "some/image/path",
   205  		Boot:       true,
   206  		Scratch:    false,
   207  		Readonly:   false,
   208  		AutoDelete: true,
   209  	}
   210  	instanceSpec := google.InstanceSpec{
   211  		ID:                id,
   212  		Type:              "mtype",
   213  		Disks:             []google.DiskSpec{diskSpec},
   214  		Network:           google.NetworkSpec{Name: "somenetwork"},
   215  		NetworkInterfaces: []string{"somenetif"},
   216  		Metadata:          s.Metadata,
   217  		Tags:              []string{id},
   218  	}
   219  	summary := google.InstanceSummary{
   220  		ID:        id,
   221  		ZoneName:  "home-zone",
   222  		Status:    google.StatusRunning,
   223  		Metadata:  s.Metadata,
   224  		Addresses: s.Addresses,
   225  	}
   226  	return google.NewInstance(summary, &instanceSpec)
   227  }
   228  
   229  func (s *BaseSuiteUnpatched) NewInstance(c *gc.C, id string) *environInstance {
   230  	base := s.NewBaseInstance(c, id)
   231  	return newInstance(base, s.Env)
   232  }
   233  
   234  type BaseSuite struct {
   235  	BaseSuiteUnpatched
   236  
   237  	FakeConn    *fakeConn
   238  	FakeCommon  *fakeCommon
   239  	FakeEnviron *fakeEnviron
   240  	FakeImages  *fakeImages
   241  }
   242  
   243  func (s *BaseSuite) SetUpTest(c *gc.C) {
   244  	s.BaseSuiteUnpatched.SetUpTest(c)
   245  
   246  	s.FakeConn = &fakeConn{}
   247  	s.FakeCommon = &fakeCommon{}
   248  	s.FakeEnviron = &fakeEnviron{}
   249  	s.FakeImages = &fakeImages{}
   250  
   251  	// Patch out all expensive external deps.
   252  	s.Env.gce = s.FakeConn
   253  	s.PatchValue(&newConnection, func(*environConfig) (gceConnection, error) {
   254  		return s.FakeConn, nil
   255  	})
   256  	s.PatchValue(&supportedArchitectures, s.FakeCommon.SupportedArchitectures)
   257  	s.PatchValue(&bootstrap, s.FakeCommon.Bootstrap)
   258  	s.PatchValue(&destroyEnv, s.FakeCommon.Destroy)
   259  	s.PatchValue(&availabilityZoneAllocations, s.FakeCommon.AvailabilityZoneAllocations)
   260  	s.PatchValue(&buildInstanceSpec, s.FakeEnviron.BuildInstanceSpec)
   261  	s.PatchValue(&getHardwareCharacteristics, s.FakeEnviron.GetHardwareCharacteristics)
   262  	s.PatchValue(&newRawInstance, s.FakeEnviron.NewRawInstance)
   263  	s.PatchValue(&findInstanceSpec, s.FakeEnviron.FindInstanceSpec)
   264  	s.PatchValue(&getInstances, s.FakeEnviron.GetInstances)
   265  	s.PatchValue(&imageMetadataFetch, s.FakeImages.ImageMetadataFetch)
   266  }
   267  
   268  func (s *BaseSuite) CheckNoAPI(c *gc.C) {
   269  	c.Check(s.FakeConn.Calls, gc.HasLen, 0)
   270  }
   271  
   272  // TODO(ericsnow) Move fakeCallArgs, fakeCall, and fake to the testing repo?
   273  
   274  type FakeCallArgs map[string]interface{}
   275  
   276  type FakeCall struct {
   277  	FuncName string
   278  	Args     FakeCallArgs
   279  }
   280  
   281  type fake struct {
   282  	calls []FakeCall
   283  
   284  	Err        error
   285  	FailOnCall int
   286  }
   287  
   288  func (f *fake) err() error {
   289  	if len(f.calls) != f.FailOnCall+1 {
   290  		return nil
   291  	}
   292  	return f.Err
   293  }
   294  
   295  func (f *fake) addCall(funcName string, args FakeCallArgs) {
   296  	f.calls = append(f.calls, FakeCall{
   297  		FuncName: funcName,
   298  		Args:     args,
   299  	})
   300  }
   301  
   302  func (f *fake) CheckCalls(c *gc.C, expected []FakeCall) {
   303  	c.Check(f.calls, jc.DeepEquals, expected)
   304  }
   305  
   306  type fakeCommon struct {
   307  	fake
   308  
   309  	Arches      []string
   310  	Arch        string
   311  	Series      string
   312  	BSFinalizer environs.BootstrapFinalizer
   313  	AZInstances []common.AvailabilityZoneInstances
   314  }
   315  
   316  func (fc *fakeCommon) SupportedArchitectures(env environs.Environ, cons *imagemetadata.ImageConstraint) ([]string, error) {
   317  	fc.addCall("SupportedArchitectures", FakeCallArgs{
   318  		"env":  env,
   319  		"cons": cons,
   320  	})
   321  	return fc.Arches, fc.err()
   322  }
   323  
   324  func (fc *fakeCommon) Bootstrap(ctx environs.BootstrapContext, env environs.Environ, params environs.BootstrapParams) (string, string, environs.BootstrapFinalizer, error) {
   325  	fc.addCall("Bootstrap", FakeCallArgs{
   326  		"ctx":    ctx,
   327  		"env":    env,
   328  		"params": params,
   329  	})
   330  	return fc.Arch, fc.Series, fc.BSFinalizer, fc.err()
   331  }
   332  
   333  func (fc *fakeCommon) Destroy(env environs.Environ) error {
   334  	fc.addCall("Destroy", FakeCallArgs{
   335  		"env": env,
   336  	})
   337  	return fc.err()
   338  }
   339  
   340  func (fc *fakeCommon) AvailabilityZoneAllocations(env common.ZonedEnviron, group []instance.Id) ([]common.AvailabilityZoneInstances, error) {
   341  	fc.addCall("AvailabilityZoneAllocations", FakeCallArgs{
   342  		"env":   env,
   343  		"group": group,
   344  	})
   345  	return fc.AZInstances, fc.err()
   346  }
   347  
   348  type fakeEnviron struct {
   349  	fake
   350  
   351  	Inst  *google.Instance
   352  	Insts []instance.Instance
   353  	Hwc   *instance.HardwareCharacteristics
   354  	Spec  *instances.InstanceSpec
   355  }
   356  
   357  func (fe *fakeEnviron) GetInstances(env *environ) ([]instance.Instance, error) {
   358  	fe.addCall("GetInstances", FakeCallArgs{
   359  		"env": env,
   360  	})
   361  	return fe.Insts, fe.err()
   362  }
   363  
   364  func (fe *fakeEnviron) BuildInstanceSpec(env *environ, args environs.StartInstanceParams) (*instances.InstanceSpec, error) {
   365  	fe.addCall("BuildInstanceSpec", FakeCallArgs{
   366  		"env":  env,
   367  		"args": args,
   368  	})
   369  	return fe.Spec, fe.err()
   370  }
   371  
   372  func (fe *fakeEnviron) GetHardwareCharacteristics(env *environ, spec *instances.InstanceSpec, inst *environInstance) *instance.HardwareCharacteristics {
   373  	fe.addCall("GetHardwareCharacteristics", FakeCallArgs{
   374  		"env":  env,
   375  		"spec": spec,
   376  		"inst": inst,
   377  	})
   378  	return fe.Hwc
   379  }
   380  
   381  func (fe *fakeEnviron) NewRawInstance(env *environ, args environs.StartInstanceParams, spec *instances.InstanceSpec) (*google.Instance, error) {
   382  	fe.addCall("NewRawInstance", FakeCallArgs{
   383  		"env":  env,
   384  		"args": args,
   385  		"spec": spec,
   386  	})
   387  	return fe.Inst, fe.err()
   388  }
   389  
   390  func (fe *fakeEnviron) FindInstanceSpec(env *environ, stream string, ic *instances.InstanceConstraint) (*instances.InstanceSpec, error) {
   391  	fe.addCall("FindInstanceSpec", FakeCallArgs{
   392  		"env":    env,
   393  		"stream": stream,
   394  		"ic":     ic,
   395  	})
   396  	return fe.Spec, fe.err()
   397  }
   398  
   399  type fakeImages struct {
   400  	fake
   401  
   402  	Metadata    []*imagemetadata.ImageMetadata
   403  	ResolveInfo *simplestreams.ResolveInfo
   404  }
   405  
   406  func (fi *fakeImages) ImageMetadataFetch(sources []simplestreams.DataSource, cons *imagemetadata.ImageConstraint, onlySigned bool) ([]*imagemetadata.ImageMetadata, *simplestreams.ResolveInfo, error) {
   407  	return fi.Metadata, fi.ResolveInfo, fi.err()
   408  }
   409  
   410  // TODO(ericsnow) Refactor fakeConnCall and fakeConn to embed fakeCall and fake.
   411  
   412  type fakeConnCall struct {
   413  	FuncName string
   414  
   415  	ID           string
   416  	IDs          []string
   417  	ZoneName     string
   418  	ZoneNames    []string
   419  	Prefix       string
   420  	Statuses     []string
   421  	InstanceSpec google.InstanceSpec
   422  	FirewallName string
   423  	PortRanges   []network.PortRange
   424  	Region       string
   425  }
   426  
   427  type fakeConn struct {
   428  	Calls []fakeConnCall
   429  
   430  	Inst       *google.Instance
   431  	Insts      []google.Instance
   432  	PortRanges []network.PortRange
   433  	Zones      []google.AvailabilityZone
   434  	Err        error
   435  	FailOnCall int
   436  }
   437  
   438  func (fc *fakeConn) err() error {
   439  	if len(fc.Calls) != fc.FailOnCall+1 {
   440  		return nil
   441  	}
   442  	return fc.Err
   443  }
   444  
   445  func (fc *fakeConn) VerifyCredentials() error {
   446  	fc.Calls = append(fc.Calls, fakeConnCall{
   447  		FuncName: "",
   448  	})
   449  	return fc.err()
   450  }
   451  
   452  func (fc *fakeConn) Instance(id, zone string) (google.Instance, error) {
   453  	fc.Calls = append(fc.Calls, fakeConnCall{
   454  		FuncName: "Instance",
   455  		ID:       id,
   456  		ZoneName: zone,
   457  	})
   458  	return *fc.Inst, fc.err()
   459  }
   460  
   461  func (fc *fakeConn) Instances(prefix string, statuses ...string) ([]google.Instance, error) {
   462  	fc.Calls = append(fc.Calls, fakeConnCall{
   463  		FuncName: "Instances",
   464  		Prefix:   prefix,
   465  		Statuses: statuses,
   466  	})
   467  	return fc.Insts, fc.err()
   468  }
   469  
   470  func (fc *fakeConn) AddInstance(spec google.InstanceSpec, zones ...string) (*google.Instance, error) {
   471  	fc.Calls = append(fc.Calls, fakeConnCall{
   472  		FuncName:     "AddInstance",
   473  		InstanceSpec: spec,
   474  		ZoneNames:    zones,
   475  	})
   476  	return fc.Inst, fc.err()
   477  }
   478  
   479  func (fc *fakeConn) RemoveInstances(prefix string, ids ...string) error {
   480  	fc.Calls = append(fc.Calls, fakeConnCall{
   481  		FuncName: "RemoveInstances",
   482  		Prefix:   prefix,
   483  		IDs:      ids,
   484  	})
   485  	return fc.err()
   486  }
   487  
   488  func (fc *fakeConn) Ports(fwname string) ([]network.PortRange, error) {
   489  	fc.Calls = append(fc.Calls, fakeConnCall{
   490  		FuncName:     "Ports",
   491  		FirewallName: fwname,
   492  	})
   493  	return fc.PortRanges, fc.err()
   494  }
   495  
   496  func (fc *fakeConn) OpenPorts(fwname string, ports ...network.PortRange) error {
   497  	fc.Calls = append(fc.Calls, fakeConnCall{
   498  		FuncName:     "OpenPorts",
   499  		FirewallName: fwname,
   500  		PortRanges:   ports,
   501  	})
   502  	return fc.err()
   503  }
   504  
   505  func (fc *fakeConn) ClosePorts(fwname string, ports ...network.PortRange) error {
   506  	fc.Calls = append(fc.Calls, fakeConnCall{
   507  		FuncName:     "ClosePorts",
   508  		FirewallName: fwname,
   509  		PortRanges:   ports,
   510  	})
   511  	return fc.err()
   512  }
   513  
   514  func (fc *fakeConn) AvailabilityZones(region string) ([]google.AvailabilityZone, error) {
   515  	fc.Calls = append(fc.Calls, fakeConnCall{
   516  		FuncName: "AvailabilityZones",
   517  		Region:   region,
   518  	})
   519  	return fc.Zones, fc.err()
   520  }
   521  
   522  func (fc *fakeConn) WasCalled(funcName string) (bool, []fakeConnCall) {
   523  	var calls []fakeConnCall
   524  	called := false
   525  	for _, call := range fc.Calls {
   526  		if call.FuncName == funcName {
   527  			called = true
   528  			calls = append(calls, call)
   529  		}
   530  	}
   531  	return called, calls
   532  }