1
// Copyright 2015 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
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
gc "gopkg.in/check.v1"
19
"github.com/juju/juju/cloudconfig/instancecfg"
20
"github.com/juju/juju/cloudconfig/providerinit"
21
"github.com/juju/juju/constraints"
22
"github.com/juju/juju/environs"
23
"github.com/juju/juju/environs/config"
24
"github.com/juju/juju/environs/tags"
25
"github.com/juju/juju/instance"
26
"github.com/juju/juju/network"
27
"github.com/juju/juju/testing"
28
coretools "github.com/juju/juju/tools"
29
"github.com/juju/juju/tools/lxdclient"
30
"github.com/juju/version"
33
// These values are stub LXD client credentials for use in tests.
35
PublicKey = `-----BEGIN CERTIFICATE-----
50
-----END CERTIFICATE-----
52
PrivateKey = `-----BEGIN PRIVATE KEY-----
67
-----END PRIVATE KEY-----
71
// These are stub config values for use in tests.
73
ConfigAttrs = testing.FakeConfig().Merge(testing.Attrs{
79
"uuid": "2d02eeac-9dbb-11e4-89d3-123b93f75cba",
83
// We test these here since they are not exported.
85
_ environs.Environ = (*environ)(nil)
86
_ instance.Instance = (*environInstance)(nil)
89
type BaseSuiteUnpatched struct {
90
gitjujutesting.IsolationSuite
95
EnvConfig *environConfig
98
Addresses []network.Address
99
Instance *environInstance
100
RawInstance *lxdclient.Instance
102
Hardware *lxdclient.InstanceHardware
103
HWC *instance.HardwareCharacteristics
104
Metadata map[string]string
105
StartInstArgs environs.StartInstanceParams
106
//InstanceType instances.InstanceType
108
Ports []network.PortRange
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
119
"/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin"
121
s.IsolationSuite.SetUpSuite(c)
124
func (s *BaseSuiteUnpatched) SetUpTest(c *gc.C) {
125
s.IsolationSuite.SetUpTest(c)
132
func (s *BaseSuiteUnpatched) initEnv(c *gc.C) {
136
cfg := s.NewConfig(c, nil)
140
func (s *BaseSuiteUnpatched) Prefix() string {
141
return s.Env.namespace.Prefix()
144
func (s *BaseSuiteUnpatched) initInst(c *gc.C) {
145
tools := []*coretools.Tools{
147
Version: version.Binary{Arch: arch.AMD64, Series: "trusty"},
148
URL: "https://example.org/amd",
151
Version: version.Binary{Arch: arch.ARM64, Series: "trusty"},
152
URL: "https://example.org/arm",
156
cons := constraints.Value{
160
instanceConfig, err := instancecfg.NewBootstrapInstanceConfig(testing.FakeControllerConfig(), cons, cons, "trusty", "")
161
c.Assert(err, jc.ErrorIsNil)
163
err = instanceConfig.SetTools(coretools.List{
166
c.Assert(err, jc.ErrorIsNil)
167
instanceConfig.AuthorizedKeys = s.Config.AuthorizedKeys()
169
userData, err := providerinit.ComposeUserData(instanceConfig, nil, lxdRenderer{})
170
c.Assert(err, jc.ErrorIsNil)
172
s.Hardware = &lxdclient.InstanceHardware{
173
Architecture: arch.ARM64,
177
var archName string = arch.ARM64
178
var numCores uint64 = 1
179
var memoryMB uint64 = 3750
180
s.HWC = &instance.HardwareCharacteristics{
186
s.Metadata = map[string]string{ // userdata
187
tags.JujuIsController: "true",
188
tags.JujuController: testing.ModelTag.Id(),
189
tags.JujuModel: s.Config.UUID(),
190
metadataKeyCloudInit: string(userData),
192
s.Addresses = []network.Address{{
194
Type: network.IPv4Address,
195
Scope: network.ScopeCloudLocal,
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)
205
s.StartInstArgs = environs.StartInstanceParams{
206
ControllerUUID: instanceConfig.Controller.Config.ControllerUUID(),
207
InstanceConfig: instanceConfig,
213
func (s *BaseSuiteUnpatched) initNet(c *gc.C) {
214
s.Ports = []network.PortRange{{
221
func (s *BaseSuiteUnpatched) setConfig(c *gc.C, cfg *config.Config) {
223
ecfg, err := newValidConfig(cfg, configDefaults)
224
c.Assert(err, jc.ErrorIsNil)
228
s.Env.ecfg = s.EnvConfig
229
namespace, err := instance.NewNamespace(uuid)
230
c.Assert(err, jc.ErrorIsNil)
231
s.Env.namespace = namespace
234
func (s *BaseSuiteUnpatched) NewConfig(c *gc.C, updates testing.Attrs) *config.Config {
236
updates = make(testing.Attrs)
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)
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)
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 {
258
summary := lxdclient.InstanceSummary{
260
Status: lxdclient.StatusRunning,
261
Hardware: *s.Hardware,
264
instanceSpec := lxdclient.InstanceSpec{
266
Profiles: []string{},
270
return lxdclient.NewInstance(summary, &instanceSpec)
273
func (s *BaseSuiteUnpatched) NewInstance(c *gc.C, name string) *environInstance {
274
raw := s.NewRawInstance(c, name)
275
return newInstance(raw, s.Env)
278
func (s *BaseSuiteUnpatched) IsRunningLocally(c *gc.C) bool {
279
restore := gitjujutesting.PatchEnvPathPrepend(s.osPathOrig)
282
running, err := lxdclient.IsRunningLocally()
283
c.Assert(err, jc.ErrorIsNil)
287
type BaseSuite struct {
290
Stub *gitjujutesting.Stub
292
Firewaller *stubFirewaller
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
s.PatchValue(&asNonLocal, s.asNonLocal)
302
func (s *BaseSuite) SetUpTest(c *gc.C) {
303
s.BaseSuiteUnpatched.SetUpTest(c)
305
s.Stub = &gitjujutesting.Stub{}
306
s.Client = &StubClient{Stub: s.Stub}
307
s.Firewaller = &stubFirewaller{stub: s.Stub}
308
s.Common = &stubCommon{stub: s.Stub}
310
// Patch out all expensive external deps.
311
s.Env.raw = &rawProvider{
312
lxdInstances: s.Client,
314
Firewaller: s.Firewaller,
316
s.Env.base = s.Common
319
func (s *BaseSuite) CheckNoAPI(c *gc.C) {
320
s.Stub.CheckCalls(c, nil)
323
func (s *BaseSuite) asNonLocal(clientCfg lxdclient.Config) (lxdclient.Config, error) {
325
return clientCfg, nil
327
s.Stub.AddCall("asNonLocal", clientCfg)
328
if err := s.Stub.NextErr(); err != nil {
329
return clientCfg, errors.Trace(err)
332
return clientCfg, nil
335
func NewBaseConfig(c *gc.C) *config.Config {
337
cfg := testing.ModelConfig(c)
339
cfg, err = cfg.Apply(ConfigAttrs)
340
c.Assert(err, jc.ErrorIsNil)
345
func NewCustomBaseConfig(c *gc.C, updates map[string]interface{}) *config.Config {
347
updates = make(testing.Attrs)
350
cfg := NewBaseConfig(c)
352
cfg, err := cfg.Apply(updates)
353
c.Assert(err, jc.ErrorIsNil)
358
type ConfigValues struct {
365
func (cv ConfigValues) CheckCert(c *gc.C) {
366
certPEM := []byte(cv.ClientCert)
367
keyPEM := []byte(cv.ClientKey)
369
_, err := tls.X509KeyPair(certPEM, keyPEM)
370
c.Check(err, jc.ErrorIsNil)
372
block, remainder := pem.Decode(certPEM)
373
c.Check(block.Type, gc.Equals, "CERTIFICATE")
374
c.Check(remainder, gc.HasLen, 0)
376
block, remainder = pem.Decode(keyPEM)
377
c.Check(block.Type, gc.Equals, "RSA PRIVATE KEY")
378
c.Check(remainder, gc.HasLen, 0)
380
if cv.ServerCert != "" {
381
block, remainder = pem.Decode([]byte(cv.ServerCert))
382
c.Check(block.Type, gc.Equals, "CERTIFICATE")
383
c.Check(remainder, gc.HasLen, 1)
391
func NewConfig(cfg *config.Config) *Config {
392
ecfg := newConfig(cfg)
396
func NewValidConfig(cfg *config.Config) (*Config, error) {
397
ecfg, err := newValidConfig(cfg, nil)
398
return &Config{ecfg}, err
401
func NewValidDefaultConfig(cfg *config.Config) (*Config, error) {
402
ecfg, err := newValidConfig(cfg, configDefaults)
403
return &Config{ecfg}, err
406
func (ecfg *Config) Values(c *gc.C) (ConfigValues, map[string]interface{}) {
407
c.Assert(ecfg.attrs, jc.DeepEquals, ecfg.UnknownAttrs())
409
var values ConfigValues
410
extras := make(map[string]interface{})
411
for k, v := range ecfg.attrs {
414
values.RemoteURL = v.(string)
416
values.ClientCert = v.(string)
418
values.ClientKey = v.(string)
419
case cfgServerPEMCert:
420
values.ServerCert = v.(string)
425
return values, extras
428
func (ecfg *Config) Apply(c *gc.C, updates map[string]interface{}) *Config {
429
cfg, err := ecfg.Config.Apply(updates)
430
c.Assert(err, jc.ErrorIsNil)
431
return NewConfig(cfg)
434
func (ecfg *Config) Validate() error {
435
return ecfg.validate()
438
func (ecfg *Config) ClientConfig() (lxdclient.Config, error) {
439
return ecfg.clientConfig()
442
func (ecfg *Config) UpdateForClientConfig(clientCfg lxdclient.Config) (*Config, error) {
443
updated, err := ecfg.updateForClientConfig(clientCfg)
444
return &Config{updated}, err
447
type stubCommon struct {
448
stub *gitjujutesting.Stub
450
BootstrapResult *environs.BootstrapResult
453
func (sc *stubCommon) BootstrapEnv(ctx environs.BootstrapContext, params environs.BootstrapParams) (*environs.BootstrapResult, error) {
454
sc.stub.AddCall("Bootstrap", ctx, params)
455
if err := sc.stub.NextErr(); err != nil {
456
return nil, errors.Trace(err)
459
return sc.BootstrapResult, nil
462
func (sc *stubCommon) DestroyEnv() error {
463
sc.stub.AddCall("Destroy")
464
if err := sc.stub.NextErr(); err != nil {
465
return errors.Trace(err)
471
type StubClient struct {
474
Insts []lxdclient.Instance
475
Inst *lxdclient.Instance
478
func (conn *StubClient) Instances(prefix string, statuses ...string) ([]lxdclient.Instance, error) {
479
conn.AddCall("Instances", prefix, statuses)
480
if err := conn.NextErr(); err != nil {
481
return nil, errors.Trace(err)
484
return conn.Insts, nil
487
func (conn *StubClient) AddInstance(spec lxdclient.InstanceSpec) (*lxdclient.Instance, error) {
488
conn.AddCall("AddInstance", spec)
489
if err := conn.NextErr(); err != nil {
490
return nil, errors.Trace(err)
493
return conn.Inst, nil
496
func (conn *StubClient) RemoveInstances(prefix string, ids ...string) error {
497
conn.AddCall("RemoveInstances", prefix, ids)
498
if err := conn.NextErr(); err != nil {
499
return errors.Trace(err)
505
func (conn *StubClient) EnsureImageExists(series string, _ []lxdclient.Remote, _ func(string)) error {
506
conn.AddCall("EnsureImageExists", series)
507
if err := conn.NextErr(); err != nil {
508
return errors.Trace(err)
514
func (conn *StubClient) Addresses(name string) ([]network.Address, error) {
515
conn.AddCall("Addresses", name)
516
if err := conn.NextErr(); err != nil {
517
return nil, errors.Trace(err)
520
return []network.Address{network.Address{
522
Type: network.IPv4Address,
523
Scope: network.ScopeCloudLocal,
527
// TODO(ericsnow) Move stubFirewaller to environs/testing or provider/common/testing.
529
type stubFirewaller struct {
530
stub *gitjujutesting.Stub
532
PortRanges []network.PortRange
535
func (fw *stubFirewaller) Ports(fwname string) ([]network.PortRange, error) {
536
fw.stub.AddCall("Ports", fwname)
537
if err := fw.stub.NextErr(); err != nil {
538
return nil, errors.Trace(err)
541
return fw.PortRanges, nil
544
func (fw *stubFirewaller) OpenPorts(fwname string, ports ...network.PortRange) error {
545
fw.stub.AddCall("OpenPorts", fwname, ports)
546
if err := fw.stub.NextErr(); err != nil {
547
return errors.Trace(err)
553
func (fw *stubFirewaller) ClosePorts(fwname string, ports ...network.PortRange) error {
554
fw.stub.AddCall("ClosePorts", fwname, ports)
555
if err := fw.stub.NextErr(); err != nil {
556
return errors.Trace(err)