github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/provider/lxd/testing_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  // +build go1.3
     5  
     6  package lxd
     7  
     8  import (
     9  	"os"
    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/lxc/lxd/shared"
    16  	gc "gopkg.in/check.v1"
    17  
    18  	"github.com/juju/juju/cloudconfig/instancecfg"
    19  	"github.com/juju/juju/cloudconfig/providerinit"
    20  	"github.com/juju/juju/constraints"
    21  	"github.com/juju/juju/environs"
    22  	"github.com/juju/juju/environs/config"
    23  	"github.com/juju/juju/environs/tags"
    24  	"github.com/juju/juju/instance"
    25  	"github.com/juju/juju/network"
    26  	"github.com/juju/juju/testing"
    27  	coretools "github.com/juju/juju/tools"
    28  	"github.com/juju/juju/tools/lxdclient"
    29  	"github.com/juju/version"
    30  )
    31  
    32  // Ensure LXD provider supports the expected interfaces.
    33  var (
    34  	_ config.ConfigSchemaSource = (*environProvider)(nil)
    35  )
    36  
    37  // These values are stub LXD client credentials for use in tests.
    38  const (
    39  	PublicKey = `-----BEGIN CERTIFICATE-----
    40  ...
    41  ...
    42  ...
    43  ...
    44  ...
    45  ...
    46  ...
    47  ...
    48  ...
    49  ...
    50  ...
    51  ...
    52  ...
    53  ...
    54  -----END CERTIFICATE-----
    55  `
    56  	PrivateKey = `-----BEGIN PRIVATE KEY-----
    57  ...
    58  ...
    59  ...
    60  ...
    61  ...
    62  ...
    63  ...
    64  ...
    65  ...
    66  ...
    67  ...
    68  ...
    69  ...
    70  ...
    71  -----END PRIVATE KEY-----
    72  `
    73  )
    74  
    75  // These are stub config values for use in tests.
    76  var (
    77  	ConfigAttrs = testing.FakeConfig().Merge(testing.Attrs{
    78  		"type": "lxd",
    79  		"uuid": "2d02eeac-9dbb-11e4-89d3-123b93f75cba",
    80  	})
    81  )
    82  
    83  // We test these here since they are not exported.
    84  var (
    85  	_ environs.Environ  = (*environ)(nil)
    86  	_ instance.Instance = (*environInstance)(nil)
    87  )
    88  
    89  type BaseSuiteUnpatched struct {
    90  	gitjujutesting.IsolationSuite
    91  
    92  	osPathOrig string
    93  
    94  	Config    *config.Config
    95  	EnvConfig *environConfig
    96  	Env       *environ
    97  
    98  	Addresses     []network.Address
    99  	Instance      *environInstance
   100  	RawInstance   *lxdclient.Instance
   101  	InstName      string
   102  	Hardware      *lxdclient.InstanceHardware
   103  	HWC           *instance.HardwareCharacteristics
   104  	Metadata      map[string]string
   105  	StartInstArgs environs.StartInstanceParams
   106  	//InstanceType  instances.InstanceType
   107  
   108  	Ports []network.PortRange
   109  }
   110  
   111  func (s *BaseSuiteUnpatched) SetUpSuite(c *gc.C) {
   112  	s.osPathOrig = os.Getenv("PATH")
   113  	if s.osPathOrig == "" {
   114  		// TODO(ericsnow) This shouldn't happen. However, an undiagnosed
   115  		// bug in testing.IsolationSuite is causing $PATH to remain unset
   116  		// sometimes.  Once that is cleared up this special-case can go
   117  		// away.
   118  		s.osPathOrig =
   119  			"/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin"
   120  	}
   121  	s.IsolationSuite.SetUpSuite(c)
   122  }
   123  
   124  func (s *BaseSuiteUnpatched) SetUpTest(c *gc.C) {
   125  	s.IsolationSuite.SetUpTest(c)
   126  
   127  	s.initEnv(c)
   128  	s.initInst(c)
   129  	s.initNet(c)
   130  }
   131  
   132  func (s *BaseSuiteUnpatched) initEnv(c *gc.C) {
   133  	s.Env = &environ{
   134  		name: "lxd",
   135  	}
   136  	cfg := s.NewConfig(c, nil)
   137  	s.setConfig(c, cfg)
   138  }
   139  
   140  func (s *BaseSuiteUnpatched) Prefix() string {
   141  	return s.Env.namespace.Prefix()
   142  }
   143  
   144  func (s *BaseSuiteUnpatched) initInst(c *gc.C) {
   145  	tools := []*coretools.Tools{
   146  		{
   147  			Version: version.Binary{Arch: arch.AMD64, Series: "trusty"},
   148  			URL:     "https://example.org/amd",
   149  		},
   150  		{
   151  			Version: version.Binary{Arch: arch.ARM64, Series: "trusty"},
   152  			URL:     "https://example.org/arm",
   153  		},
   154  	}
   155  
   156  	cons := constraints.Value{
   157  	// nothing
   158  	}
   159  
   160  	instanceConfig, err := instancecfg.NewBootstrapInstanceConfig(testing.FakeControllerConfig(), cons, cons, "trusty", "")
   161  	c.Assert(err, jc.ErrorIsNil)
   162  
   163  	err = instanceConfig.SetTools(coretools.List{
   164  		tools[0],
   165  	})
   166  	c.Assert(err, jc.ErrorIsNil)
   167  	instanceConfig.AuthorizedKeys = s.Config.AuthorizedKeys()
   168  
   169  	userData, err := providerinit.ComposeUserData(instanceConfig, nil, lxdRenderer{})
   170  	c.Assert(err, jc.ErrorIsNil)
   171  
   172  	s.Hardware = &lxdclient.InstanceHardware{
   173  		Architecture: arch.ARM64,
   174  		NumCores:     1,
   175  		MemoryMB:     3750,
   176  	}
   177  	var archName string = arch.ARM64
   178  	var numCores uint64 = 1
   179  	var memoryMB uint64 = 3750
   180  	s.HWC = &instance.HardwareCharacteristics{
   181  		Arch:     &archName,
   182  		CpuCores: &numCores,
   183  		Mem:      &memoryMB,
   184  	}
   185  
   186  	s.Metadata = map[string]string{ // userdata
   187  		tags.JujuIsController: "true",
   188  		tags.JujuController:   testing.ControllerTag.Id(),
   189  		tags.JujuModel:        s.Config.UUID(),
   190  		metadataKeyCloudInit:  string(userData),
   191  	}
   192  	s.Addresses = []network.Address{{
   193  		Value: "10.0.0.1",
   194  		Type:  network.IPv4Address,
   195  		Scope: network.ScopeCloudLocal,
   196  	}}
   197  	// NOTE: the instance ids used throughout this package are not at all
   198  	// representative of what they would normally be. They would normally
   199  	// determined by the instance namespace and the machine id.
   200  	s.Instance = s.NewInstance(c, "spam")
   201  	s.RawInstance = s.Instance.raw
   202  	s.InstName, err = s.Env.namespace.Hostname("42")
   203  	c.Assert(err, jc.ErrorIsNil)
   204  
   205  	s.StartInstArgs = environs.StartInstanceParams{
   206  		ControllerUUID: instanceConfig.Controller.Config.ControllerUUID(),
   207  		InstanceConfig: instanceConfig,
   208  		Tools:          tools,
   209  		Constraints:    cons,
   210  	}
   211  }
   212  
   213  func (s *BaseSuiteUnpatched) initNet(c *gc.C) {
   214  	s.Ports = []network.PortRange{{
   215  		FromPort: 80,
   216  		ToPort:   80,
   217  		Protocol: "tcp",
   218  	}}
   219  }
   220  
   221  func (s *BaseSuiteUnpatched) setConfig(c *gc.C, cfg *config.Config) {
   222  	s.Config = cfg
   223  	ecfg, err := newValidConfig(cfg)
   224  	c.Assert(err, jc.ErrorIsNil)
   225  	s.EnvConfig = ecfg
   226  	uuid := cfg.UUID()
   227  	s.Env.uuid = uuid
   228  	s.Env.ecfg = s.EnvConfig
   229  	namespace, err := instance.NewNamespace(uuid)
   230  	c.Assert(err, jc.ErrorIsNil)
   231  	s.Env.namespace = namespace
   232  }
   233  
   234  func (s *BaseSuiteUnpatched) NewConfig(c *gc.C, updates testing.Attrs) *config.Config {
   235  	if updates == nil {
   236  		updates = make(testing.Attrs)
   237  	}
   238  	var err error
   239  	cfg := testing.ModelConfig(c)
   240  	cfg, err = cfg.Apply(ConfigAttrs)
   241  	c.Assert(err, jc.ErrorIsNil)
   242  	cfg, err = cfg.Apply(updates)
   243  	c.Assert(err, jc.ErrorIsNil)
   244  	return cfg
   245  }
   246  
   247  func (s *BaseSuiteUnpatched) UpdateConfig(c *gc.C, attrs map[string]interface{}) {
   248  	cfg, err := s.Config.Apply(attrs)
   249  	c.Assert(err, jc.ErrorIsNil)
   250  	s.setConfig(c, cfg)
   251  }
   252  
   253  func (s *BaseSuiteUnpatched) NewRawInstance(c *gc.C, name string) *lxdclient.Instance {
   254  	metadata := make(map[string]string)
   255  	for k, v := range s.Metadata {
   256  		metadata[k] = v
   257  	}
   258  	summary := lxdclient.InstanceSummary{
   259  		Name:     name,
   260  		Status:   lxdclient.StatusRunning,
   261  		Hardware: *s.Hardware,
   262  		Metadata: metadata,
   263  	}
   264  	instanceSpec := lxdclient.InstanceSpec{
   265  		Name:      name,
   266  		Profiles:  []string{},
   267  		Ephemeral: false,
   268  		Metadata:  metadata,
   269  	}
   270  	return lxdclient.NewInstance(summary, &instanceSpec)
   271  }
   272  
   273  func (s *BaseSuiteUnpatched) NewInstance(c *gc.C, name string) *environInstance {
   274  	raw := s.NewRawInstance(c, name)
   275  	return newInstance(raw, s.Env)
   276  }
   277  
   278  func (s *BaseSuiteUnpatched) IsRunningLocally(c *gc.C) bool {
   279  	restore := gitjujutesting.PatchEnvPathPrepend(s.osPathOrig)
   280  	defer restore()
   281  
   282  	running, err := lxdclient.IsRunningLocally()
   283  	c.Assert(err, jc.ErrorIsNil)
   284  	return running
   285  }
   286  
   287  type BaseSuite struct {
   288  	BaseSuiteUnpatched
   289  
   290  	Stub       *gitjujutesting.Stub
   291  	Client     *StubClient
   292  	Firewaller *stubFirewaller
   293  	Common     *stubCommon
   294  }
   295  
   296  func (s *BaseSuite) SetUpSuite(c *gc.C) {
   297  	s.BaseSuiteUnpatched.SetUpSuite(c)
   298  	// Do this *before* s.initEnv() gets called in BaseSuiteUnpatched.SetUpTest
   299  }
   300  
   301  func (s *BaseSuite) SetUpTest(c *gc.C) {
   302  	s.BaseSuiteUnpatched.SetUpTest(c)
   303  
   304  	s.Stub = &gitjujutesting.Stub{}
   305  	s.Client = &StubClient{Stub: s.Stub}
   306  	s.Firewaller = &stubFirewaller{stub: s.Stub}
   307  	s.Common = &stubCommon{stub: s.Stub}
   308  
   309  	// Patch out all expensive external deps.
   310  	s.Env.raw = &rawProvider{
   311  		lxdCerts:     s.Client,
   312  		lxdConfig:    s.Client,
   313  		lxdInstances: s.Client,
   314  		lxdImages:    s.Client,
   315  		Firewaller:   s.Firewaller,
   316  	}
   317  	s.Env.base = s.Common
   318  }
   319  
   320  func (s *BaseSuite) CheckNoAPI(c *gc.C) {
   321  	s.Stub.CheckCalls(c, nil)
   322  }
   323  
   324  func NewBaseConfig(c *gc.C) *config.Config {
   325  	var err error
   326  	cfg := testing.ModelConfig(c)
   327  
   328  	cfg, err = cfg.Apply(ConfigAttrs)
   329  	c.Assert(err, jc.ErrorIsNil)
   330  
   331  	return cfg
   332  }
   333  
   334  func NewCustomBaseConfig(c *gc.C, updates map[string]interface{}) *config.Config {
   335  	if updates == nil {
   336  		updates = make(testing.Attrs)
   337  	}
   338  
   339  	cfg := NewBaseConfig(c)
   340  
   341  	cfg, err := cfg.Apply(updates)
   342  	c.Assert(err, jc.ErrorIsNil)
   343  
   344  	return cfg
   345  }
   346  
   347  type ConfigValues struct {
   348  }
   349  
   350  type Config struct {
   351  	*environConfig
   352  }
   353  
   354  func NewConfig(cfg *config.Config) *Config {
   355  	ecfg := newConfig(cfg)
   356  	return &Config{ecfg}
   357  }
   358  
   359  func (ecfg *Config) Values(c *gc.C) (ConfigValues, map[string]interface{}) {
   360  	c.Assert(ecfg.attrs, jc.DeepEquals, ecfg.UnknownAttrs())
   361  
   362  	var values ConfigValues
   363  	extras := make(map[string]interface{})
   364  	for k, v := range ecfg.attrs {
   365  		switch k {
   366  		default:
   367  			extras[k] = v
   368  		}
   369  	}
   370  	return values, extras
   371  }
   372  
   373  func (ecfg *Config) Apply(c *gc.C, updates map[string]interface{}) *Config {
   374  	cfg, err := ecfg.Config.Apply(updates)
   375  	c.Assert(err, jc.ErrorIsNil)
   376  	return NewConfig(cfg)
   377  }
   378  
   379  func (ecfg *Config) Validate() error {
   380  	return ecfg.validate()
   381  }
   382  
   383  type stubCommon struct {
   384  	stub *gitjujutesting.Stub
   385  
   386  	BootstrapResult *environs.BootstrapResult
   387  }
   388  
   389  func (sc *stubCommon) BootstrapEnv(ctx environs.BootstrapContext, params environs.BootstrapParams) (*environs.BootstrapResult, error) {
   390  	sc.stub.AddCall("Bootstrap", ctx, params)
   391  	if err := sc.stub.NextErr(); err != nil {
   392  		return nil, errors.Trace(err)
   393  	}
   394  
   395  	return sc.BootstrapResult, nil
   396  }
   397  
   398  func (sc *stubCommon) DestroyEnv() error {
   399  	sc.stub.AddCall("Destroy")
   400  	if err := sc.stub.NextErr(); err != nil {
   401  		return errors.Trace(err)
   402  	}
   403  
   404  	return nil
   405  }
   406  
   407  type StubClient struct {
   408  	*gitjujutesting.Stub
   409  
   410  	Insts []lxdclient.Instance
   411  	Inst  *lxdclient.Instance
   412  }
   413  
   414  func (conn *StubClient) Instances(prefix string, statuses ...string) ([]lxdclient.Instance, error) {
   415  	conn.AddCall("Instances", prefix, statuses)
   416  	if err := conn.NextErr(); err != nil {
   417  		return nil, errors.Trace(err)
   418  	}
   419  
   420  	return conn.Insts, nil
   421  }
   422  
   423  func (conn *StubClient) AddInstance(spec lxdclient.InstanceSpec) (*lxdclient.Instance, error) {
   424  	conn.AddCall("AddInstance", spec)
   425  	if err := conn.NextErr(); err != nil {
   426  		return nil, errors.Trace(err)
   427  	}
   428  
   429  	return conn.Inst, nil
   430  }
   431  
   432  func (conn *StubClient) RemoveInstances(prefix string, ids ...string) error {
   433  	conn.AddCall("RemoveInstances", prefix, ids)
   434  	if err := conn.NextErr(); err != nil {
   435  		return errors.Trace(err)
   436  	}
   437  
   438  	return nil
   439  }
   440  
   441  func (conn *StubClient) EnsureImageExists(series string, _ []lxdclient.Remote, _ func(string)) error {
   442  	conn.AddCall("EnsureImageExists", series)
   443  	if err := conn.NextErr(); err != nil {
   444  		return errors.Trace(err)
   445  	}
   446  
   447  	return nil
   448  }
   449  
   450  func (conn *StubClient) Addresses(name string) ([]network.Address, error) {
   451  	conn.AddCall("Addresses", name)
   452  	if err := conn.NextErr(); err != nil {
   453  		return nil, errors.Trace(err)
   454  	}
   455  
   456  	return []network.Address{network.Address{
   457  		Value: "10.0.0.1",
   458  		Type:  network.IPv4Address,
   459  		Scope: network.ScopeCloudLocal,
   460  	}}, nil
   461  }
   462  
   463  func (conn *StubClient) AddCert(cert lxdclient.Cert) error {
   464  	conn.AddCall("AddCert", cert)
   465  	return conn.NextErr()
   466  }
   467  
   468  func (conn *StubClient) RemoveCertByFingerprint(fingerprint string) error {
   469  	conn.AddCall("RemoveCertByFingerprint", fingerprint)
   470  	return conn.NextErr()
   471  }
   472  
   473  func (conn *StubClient) ServerStatus() (*shared.ServerState, error) {
   474  	conn.AddCall("ServerStatus")
   475  	if err := conn.NextErr(); err != nil {
   476  		return nil, err
   477  	}
   478  	return &shared.ServerState{
   479  		Environment: shared.ServerStateEnvironment{
   480  			Certificate: "server-cert",
   481  		},
   482  	}, nil
   483  }
   484  
   485  func (conn *StubClient) SetConfig(k, v string) error {
   486  	conn.AddCall("SetConfig", k, v)
   487  	return conn.NextErr()
   488  }
   489  
   490  // TODO(ericsnow) Move stubFirewaller to environs/testing or provider/common/testing.
   491  
   492  type stubFirewaller struct {
   493  	stub *gitjujutesting.Stub
   494  
   495  	PortRanges []network.PortRange
   496  }
   497  
   498  func (fw *stubFirewaller) Ports(fwname string) ([]network.PortRange, error) {
   499  	fw.stub.AddCall("Ports", fwname)
   500  	if err := fw.stub.NextErr(); err != nil {
   501  		return nil, errors.Trace(err)
   502  	}
   503  
   504  	return fw.PortRanges, nil
   505  }
   506  
   507  func (fw *stubFirewaller) OpenPorts(fwname string, ports ...network.PortRange) error {
   508  	fw.stub.AddCall("OpenPorts", fwname, ports)
   509  	if err := fw.stub.NextErr(); err != nil {
   510  		return errors.Trace(err)
   511  	}
   512  
   513  	return nil
   514  }
   515  
   516  func (fw *stubFirewaller) ClosePorts(fwname string, ports ...network.PortRange) error {
   517  	fw.stub.AddCall("ClosePorts", fwname, ports)
   518  	if err := fw.stub.NextErr(); err != nil {
   519  		return errors.Trace(err)
   520  	}
   521  
   522  	return nil
   523  }