~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/provider/lxd/testing_test.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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
        "crypto/tls"
 
10
        "encoding/pem"
 
11
        "os"
 
12
 
 
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"
 
18
 
 
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"
 
31
)
 
32
 
 
33
// These values are stub LXD client credentials for use in tests.
 
34
const (
 
35
        PublicKey = `-----BEGIN CERTIFICATE-----
 
36
...
 
37
...
 
38
...
 
39
...
 
40
...
 
41
...
 
42
...
 
43
...
 
44
...
 
45
...
 
46
...
 
47
...
 
48
...
 
49
...
 
50
-----END CERTIFICATE-----
 
51
`
 
52
        PrivateKey = `-----BEGIN PRIVATE KEY-----
 
53
...
 
54
...
 
55
...
 
56
...
 
57
...
 
58
...
 
59
...
 
60
...
 
61
...
 
62
...
 
63
...
 
64
...
 
65
...
 
66
...
 
67
-----END PRIVATE KEY-----
 
68
`
 
69
)
 
70
 
 
71
// These are stub config values for use in tests.
 
72
var (
 
73
        ConfigAttrs = testing.FakeConfig().Merge(testing.Attrs{
 
74
                "type":        "lxd",
 
75
                "remote-url":  "",
 
76
                "client-cert": "",
 
77
                "client-key":  "",
 
78
                "server-cert": "",
 
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.ModelTag.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, configDefaults)
 
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
        s.PatchValue(&asNonLocal, s.asNonLocal)
 
300
}
 
301
 
 
302
func (s *BaseSuite) SetUpTest(c *gc.C) {
 
303
        s.BaseSuiteUnpatched.SetUpTest(c)
 
304
 
 
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}
 
309
 
 
310
        // Patch out all expensive external deps.
 
311
        s.Env.raw = &rawProvider{
 
312
                lxdInstances: s.Client,
 
313
                lxdImages:    s.Client,
 
314
                Firewaller:   s.Firewaller,
 
315
        }
 
316
        s.Env.base = s.Common
 
317
}
 
318
 
 
319
func (s *BaseSuite) CheckNoAPI(c *gc.C) {
 
320
        s.Stub.CheckCalls(c, nil)
 
321
}
 
322
 
 
323
func (s *BaseSuite) asNonLocal(clientCfg lxdclient.Config) (lxdclient.Config, error) {
 
324
        if s.Stub == nil {
 
325
                return clientCfg, nil
 
326
        }
 
327
        s.Stub.AddCall("asNonLocal", clientCfg)
 
328
        if err := s.Stub.NextErr(); err != nil {
 
329
                return clientCfg, errors.Trace(err)
 
330
        }
 
331
 
 
332
        return clientCfg, nil
 
333
}
 
334
 
 
335
func NewBaseConfig(c *gc.C) *config.Config {
 
336
        var err error
 
337
        cfg := testing.ModelConfig(c)
 
338
 
 
339
        cfg, err = cfg.Apply(ConfigAttrs)
 
340
        c.Assert(err, jc.ErrorIsNil)
 
341
 
 
342
        return cfg
 
343
}
 
344
 
 
345
func NewCustomBaseConfig(c *gc.C, updates map[string]interface{}) *config.Config {
 
346
        if updates == nil {
 
347
                updates = make(testing.Attrs)
 
348
        }
 
349
 
 
350
        cfg := NewBaseConfig(c)
 
351
 
 
352
        cfg, err := cfg.Apply(updates)
 
353
        c.Assert(err, jc.ErrorIsNil)
 
354
 
 
355
        return cfg
 
356
}
 
357
 
 
358
type ConfigValues struct {
 
359
        RemoteURL  string
 
360
        ClientCert string
 
361
        ClientKey  string
 
362
        ServerCert string
 
363
}
 
364
 
 
365
func (cv ConfigValues) CheckCert(c *gc.C) {
 
366
        certPEM := []byte(cv.ClientCert)
 
367
        keyPEM := []byte(cv.ClientKey)
 
368
 
 
369
        _, err := tls.X509KeyPair(certPEM, keyPEM)
 
370
        c.Check(err, jc.ErrorIsNil)
 
371
 
 
372
        block, remainder := pem.Decode(certPEM)
 
373
        c.Check(block.Type, gc.Equals, "CERTIFICATE")
 
374
        c.Check(remainder, gc.HasLen, 0)
 
375
 
 
376
        block, remainder = pem.Decode(keyPEM)
 
377
        c.Check(block.Type, gc.Equals, "RSA PRIVATE KEY")
 
378
        c.Check(remainder, gc.HasLen, 0)
 
379
 
 
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)
 
384
        }
 
385
}
 
386
 
 
387
type Config struct {
 
388
        *environConfig
 
389
}
 
390
 
 
391
func NewConfig(cfg *config.Config) *Config {
 
392
        ecfg := newConfig(cfg)
 
393
        return &Config{ecfg}
 
394
}
 
395
 
 
396
func NewValidConfig(cfg *config.Config) (*Config, error) {
 
397
        ecfg, err := newValidConfig(cfg, nil)
 
398
        return &Config{ecfg}, err
 
399
}
 
400
 
 
401
func NewValidDefaultConfig(cfg *config.Config) (*Config, error) {
 
402
        ecfg, err := newValidConfig(cfg, configDefaults)
 
403
        return &Config{ecfg}, err
 
404
}
 
405
 
 
406
func (ecfg *Config) Values(c *gc.C) (ConfigValues, map[string]interface{}) {
 
407
        c.Assert(ecfg.attrs, jc.DeepEquals, ecfg.UnknownAttrs())
 
408
 
 
409
        var values ConfigValues
 
410
        extras := make(map[string]interface{})
 
411
        for k, v := range ecfg.attrs {
 
412
                switch k {
 
413
                case cfgRemoteURL:
 
414
                        values.RemoteURL = v.(string)
 
415
                case cfgClientCert:
 
416
                        values.ClientCert = v.(string)
 
417
                case cfgClientKey:
 
418
                        values.ClientKey = v.(string)
 
419
                case cfgServerPEMCert:
 
420
                        values.ServerCert = v.(string)
 
421
                default:
 
422
                        extras[k] = v
 
423
                }
 
424
        }
 
425
        return values, extras
 
426
}
 
427
 
 
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)
 
432
}
 
433
 
 
434
func (ecfg *Config) Validate() error {
 
435
        return ecfg.validate()
 
436
}
 
437
 
 
438
func (ecfg *Config) ClientConfig() (lxdclient.Config, error) {
 
439
        return ecfg.clientConfig()
 
440
}
 
441
 
 
442
func (ecfg *Config) UpdateForClientConfig(clientCfg lxdclient.Config) (*Config, error) {
 
443
        updated, err := ecfg.updateForClientConfig(clientCfg)
 
444
        return &Config{updated}, err
 
445
}
 
446
 
 
447
type stubCommon struct {
 
448
        stub *gitjujutesting.Stub
 
449
 
 
450
        BootstrapResult *environs.BootstrapResult
 
451
}
 
452
 
 
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)
 
457
        }
 
458
 
 
459
        return sc.BootstrapResult, nil
 
460
}
 
461
 
 
462
func (sc *stubCommon) DestroyEnv() error {
 
463
        sc.stub.AddCall("Destroy")
 
464
        if err := sc.stub.NextErr(); err != nil {
 
465
                return errors.Trace(err)
 
466
        }
 
467
 
 
468
        return nil
 
469
}
 
470
 
 
471
type StubClient struct {
 
472
        *gitjujutesting.Stub
 
473
 
 
474
        Insts []lxdclient.Instance
 
475
        Inst  *lxdclient.Instance
 
476
}
 
477
 
 
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)
 
482
        }
 
483
 
 
484
        return conn.Insts, nil
 
485
}
 
486
 
 
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)
 
491
        }
 
492
 
 
493
        return conn.Inst, nil
 
494
}
 
495
 
 
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)
 
500
        }
 
501
 
 
502
        return nil
 
503
}
 
504
 
 
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)
 
509
        }
 
510
 
 
511
        return nil
 
512
}
 
513
 
 
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)
 
518
        }
 
519
 
 
520
        return []network.Address{network.Address{
 
521
                Value: "10.0.0.1",
 
522
                Type:  network.IPv4Address,
 
523
                Scope: network.ScopeCloudLocal,
 
524
        }}, nil
 
525
}
 
526
 
 
527
// TODO(ericsnow) Move stubFirewaller to environs/testing or provider/common/testing.
 
528
 
 
529
type stubFirewaller struct {
 
530
        stub *gitjujutesting.Stub
 
531
 
 
532
        PortRanges []network.PortRange
 
533
}
 
534
 
 
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)
 
539
        }
 
540
 
 
541
        return fw.PortRanges, nil
 
542
}
 
543
 
 
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)
 
548
        }
 
549
 
 
550
        return nil
 
551
}
 
552
 
 
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)
 
557
        }
 
558
 
 
559
        return nil
 
560
}