github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/provider/lxd/testing_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package lxd
     5  
     6  import (
     7  	"net"
     8  	"os"
     9  	"strconv"
    10  	"time"
    11  
    12  	"github.com/juju/clock"
    13  	"github.com/juju/errors"
    14  	gitjujutesting "github.com/juju/testing"
    15  	jc "github.com/juju/testing/checkers"
    16  	"github.com/juju/utils/arch"
    17  	"github.com/juju/version"
    18  	lxdclient "github.com/lxc/lxd/client"
    19  	"github.com/lxc/lxd/shared/api"
    20  	gc "gopkg.in/check.v1"
    21  
    22  	"github.com/juju/juju/cloud"
    23  	"github.com/juju/juju/cloudconfig/instancecfg"
    24  	"github.com/juju/juju/cloudconfig/providerinit"
    25  	"github.com/juju/juju/container/lxd"
    26  	containerlxd "github.com/juju/juju/container/lxd"
    27  	"github.com/juju/juju/core/constraints"
    28  	"github.com/juju/juju/core/instance"
    29  	"github.com/juju/juju/environs"
    30  	"github.com/juju/juju/environs/config"
    31  	"github.com/juju/juju/environs/context"
    32  	"github.com/juju/juju/environs/instances"
    33  	"github.com/juju/juju/environs/tags"
    34  	"github.com/juju/juju/network"
    35  	"github.com/juju/juju/testing"
    36  	coretools "github.com/juju/juju/tools"
    37  )
    38  
    39  // Ensure LXD provider supports the expected interfaces.
    40  var (
    41  	_ config.ConfigSchemaSource = (*environProvider)(nil)
    42  )
    43  
    44  // These values are stub LXD client credentials for use in tests.
    45  const (
    46  	PublicKey = `-----BEGIN CERTIFICATE-----
    47  ...
    48  ...
    49  ...
    50  ...
    51  ...
    52  ...
    53  ...
    54  ...
    55  ...
    56  ...
    57  ...
    58  ...
    59  ...
    60  ...
    61  -----END CERTIFICATE-----
    62  `
    63  	PrivateKey = `-----BEGIN PRIVATE KEY-----
    64  ...
    65  ...
    66  ...
    67  ...
    68  ...
    69  ...
    70  ...
    71  ...
    72  ...
    73  ...
    74  ...
    75  ...
    76  ...
    77  ...
    78  -----END PRIVATE KEY-----
    79  `
    80  )
    81  
    82  // These are stub config values for use in tests.
    83  var (
    84  	ConfigAttrs = testing.FakeConfig().Merge(testing.Attrs{
    85  		"type": "lxd",
    86  		"uuid": "2d02eeac-9dbb-11e4-89d3-123b93f75cba",
    87  	})
    88  )
    89  
    90  // We test these here since they are not exported.
    91  var (
    92  	_ environs.Environ   = (*environ)(nil)
    93  	_ instances.Instance = (*environInstance)(nil)
    94  )
    95  
    96  type BaseSuiteUnpatched struct {
    97  	testing.BaseSuite
    98  
    99  	osPathOrig string
   100  
   101  	Config    *config.Config
   102  	EnvConfig *environConfig
   103  	Provider  *environProvider
   104  	Env       *environ
   105  
   106  	Addresses     []network.Address
   107  	Instance      *environInstance
   108  	Container     *lxd.Container
   109  	InstName      string
   110  	HWC           *instance.HardwareCharacteristics
   111  	Metadata      map[string]string
   112  	StartInstArgs environs.StartInstanceParams
   113  
   114  	Rules          []network.IngressRule
   115  	EndpointAddrs  []string
   116  	InterfaceAddr  string
   117  	InterfaceAddrs []net.Addr
   118  }
   119  
   120  func (s *BaseSuiteUnpatched) SetUpSuite(c *gc.C) {
   121  	s.osPathOrig = os.Getenv("PATH")
   122  	if s.osPathOrig == "" {
   123  		// TODO(ericsnow) This shouldn't happen. However, an undiagnosed
   124  		// bug in testing.BaseSuite is causing $PATH to remain unset
   125  		// sometimes.  Once that is cleared up this special-case can go
   126  		// away.
   127  		s.osPathOrig =
   128  			"/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin"
   129  	}
   130  	s.BaseSuite.SetUpSuite(c)
   131  }
   132  
   133  func (s *BaseSuiteUnpatched) SetUpTest(c *gc.C) {
   134  	s.BaseSuite.SetUpTest(c)
   135  
   136  	s.initProvider(c)
   137  	s.initEnv(c)
   138  	s.initInst(c)
   139  	s.initNet(c)
   140  }
   141  
   142  func (s *BaseSuiteUnpatched) initProvider(c *gc.C) {
   143  	s.Provider = &environProvider{}
   144  	s.EndpointAddrs = []string{"1.2.3.4"}
   145  	s.InterfaceAddr = "1.2.3.4"
   146  	s.InterfaceAddrs = []net.Addr{
   147  		&net.IPNet{IP: net.ParseIP("127.0.0.1")},
   148  		&net.IPNet{IP: net.ParseIP("1.2.3.4")},
   149  	}
   150  }
   151  
   152  func (s *BaseSuiteUnpatched) initEnv(c *gc.C) {
   153  	certCred := cloud.NewCredential(cloud.CertificateAuthType, map[string]string{
   154  		"client-cert": testing.CACert,
   155  		"client-key":  testing.CAKey,
   156  		"server-cert": testing.ServerCert,
   157  	})
   158  	s.Env = &environ{
   159  		cloud: environs.CloudSpec{
   160  			Name:       "localhost",
   161  			Type:       "lxd",
   162  			Credential: &certCred,
   163  		},
   164  		provider: s.Provider,
   165  		name:     "lxd",
   166  	}
   167  	cfg := s.NewConfig(c, nil)
   168  	s.setConfig(c, cfg)
   169  }
   170  
   171  func (s *BaseSuiteUnpatched) Prefix() string {
   172  	return s.Env.namespace.Prefix()
   173  }
   174  
   175  func (s *BaseSuiteUnpatched) initInst(c *gc.C) {
   176  	tools := []*coretools.Tools{
   177  		{
   178  			Version: version.Binary{Arch: arch.AMD64, Series: "trusty"},
   179  			URL:     "https://example.org/amd",
   180  		},
   181  		{
   182  			Version: version.Binary{Arch: arch.ARM64, Series: "trusty"},
   183  			URL:     "https://example.org/arm",
   184  		},
   185  	}
   186  
   187  	cons := constraints.Value{}
   188  
   189  	instanceConfig, err := instancecfg.NewBootstrapInstanceConfig(testing.FakeControllerConfig(), cons, cons, "trusty", "")
   190  	c.Assert(err, jc.ErrorIsNil)
   191  
   192  	err = instanceConfig.SetTools(coretools.List{
   193  		tools[0],
   194  	})
   195  	c.Assert(err, jc.ErrorIsNil)
   196  	instanceConfig.AuthorizedKeys = s.Config.AuthorizedKeys()
   197  
   198  	userData, err := providerinit.ComposeUserData(instanceConfig, nil, lxdRenderer{})
   199  	c.Assert(err, jc.ErrorIsNil)
   200  
   201  	var archName = arch.ARM64
   202  	var numCores uint64 = 1
   203  	var memoryMB uint64 = 3750
   204  	s.HWC = &instance.HardwareCharacteristics{
   205  		Arch:     &archName,
   206  		CpuCores: &numCores,
   207  		Mem:      &memoryMB,
   208  	}
   209  
   210  	s.Metadata = map[string]string{
   211  		containerlxd.UserNamespacePrefix + tags.JujuIsController: "true",
   212  		containerlxd.UserNamespacePrefix + tags.JujuController:   testing.ControllerTag.Id(),
   213  		containerlxd.JujuModelKey:                                s.Config.UUID(),
   214  		containerlxd.UserDataKey:                                 string(userData),
   215  		"limits.cpu":                                             "1",
   216  		"limits.memory":                                          strconv.Itoa(3750 * 1024 * 1024),
   217  	}
   218  	s.Addresses = []network.Address{{
   219  		Value: "10.0.0.1",
   220  		Type:  network.IPv4Address,
   221  		Scope: network.ScopeCloudLocal,
   222  	}}
   223  	// NOTE: the instance ids used throughout this package are not at all
   224  	// representative of what they would normally be. They would normally
   225  	// determined by the instance namespace and the machine id.
   226  	s.Instance = s.NewInstance(c, "spam")
   227  	s.Container = s.Instance.container
   228  	s.InstName, err = s.Env.namespace.Hostname("42")
   229  	c.Assert(err, jc.ErrorIsNil)
   230  
   231  	s.StartInstArgs = environs.StartInstanceParams{
   232  		ControllerUUID: instanceConfig.Controller.Config.ControllerUUID(),
   233  		InstanceConfig: instanceConfig,
   234  		Tools:          tools,
   235  		Constraints:    cons,
   236  	}
   237  }
   238  
   239  func (s *BaseSuiteUnpatched) initNet(c *gc.C) {
   240  	s.Rules = []network.IngressRule{network.MustNewIngressRule("tcp", 80, 80)}
   241  }
   242  
   243  func (s *BaseSuiteUnpatched) setConfig(c *gc.C, cfg *config.Config) {
   244  	s.Config = cfg
   245  	ecfg, err := newValidConfig(cfg)
   246  	c.Assert(err, jc.ErrorIsNil)
   247  	s.EnvConfig = ecfg
   248  	uuid := cfg.UUID()
   249  	s.Env.uuid = uuid
   250  	s.Env.ecfg = s.EnvConfig
   251  	namespace, err := instance.NewNamespace(uuid)
   252  	c.Assert(err, jc.ErrorIsNil)
   253  	s.Env.namespace = namespace
   254  }
   255  
   256  func (s *BaseSuiteUnpatched) NewConfig(c *gc.C, updates testing.Attrs) *config.Config {
   257  	if updates == nil {
   258  		updates = make(testing.Attrs)
   259  	}
   260  	var err error
   261  	cfg := testing.ModelConfig(c)
   262  	cfg, err = cfg.Apply(ConfigAttrs)
   263  	c.Assert(err, jc.ErrorIsNil)
   264  	cfg, err = cfg.Apply(updates)
   265  	c.Assert(err, jc.ErrorIsNil)
   266  	return cfg
   267  }
   268  
   269  func (s *BaseSuiteUnpatched) UpdateConfig(c *gc.C, attrs map[string]interface{}) {
   270  	cfg, err := s.Config.Apply(attrs)
   271  	c.Assert(err, jc.ErrorIsNil)
   272  	s.setConfig(c, cfg)
   273  }
   274  
   275  func (s *BaseSuiteUnpatched) NewContainer(c *gc.C, name string) *containerlxd.Container {
   276  	metadata := make(map[string]string)
   277  	for k, v := range s.Metadata {
   278  		metadata[k] = v
   279  	}
   280  
   281  	return &containerlxd.Container{
   282  		Container: api.Container{
   283  			Name:       name,
   284  			StatusCode: api.Running,
   285  			Status:     api.Running.String(),
   286  			ContainerPut: api.ContainerPut{
   287  				Config: metadata,
   288  			},
   289  		},
   290  	}
   291  }
   292  
   293  func (s *BaseSuiteUnpatched) NewInstance(c *gc.C, name string) *environInstance {
   294  	container := s.NewContainer(c, name)
   295  	return newInstance(container, s.Env)
   296  }
   297  
   298  func (s *BaseSuiteUnpatched) IsRunningLocally(c *gc.C) bool {
   299  	restore := gitjujutesting.PatchEnvPathPrepend(s.osPathOrig)
   300  	defer restore()
   301  
   302  	running, err := lxd.IsRunningLocally()
   303  	c.Assert(err, jc.ErrorIsNil)
   304  	return running
   305  }
   306  
   307  type BaseSuite struct {
   308  	BaseSuiteUnpatched
   309  
   310  	Stub   *gitjujutesting.Stub
   311  	Client *StubClient
   312  	Common *stubCommon
   313  }
   314  
   315  func (s *BaseSuite) SetUpSuite(c *gc.C) {
   316  	s.BaseSuiteUnpatched.SetUpSuite(c)
   317  	// Do this *before* s.initEnv() gets called in BaseSuiteUnpatched.SetUpTest
   318  }
   319  
   320  func (s *BaseSuite) SetUpTest(c *gc.C) {
   321  	s.BaseSuiteUnpatched.SetUpTest(c)
   322  
   323  	s.Stub = &gitjujutesting.Stub{}
   324  	s.Client = &StubClient{
   325  		Stub:               s.Stub,
   326  		StorageIsSupported: true,
   327  		Server: &api.Server{
   328  			ServerPut: api.ServerPut{
   329  				Config: map[string]interface{}{},
   330  			},
   331  			Environment: api.ServerEnvironment{
   332  				Certificate: "server-cert",
   333  			},
   334  		},
   335  		Profile: &api.Profile{},
   336  	}
   337  	s.Common = &stubCommon{stub: s.Stub}
   338  
   339  	// Patch out all expensive external deps.
   340  	s.Env.server = s.Client
   341  	s.Env.base = s.Common
   342  }
   343  
   344  func (s *BaseSuite) TestingCert(c *gc.C) (lxd.Certificate, string) {
   345  	cert := lxd.Certificate{
   346  		Name:    "juju",
   347  		CertPEM: []byte(testing.CACert),
   348  		KeyPEM:  []byte(testing.CAKey),
   349  	}
   350  	fingerprint, err := cert.Fingerprint()
   351  	c.Assert(err, jc.ErrorIsNil)
   352  	return cert, fingerprint
   353  }
   354  
   355  func (s *BaseSuite) CheckNoAPI(c *gc.C) {
   356  	s.Stub.CheckCalls(c, nil)
   357  }
   358  
   359  func NewBaseConfig(c *gc.C) *config.Config {
   360  	var err error
   361  	cfg := testing.ModelConfig(c)
   362  
   363  	cfg, err = cfg.Apply(ConfigAttrs)
   364  	c.Assert(err, jc.ErrorIsNil)
   365  
   366  	return cfg
   367  }
   368  
   369  type ConfigValues struct{}
   370  
   371  type Config struct {
   372  	*environConfig
   373  }
   374  
   375  func NewConfig(cfg *config.Config) *Config {
   376  	ecfg := newConfig(cfg)
   377  	return &Config{ecfg}
   378  }
   379  
   380  func (ecfg *Config) Values(c *gc.C) (ConfigValues, map[string]interface{}) {
   381  	c.Assert(ecfg.attrs, jc.DeepEquals, ecfg.UnknownAttrs())
   382  
   383  	var values ConfigValues
   384  	extras := make(map[string]interface{})
   385  	for k, v := range ecfg.attrs {
   386  		switch k {
   387  		default:
   388  			extras[k] = v
   389  		}
   390  	}
   391  	return values, extras
   392  }
   393  
   394  func (ecfg *Config) Apply(c *gc.C, updates map[string]interface{}) *Config {
   395  	cfg, err := ecfg.Config.Apply(updates)
   396  	c.Assert(err, jc.ErrorIsNil)
   397  	return NewConfig(cfg)
   398  }
   399  
   400  func (ecfg *Config) Validate() error {
   401  	return ecfg.validate()
   402  }
   403  
   404  type stubCommon struct {
   405  	stub *gitjujutesting.Stub
   406  
   407  	BootstrapResult *environs.BootstrapResult
   408  }
   409  
   410  func (sc *stubCommon) BootstrapEnv(ctx environs.BootstrapContext, callCtx context.ProviderCallContext, params environs.BootstrapParams) (*environs.BootstrapResult, error) {
   411  	sc.stub.AddCall("Bootstrap", ctx, callCtx, params)
   412  	if err := sc.stub.NextErr(); err != nil {
   413  		return nil, errors.Trace(err)
   414  	}
   415  
   416  	return sc.BootstrapResult, nil
   417  }
   418  
   419  func (sc *stubCommon) DestroyEnv(callCtx context.ProviderCallContext) error {
   420  	sc.stub.AddCall("Destroy", callCtx)
   421  	if err := sc.stub.NextErr(); err != nil {
   422  		return errors.Trace(err)
   423  	}
   424  
   425  	return nil
   426  }
   427  
   428  type StubClient struct {
   429  	*gitjujutesting.Stub
   430  
   431  	Containers         []lxd.Container
   432  	Container          *lxd.Container
   433  	Server             *api.Server
   434  	Profile            *api.Profile
   435  	StorageIsSupported bool
   436  	Volumes            map[string][]api.StorageVolume
   437  	ServerCert         string
   438  	ServerHostArch     string
   439  }
   440  
   441  func (conn *StubClient) FilterContainers(prefix string, statuses ...string) ([]lxd.Container, error) {
   442  	conn.AddCall("FilterContainers", prefix, statuses)
   443  	if err := conn.NextErr(); err != nil {
   444  		return nil, errors.Trace(err)
   445  	}
   446  
   447  	return conn.Containers, nil
   448  }
   449  
   450  func (conn *StubClient) CreateContainerFromSpec(spec lxd.ContainerSpec) (*lxd.Container, error) {
   451  	conn.AddCall("CreateContainerFromSpec", spec)
   452  	if err := conn.NextErr(); err != nil {
   453  		return nil, errors.Trace(err)
   454  	}
   455  
   456  	return conn.Container, nil
   457  }
   458  
   459  func (conn *StubClient) FindImage(
   460  	series, arch string, sources []lxd.ServerSpec, copyLocal bool, callback environs.StatusCallbackFunc,
   461  ) (lxd.SourcedImage, error) {
   462  	conn.AddCall("FindImage", series, arch)
   463  	if err := conn.NextErr(); err != nil {
   464  		return lxd.SourcedImage{}, errors.Trace(err)
   465  	}
   466  
   467  	return lxd.SourcedImage{}, nil
   468  }
   469  
   470  func (conn *StubClient) CreateCertificate(cert api.CertificatesPost) error {
   471  	conn.AddCall("CreateCertificate", cert)
   472  	return conn.NextErr()
   473  }
   474  
   475  func (conn *StubClient) CreateClientCertificate(cert *lxd.Certificate) error {
   476  	conn.AddCall("CreateClientCertificate", cert)
   477  	return conn.NextErr()
   478  }
   479  
   480  func (conn *StubClient) DeleteCertificate(fingerprint string) error {
   481  	conn.AddCall("RemoveCertByFingerprint", fingerprint)
   482  	return conn.NextErr()
   483  }
   484  
   485  func (conn *StubClient) GetCertificate(fingerprint string) (*api.Certificate, string, error) {
   486  	conn.AddCall("GetCertificate", fingerprint)
   487  	return &api.Certificate{}, "", conn.NextErr()
   488  }
   489  
   490  func (conn *StubClient) GetServer() (*api.Server, string, error) {
   491  	conn.AddCall("ServerStatus")
   492  	if err := conn.NextErr(); err != nil {
   493  		return nil, "", err
   494  	}
   495  	return &api.Server{
   496  		Environment: api.ServerEnvironment{
   497  			Certificate: "server-cert",
   498  		},
   499  	}, "etag", nil
   500  }
   501  
   502  func (conn *StubClient) GetConnectionInfo() (info *lxdclient.ConnectionInfo, err error) {
   503  	conn.AddCall("ServerAddresses")
   504  	return &lxdclient.ConnectionInfo{
   505  		Addresses: []string{"127.0.0.1:1234", "1.2.3.4:1234"},
   506  	}, conn.NextErr()
   507  }
   508  
   509  func (conn *StubClient) UpdateServerConfig(cfg map[string]string) error {
   510  	conn.AddCall("UpdateServerConfig", cfg)
   511  	return conn.NextErr()
   512  }
   513  
   514  func (conn *StubClient) UpdateContainerConfig(container string, cfg map[string]string) error {
   515  	conn.AddCall("UpdateContainerConfig", container, cfg)
   516  	return conn.NextErr()
   517  }
   518  
   519  func (conn *StubClient) LocalBridgeName() string {
   520  	conn.AddCall("LocalBridgeName")
   521  	return "test-bridge"
   522  }
   523  
   524  func (conn *StubClient) GetProfile(name string) (*api.Profile, string, error) {
   525  	conn.AddCall("GetProfile", name)
   526  	return conn.Profile, "etag", conn.NextErr()
   527  }
   528  
   529  func (conn *StubClient) GetContainerProfiles(name string) ([]string, error) {
   530  	conn.AddCall("GetContainerProfiles", name)
   531  	return []string{
   532  		"default",
   533  		"juju-model-name",
   534  	}, conn.NextErr()
   535  }
   536  
   537  func (conn *StubClient) DeleteProfile(name string) error {
   538  	conn.AddCall("DeleteProfile", name)
   539  	return conn.NextErr()
   540  }
   541  
   542  func (conn *StubClient) HasProfile(name string) (bool, error) {
   543  	conn.AddCall("HasProfile", name)
   544  	return false, conn.NextErr()
   545  }
   546  
   547  func (conn *StubClient) ReplaceOrAddContainerProfile(name, oldProfile, newProfile string) error {
   548  	conn.AddCall("ReplaceOrAddContainerProfile", name, oldProfile, newProfile)
   549  	return conn.NextErr()
   550  }
   551  
   552  func (conn *StubClient) VerifyNetworkDevice(profile *api.Profile, ETag string) error {
   553  	conn.AddCall("VerifyNetworkDevice", profile, ETag)
   554  	return conn.NextErr()
   555  }
   556  
   557  func (conn *StubClient) StorageSupported() bool {
   558  	conn.AddCall("StorageSupported")
   559  	return conn.StorageIsSupported
   560  }
   561  
   562  func (conn *StubClient) EnsureDefaultStorage(profile *api.Profile, ETag string) error {
   563  	conn.AddCall("EnsureDefaultStorage", profile, ETag)
   564  	return conn.NextErr()
   565  }
   566  
   567  func (conn *StubClient) GetStoragePool(name string) (pool *api.StoragePool, ETag string, err error) {
   568  	conn.AddCall("GetStoragePool", name)
   569  	return &api.StoragePool{
   570  		Name:   name,
   571  		Driver: "dir",
   572  	}, "", conn.NextErr()
   573  }
   574  
   575  func (conn *StubClient) GetStoragePools() ([]api.StoragePool, error) {
   576  	conn.AddCall("GetStoragePools")
   577  	return []api.StoragePool{{
   578  		Name:   "juju",
   579  		Driver: "dir",
   580  	}, {
   581  		Name:   "juju-zfs",
   582  		Driver: "zfs",
   583  	}}, conn.NextErr()
   584  }
   585  
   586  func (conn *StubClient) CreatePool(name, driver string, attrs map[string]string) error {
   587  	conn.AddCall("CreatePool", name, driver, attrs)
   588  	return conn.NextErr()
   589  }
   590  
   591  func (conn *StubClient) CreateVolume(pool, volume string, config map[string]string) error {
   592  	conn.AddCall("CreateVolume", pool, volume, config)
   593  	return conn.NextErr()
   594  }
   595  
   596  func (conn *StubClient) DeleteStoragePoolVolume(pool, volType, volume string) error {
   597  	conn.AddCall("DeleteStoragePoolVolume", pool, volType, volume)
   598  	return conn.NextErr()
   599  }
   600  
   601  func (conn *StubClient) GetStoragePoolVolume(
   602  	pool string, volType string, name string,
   603  ) (*api.StorageVolume, string, error) {
   604  	conn.AddCall("GetStoragePoolVolume", pool, volType, name)
   605  	if err := conn.NextErr(); err != nil {
   606  		return nil, "", err
   607  	}
   608  	for _, v := range conn.Volumes[pool] {
   609  		if v.Name == name {
   610  			return &v, "eTag", nil
   611  		}
   612  	}
   613  	return nil, "", errors.NotFoundf("volume %q in pool %q", name, pool)
   614  }
   615  
   616  func (conn *StubClient) GetStoragePoolVolumes(pool string) ([]api.StorageVolume, error) {
   617  	conn.AddCall("GetStoragePoolVolumes", pool)
   618  	if err := conn.NextErr(); err != nil {
   619  		return nil, err
   620  	}
   621  	return conn.Volumes[pool], nil
   622  }
   623  
   624  func (conn *StubClient) UpdateStoragePoolVolume(
   625  	pool string, volType string, name string, volume api.StorageVolumePut, ETag string,
   626  ) error {
   627  	conn.AddCall("UpdateStoragePoolVolume", pool, volType, name, volume, ETag)
   628  	return conn.NextErr()
   629  }
   630  
   631  func (conn *StubClient) AliveContainers(prefix string) ([]lxd.Container, error) {
   632  	conn.AddCall("AliveContainers", prefix)
   633  	if err := conn.NextErr(); err != nil {
   634  		return nil, err
   635  	}
   636  	return conn.Containers, nil
   637  }
   638  
   639  func (conn *StubClient) ContainerAddresses(name string) ([]network.Address, error) {
   640  	conn.AddCall("ContainerAddresses", name)
   641  	if err := conn.NextErr(); err != nil {
   642  		return nil, err
   643  	}
   644  
   645  	return []network.Address{{
   646  		Value: "10.0.0.1",
   647  		Type:  network.IPv4Address,
   648  		Scope: network.ScopeCloudLocal,
   649  	}}, nil
   650  }
   651  
   652  func (conn *StubClient) RemoveContainer(name string) error {
   653  	conn.AddCall("RemoveContainer", name)
   654  	return conn.NextErr()
   655  }
   656  
   657  func (conn *StubClient) RemoveContainers(names []string) error {
   658  	conn.AddCall("RemoveContainers", names)
   659  	return conn.NextErr()
   660  }
   661  
   662  func (conn *StubClient) WriteContainer(container *lxd.Container) error {
   663  	conn.AddCall("WriteContainer", container)
   664  	return conn.NextErr()
   665  }
   666  
   667  func (conn *StubClient) CreateProfileWithConfig(name string, cfg map[string]string) error {
   668  	conn.AddCall("CreateProfileWithConfig", name, cfg)
   669  	return conn.NextErr()
   670  }
   671  
   672  func (conn *StubClient) CreateProfile(post api.ProfilesPost) error {
   673  	conn.AddCall("CreateProfile", post)
   674  	return conn.NextErr()
   675  }
   676  
   677  func (conn *StubClient) ServerCertificate() string {
   678  	conn.AddCall("ServerCertificate")
   679  	return conn.ServerCert
   680  }
   681  
   682  func (conn *StubClient) HostArch() string {
   683  	conn.AddCall("HostArch")
   684  	return conn.ServerHostArch
   685  }
   686  
   687  func (conn *StubClient) EnableHTTPSListener() error {
   688  	conn.AddCall("EnableHTTPSListener")
   689  	return conn.NextErr()
   690  }
   691  
   692  func (conn *StubClient) GetNICsFromProfile(profName string) (map[string]map[string]string, error) {
   693  	conn.AddCall("GetNICsFromProfile", profName)
   694  	return conn.Profile.Devices, conn.NextErr()
   695  }
   696  
   697  func (conn *StubClient) IsClustered() bool {
   698  	conn.AddCall("IsClustered")
   699  	return true
   700  }
   701  
   702  func (conn *StubClient) Name() string {
   703  	conn.AddCall("Name")
   704  	return "server"
   705  }
   706  
   707  // TODO (manadart 2018-07-20): This exists to satisfy the testing stub
   708  // interface. It is temporary, pending replacement with mocks and
   709  // should not be called in tests.
   710  func (conn *StubClient) UseTargetServer(name string) (*lxd.Server, error) {
   711  	conn.AddCall("UseTargetServer", name)
   712  	return nil, conn.NextErr()
   713  }
   714  
   715  func (conn *StubClient) GetClusterMembers() (members []api.ClusterMember, err error) {
   716  	conn.AddCall("GetClusterMembers")
   717  	return nil, conn.NextErr()
   718  }
   719  
   720  type MockClock struct {
   721  	clock.Clock
   722  	now time.Time
   723  }
   724  
   725  func (m *MockClock) Now() time.Time {
   726  	return m.now
   727  }
   728  
   729  func (m *MockClock) After(delay time.Duration) <-chan time.Time {
   730  	return time.After(time.Millisecond)
   731  }
   732  
   733  // TODO (manadart 2018-07-20): All of the above logic should ultimately be
   734  // replaced by what follows (in some form). The stub usage will be abandoned
   735  // and replaced by mocks.
   736  
   737  type EnvironSuite struct {
   738  	testing.BaseSuite
   739  }
   740  
   741  func (s *EnvironSuite) NewEnviron(c *gc.C, svr Server, cfgEdit map[string]interface{}) environs.Environ {
   742  	cfg, err := testing.ModelConfig(c).Apply(ConfigAttrs)
   743  	c.Assert(err, jc.ErrorIsNil)
   744  
   745  	if cfgEdit != nil {
   746  		var err error
   747  		cfg, err = cfg.Apply(cfgEdit)
   748  		c.Assert(err, jc.ErrorIsNil)
   749  	}
   750  
   751  	eCfg, err := newValidConfig(cfg)
   752  	c.Assert(err, jc.ErrorIsNil)
   753  
   754  	namespace, err := instance.NewNamespace(cfg.UUID())
   755  	c.Assert(err, jc.ErrorIsNil)
   756  
   757  	return &environ{
   758  		server:    svr,
   759  		ecfg:      eCfg,
   760  		namespace: namespace,
   761  	}
   762  }
   763  
   764  func (s *EnvironSuite) GetStartInstanceArgs(c *gc.C, series string) environs.StartInstanceParams {
   765  	tools := []*coretools.Tools{
   766  		{
   767  			Version: version.Binary{Arch: arch.AMD64, Series: series},
   768  			URL:     "https://example.org/amd",
   769  		},
   770  		{
   771  			Version: version.Binary{Arch: arch.ARM64, Series: series},
   772  			URL:     "https://example.org/arm",
   773  		},
   774  	}
   775  
   776  	cons := constraints.Value{}
   777  	iConfig, err := instancecfg.NewBootstrapInstanceConfig(testing.FakeControllerConfig(), cons, cons, series, "")
   778  	c.Assert(err, jc.ErrorIsNil)
   779  
   780  	return environs.StartInstanceParams{
   781  		ControllerUUID: iConfig.Controller.Config.ControllerUUID(),
   782  		InstanceConfig: iConfig,
   783  		Tools:          tools,
   784  		Constraints:    cons,
   785  	}
   786  }