~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/provider/openstack/local_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 2012, 2013 Canonical Ltd.
 
2
// Licensed under the AGPLv3, see LICENCE file for details.
 
3
 
 
4
package openstack_test
 
5
 
 
6
import (
 
7
        "bytes"
 
8
        "errors"
 
9
        "fmt"
 
10
        "io/ioutil"
 
11
        "net/http"
 
12
        "net/http/httptest"
 
13
        "net/url"
 
14
        "os"
 
15
        "path/filepath"
 
16
        "regexp"
 
17
        "strings"
 
18
 
 
19
        jujuerrors "github.com/juju/errors"
 
20
        gitjujutesting "github.com/juju/testing"
 
21
        jc "github.com/juju/testing/checkers"
 
22
        "github.com/juju/utils"
 
23
        "github.com/juju/utils/arch"
 
24
        "github.com/juju/utils/series"
 
25
        "github.com/juju/utils/ssh"
 
26
        "github.com/juju/version"
 
27
        gc "gopkg.in/check.v1"
 
28
        "gopkg.in/goose.v1/client"
 
29
        "gopkg.in/goose.v1/identity"
 
30
        "gopkg.in/goose.v1/nova"
 
31
        "gopkg.in/goose.v1/testservices/hook"
 
32
        "gopkg.in/goose.v1/testservices/identityservice"
 
33
        "gopkg.in/goose.v1/testservices/novaservice"
 
34
        "gopkg.in/goose.v1/testservices/openstackservice"
 
35
 
 
36
        "github.com/juju/juju/cloud"
 
37
        "github.com/juju/juju/cloudconfig/instancecfg"
 
38
        "github.com/juju/juju/constraints"
 
39
        "github.com/juju/juju/environs"
 
40
        "github.com/juju/juju/environs/bootstrap"
 
41
        "github.com/juju/juju/environs/config"
 
42
        "github.com/juju/juju/environs/filestorage"
 
43
        "github.com/juju/juju/environs/imagemetadata"
 
44
        imagetesting "github.com/juju/juju/environs/imagemetadata/testing"
 
45
        "github.com/juju/juju/environs/jujutest"
 
46
        "github.com/juju/juju/environs/simplestreams"
 
47
        sstesting "github.com/juju/juju/environs/simplestreams/testing"
 
48
        "github.com/juju/juju/environs/storage"
 
49
        envtesting "github.com/juju/juju/environs/testing"
 
50
        "github.com/juju/juju/environs/tools"
 
51
        "github.com/juju/juju/instance"
 
52
        "github.com/juju/juju/juju/keys"
 
53
        "github.com/juju/juju/juju/testing"
 
54
        "github.com/juju/juju/jujuclient/jujuclienttesting"
 
55
        "github.com/juju/juju/network"
 
56
        "github.com/juju/juju/provider/common"
 
57
        "github.com/juju/juju/provider/openstack"
 
58
        "github.com/juju/juju/status"
 
59
        coretesting "github.com/juju/juju/testing"
 
60
        jujuversion "github.com/juju/juju/version"
 
61
)
 
62
 
 
63
type ProviderSuite struct {
 
64
        restoreTimeouts func()
 
65
}
 
66
 
 
67
var _ = gc.Suite(&ProviderSuite{})
 
68
var _ = gc.Suite(&localHTTPSServerSuite{})
 
69
var _ = gc.Suite(&noSwiftSuite{})
 
70
 
 
71
func (s *ProviderSuite) SetUpTest(c *gc.C) {
 
72
        s.restoreTimeouts = envtesting.PatchAttemptStrategies(openstack.ShortAttempt, openstack.StorageAttempt)
 
73
}
 
74
 
 
75
func (s *ProviderSuite) TearDownTest(c *gc.C) {
 
76
        s.restoreTimeouts()
 
77
}
 
78
 
 
79
// Register tests to run against a test Openstack instance (service doubles).
 
80
func registerLocalTests() {
 
81
        cred := &identity.Credentials{
 
82
                User:       "fred",
 
83
                Secrets:    "secret",
 
84
                Region:     "some-region",
 
85
                TenantName: "some tenant",
 
86
        }
 
87
        config := makeTestConfig(cred)
 
88
        config["agent-version"] = coretesting.FakeVersionNumber.String()
 
89
        config["authorized-keys"] = "fakekey"
 
90
        gc.Suite(&localLiveSuite{
 
91
                LiveTests: LiveTests{
 
92
                        cred: cred,
 
93
                        LiveTests: jujutest.LiveTests{
 
94
                                TestConfig: config,
 
95
                        },
 
96
                },
 
97
        })
 
98
        gc.Suite(&localServerSuite{
 
99
                cred: cred,
 
100
                Tests: jujutest.Tests{
 
101
                        TestConfig: config,
 
102
                },
 
103
        })
 
104
}
 
105
 
 
106
// localServer is used to spin up a local Openstack service double.
 
107
type localServer struct {
 
108
        Server          *httptest.Server
 
109
        Mux             *http.ServeMux
 
110
        oldHandler      http.Handler
 
111
        Nova            *novaservice.Nova
 
112
        restoreTimeouts func()
 
113
        UseTLS          bool
 
114
}
 
115
 
 
116
type newOpenstackFunc func(*http.ServeMux, *identity.Credentials, identity.AuthMode) *novaservice.Nova
 
117
 
 
118
func (s *localServer) start(
 
119
        c *gc.C, cred *identity.Credentials, newOpenstackFunc newOpenstackFunc,
 
120
) {
 
121
        // Set up the HTTP server.
 
122
        if s.UseTLS {
 
123
                s.Server = httptest.NewTLSServer(nil)
 
124
        } else {
 
125
                s.Server = httptest.NewServer(nil)
 
126
        }
 
127
        c.Assert(s.Server, gc.NotNil)
 
128
        s.oldHandler = s.Server.Config.Handler
 
129
        s.Mux = http.NewServeMux()
 
130
        s.Server.Config.Handler = s.Mux
 
131
        cred.URL = s.Server.URL
 
132
        c.Logf("Started service at: %v", s.Server.URL)
 
133
        s.Nova = newOpenstackFunc(s.Mux, cred, identity.AuthUserPass)
 
134
        s.restoreTimeouts = envtesting.PatchAttemptStrategies(openstack.ShortAttempt, openstack.StorageAttempt)
 
135
        s.Nova.SetAvailabilityZones(
 
136
                nova.AvailabilityZone{Name: "test-unavailable"},
 
137
                nova.AvailabilityZone{
 
138
                        Name: "test-available",
 
139
                        State: nova.AvailabilityZoneState{
 
140
                                Available: true,
 
141
                        },
 
142
                },
 
143
        )
 
144
}
 
145
 
 
146
func (s *localServer) stop() {
 
147
        s.Mux = nil
 
148
        s.Server.Config.Handler = s.oldHandler
 
149
        s.Server.Close()
 
150
        s.restoreTimeouts()
 
151
}
 
152
 
 
153
// localLiveSuite runs tests from LiveTests using an Openstack service double.
 
154
type localLiveSuite struct {
 
155
        coretesting.BaseSuite
 
156
        LiveTests
 
157
        srv localServer
 
158
}
 
159
 
 
160
func overrideCinderProvider(c *gc.C, s *gitjujutesting.CleanupSuite) {
 
161
        s.PatchValue(openstack.NewOpenstackStorage, func(*openstack.Environ) (openstack.OpenstackStorage, error) {
 
162
                return &mockAdapter{}, nil
 
163
        })
 
164
}
 
165
 
 
166
func (s *localLiveSuite) SetUpSuite(c *gc.C) {
 
167
        s.BaseSuite.SetUpSuite(c)
 
168
 
 
169
        c.Logf("Running live tests using openstack service test double")
 
170
        s.srv.start(c, s.cred, newFullOpenstackService)
 
171
 
 
172
        // Set credentials to use when bootstrapping. Must be done after
 
173
        // starting server to get the auth URL.
 
174
        s.Credential = makeCredential(s.cred)
 
175
        s.CloudEndpoint = s.cred.URL
 
176
        s.CloudRegion = s.cred.Region
 
177
 
 
178
        s.LiveTests.SetUpSuite(c)
 
179
        openstack.UseTestImageData(openstack.ImageMetadataStorage(s.Env), s.cred)
 
180
        restoreFinishBootstrap := envtesting.DisableFinishBootstrap()
 
181
        s.AddCleanup(func(*gc.C) { restoreFinishBootstrap() })
 
182
        overrideCinderProvider(c, &s.CleanupSuite)
 
183
}
 
184
 
 
185
func (s *localLiveSuite) TearDownSuite(c *gc.C) {
 
186
        openstack.RemoveTestImageData(openstack.ImageMetadataStorage(s.Env))
 
187
        s.LiveTests.TearDownSuite(c)
 
188
        s.srv.stop()
 
189
        s.BaseSuite.TearDownSuite(c)
 
190
}
 
191
 
 
192
func (s *localLiveSuite) SetUpTest(c *gc.C) {
 
193
        s.BaseSuite.SetUpTest(c)
 
194
        s.LiveTests.SetUpTest(c)
 
195
        imagetesting.PatchOfficialDataSources(&s.CleanupSuite, "")
 
196
}
 
197
 
 
198
func (s *localLiveSuite) TearDownTest(c *gc.C) {
 
199
        s.LiveTests.TearDownTest(c)
 
200
        s.BaseSuite.TearDownTest(c)
 
201
}
 
202
 
 
203
// localServerSuite contains tests that run against an Openstack service double.
 
204
// These tests can test things that would be unreasonably slow or expensive
 
205
// to test on a live Openstack server. The service double is started and stopped for
 
206
// each test.
 
207
type localServerSuite struct {
 
208
        coretesting.BaseSuite
 
209
        jujutest.Tests
 
210
        cred                 *identity.Credentials
 
211
        srv                  localServer
 
212
        env                  environs.Environ
 
213
        toolsMetadataStorage storage.Storage
 
214
        imageMetadataStorage storage.Storage
 
215
}
 
216
 
 
217
func (s *localServerSuite) SetUpSuite(c *gc.C) {
 
218
        s.BaseSuite.SetUpSuite(c)
 
219
        restoreFinishBootstrap := envtesting.DisableFinishBootstrap()
 
220
        s.AddCleanup(func(*gc.C) { restoreFinishBootstrap() })
 
221
        overrideCinderProvider(c, &s.CleanupSuite)
 
222
        c.Logf("Running local tests")
 
223
}
 
224
 
 
225
func (s *localServerSuite) SetUpTest(c *gc.C) {
 
226
        s.BaseSuite.SetUpTest(c)
 
227
        s.srv.start(c, s.cred, newFullOpenstackService)
 
228
 
 
229
        // Set credentials to use when bootstrapping. Must be done after
 
230
        // starting server to get the auth URL.
 
231
        s.Credential = makeCredential(s.cred)
 
232
        s.CloudEndpoint = s.cred.URL
 
233
        s.CloudRegion = s.cred.Region
 
234
 
 
235
        cl := client.NewClient(s.cred, identity.AuthUserPass, nil)
 
236
        err := cl.Authenticate()
 
237
        c.Assert(err, jc.ErrorIsNil)
 
238
        containerURL, err := cl.MakeServiceURL("object-store", nil)
 
239
        c.Assert(err, jc.ErrorIsNil)
 
240
        s.TestConfig = s.TestConfig.Merge(coretesting.Attrs{
 
241
                "agent-metadata-url": containerURL + "/juju-dist-test/tools",
 
242
                "image-metadata-url": containerURL + "/juju-dist-test",
 
243
                "auth-url":           s.cred.URL,
 
244
        })
 
245
        s.PatchValue(&jujuversion.Current, coretesting.FakeVersionNumber)
 
246
        s.Tests.SetUpTest(c)
 
247
        // For testing, we create a storage instance to which is uploaded tools and image metadata.
 
248
        s.env = s.Prepare(c)
 
249
        s.toolsMetadataStorage = openstack.MetadataStorage(s.env)
 
250
        // Put some fake metadata in place so that tests that are simply
 
251
        // starting instances without any need to check if those instances
 
252
        // are running can find the metadata.
 
253
        envtesting.UploadFakeTools(c, s.toolsMetadataStorage, s.env.Config().AgentStream(), s.env.Config().AgentStream())
 
254
        s.imageMetadataStorage = openstack.ImageMetadataStorage(s.env)
 
255
        openstack.UseTestImageData(s.imageMetadataStorage, s.cred)
 
256
}
 
257
 
 
258
func (s *localServerSuite) TearDownTest(c *gc.C) {
 
259
        if s.imageMetadataStorage != nil {
 
260
                openstack.RemoveTestImageData(s.imageMetadataStorage)
 
261
        }
 
262
        if s.toolsMetadataStorage != nil {
 
263
                envtesting.RemoveFakeToolsMetadata(c, s.toolsMetadataStorage)
 
264
        }
 
265
        s.Tests.TearDownTest(c)
 
266
        s.srv.stop()
 
267
        s.BaseSuite.TearDownTest(c)
 
268
}
 
269
 
 
270
func (s *localServerSuite) openEnviron(c *gc.C, attrs coretesting.Attrs) environs.Environ {
 
271
        cfg, err := config.New(config.NoDefaults, s.TestConfig.Merge(attrs))
 
272
        c.Assert(err, jc.ErrorIsNil)
 
273
        env, err := environs.New(environs.OpenParams{
 
274
                Cloud:  s.CloudSpec(),
 
275
                Config: cfg,
 
276
        })
 
277
        c.Assert(err, jc.ErrorIsNil)
 
278
        return env
 
279
}
 
280
 
 
281
func (s *localServerSuite) TestBootstrap(c *gc.C) {
 
282
        // Tests uses Prepare, so destroy first.
 
283
        err := environs.Destroy(s.env.Config().Name(), s.env, s.ControllerStore)
 
284
        c.Assert(err, jc.ErrorIsNil)
 
285
        s.Tests.TestBootstrap(c)
 
286
}
 
287
 
 
288
func (s *localServerSuite) TestStartStop(c *gc.C) {
 
289
        // Tests uses Prepare, so destroy first.
 
290
        err := environs.Destroy(s.env.Config().Name(), s.env, s.ControllerStore)
 
291
        c.Assert(err, jc.ErrorIsNil)
 
292
        s.Tests.TestStartStop(c)
 
293
}
 
294
 
 
295
// If the bootstrap node is configured to require a public IP address,
 
296
// bootstrapping fails if an address cannot be allocated.
 
297
func (s *localServerSuite) TestBootstrapFailsWhenPublicIPError(c *gc.C) {
 
298
        coretesting.SkipIfPPC64EL(c, "lp:1425242")
 
299
 
 
300
        cleanup := s.srv.Nova.RegisterControlPoint(
 
301
                "addFloatingIP",
 
302
                func(sc hook.ServiceControl, args ...interface{}) error {
 
303
                        return fmt.Errorf("failed on purpose")
 
304
                },
 
305
        )
 
306
        defer cleanup()
 
307
 
 
308
        err := environs.Destroy(s.env.Config().Name(), s.env, s.ControllerStore)
 
309
        c.Assert(err, jc.ErrorIsNil)
 
310
 
 
311
        env := s.openEnviron(c, coretesting.Attrs{"use-floating-ip": true})
 
312
        err = bootstrapEnv(c, env)
 
313
        c.Assert(err, gc.ErrorMatches, "(.|\n)*cannot allocate a public IP as needed(.|\n)*")
 
314
}
 
315
 
 
316
func (s *localServerSuite) TestAddressesWithPublicIP(c *gc.C) {
 
317
        // Floating IP address is 10.0.0.1
 
318
        bootstrapFinished := false
 
319
        s.PatchValue(&common.FinishBootstrap, func(
 
320
                ctx environs.BootstrapContext,
 
321
                client ssh.Client,
 
322
                env environs.Environ,
 
323
                inst instance.Instance,
 
324
                instanceConfig *instancecfg.InstanceConfig,
 
325
                _ environs.BootstrapDialOpts,
 
326
        ) error {
 
327
                addr, err := inst.Addresses()
 
328
                c.Assert(err, jc.ErrorIsNil)
 
329
                c.Assert(addr, jc.SameContents, []network.Address{
 
330
                        {Value: "10.0.0.1", Type: "ipv4", Scope: "public"},
 
331
                        {Value: "127.0.0.1", Type: "ipv4", Scope: "local-machine"},
 
332
                        {Value: "::face::000f", Type: "hostname", Scope: ""},
 
333
                        {Value: "127.10.0.1", Type: "ipv4", Scope: "public"},
 
334
                        {Value: "::dead:beef:f00d", Type: "ipv6", Scope: "public"},
 
335
                })
 
336
                bootstrapFinished = true
 
337
                return nil
 
338
        })
 
339
 
 
340
        env := s.openEnviron(c, coretesting.Attrs{"use-floating-ip": true})
 
341
        err := bootstrapEnv(c, env)
 
342
        c.Assert(err, jc.ErrorIsNil)
 
343
        c.Assert(bootstrapFinished, jc.IsTrue)
 
344
}
 
345
 
 
346
func (s *localServerSuite) TestAddressesWithoutPublicIP(c *gc.C) {
 
347
        bootstrapFinished := false
 
348
        s.PatchValue(&common.FinishBootstrap, func(
 
349
                ctx environs.BootstrapContext,
 
350
                client ssh.Client,
 
351
                env environs.Environ,
 
352
                inst instance.Instance,
 
353
                instanceConfig *instancecfg.InstanceConfig,
 
354
                _ environs.BootstrapDialOpts,
 
355
        ) error {
 
356
                addr, err := inst.Addresses()
 
357
                c.Assert(err, jc.ErrorIsNil)
 
358
                c.Assert(addr, jc.SameContents, []network.Address{
 
359
                        {Value: "127.0.0.1", Type: "ipv4", Scope: "local-machine"},
 
360
                        {Value: "::face::000f", Type: "hostname", Scope: ""},
 
361
                        {Value: "127.10.0.1", Type: "ipv4", Scope: "public"},
 
362
                        {Value: "::dead:beef:f00d", Type: "ipv6", Scope: "public"},
 
363
                })
 
364
                bootstrapFinished = true
 
365
                return nil
 
366
        })
 
367
 
 
368
        env := s.openEnviron(c, coretesting.Attrs{"use-floating-ip": false})
 
369
        err := bootstrapEnv(c, env)
 
370
        c.Assert(err, jc.ErrorIsNil)
 
371
        c.Assert(bootstrapFinished, jc.IsTrue)
 
372
}
 
373
 
 
374
// If the environment is configured not to require a public IP address for nodes,
 
375
// bootstrapping and starting an instance should occur without any attempt to
 
376
// allocate a public address.
 
377
func (s *localServerSuite) TestStartInstanceWithoutPublicIP(c *gc.C) {
 
378
        cleanup := s.srv.Nova.RegisterControlPoint(
 
379
                "addFloatingIP",
 
380
                func(sc hook.ServiceControl, args ...interface{}) error {
 
381
                        return fmt.Errorf("add floating IP should not have been called")
 
382
                },
 
383
        )
 
384
        defer cleanup()
 
385
        cleanup = s.srv.Nova.RegisterControlPoint(
 
386
                "addServerFloatingIP",
 
387
                func(sc hook.ServiceControl, args ...interface{}) error {
 
388
                        return fmt.Errorf("add server floating IP should not have been called")
 
389
                },
 
390
        )
 
391
        defer cleanup()
 
392
 
 
393
        err := environs.Destroy(s.env.Config().Name(), s.env, s.ControllerStore)
 
394
        c.Assert(err, jc.ErrorIsNil)
 
395
 
 
396
        s.TestConfig["use-floating-ip"] = false
 
397
        env := s.Prepare(c)
 
398
        err = bootstrapEnv(c, env)
 
399
        c.Assert(err, jc.ErrorIsNil)
 
400
        inst, _ := testing.AssertStartInstance(c, env, s.ControllerUUID, "100")
 
401
        err = env.StopInstances(inst.Id())
 
402
        c.Assert(err, jc.ErrorIsNil)
 
403
}
 
404
 
 
405
func (s *localServerSuite) TestStartInstanceHardwareCharacteristics(c *gc.C) {
 
406
        // Ensure amd64 tools are available, to ensure an amd64 image.
 
407
        amd64Version := version.Binary{
 
408
                Number: jujuversion.Current,
 
409
                Arch:   arch.AMD64,
 
410
        }
 
411
        for _, series := range series.SupportedSeries() {
 
412
                amd64Version.Series = series
 
413
                envtesting.AssertUploadFakeToolsVersions(
 
414
                        c, s.toolsMetadataStorage, s.env.Config().AgentStream(), s.env.Config().AgentStream(), amd64Version)
 
415
        }
 
416
 
 
417
        err := environs.Destroy(s.env.Config().Name(), s.env, s.ControllerStore)
 
418
        c.Assert(err, jc.ErrorIsNil)
 
419
 
 
420
        env := s.Prepare(c)
 
421
        err = bootstrapEnv(c, env)
 
422
        c.Assert(err, jc.ErrorIsNil)
 
423
        _, hc := testing.AssertStartInstanceWithConstraints(c, env, s.ControllerUUID, "100", constraints.MustParse("mem=1024"))
 
424
        c.Check(*hc.Arch, gc.Equals, "amd64")
 
425
        c.Check(*hc.Mem, gc.Equals, uint64(2048))
 
426
        c.Check(*hc.CpuCores, gc.Equals, uint64(1))
 
427
        c.Assert(hc.CpuPower, gc.IsNil)
 
428
}
 
429
 
 
430
func (s *localServerSuite) TestStartInstanceNetwork(c *gc.C) {
 
431
        cfg, err := s.env.Config().Apply(coretesting.Attrs{
 
432
                // A label that corresponds to a nova test service network
 
433
                "network": "net",
 
434
        })
 
435
        c.Assert(err, jc.ErrorIsNil)
 
436
        err = s.env.SetConfig(cfg)
 
437
        c.Assert(err, jc.ErrorIsNil)
 
438
 
 
439
        inst, _ := testing.AssertStartInstance(c, s.env, s.ControllerUUID, "100")
 
440
        err = s.env.StopInstances(inst.Id())
 
441
        c.Assert(err, jc.ErrorIsNil)
 
442
}
 
443
 
 
444
func (s *localServerSuite) TestStartInstanceNetworkUnknownLabel(c *gc.C) {
 
445
        cfg, err := s.env.Config().Apply(coretesting.Attrs{
 
446
                // A label that has no related network in the nova test service
 
447
                "network": "no-network-with-this-label",
 
448
        })
 
449
        c.Assert(err, jc.ErrorIsNil)
 
450
        err = s.env.SetConfig(cfg)
 
451
        c.Assert(err, jc.ErrorIsNil)
 
452
 
 
453
        inst, _, _, err := testing.StartInstance(s.env, s.ControllerUUID, "100")
 
454
        c.Check(inst, gc.IsNil)
 
455
        c.Assert(err, gc.ErrorMatches, "No networks exist with label .*")
 
456
}
 
457
 
 
458
func (s *localServerSuite) TestStartInstanceNetworkUnknownId(c *gc.C) {
 
459
        cfg, err := s.env.Config().Apply(coretesting.Attrs{
 
460
                // A valid UUID but no related network in the nova test service
 
461
                "network": "f81d4fae-7dec-11d0-a765-00a0c91e6bf6",
 
462
        })
 
463
        c.Assert(err, jc.ErrorIsNil)
 
464
        err = s.env.SetConfig(cfg)
 
465
        c.Assert(err, jc.ErrorIsNil)
 
466
 
 
467
        inst, _, _, err := testing.StartInstance(s.env, s.ControllerUUID, "100")
 
468
        c.Check(inst, gc.IsNil)
 
469
        c.Assert(err, gc.ErrorMatches, "cannot run instance: (\\n|.)*"+
 
470
                "caused by: "+
 
471
                "request \\(.*/servers\\) returned unexpected status: "+
 
472
                "404; error info: .*itemNotFound.*")
 
473
}
 
474
 
 
475
func assertSecurityGroups(c *gc.C, env environs.Environ, expected []string) {
 
476
        novaClient := openstack.GetNovaClient(env)
 
477
        groups, err := novaClient.ListSecurityGroups()
 
478
        c.Assert(err, jc.ErrorIsNil)
 
479
        for _, name := range expected {
 
480
                found := false
 
481
                for _, group := range groups {
 
482
                        if group.Name == name {
 
483
                                found = true
 
484
                                break
 
485
                        }
 
486
                }
 
487
                if !found {
 
488
                        c.Errorf("expected security group %q not found", name)
 
489
                }
 
490
        }
 
491
        for _, group := range groups {
 
492
                found := false
 
493
                for _, name := range expected {
 
494
                        if group.Name == name {
 
495
                                found = true
 
496
                                break
 
497
                        }
 
498
                }
 
499
                if !found {
 
500
                        c.Errorf("existing security group %q is not expected", group.Name)
 
501
                }
 
502
        }
 
503
}
 
504
 
 
505
func assertInstanceIds(c *gc.C, env environs.Environ, expected ...instance.Id) {
 
506
        insts, err := env.AllInstances()
 
507
        c.Assert(err, jc.ErrorIsNil)
 
508
        instIds := make([]instance.Id, len(insts))
 
509
        for i, inst := range insts {
 
510
                instIds[i] = inst.Id()
 
511
        }
 
512
        c.Assert(instIds, jc.SameContents, expected)
 
513
}
 
514
 
 
515
func (s *localServerSuite) TestStopInstance(c *gc.C) {
 
516
        env := s.openEnviron(c, coretesting.Attrs{"firewall-mode": config.FwInstance})
 
517
        instanceName := "100"
 
518
        inst, _ := testing.AssertStartInstance(c, env, s.ControllerUUID, instanceName)
 
519
        // Openstack now has three security groups for the server, the default
 
520
        // group, one group for the entire environment, and another for the
 
521
        // new instance.
 
522
        modelUUID := env.Config().UUID()
 
523
        allSecurityGroups := []string{
 
524
                "default", fmt.Sprintf("juju-%v-%v", s.ControllerUUID, modelUUID),
 
525
                fmt.Sprintf("juju-%v-%v-%v", s.ControllerUUID, modelUUID, instanceName),
 
526
        }
 
527
        assertSecurityGroups(c, env, allSecurityGroups)
 
528
        err := env.StopInstances(inst.Id())
 
529
        c.Assert(err, jc.ErrorIsNil)
 
530
        // The security group for this instance is now removed.
 
531
        assertSecurityGroups(c, env, []string{
 
532
                "default", fmt.Sprintf("juju-%v-%v", s.ControllerUUID, modelUUID),
 
533
        })
 
534
}
 
535
 
 
536
// Due to bug #1300755 it can happen that the security group intended for
 
537
// an instance is also used as the common security group of another
 
538
// environment. If this is the case, the attempt to delete the instance's
 
539
// security group fails but StopInstance succeeds.
 
540
func (s *localServerSuite) TestStopInstanceSecurityGroupNotDeleted(c *gc.C) {
 
541
        coretesting.SkipIfPPC64EL(c, "lp:1425242")
 
542
 
 
543
        // Force an error when a security group is deleted.
 
544
        cleanup := s.srv.Nova.RegisterControlPoint(
 
545
                "removeSecurityGroup",
 
546
                func(sc hook.ServiceControl, args ...interface{}) error {
 
547
                        return fmt.Errorf("failed on purpose")
 
548
                },
 
549
        )
 
550
        defer cleanup()
 
551
        env := s.openEnviron(c, coretesting.Attrs{"firewall-mode": config.FwInstance})
 
552
        instanceName := "100"
 
553
        inst, _ := testing.AssertStartInstance(c, env, s.ControllerUUID, instanceName)
 
554
        modelUUID := env.Config().UUID()
 
555
        allSecurityGroups := []string{
 
556
                "default", fmt.Sprintf("juju-%v-%v", s.ControllerUUID, modelUUID),
 
557
                fmt.Sprintf("juju-%v-%v-%v", s.ControllerUUID, modelUUID, instanceName),
 
558
        }
 
559
        assertSecurityGroups(c, env, allSecurityGroups)
 
560
        err := env.StopInstances(inst.Id())
 
561
        c.Assert(err, jc.ErrorIsNil)
 
562
        assertSecurityGroups(c, env, allSecurityGroups)
 
563
}
 
564
 
 
565
func (s *localServerSuite) TestDestroyEnvironmentDeletesSecurityGroupsFWModeInstance(c *gc.C) {
 
566
        env := s.openEnviron(c, coretesting.Attrs{"firewall-mode": config.FwInstance})
 
567
        instanceName := "100"
 
568
        testing.AssertStartInstance(c, env, s.ControllerUUID, instanceName)
 
569
        modelUUID := env.Config().UUID()
 
570
        allSecurityGroups := []string{
 
571
                "default", fmt.Sprintf("juju-%v-%v", s.ControllerUUID, modelUUID),
 
572
                fmt.Sprintf("juju-%v-%v-%v", s.ControllerUUID, modelUUID, instanceName),
 
573
        }
 
574
        assertSecurityGroups(c, env, allSecurityGroups)
 
575
        err := env.Destroy()
 
576
        c.Check(err, jc.ErrorIsNil)
 
577
        assertSecurityGroups(c, env, []string{"default"})
 
578
}
 
579
 
 
580
func (s *localServerSuite) TestDestroyEnvironmentDeletesSecurityGroupsFWModeGlobal(c *gc.C) {
 
581
        env := s.openEnviron(c, coretesting.Attrs{"firewall-mode": config.FwGlobal})
 
582
        instanceName := "100"
 
583
        testing.AssertStartInstance(c, env, s.ControllerUUID, instanceName)
 
584
        modelUUID := env.Config().UUID()
 
585
        allSecurityGroups := []string{
 
586
                "default", fmt.Sprintf("juju-%v-%v", s.ControllerUUID, modelUUID),
 
587
                fmt.Sprintf("juju-%v-%v-global", s.ControllerUUID, modelUUID),
 
588
        }
 
589
        assertSecurityGroups(c, env, allSecurityGroups)
 
590
        err := env.Destroy()
 
591
        c.Check(err, jc.ErrorIsNil)
 
592
        assertSecurityGroups(c, env, []string{"default"})
 
593
}
 
594
 
 
595
func (s *localServerSuite) TestDestroyController(c *gc.C) {
 
596
        env := s.openEnviron(c, coretesting.Attrs{"uuid": utils.MustNewUUID().String()})
 
597
        controllerEnv := s.env
 
598
 
 
599
        controllerInstanceName := "100"
 
600
        testing.AssertStartInstance(c, controllerEnv, s.ControllerUUID, controllerInstanceName)
 
601
        hostedModelInstanceName := "200"
 
602
        testing.AssertStartInstance(c, env, s.ControllerUUID, hostedModelInstanceName)
 
603
        modelUUID := env.Config().UUID()
 
604
        allControllerSecurityGroups := []string{
 
605
                "default", fmt.Sprintf("juju-%v-%v", s.ControllerUUID, s.ControllerUUID),
 
606
                fmt.Sprintf("juju-%v-%v-%v", s.ControllerUUID, s.ControllerUUID, controllerInstanceName),
 
607
        }
 
608
        allHostedModelSecurityGroups := []string{
 
609
                "default", fmt.Sprintf("juju-%v-%v", s.ControllerUUID, modelUUID),
 
610
                fmt.Sprintf("juju-%v-%v-%v", s.ControllerUUID, modelUUID, hostedModelInstanceName),
 
611
        }
 
612
        assertSecurityGroups(c, controllerEnv, append(
 
613
                allControllerSecurityGroups, allHostedModelSecurityGroups...,
 
614
        ))
 
615
 
 
616
        err := controllerEnv.DestroyController(s.ControllerUUID)
 
617
        c.Check(err, jc.ErrorIsNil)
 
618
        assertSecurityGroups(c, controllerEnv, []string{"default"})
 
619
        assertInstanceIds(c, env)
 
620
        assertInstanceIds(c, controllerEnv)
 
621
}
 
622
 
 
623
func (s *localServerSuite) TestDestroyHostedModel(c *gc.C) {
 
624
        env := s.openEnviron(c, coretesting.Attrs{"uuid": utils.MustNewUUID().String()})
 
625
        controllerEnv := s.env
 
626
 
 
627
        controllerInstanceName := "100"
 
628
        controllerInstance, _ := testing.AssertStartInstance(c, controllerEnv, s.ControllerUUID, controllerInstanceName)
 
629
        hostedModelInstanceName := "200"
 
630
        testing.AssertStartInstance(c, env, s.ControllerUUID, hostedModelInstanceName)
 
631
        modelUUID := env.Config().UUID()
 
632
        allControllerSecurityGroups := []string{
 
633
                "default", fmt.Sprintf("juju-%v-%v", s.ControllerUUID, s.ControllerUUID),
 
634
                fmt.Sprintf("juju-%v-%v-%v", s.ControllerUUID, s.ControllerUUID, controllerInstanceName),
 
635
        }
 
636
        allHostedModelSecurityGroups := []string{
 
637
                "default", fmt.Sprintf("juju-%v-%v", s.ControllerUUID, modelUUID),
 
638
                fmt.Sprintf("juju-%v-%v-%v", s.ControllerUUID, modelUUID, hostedModelInstanceName),
 
639
        }
 
640
        assertSecurityGroups(c, controllerEnv, append(
 
641
                allControllerSecurityGroups, allHostedModelSecurityGroups...,
 
642
        ))
 
643
 
 
644
        err := env.Destroy()
 
645
        c.Check(err, jc.ErrorIsNil)
 
646
        assertSecurityGroups(c, controllerEnv, allControllerSecurityGroups)
 
647
        assertInstanceIds(c, env)
 
648
        assertInstanceIds(c, controllerEnv, controllerInstance.Id())
 
649
}
 
650
 
 
651
var instanceGathering = []struct {
 
652
        ids []instance.Id
 
653
        err error
 
654
}{
 
655
        {ids: []instance.Id{"id0"}},
 
656
        {ids: []instance.Id{"id0", "id0"}},
 
657
        {ids: []instance.Id{"id0", "id1"}},
 
658
        {ids: []instance.Id{"id1", "id0"}},
 
659
        {ids: []instance.Id{"id1", "id0", "id1"}},
 
660
        {
 
661
                ids: []instance.Id{""},
 
662
                err: environs.ErrNoInstances,
 
663
        },
 
664
        {
 
665
                ids: []instance.Id{"", ""},
 
666
                err: environs.ErrNoInstances,
 
667
        },
 
668
        {
 
669
                ids: []instance.Id{"", "", ""},
 
670
                err: environs.ErrNoInstances,
 
671
        },
 
672
        {
 
673
                ids: []instance.Id{"id0", ""},
 
674
                err: environs.ErrPartialInstances,
 
675
        },
 
676
        {
 
677
                ids: []instance.Id{"", "id1"},
 
678
                err: environs.ErrPartialInstances,
 
679
        },
 
680
        {
 
681
                ids: []instance.Id{"id0", "id1", ""},
 
682
                err: environs.ErrPartialInstances,
 
683
        },
 
684
        {
 
685
                ids: []instance.Id{"id0", "", "id0"},
 
686
                err: environs.ErrPartialInstances,
 
687
        },
 
688
        {
 
689
                ids: []instance.Id{"id0", "id0", ""},
 
690
                err: environs.ErrPartialInstances,
 
691
        },
 
692
        {
 
693
                ids: []instance.Id{"", "id0", "id1"},
 
694
                err: environs.ErrPartialInstances,
 
695
        },
 
696
}
 
697
 
 
698
func (s *localServerSuite) TestInstanceStatus(c *gc.C) {
 
699
        // goose's test service always returns ACTIVE state.
 
700
        inst, _ := testing.AssertStartInstance(c, s.env, s.ControllerUUID, "100")
 
701
        c.Assert(inst.Status().Status, gc.Equals, status.StatusRunning)
 
702
        err := s.env.StopInstances(inst.Id())
 
703
        c.Assert(err, jc.ErrorIsNil)
 
704
}
 
705
 
 
706
func (s *localServerSuite) TestAllInstancesFloatingIP(c *gc.C) {
 
707
        env := s.openEnviron(c, coretesting.Attrs{"use-floating-ip": true})
 
708
 
 
709
        inst0, _ := testing.AssertStartInstance(c, env, s.ControllerUUID, "100")
 
710
        inst1, _ := testing.AssertStartInstance(c, env, s.ControllerUUID, "101")
 
711
        defer func() {
 
712
                err := env.StopInstances(inst0.Id(), inst1.Id())
 
713
                c.Assert(err, jc.ErrorIsNil)
 
714
        }()
 
715
 
 
716
        insts, err := env.AllInstances()
 
717
        c.Assert(err, jc.ErrorIsNil)
 
718
        for _, inst := range insts {
 
719
                c.Assert(openstack.InstanceFloatingIP(inst).IP, gc.Equals, fmt.Sprintf("10.0.0.%v", inst.Id()))
 
720
        }
 
721
}
 
722
 
 
723
func (s *localServerSuite) assertInstancesGathering(c *gc.C, withFloatingIP bool) {
 
724
        env := s.openEnviron(c, coretesting.Attrs{"use-floating-ip": withFloatingIP})
 
725
 
 
726
        inst0, _ := testing.AssertStartInstance(c, env, s.ControllerUUID, "100")
 
727
        id0 := inst0.Id()
 
728
        inst1, _ := testing.AssertStartInstance(c, env, s.ControllerUUID, "101")
 
729
        id1 := inst1.Id()
 
730
        defer func() {
 
731
                err := env.StopInstances(inst0.Id(), inst1.Id())
 
732
                c.Assert(err, jc.ErrorIsNil)
 
733
        }()
 
734
 
 
735
        for i, test := range instanceGathering {
 
736
                c.Logf("test %d: find %v -> expect len %d, err: %v", i, test.ids, len(test.ids), test.err)
 
737
                ids := make([]instance.Id, len(test.ids))
 
738
                for j, id := range test.ids {
 
739
                        switch id {
 
740
                        case "id0":
 
741
                                ids[j] = id0
 
742
                        case "id1":
 
743
                                ids[j] = id1
 
744
                        }
 
745
                }
 
746
                insts, err := env.Instances(ids)
 
747
                c.Assert(err, gc.Equals, test.err)
 
748
                if err == environs.ErrNoInstances {
 
749
                        c.Assert(insts, gc.HasLen, 0)
 
750
                } else {
 
751
                        c.Assert(insts, gc.HasLen, len(test.ids))
 
752
                }
 
753
                for j, inst := range insts {
 
754
                        if ids[j] != "" {
 
755
                                c.Assert(inst.Id(), gc.Equals, ids[j])
 
756
                                if withFloatingIP {
 
757
                                        c.Assert(openstack.InstanceFloatingIP(inst).IP, gc.Equals, fmt.Sprintf("10.0.0.%v", inst.Id()))
 
758
                                } else {
 
759
                                        c.Assert(openstack.InstanceFloatingIP(inst), gc.IsNil)
 
760
                                }
 
761
                        } else {
 
762
                                c.Assert(inst, gc.IsNil)
 
763
                        }
 
764
                }
 
765
        }
 
766
}
 
767
 
 
768
func (s *localServerSuite) TestInstancesGathering(c *gc.C) {
 
769
        s.assertInstancesGathering(c, false)
 
770
}
 
771
 
 
772
func (s *localServerSuite) TestInstancesGatheringWithFloatingIP(c *gc.C) {
 
773
        s.assertInstancesGathering(c, true)
 
774
}
 
775
 
 
776
func (s *localServerSuite) TestInstancesBuildSpawning(c *gc.C) {
 
777
        coretesting.SkipIfPPC64EL(c, "lp:1425242")
 
778
 
 
779
        // HP servers are available once they are BUILD(spawning).
 
780
        cleanup := s.srv.Nova.RegisterControlPoint(
 
781
                "addServer",
 
782
                func(sc hook.ServiceControl, args ...interface{}) error {
 
783
                        details := args[0].(*nova.ServerDetail)
 
784
                        details.Status = nova.StatusBuildSpawning
 
785
                        return nil
 
786
                },
 
787
        )
 
788
        defer cleanup()
 
789
        stateInst, _ := testing.AssertStartInstance(c, s.env, s.ControllerUUID, "100")
 
790
        defer func() {
 
791
                err := s.env.StopInstances(stateInst.Id())
 
792
                c.Assert(err, jc.ErrorIsNil)
 
793
        }()
 
794
 
 
795
        instances, err := s.env.Instances([]instance.Id{stateInst.Id()})
 
796
 
 
797
        c.Assert(err, jc.ErrorIsNil)
 
798
        c.Assert(instances, gc.HasLen, 1)
 
799
        c.Assert(instances[0].Status().Message, gc.Equals, nova.StatusBuildSpawning)
 
800
}
 
801
 
 
802
func (s *localServerSuite) TestInstancesShutoffSuspended(c *gc.C) {
 
803
        coretesting.SkipIfPPC64EL(c, "lp:1425242")
 
804
 
 
805
        cleanup := s.srv.Nova.RegisterControlPoint(
 
806
                "addServer",
 
807
                func(sc hook.ServiceControl, args ...interface{}) error {
 
808
                        details := args[0].(*nova.ServerDetail)
 
809
                        switch {
 
810
                        case strings.HasSuffix(details.Name, "-100"):
 
811
                                details.Status = nova.StatusShutoff
 
812
                        case strings.HasSuffix(details.Name, "-101"):
 
813
                                details.Status = nova.StatusSuspended
 
814
                        default:
 
815
                                c.Fatalf("unexpected instance details: %#v", details)
 
816
                        }
 
817
                        return nil
 
818
                },
 
819
        )
 
820
        defer cleanup()
 
821
        stateInst1, _ := testing.AssertStartInstance(c, s.env, s.ControllerUUID, "100")
 
822
        stateInst2, _ := testing.AssertStartInstance(c, s.env, s.ControllerUUID, "101")
 
823
        defer func() {
 
824
                err := s.env.StopInstances(stateInst1.Id(), stateInst2.Id())
 
825
                c.Assert(err, jc.ErrorIsNil)
 
826
        }()
 
827
 
 
828
        instances, err := s.env.Instances([]instance.Id{stateInst1.Id(), stateInst2.Id()})
 
829
 
 
830
        c.Assert(err, jc.ErrorIsNil)
 
831
        c.Assert(instances, gc.HasLen, 2)
 
832
        c.Assert(instances[0].Status().Message, gc.Equals, nova.StatusShutoff)
 
833
        c.Assert(instances[1].Status().Message, gc.Equals, nova.StatusSuspended)
 
834
}
 
835
 
 
836
func (s *localServerSuite) TestInstancesErrorResponse(c *gc.C) {
 
837
        coretesting.SkipIfPPC64EL(c, "lp:1425242")
 
838
 
 
839
        cleanup := s.srv.Nova.RegisterControlPoint(
 
840
                "server",
 
841
                func(sc hook.ServiceControl, args ...interface{}) error {
 
842
                        return fmt.Errorf("strange error not instance")
 
843
                },
 
844
        )
 
845
        defer cleanup()
 
846
 
 
847
        instances, err := s.env.Instances([]instance.Id{"1"})
 
848
        c.Check(instances, gc.IsNil)
 
849
        c.Assert(err, gc.ErrorMatches, "(?s).*strange error not instance.*")
 
850
}
 
851
 
 
852
func (s *localServerSuite) TestInstancesMultiErrorResponse(c *gc.C) {
 
853
        coretesting.SkipIfPPC64EL(c, "lp:1425242")
 
854
 
 
855
        cleanup := s.srv.Nova.RegisterControlPoint(
 
856
                "matchServers",
 
857
                func(sc hook.ServiceControl, args ...interface{}) error {
 
858
                        return fmt.Errorf("strange error no instances")
 
859
                },
 
860
        )
 
861
        defer cleanup()
 
862
 
 
863
        instances, err := s.env.Instances([]instance.Id{"1", "2"})
 
864
        c.Check(instances, gc.IsNil)
 
865
        c.Assert(err, gc.ErrorMatches, "(?s).*strange error no instances.*")
 
866
}
 
867
 
 
868
// TODO (wallyworld) - this test was copied from the ec2 provider.
 
869
// It should be moved to environs.jujutests.Tests.
 
870
func (s *localServerSuite) TestBootstrapInstanceUserDataAndState(c *gc.C) {
 
871
        err := bootstrapEnv(c, s.env)
 
872
        c.Assert(err, jc.ErrorIsNil)
 
873
 
 
874
        // Check that ControllerInstances returns the ID of the bootstrap machine.
 
875
        ids, err := s.env.ControllerInstances(s.ControllerUUID)
 
876
        c.Assert(err, jc.ErrorIsNil)
 
877
        c.Assert(ids, gc.HasLen, 1)
 
878
 
 
879
        insts, err := s.env.AllInstances()
 
880
        c.Assert(err, jc.ErrorIsNil)
 
881
        c.Assert(insts, gc.HasLen, 1)
 
882
        c.Check(insts[0].Id(), gc.Equals, ids[0])
 
883
 
 
884
        addresses, err := insts[0].Addresses()
 
885
        c.Assert(err, jc.ErrorIsNil)
 
886
        c.Assert(addresses, gc.Not(gc.HasLen), 0)
 
887
 
 
888
        // TODO(wallyworld) - 2013-03-01 bug=1137005
 
889
        // The nova test double needs to be updated to support retrieving instance userData.
 
890
        // Until then, we can't check the cloud init script was generated correctly.
 
891
        // When we can, we should also check cloudinit for a non-manager node (as in the
 
892
        // ec2 tests).
 
893
}
 
894
 
 
895
func (s *localServerSuite) assertGetImageMetadataSources(c *gc.C, stream, officialSourcePath string) {
 
896
        // Create a config that matches s.TestConfig but with the specified stream.
 
897
        attrs := coretesting.Attrs{}
 
898
        if stream != "" {
 
899
                attrs = coretesting.Attrs{"image-stream": stream}
 
900
        }
 
901
        env := s.openEnviron(c, attrs)
 
902
 
 
903
        sources, err := environs.ImageMetadataSources(env)
 
904
        c.Assert(err, jc.ErrorIsNil)
 
905
        c.Assert(sources, gc.HasLen, 4)
 
906
        var urls = make([]string, len(sources))
 
907
        for i, source := range sources {
 
908
                url, err := source.URL("")
 
909
                c.Assert(err, jc.ErrorIsNil)
 
910
                urls[i] = url
 
911
        }
 
912
        // The image-metadata-url ends with "/juju-dist-test/".
 
913
        c.Check(strings.HasSuffix(urls[0], "/juju-dist-test/"), jc.IsTrue)
 
914
        // The product-streams URL ends with "/imagemetadata".
 
915
        c.Check(strings.HasSuffix(urls[1], "/imagemetadata/"), jc.IsTrue)
 
916
        c.Assert(urls[2], gc.Equals, fmt.Sprintf("https://streams.canonical.com/juju/images/%s/", officialSourcePath))
 
917
        c.Assert(urls[3], gc.Equals, fmt.Sprintf("http://cloud-images.ubuntu.com/%s/", officialSourcePath))
 
918
}
 
919
 
 
920
func (s *localServerSuite) TestGetImageMetadataSources(c *gc.C) {
 
921
        s.assertGetImageMetadataSources(c, "", "releases")
 
922
        s.assertGetImageMetadataSources(c, "released", "releases")
 
923
        s.assertGetImageMetadataSources(c, "daily", "daily")
 
924
}
 
925
 
 
926
func (s *localServerSuite) TestGetImageMetadataSourcesNoProductStreams(c *gc.C) {
 
927
        s.PatchValue(openstack.MakeServiceURL, func(client.AuthenticatingClient, string, []string) (string, error) {
 
928
                return "", errors.New("cannae do it captain")
 
929
        })
 
930
        env := s.Open(c, s.env.Config())
 
931
        sources, err := environs.ImageMetadataSources(env)
 
932
        c.Assert(err, jc.ErrorIsNil)
 
933
        c.Assert(sources, gc.HasLen, 3)
 
934
 
 
935
        // Check that data sources are in the right order
 
936
        c.Check(sources[0].Description(), gc.Equals, "image-metadata-url")
 
937
        c.Check(sources[1].Description(), gc.Equals, "default cloud images")
 
938
        c.Check(sources[2].Description(), gc.Equals, "default ubuntu cloud images")
 
939
}
 
940
 
 
941
func (s *localServerSuite) TestGetToolsMetadataSources(c *gc.C) {
 
942
        s.PatchValue(&tools.DefaultBaseURL, "")
 
943
 
 
944
        env := s.Open(c, s.env.Config())
 
945
        sources, err := tools.GetMetadataSources(env)
 
946
        c.Assert(err, jc.ErrorIsNil)
 
947
        c.Assert(sources, gc.HasLen, 2)
 
948
        var urls = make([]string, len(sources))
 
949
        for i, source := range sources {
 
950
                url, err := source.URL("")
 
951
                c.Assert(err, jc.ErrorIsNil)
 
952
                urls[i] = url
 
953
        }
 
954
        // The agent-metadata-url ends with "/juju-dist-test/tools/".
 
955
        c.Check(strings.HasSuffix(urls[0], "/juju-dist-test/tools/"), jc.IsTrue)
 
956
        // Check that the URL from keystone parses.
 
957
        _, err = url.Parse(urls[1])
 
958
        c.Assert(err, jc.ErrorIsNil)
 
959
}
 
960
 
 
961
func (s *localServerSuite) TestSupportsNetworking(c *gc.C) {
 
962
        env := s.Open(c, s.env.Config())
 
963
        _, ok := environs.SupportsNetworking(env)
 
964
        c.Assert(ok, jc.IsFalse)
 
965
}
 
966
 
 
967
func (s *localServerSuite) TestFindImageBadDefaultImage(c *gc.C) {
 
968
        imagetesting.PatchOfficialDataSources(&s.CleanupSuite, "")
 
969
        env := s.Open(c, s.env.Config())
 
970
 
 
971
        // An error occurs if no suitable image is found.
 
972
        _, err := openstack.FindInstanceSpec(env, "saucy", "amd64", "mem=1G", nil)
 
973
        c.Assert(err, gc.ErrorMatches, `no "saucy" images in some-region with arches \[amd64\]`)
 
974
}
 
975
 
 
976
func (s *localServerSuite) TestConstraintsValidator(c *gc.C) {
 
977
        env := s.Open(c, s.env.Config())
 
978
        validator, err := env.ConstraintsValidator()
 
979
        c.Assert(err, jc.ErrorIsNil)
 
980
        cons := constraints.MustParse("arch=amd64 cpu-power=10 virt-type=lxd")
 
981
        unsupported, err := validator.Validate(cons)
 
982
        c.Assert(err, jc.ErrorIsNil)
 
983
        c.Assert(unsupported, jc.SameContents, []string{"cpu-power"})
 
984
}
 
985
 
 
986
func (s *localServerSuite) TestConstraintsValidatorVocab(c *gc.C) {
 
987
        env := s.Open(c, s.env.Config())
 
988
        validator, err := env.ConstraintsValidator()
 
989
        c.Assert(err, jc.ErrorIsNil)
 
990
 
 
991
        // i386 is a valid arch, but is no longer supported.  No image
 
992
        // data was created for it for the test.
 
993
        cons := constraints.MustParse("arch=i386")
 
994
        _, err = validator.Validate(cons)
 
995
        c.Assert(err, gc.ErrorMatches, "invalid constraint value: arch=i386\nvalid values are: \\[amd64 arm64 ppc64el s390x\\]")
 
996
        cons = constraints.MustParse("instance-type=foo")
 
997
        _, err = validator.Validate(cons)
 
998
        c.Assert(err, gc.ErrorMatches, "invalid constraint value: instance-type=foo\nvalid values are:.*")
 
999
 
 
1000
        cons = constraints.MustParse("virt-type=foo")
 
1001
        _, err = validator.Validate(cons)
 
1002
        c.Assert(err, gc.ErrorMatches, regexp.QuoteMeta("invalid constraint value: virt-type=foo\nvalid values are: [kvm lxd]"))
 
1003
}
 
1004
 
 
1005
func (s *localServerSuite) TestConstraintsMerge(c *gc.C) {
 
1006
        env := s.Open(c, s.env.Config())
 
1007
        validator, err := env.ConstraintsValidator()
 
1008
        c.Assert(err, jc.ErrorIsNil)
 
1009
        consA := constraints.MustParse("arch=amd64 mem=1G root-disk=10G")
 
1010
        consB := constraints.MustParse("instance-type=m1.small")
 
1011
        cons, err := validator.Merge(consA, consB)
 
1012
        c.Assert(err, jc.ErrorIsNil)
 
1013
        c.Assert(cons, gc.DeepEquals, constraints.MustParse("instance-type=m1.small"))
 
1014
}
 
1015
 
 
1016
func (s *localServerSuite) TestFindImageInstanceConstraint(c *gc.C) {
 
1017
        env := s.Open(c, s.env.Config())
 
1018
        imageMetadata := []*imagemetadata.ImageMetadata{{
 
1019
                Id:   "image-id",
 
1020
                Arch: "amd64",
 
1021
        }}
 
1022
 
 
1023
        spec, err := openstack.FindInstanceSpec(
 
1024
                env, series.LatestLts(), "amd64", "instance-type=m1.tiny",
 
1025
                imageMetadata,
 
1026
        )
 
1027
        c.Assert(err, jc.ErrorIsNil)
 
1028
        c.Assert(spec.InstanceType.Name, gc.Equals, "m1.tiny")
 
1029
}
 
1030
 
 
1031
func (s *localServerSuite) TestFindInstanceImageConstraintHypervisor(c *gc.C) {
 
1032
        testVirtType := "qemu"
 
1033
        env := s.Open(c, s.env.Config())
 
1034
        imageMetadata := []*imagemetadata.ImageMetadata{{
 
1035
                Id:       "image-id",
 
1036
                Arch:     "amd64",
 
1037
                VirtType: testVirtType,
 
1038
        }}
 
1039
 
 
1040
        spec, err := openstack.FindInstanceSpec(
 
1041
                env, series.LatestLts(), "amd64", "virt-type="+testVirtType,
 
1042
                imageMetadata,
 
1043
        )
 
1044
        c.Assert(err, jc.ErrorIsNil)
 
1045
        c.Assert(spec.InstanceType.VirtType, gc.NotNil)
 
1046
        c.Assert(*spec.InstanceType.VirtType, gc.Equals, testVirtType)
 
1047
        c.Assert(spec.InstanceType.Name, gc.Equals, "m1.small")
 
1048
}
 
1049
 
 
1050
func (s *localServerSuite) TestFindInstanceImageWithHypervisorNoConstraint(c *gc.C) {
 
1051
        testVirtType := "qemu"
 
1052
        env := s.Open(c, s.env.Config())
 
1053
        imageMetadata := []*imagemetadata.ImageMetadata{{
 
1054
                Id:       "image-id",
 
1055
                Arch:     "amd64",
 
1056
                VirtType: testVirtType,
 
1057
        }}
 
1058
 
 
1059
        spec, err := openstack.FindInstanceSpec(
 
1060
                env, series.LatestLts(), "amd64", "",
 
1061
                imageMetadata,
 
1062
        )
 
1063
        c.Assert(err, jc.ErrorIsNil)
 
1064
        c.Assert(spec.InstanceType.VirtType, gc.NotNil)
 
1065
        c.Assert(*spec.InstanceType.VirtType, gc.Equals, testVirtType)
 
1066
        c.Assert(spec.InstanceType.Name, gc.Equals, "m1.small")
 
1067
}
 
1068
 
 
1069
func (s *localServerSuite) TestFindInstanceNoConstraint(c *gc.C) {
 
1070
        env := s.Open(c, s.env.Config())
 
1071
        imageMetadata := []*imagemetadata.ImageMetadata{{
 
1072
                Id:   "image-id",
 
1073
                Arch: "amd64",
 
1074
        }}
 
1075
 
 
1076
        spec, err := openstack.FindInstanceSpec(
 
1077
                env, series.LatestLts(), "amd64", "",
 
1078
                imageMetadata,
 
1079
        )
 
1080
        c.Assert(err, jc.ErrorIsNil)
 
1081
        c.Assert(spec.InstanceType.VirtType, gc.IsNil)
 
1082
        c.Assert(spec.InstanceType.Name, gc.Equals, "m1.small")
 
1083
}
 
1084
 
 
1085
func (s *localServerSuite) TestFindImageInvalidInstanceConstraint(c *gc.C) {
 
1086
        env := s.Open(c, s.env.Config())
 
1087
        imageMetadata := []*imagemetadata.ImageMetadata{{
 
1088
                Id:   "image-id",
 
1089
                Arch: "amd64",
 
1090
        }}
 
1091
        _, err := openstack.FindInstanceSpec(
 
1092
                env, series.LatestLts(), "amd64", "instance-type=m1.large",
 
1093
                imageMetadata,
 
1094
        )
 
1095
        c.Assert(err, gc.ErrorMatches, `no instance types in some-region matching constraints "instance-type=m1.large"`)
 
1096
}
 
1097
 
 
1098
func (s *localServerSuite) TestPrecheckInstanceValidInstanceType(c *gc.C) {
 
1099
        env := s.Open(c, s.env.Config())
 
1100
        cons := constraints.MustParse("instance-type=m1.small")
 
1101
        placement := ""
 
1102
        err := env.PrecheckInstance(series.LatestLts(), cons, placement)
 
1103
        c.Assert(err, jc.ErrorIsNil)
 
1104
}
 
1105
 
 
1106
func (s *localServerSuite) TestPrecheckInstanceInvalidInstanceType(c *gc.C) {
 
1107
        env := s.Open(c, s.env.Config())
 
1108
        cons := constraints.MustParse("instance-type=m1.large")
 
1109
        placement := ""
 
1110
        err := env.PrecheckInstance(series.LatestLts(), cons, placement)
 
1111
        c.Assert(err, gc.ErrorMatches, `invalid Openstack flavour "m1.large" specified`)
 
1112
}
 
1113
 
 
1114
func (t *localServerSuite) TestPrecheckInstanceAvailZone(c *gc.C) {
 
1115
        placement := "zone=test-available"
 
1116
        err := t.env.PrecheckInstance(series.LatestLts(), constraints.Value{}, placement)
 
1117
        c.Assert(err, jc.ErrorIsNil)
 
1118
}
 
1119
 
 
1120
func (t *localServerSuite) TestPrecheckInstanceAvailZoneUnavailable(c *gc.C) {
 
1121
        placement := "zone=test-unavailable"
 
1122
        err := t.env.PrecheckInstance(series.LatestLts(), constraints.Value{}, placement)
 
1123
        c.Assert(err, jc.ErrorIsNil)
 
1124
}
 
1125
 
 
1126
func (t *localServerSuite) TestPrecheckInstanceAvailZoneUnknown(c *gc.C) {
 
1127
        placement := "zone=test-unknown"
 
1128
        err := t.env.PrecheckInstance(series.LatestLts(), constraints.Value{}, placement)
 
1129
        c.Assert(err, gc.ErrorMatches, `invalid availability zone "test-unknown"`)
 
1130
}
 
1131
 
 
1132
func (t *localServerSuite) TestPrecheckInstanceAvailZonesUnsupported(c *gc.C) {
 
1133
        t.srv.Nova.SetAvailabilityZones() // no availability zone support
 
1134
        placement := "zone=test-unknown"
 
1135
        err := t.env.PrecheckInstance(series.LatestLts(), constraints.Value{}, placement)
 
1136
        c.Assert(err, jc.Satisfies, jujuerrors.IsNotImplemented)
 
1137
}
 
1138
 
 
1139
func (s *localServerSuite) TestValidateImageMetadata(c *gc.C) {
 
1140
        env := s.Open(c, s.env.Config())
 
1141
        params, err := env.(simplestreams.MetadataValidator).MetadataLookupParams("some-region")
 
1142
        c.Assert(err, jc.ErrorIsNil)
 
1143
        params.Sources, err = environs.ImageMetadataSources(env)
 
1144
        c.Assert(err, jc.ErrorIsNil)
 
1145
        params.Series = "raring"
 
1146
        image_ids, _, err := imagemetadata.ValidateImageMetadata(params)
 
1147
        c.Assert(err, jc.ErrorIsNil)
 
1148
        c.Assert(image_ids, jc.SameContents, []string{"id-y"})
 
1149
}
 
1150
 
 
1151
func (s *localServerSuite) TestImageMetadataSourceOrder(c *gc.C) {
 
1152
        src := func(env environs.Environ) (simplestreams.DataSource, error) {
 
1153
                return simplestreams.NewURLDataSource("my datasource", "bar", false, simplestreams.CUSTOM_CLOUD_DATA, false), nil
 
1154
        }
 
1155
        environs.RegisterUserImageDataSourceFunc("my func", src)
 
1156
        env := s.Open(c, s.env.Config())
 
1157
        sources, err := environs.ImageMetadataSources(env)
 
1158
        c.Assert(err, jc.ErrorIsNil)
 
1159
        var sourceIds []string
 
1160
        for _, s := range sources {
 
1161
                sourceIds = append(sourceIds, s.Description())
 
1162
        }
 
1163
        c.Assert(sourceIds, jc.DeepEquals, []string{
 
1164
                "image-metadata-url", "my datasource", "keystone catalog", "default cloud images", "default ubuntu cloud images"})
 
1165
}
 
1166
 
 
1167
// TestEnsureGroup checks that when creating a duplicate security group, the existing group is
 
1168
// returned and the existing rules have been left as is.
 
1169
func (s *localServerSuite) TestEnsureGroup(c *gc.C) {
 
1170
        rule := []nova.RuleInfo{
 
1171
                {
 
1172
                        IPProtocol: "tcp",
 
1173
                        FromPort:   22,
 
1174
                        ToPort:     22,
 
1175
                },
 
1176
        }
 
1177
 
 
1178
        assertRule := func(group nova.SecurityGroup) {
 
1179
                c.Check(len(group.Rules), gc.Equals, 1)
 
1180
                c.Check(*group.Rules[0].IPProtocol, gc.Equals, "tcp")
 
1181
                c.Check(*group.Rules[0].FromPort, gc.Equals, 22)
 
1182
                c.Check(*group.Rules[0].ToPort, gc.Equals, 22)
 
1183
        }
 
1184
 
 
1185
        group, err := openstack.EnsureGroup(s.env, "test group", rule)
 
1186
        c.Assert(err, jc.ErrorIsNil)
 
1187
        c.Assert(group.Name, gc.Equals, "test group")
 
1188
        assertRule(group)
 
1189
        id := group.Id
 
1190
        // Do it again and check that the existing group is returned.
 
1191
        anotherRule := []nova.RuleInfo{
 
1192
                {
 
1193
                        IPProtocol: "tcp",
 
1194
                        FromPort:   1,
 
1195
                        ToPort:     65535,
 
1196
                },
 
1197
        }
 
1198
        group, err = openstack.EnsureGroup(s.env, "test group", anotherRule)
 
1199
        c.Assert(err, jc.ErrorIsNil)
 
1200
        c.Check(group.Id, gc.Equals, id)
 
1201
        c.Assert(group.Name, gc.Equals, "test group")
 
1202
        assertRule(group)
 
1203
}
 
1204
 
 
1205
// localHTTPSServerSuite contains tests that run against an Openstack service
 
1206
// double connected on an HTTPS port with a self-signed certificate. This
 
1207
// service is set up and torn down for every test.  This should only test
 
1208
// things that depend on the HTTPS connection, all other functional tests on a
 
1209
// local connection should be in localServerSuite
 
1210
type localHTTPSServerSuite struct {
 
1211
        coretesting.BaseSuite
 
1212
        attrs map[string]interface{}
 
1213
        cred  *identity.Credentials
 
1214
        srv   localServer
 
1215
        env   environs.Environ
 
1216
}
 
1217
 
 
1218
func (s *localHTTPSServerSuite) SetUpSuite(c *gc.C) {
 
1219
        s.BaseSuite.SetUpSuite(c)
 
1220
        overrideCinderProvider(c, &s.CleanupSuite)
 
1221
}
 
1222
 
 
1223
func (s *localHTTPSServerSuite) createConfigAttrs(c *gc.C) map[string]interface{} {
 
1224
        attrs := makeTestConfig(s.cred)
 
1225
        attrs["agent-version"] = coretesting.FakeVersionNumber.String()
 
1226
        attrs["authorized-keys"] = "fakekey"
 
1227
        // In order to set up and tear down the environment properly, we must
 
1228
        // disable hostname verification
 
1229
        attrs["ssl-hostname-verification"] = false
 
1230
        attrs["auth-url"] = s.cred.URL
 
1231
        // Now connect and set up test-local tools and image-metadata URLs
 
1232
        cl := client.NewNonValidatingClient(s.cred, identity.AuthUserPass, nil)
 
1233
        err := cl.Authenticate()
 
1234
        c.Assert(err, jc.ErrorIsNil)
 
1235
        containerURL, err := cl.MakeServiceURL("object-store", nil)
 
1236
        c.Assert(err, jc.ErrorIsNil)
 
1237
        c.Check(containerURL[:8], gc.Equals, "https://")
 
1238
        attrs["agent-metadata-url"] = containerURL + "/juju-dist-test/tools"
 
1239
        c.Logf("Set agent-metadata-url=%q", attrs["agent-metadata-url"])
 
1240
        attrs["image-metadata-url"] = containerURL + "/juju-dist-test"
 
1241
        c.Logf("Set image-metadata-url=%q", attrs["image-metadata-url"])
 
1242
        return attrs
 
1243
}
 
1244
 
 
1245
func (s *localHTTPSServerSuite) SetUpTest(c *gc.C) {
 
1246
        s.BaseSuite.SetUpTest(c)
 
1247
        s.PatchValue(&jujuversion.Current, coretesting.FakeVersionNumber)
 
1248
        s.srv.UseTLS = true
 
1249
        cred := &identity.Credentials{
 
1250
                User:       "fred",
 
1251
                Secrets:    "secret",
 
1252
                Region:     "some-region",
 
1253
                TenantName: "some tenant",
 
1254
        }
 
1255
        // Note: start() will change cred.URL to point to s.srv.Server.URL
 
1256
        s.srv.start(c, cred, newFullOpenstackService)
 
1257
        s.cred = cred
 
1258
        attrs := s.createConfigAttrs(c)
 
1259
        c.Assert(attrs["auth-url"].(string)[:8], gc.Equals, "https://")
 
1260
        var err error
 
1261
        s.env, err = bootstrap.Prepare(
 
1262
                envtesting.BootstrapContext(c),
 
1263
                jujuclienttesting.NewMemStore(),
 
1264
                prepareParams(attrs, s.cred),
 
1265
        )
 
1266
        c.Assert(err, jc.ErrorIsNil)
 
1267
        s.attrs = s.env.Config().AllAttrs()
 
1268
}
 
1269
 
 
1270
func (s *localHTTPSServerSuite) TearDownTest(c *gc.C) {
 
1271
        if s.env != nil {
 
1272
                err := s.env.Destroy()
 
1273
                c.Check(err, jc.ErrorIsNil)
 
1274
                s.env = nil
 
1275
        }
 
1276
        s.srv.stop()
 
1277
        s.BaseSuite.TearDownTest(c)
 
1278
}
 
1279
 
 
1280
func (s *localHTTPSServerSuite) TestMustDisableSSLVerify(c *gc.C) {
 
1281
        coretesting.SkipIfPPC64EL(c, "lp:1425242")
 
1282
 
 
1283
        // If you don't have ssl-hostname-verification set to false, then we
 
1284
        // fail to connect to the environment. Copy the attrs used by SetUp and
 
1285
        // force hostname verification.
 
1286
        newattrs := make(map[string]interface{}, len(s.attrs))
 
1287
        for k, v := range s.attrs {
 
1288
                newattrs[k] = v
 
1289
        }
 
1290
        newattrs["ssl-hostname-verification"] = true
 
1291
        cfg, err := config.New(config.NoDefaults, newattrs)
 
1292
        c.Assert(err, jc.ErrorIsNil)
 
1293
        env, err := environs.New(environs.OpenParams{
 
1294
                Cloud:  makeCloudSpec(s.cred),
 
1295
                Config: cfg,
 
1296
        })
 
1297
        c.Assert(err, jc.ErrorIsNil)
 
1298
        _, err = env.AllInstances()
 
1299
        c.Assert(err, gc.ErrorMatches, "(.|\n)*x509: certificate signed by unknown authority")
 
1300
}
 
1301
 
 
1302
func (s *localHTTPSServerSuite) TestCanBootstrap(c *gc.C) {
 
1303
        restoreFinishBootstrap := envtesting.DisableFinishBootstrap()
 
1304
        defer restoreFinishBootstrap()
 
1305
 
 
1306
        // For testing, we create a storage instance to which is uploaded tools and image metadata.
 
1307
        metadataStorage := openstack.MetadataStorage(s.env)
 
1308
        url, err := metadataStorage.URL("")
 
1309
        c.Assert(err, jc.ErrorIsNil)
 
1310
        c.Logf("Generating fake tools for: %v", url)
 
1311
        envtesting.UploadFakeTools(c, metadataStorage, s.env.Config().AgentStream(), s.env.Config().AgentStream())
 
1312
        defer envtesting.RemoveFakeTools(c, metadataStorage, s.env.Config().AgentStream())
 
1313
        openstack.UseTestImageData(metadataStorage, s.cred)
 
1314
        defer openstack.RemoveTestImageData(metadataStorage)
 
1315
 
 
1316
        err = bootstrapEnv(c, s.env)
 
1317
        c.Assert(err, jc.ErrorIsNil)
 
1318
}
 
1319
 
 
1320
func (s *localHTTPSServerSuite) TestFetchFromImageMetadataSources(c *gc.C) {
 
1321
        // Setup a custom URL for image metadata
 
1322
        customStorage := openstack.CreateCustomStorage(s.env, "custom-metadata")
 
1323
        customURL, err := customStorage.URL("")
 
1324
        c.Assert(err, jc.ErrorIsNil)
 
1325
        c.Check(customURL[:8], gc.Equals, "https://")
 
1326
 
 
1327
        config, err := s.env.Config().Apply(
 
1328
                map[string]interface{}{"image-metadata-url": customURL},
 
1329
        )
 
1330
        c.Assert(err, jc.ErrorIsNil)
 
1331
        err = s.env.SetConfig(config)
 
1332
        c.Assert(err, jc.ErrorIsNil)
 
1333
        sources, err := environs.ImageMetadataSources(s.env)
 
1334
        c.Assert(err, jc.ErrorIsNil)
 
1335
        c.Assert(sources, gc.HasLen, 4)
 
1336
 
 
1337
        // Make sure there is something to download from each location
 
1338
        metadata := "metadata-content"
 
1339
        metadataStorage := openstack.ImageMetadataStorage(s.env)
 
1340
        err = metadataStorage.Put(metadata, bytes.NewBufferString(metadata), int64(len(metadata)))
 
1341
        c.Assert(err, jc.ErrorIsNil)
 
1342
 
 
1343
        custom := "custom-content"
 
1344
        err = customStorage.Put(custom, bytes.NewBufferString(custom), int64(len(custom)))
 
1345
        c.Assert(err, jc.ErrorIsNil)
 
1346
 
 
1347
        // Produce map of data sources keyed on description
 
1348
        mappedSources := make(map[string]simplestreams.DataSource, len(sources))
 
1349
        for i, s := range sources {
 
1350
                c.Logf("datasource %d: %+v", i, s)
 
1351
                mappedSources[s.Description()] = s
 
1352
        }
 
1353
 
 
1354
        // Read from the Config entry's image-metadata-url
 
1355
        contentReader, url, err := mappedSources["image-metadata-url"].Fetch(custom)
 
1356
        c.Assert(err, jc.ErrorIsNil)
 
1357
        defer contentReader.Close()
 
1358
        content, err := ioutil.ReadAll(contentReader)
 
1359
        c.Assert(err, jc.ErrorIsNil)
 
1360
        c.Assert(string(content), gc.Equals, custom)
 
1361
        c.Check(url[:8], gc.Equals, "https://")
 
1362
 
 
1363
        // Check the entry we got from keystone
 
1364
        contentReader, url, err = mappedSources["keystone catalog"].Fetch(metadata)
 
1365
        c.Assert(err, jc.ErrorIsNil)
 
1366
        defer contentReader.Close()
 
1367
        content, err = ioutil.ReadAll(contentReader)
 
1368
        c.Assert(err, jc.ErrorIsNil)
 
1369
        c.Assert(string(content), gc.Equals, metadata)
 
1370
        c.Check(url[:8], gc.Equals, "https://")
 
1371
        // Verify that we are pointing at exactly where metadataStorage thinks we are
 
1372
        metaURL, err := metadataStorage.URL(metadata)
 
1373
        c.Assert(err, jc.ErrorIsNil)
 
1374
        c.Check(url, gc.Equals, metaURL)
 
1375
 
 
1376
}
 
1377
 
 
1378
func (s *localHTTPSServerSuite) TestFetchFromToolsMetadataSources(c *gc.C) {
 
1379
        // Setup a custom URL for image metadata
 
1380
        customStorage := openstack.CreateCustomStorage(s.env, "custom-tools-metadata")
 
1381
        customURL, err := customStorage.URL("")
 
1382
        c.Assert(err, jc.ErrorIsNil)
 
1383
        c.Check(customURL[:8], gc.Equals, "https://")
 
1384
 
 
1385
        config, err := s.env.Config().Apply(
 
1386
                map[string]interface{}{"agent-metadata-url": customURL},
 
1387
        )
 
1388
        c.Assert(err, jc.ErrorIsNil)
 
1389
        err = s.env.SetConfig(config)
 
1390
        c.Assert(err, jc.ErrorIsNil)
 
1391
        sources, err := tools.GetMetadataSources(s.env)
 
1392
        c.Assert(err, jc.ErrorIsNil)
 
1393
        c.Assert(sources, gc.HasLen, 3)
 
1394
 
 
1395
        // Make sure there is something to download from each location
 
1396
 
 
1397
        keystone := "keystone-tools-content"
 
1398
        // The keystone entry just points at the root of the Swift storage, and
 
1399
        // we have to create a container to upload any data. So we just point
 
1400
        // into a subdirectory for the data we are downloading
 
1401
        keystoneContainer := "tools-test"
 
1402
        keystoneStorage := openstack.CreateCustomStorage(s.env, "tools-test")
 
1403
        err = keystoneStorage.Put(keystone, bytes.NewBufferString(keystone), int64(len(keystone)))
 
1404
        c.Assert(err, jc.ErrorIsNil)
 
1405
 
 
1406
        custom := "custom-tools-content"
 
1407
        err = customStorage.Put(custom, bytes.NewBufferString(custom), int64(len(custom)))
 
1408
        c.Assert(err, jc.ErrorIsNil)
 
1409
 
 
1410
        // Read from the Config entry's agent-metadata-url
 
1411
        contentReader, url, err := sources[0].Fetch(custom)
 
1412
        c.Assert(err, jc.ErrorIsNil)
 
1413
        defer contentReader.Close()
 
1414
        content, err := ioutil.ReadAll(contentReader)
 
1415
        c.Assert(err, jc.ErrorIsNil)
 
1416
        c.Assert(string(content), gc.Equals, custom)
 
1417
        c.Check(url[:8], gc.Equals, "https://")
 
1418
 
 
1419
        // Check the entry we got from keystone
 
1420
        // Now fetch the data, and verify the contents.
 
1421
        contentReader, url, err = sources[1].Fetch(keystoneContainer + "/" + keystone)
 
1422
        c.Assert(err, jc.ErrorIsNil)
 
1423
        defer contentReader.Close()
 
1424
        content, err = ioutil.ReadAll(contentReader)
 
1425
        c.Assert(err, jc.ErrorIsNil)
 
1426
        c.Assert(string(content), gc.Equals, keystone)
 
1427
        c.Check(url[:8], gc.Equals, "https://")
 
1428
        keystoneURL, err := keystoneStorage.URL(keystone)
 
1429
        c.Assert(err, jc.ErrorIsNil)
 
1430
        c.Check(url, gc.Equals, keystoneURL)
 
1431
 
 
1432
        // We *don't* test Fetch for sources[3] because it points to
 
1433
        // streams.canonical.com
 
1434
}
 
1435
 
 
1436
func (s *localServerSuite) TestRemoveBlankContainer(c *gc.C) {
 
1437
        storage := openstack.BlankContainerStorage()
 
1438
        err := storage.Remove("some-file")
 
1439
        c.Assert(err, gc.ErrorMatches, `cannot remove "some-file": swift container name is empty`)
 
1440
}
 
1441
 
 
1442
func (s *localServerSuite) TestAllInstancesIgnoresOtherMachines(c *gc.C) {
 
1443
        err := bootstrapEnv(c, s.env)
 
1444
        c.Assert(err, jc.ErrorIsNil)
 
1445
 
 
1446
        // Check that we see 1 instance in the environment
 
1447
        insts, err := s.env.AllInstances()
 
1448
        c.Assert(err, jc.ErrorIsNil)
 
1449
        c.Check(insts, gc.HasLen, 1)
 
1450
 
 
1451
        // Now start a machine 'manually' in the same account, with a similar
 
1452
        // but not matching name, and ensure it isn't seen by AllInstances
 
1453
        // See bug #1257481, for how similar names were causing them to get
 
1454
        // listed (and thus destroyed) at the wrong time
 
1455
        existingModelName := s.TestConfig["name"]
 
1456
        newMachineName := fmt.Sprintf("juju-%s-2-machine-0", existingModelName)
 
1457
 
 
1458
        // We grab the Nova client directly from the env, just to save time
 
1459
        // looking all the stuff up
 
1460
        novaClient := openstack.GetNovaClient(s.env)
 
1461
        entity, err := novaClient.RunServer(nova.RunServerOpts{
 
1462
                Name:     newMachineName,
 
1463
                FlavorId: "1", // test service has 1,2,3 for flavor ids
 
1464
                ImageId:  "1", // UseTestImageData sets up images 1 and 2
 
1465
        })
 
1466
        c.Assert(err, jc.ErrorIsNil)
 
1467
        c.Assert(entity, gc.NotNil)
 
1468
 
 
1469
        // List all servers with no filter, we should see both instances
 
1470
        servers, err := novaClient.ListServersDetail(nova.NewFilter())
 
1471
        c.Assert(err, jc.ErrorIsNil)
 
1472
        c.Assert(servers, gc.HasLen, 2)
 
1473
 
 
1474
        insts, err = s.env.AllInstances()
 
1475
        c.Assert(err, jc.ErrorIsNil)
 
1476
        c.Check(insts, gc.HasLen, 1)
 
1477
}
 
1478
 
 
1479
func (s *localServerSuite) TestResolveNetworkUUID(c *gc.C) {
 
1480
        var sampleUUID = "f81d4fae-7dec-11d0-a765-00a0c91e6bf6"
 
1481
        networkId, err := openstack.ResolveNetwork(s.env, sampleUUID)
 
1482
        c.Assert(err, jc.ErrorIsNil)
 
1483
        c.Assert(networkId, gc.Equals, sampleUUID)
 
1484
}
 
1485
 
 
1486
func (s *localServerSuite) TestResolveNetworkLabel(c *gc.C) {
 
1487
        // For now this test has to cheat and use knowledge of goose internals
 
1488
        var networkLabel = "net"
 
1489
        var expectNetworkId = "1"
 
1490
        networkId, err := openstack.ResolveNetwork(s.env, networkLabel)
 
1491
        c.Assert(err, jc.ErrorIsNil)
 
1492
        c.Assert(networkId, gc.Equals, expectNetworkId)
 
1493
}
 
1494
 
 
1495
func (s *localServerSuite) TestResolveNetworkNotPresent(c *gc.C) {
 
1496
        var notPresentNetwork = "no-network-with-this-label"
 
1497
        networkId, err := openstack.ResolveNetwork(s.env, notPresentNetwork)
 
1498
        c.Check(networkId, gc.Equals, "")
 
1499
        c.Assert(err, gc.ErrorMatches, `No networks exist with label "no-network-with-this-label"`)
 
1500
}
 
1501
 
 
1502
// TODO(gz): TestResolveNetworkMultipleMatching when can inject new networks
 
1503
 
 
1504
func (t *localServerSuite) TestStartInstanceAvailZone(c *gc.C) {
 
1505
        inst, err := t.testStartInstanceAvailZone(c, "test-available")
 
1506
        c.Assert(err, jc.ErrorIsNil)
 
1507
        c.Assert(openstack.InstanceServerDetail(inst).AvailabilityZone, gc.Equals, "test-available")
 
1508
}
 
1509
 
 
1510
func (t *localServerSuite) TestStartInstanceAvailZoneUnavailable(c *gc.C) {
 
1511
        _, err := t.testStartInstanceAvailZone(c, "test-unavailable")
 
1512
        c.Assert(err, gc.ErrorMatches, `availability zone "test-unavailable" is unavailable`)
 
1513
}
 
1514
 
 
1515
func (t *localServerSuite) TestStartInstanceAvailZoneUnknown(c *gc.C) {
 
1516
        _, err := t.testStartInstanceAvailZone(c, "test-unknown")
 
1517
        c.Assert(err, gc.ErrorMatches, `invalid availability zone "test-unknown"`)
 
1518
}
 
1519
 
 
1520
func (t *localServerSuite) testStartInstanceAvailZone(c *gc.C, zone string) (instance.Instance, error) {
 
1521
        err := bootstrapEnv(c, t.env)
 
1522
        c.Assert(err, jc.ErrorIsNil)
 
1523
 
 
1524
        params := environs.StartInstanceParams{
 
1525
                ControllerUUID: t.ControllerUUID,
 
1526
                Placement:      "zone=" + zone,
 
1527
        }
 
1528
        result, err := testing.StartInstanceWithParams(t.env, "1", params)
 
1529
        if err != nil {
 
1530
                return nil, err
 
1531
        }
 
1532
        return result.Instance, nil
 
1533
}
 
1534
 
 
1535
func (t *localServerSuite) TestGetAvailabilityZones(c *gc.C) {
 
1536
        var resultZones []nova.AvailabilityZone
 
1537
        var resultErr error
 
1538
        t.PatchValue(openstack.NovaListAvailabilityZones, func(c *nova.Client) ([]nova.AvailabilityZone, error) {
 
1539
                return append([]nova.AvailabilityZone{}, resultZones...), resultErr
 
1540
        })
 
1541
        env := t.env.(common.ZonedEnviron)
 
1542
 
 
1543
        resultErr = fmt.Errorf("failed to get availability zones")
 
1544
        zones, err := env.AvailabilityZones()
 
1545
        c.Assert(err, gc.Equals, resultErr)
 
1546
        c.Assert(zones, gc.IsNil)
 
1547
 
 
1548
        resultErr = nil
 
1549
        resultZones = make([]nova.AvailabilityZone, 1)
 
1550
        resultZones[0].Name = "whatever"
 
1551
        zones, err = env.AvailabilityZones()
 
1552
        c.Assert(err, jc.ErrorIsNil)
 
1553
        c.Assert(zones, gc.HasLen, 1)
 
1554
        c.Assert(zones[0].Name(), gc.Equals, "whatever")
 
1555
 
 
1556
        // A successful result is cached, currently for the lifetime
 
1557
        // of the Environ. This will change if/when we have long-lived
 
1558
        // Environs to cut down repeated IaaS requests.
 
1559
        resultErr = fmt.Errorf("failed to get availability zones")
 
1560
        resultZones[0].Name = "andever"
 
1561
        zones, err = env.AvailabilityZones()
 
1562
        c.Assert(err, jc.ErrorIsNil)
 
1563
        c.Assert(zones, gc.HasLen, 1)
 
1564
        c.Assert(zones[0].Name(), gc.Equals, "whatever")
 
1565
}
 
1566
 
 
1567
func (t *localServerSuite) TestGetAvailabilityZonesCommon(c *gc.C) {
 
1568
        var resultZones []nova.AvailabilityZone
 
1569
        t.PatchValue(openstack.NovaListAvailabilityZones, func(c *nova.Client) ([]nova.AvailabilityZone, error) {
 
1570
                return append([]nova.AvailabilityZone{}, resultZones...), nil
 
1571
        })
 
1572
        env := t.env.(common.ZonedEnviron)
 
1573
        resultZones = make([]nova.AvailabilityZone, 2)
 
1574
        resultZones[0].Name = "az1"
 
1575
        resultZones[1].Name = "az2"
 
1576
        resultZones[0].State.Available = true
 
1577
        resultZones[1].State.Available = false
 
1578
        zones, err := env.AvailabilityZones()
 
1579
        c.Assert(err, jc.ErrorIsNil)
 
1580
        c.Assert(zones, gc.HasLen, 2)
 
1581
        c.Assert(zones[0].Name(), gc.Equals, resultZones[0].Name)
 
1582
        c.Assert(zones[1].Name(), gc.Equals, resultZones[1].Name)
 
1583
        c.Assert(zones[0].Available(), jc.IsTrue)
 
1584
        c.Assert(zones[1].Available(), jc.IsFalse)
 
1585
}
 
1586
 
 
1587
type mockAvailabilityZoneAllocations struct {
 
1588
        group  []instance.Id // input param
 
1589
        result []common.AvailabilityZoneInstances
 
1590
        err    error
 
1591
}
 
1592
 
 
1593
func (t *mockAvailabilityZoneAllocations) AvailabilityZoneAllocations(
 
1594
        e common.ZonedEnviron, group []instance.Id,
 
1595
) ([]common.AvailabilityZoneInstances, error) {
 
1596
        t.group = group
 
1597
        return t.result, t.err
 
1598
}
 
1599
 
 
1600
func (t *localServerSuite) TestStartInstanceDistributionParams(c *gc.C) {
 
1601
        err := bootstrapEnv(c, t.env)
 
1602
        c.Assert(err, jc.ErrorIsNil)
 
1603
 
 
1604
        var mock mockAvailabilityZoneAllocations
 
1605
        t.PatchValue(openstack.AvailabilityZoneAllocations, mock.AvailabilityZoneAllocations)
 
1606
 
 
1607
        // no distribution group specified
 
1608
        testing.AssertStartInstance(c, t.env, t.ControllerUUID, "1")
 
1609
        c.Assert(mock.group, gc.HasLen, 0)
 
1610
 
 
1611
        // distribution group specified: ensure it's passed through to AvailabilityZone.
 
1612
        expectedInstances := []instance.Id{"i-0", "i-1"}
 
1613
        params := environs.StartInstanceParams{
 
1614
                ControllerUUID: t.ControllerUUID,
 
1615
                DistributionGroup: func() ([]instance.Id, error) {
 
1616
                        return expectedInstances, nil
 
1617
                },
 
1618
        }
 
1619
        _, err = testing.StartInstanceWithParams(t.env, "1", params)
 
1620
        c.Assert(err, jc.ErrorIsNil)
 
1621
        c.Assert(mock.group, gc.DeepEquals, expectedInstances)
 
1622
}
 
1623
 
 
1624
func (t *localServerSuite) TestStartInstanceDistributionErrors(c *gc.C) {
 
1625
        err := bootstrapEnv(c, t.env)
 
1626
        c.Assert(err, jc.ErrorIsNil)
 
1627
 
 
1628
        mock := mockAvailabilityZoneAllocations{
 
1629
                err: fmt.Errorf("AvailabilityZoneAllocations failed"),
 
1630
        }
 
1631
        t.PatchValue(openstack.AvailabilityZoneAllocations, mock.AvailabilityZoneAllocations)
 
1632
        _, _, _, err = testing.StartInstance(t.env, t.ControllerUUID, "1")
 
1633
        c.Assert(jujuerrors.Cause(err), gc.Equals, mock.err)
 
1634
 
 
1635
        mock.err = nil
 
1636
        dgErr := fmt.Errorf("DistributionGroup failed")
 
1637
        params := environs.StartInstanceParams{
 
1638
                ControllerUUID: t.ControllerUUID,
 
1639
                DistributionGroup: func() ([]instance.Id, error) {
 
1640
                        return nil, dgErr
 
1641
                },
 
1642
        }
 
1643
        _, err = testing.StartInstanceWithParams(t.env, "1", params)
 
1644
        c.Assert(jujuerrors.Cause(err), gc.Equals, dgErr)
 
1645
}
 
1646
 
 
1647
func (t *localServerSuite) TestStartInstanceDistribution(c *gc.C) {
 
1648
        err := bootstrapEnv(c, t.env)
 
1649
        c.Assert(err, jc.ErrorIsNil)
 
1650
 
 
1651
        // test-available is the only available AZ, so AvailabilityZoneAllocations
 
1652
        // is guaranteed to return that.
 
1653
        inst, _ := testing.AssertStartInstance(c, t.env, t.ControllerUUID, "1")
 
1654
        c.Assert(openstack.InstanceServerDetail(inst).AvailabilityZone, gc.Equals, "test-available")
 
1655
}
 
1656
 
 
1657
func (t *localServerSuite) TestStartInstancePicksValidZoneForHost(c *gc.C) {
 
1658
        coretesting.SkipIfPPC64EL(c, "lp:1425242")
 
1659
 
 
1660
        t.srv.Nova.SetAvailabilityZones(
 
1661
                // bootstrap node will be on az1.
 
1662
                nova.AvailabilityZone{
 
1663
                        Name: "az1",
 
1664
                        State: nova.AvailabilityZoneState{
 
1665
                                Available: true,
 
1666
                        },
 
1667
                },
 
1668
                // az2 will be made to return an error.
 
1669
                nova.AvailabilityZone{
 
1670
                        Name: "az2",
 
1671
                        State: nova.AvailabilityZoneState{
 
1672
                                Available: true,
 
1673
                        },
 
1674
                },
 
1675
                // az3 will be valid to host an instance.
 
1676
                nova.AvailabilityZone{
 
1677
                        Name: "az3",
 
1678
                        State: nova.AvailabilityZoneState{
 
1679
                                Available: true,
 
1680
                        },
 
1681
                },
 
1682
        )
 
1683
 
 
1684
        err := bootstrapEnv(c, t.env)
 
1685
        c.Assert(err, jc.ErrorIsNil)
 
1686
 
 
1687
        cleanup := t.srv.Nova.RegisterControlPoint(
 
1688
                "addServer",
 
1689
                func(sc hook.ServiceControl, args ...interface{}) error {
 
1690
                        serverDetail := args[0].(*nova.ServerDetail)
 
1691
                        if serverDetail.AvailabilityZone == "az2" {
 
1692
                                return fmt.Errorf("No valid host was found")
 
1693
                        }
 
1694
                        return nil
 
1695
                },
 
1696
        )
 
1697
        defer cleanup()
 
1698
        inst, _ := testing.AssertStartInstance(c, t.env, t.ControllerUUID, "1")
 
1699
        c.Assert(openstack.InstanceServerDetail(inst).AvailabilityZone, gc.Equals, "az3")
 
1700
}
 
1701
 
 
1702
func (t *localServerSuite) TestStartInstanceWithUnknownAZError(c *gc.C) {
 
1703
        coretesting.SkipIfPPC64EL(c, "lp:1425242")
 
1704
 
 
1705
        t.srv.Nova.SetAvailabilityZones(
 
1706
                // bootstrap node will be on az1.
 
1707
                nova.AvailabilityZone{
 
1708
                        Name: "az1",
 
1709
                        State: nova.AvailabilityZoneState{
 
1710
                                Available: true,
 
1711
                        },
 
1712
                },
 
1713
                // az2 will be made to return an unknown error.
 
1714
                nova.AvailabilityZone{
 
1715
                        Name: "az2",
 
1716
                        State: nova.AvailabilityZoneState{
 
1717
                                Available: true,
 
1718
                        },
 
1719
                },
 
1720
        )
 
1721
 
 
1722
        err := bootstrapEnv(c, t.env)
 
1723
        c.Assert(err, jc.ErrorIsNil)
 
1724
 
 
1725
        cleanup := t.srv.Nova.RegisterControlPoint(
 
1726
                "addServer",
 
1727
                func(sc hook.ServiceControl, args ...interface{}) error {
 
1728
                        serverDetail := args[0].(*nova.ServerDetail)
 
1729
                        if serverDetail.AvailabilityZone == "az2" {
 
1730
                                return fmt.Errorf("Some unknown error")
 
1731
                        }
 
1732
                        return nil
 
1733
                },
 
1734
        )
 
1735
        defer cleanup()
 
1736
        _, _, _, err = testing.StartInstance(t.env, t.ControllerUUID, "1")
 
1737
        c.Assert(err, gc.ErrorMatches, "(?s).*Some unknown error.*")
 
1738
}
 
1739
 
 
1740
func (t *localServerSuite) TestStartInstanceDistributionAZNotImplemented(c *gc.C) {
 
1741
        err := bootstrapEnv(c, t.env)
 
1742
        c.Assert(err, jc.ErrorIsNil)
 
1743
 
 
1744
        mock := mockAvailabilityZoneAllocations{
 
1745
                err: jujuerrors.NotImplementedf("availability zones"),
 
1746
        }
 
1747
        t.PatchValue(openstack.AvailabilityZoneAllocations, mock.AvailabilityZoneAllocations)
 
1748
 
 
1749
        // Instance will be created without an availability zone specified.
 
1750
        inst, _ := testing.AssertStartInstance(c, t.env, t.ControllerUUID, "1")
 
1751
        c.Assert(openstack.InstanceServerDetail(inst).AvailabilityZone, gc.Equals, "")
 
1752
}
 
1753
 
 
1754
func (t *localServerSuite) TestInstanceTags(c *gc.C) {
 
1755
        err := bootstrapEnv(c, t.env)
 
1756
        c.Assert(err, jc.ErrorIsNil)
 
1757
 
 
1758
        instances, err := t.env.AllInstances()
 
1759
        c.Assert(err, jc.ErrorIsNil)
 
1760
        c.Assert(instances, gc.HasLen, 1)
 
1761
 
 
1762
        c.Assert(
 
1763
                openstack.InstanceServerDetail(instances[0]).Metadata,
 
1764
                jc.DeepEquals,
 
1765
                map[string]string{
 
1766
                        "juju-model-uuid":      coretesting.ModelTag.Id(),
 
1767
                        "juju-controller-uuid": coretesting.ModelTag.Id(),
 
1768
                        "juju-is-controller":   "true",
 
1769
                },
 
1770
        )
 
1771
}
 
1772
 
 
1773
func (t *localServerSuite) TestTagInstance(c *gc.C) {
 
1774
        err := bootstrapEnv(c, t.env)
 
1775
        c.Assert(err, jc.ErrorIsNil)
 
1776
 
 
1777
        assertMetadata := func(extraKey, extraValue string) {
 
1778
                // Refresh instance
 
1779
                instances, err := t.env.AllInstances()
 
1780
                c.Assert(err, jc.ErrorIsNil)
 
1781
                c.Assert(instances, gc.HasLen, 1)
 
1782
                c.Assert(
 
1783
                        openstack.InstanceServerDetail(instances[0]).Metadata,
 
1784
                        jc.DeepEquals,
 
1785
                        map[string]string{
 
1786
                                "juju-model-uuid":      coretesting.ModelTag.Id(),
 
1787
                                "juju-controller-uuid": coretesting.ModelTag.Id(),
 
1788
                                "juju-is-controller":   "true",
 
1789
                                extraKey:               extraValue,
 
1790
                        },
 
1791
                )
 
1792
        }
 
1793
 
 
1794
        instances, err := t.env.AllInstances()
 
1795
        c.Assert(err, jc.ErrorIsNil)
 
1796
        c.Assert(instances, gc.HasLen, 1)
 
1797
 
 
1798
        extraKey := "extra-k"
 
1799
        extraValue := "extra-v"
 
1800
        err = t.env.(environs.InstanceTagger).TagInstance(
 
1801
                instances[0].Id(), map[string]string{extraKey: extraValue},
 
1802
        )
 
1803
        c.Assert(err, jc.ErrorIsNil)
 
1804
        assertMetadata(extraKey, extraValue)
 
1805
 
 
1806
        // Ensure that a second call updates existing tags.
 
1807
        extraValue = "extra-v2"
 
1808
        err = t.env.(environs.InstanceTagger).TagInstance(
 
1809
                instances[0].Id(), map[string]string{extraKey: extraValue},
 
1810
        )
 
1811
        c.Assert(err, jc.ErrorIsNil)
 
1812
        assertMetadata(extraKey, extraValue)
 
1813
}
 
1814
 
 
1815
func prepareParams(attrs map[string]interface{}, cred *identity.Credentials) bootstrap.PrepareParams {
 
1816
        return bootstrap.PrepareParams{
 
1817
                ControllerConfig: coretesting.FakeControllerConfig(),
 
1818
                ModelConfig:      attrs,
 
1819
                ControllerName:   attrs["name"].(string),
 
1820
                Cloud:            makeCloudSpec(cred),
 
1821
                AdminSecret:      testing.AdminSecret,
 
1822
        }
 
1823
}
 
1824
 
 
1825
func makeCloudSpec(cred *identity.Credentials) environs.CloudSpec {
 
1826
        credential := makeCredential(cred)
 
1827
        return environs.CloudSpec{
 
1828
                Type:       "openstack",
 
1829
                Name:       "openstack",
 
1830
                Endpoint:   cred.URL,
 
1831
                Region:     cred.Region,
 
1832
                Credential: &credential,
 
1833
        }
 
1834
}
 
1835
 
 
1836
func makeCredential(cred *identity.Credentials) cloud.Credential {
 
1837
        return cloud.NewCredential(
 
1838
                cloud.UserPassAuthType,
 
1839
                map[string]string{
 
1840
                        "username":    cred.User,
 
1841
                        "password":    cred.Secrets,
 
1842
                        "tenant-name": cred.TenantName,
 
1843
                },
 
1844
        )
 
1845
}
 
1846
 
 
1847
// noSwiftSuite contains tests that run against an OpenStack service double
 
1848
// that lacks Swift.
 
1849
type noSwiftSuite struct {
 
1850
        coretesting.BaseSuite
 
1851
        cred *identity.Credentials
 
1852
        srv  localServer
 
1853
        env  environs.Environ
 
1854
}
 
1855
 
 
1856
func (s *noSwiftSuite) SetUpSuite(c *gc.C) {
 
1857
        s.BaseSuite.SetUpSuite(c)
 
1858
        restoreFinishBootstrap := envtesting.DisableFinishBootstrap()
 
1859
        s.AddCleanup(func(*gc.C) { restoreFinishBootstrap() })
 
1860
 
 
1861
        s.PatchValue(&imagemetadata.SimplestreamsImagesPublicKey, sstesting.SignedMetadataPublicKey)
 
1862
        s.PatchValue(&keys.JujuPublicKey, sstesting.SignedMetadataPublicKey)
 
1863
}
 
1864
 
 
1865
func (s *noSwiftSuite) SetUpTest(c *gc.C) {
 
1866
        s.BaseSuite.SetUpTest(c)
 
1867
        s.cred = &identity.Credentials{
 
1868
                User:       "fred",
 
1869
                Secrets:    "secret",
 
1870
                Region:     "some-region",
 
1871
                TenantName: "some tenant",
 
1872
        }
 
1873
        s.srv.start(c, s.cred, newNovaOnlyOpenstackService)
 
1874
 
 
1875
        attrs := coretesting.FakeConfig().Merge(coretesting.Attrs{
 
1876
                "name":            "sample-no-swift",
 
1877
                "type":            "openstack",
 
1878
                "auth-mode":       "userpass",
 
1879
                "agent-version":   coretesting.FakeVersionNumber.String(),
 
1880
                "authorized-keys": "fakekey",
 
1881
        })
 
1882
        s.PatchValue(&jujuversion.Current, coretesting.FakeVersionNumber)
 
1883
        // Serve fake tools and image metadata using "filestorage",
 
1884
        // rather than Swift as the rest of the tests do.
 
1885
        storageDir := c.MkDir()
 
1886
        imagesDir := filepath.Join(storageDir, "images")
 
1887
        toolsDir := filepath.Join(storageDir, "tools")
 
1888
        for _, dir := range []string{imagesDir, toolsDir} {
 
1889
                err := os.MkdirAll(dir, 0755)
 
1890
                c.Assert(err, jc.ErrorIsNil)
 
1891
        }
 
1892
        toolsStorage, err := filestorage.NewFileStorageWriter(storageDir)
 
1893
        c.Assert(err, jc.ErrorIsNil)
 
1894
        envtesting.UploadFakeTools(c, toolsStorage, "released", "released")
 
1895
        s.PatchValue(&tools.DefaultBaseURL, storageDir)
 
1896
        imageStorage, err := filestorage.NewFileStorageWriter(imagesDir)
 
1897
        openstack.UseTestImageData(imageStorage, s.cred)
 
1898
        imagetesting.PatchOfficialDataSources(&s.CleanupSuite, storageDir)
 
1899
 
 
1900
        env, err := bootstrap.Prepare(
 
1901
                envtesting.BootstrapContext(c),
 
1902
                jujuclienttesting.NewMemStore(),
 
1903
                prepareParams(attrs, s.cred),
 
1904
        )
 
1905
        c.Assert(err, jc.ErrorIsNil)
 
1906
        s.env = env
 
1907
}
 
1908
 
 
1909
func (s *noSwiftSuite) TearDownTest(c *gc.C) {
 
1910
        s.srv.stop()
 
1911
        s.BaseSuite.TearDownTest(c)
 
1912
}
 
1913
 
 
1914
func (s *noSwiftSuite) TestBootstrap(c *gc.C) {
 
1915
        err := bootstrap.Bootstrap(envtesting.BootstrapContext(c), s.env, bootstrap.BootstrapParams{
 
1916
                ControllerConfig: coretesting.FakeControllerConfig(),
 
1917
                AdminSecret:      testing.AdminSecret,
 
1918
                CAPrivateKey:     coretesting.CAKey,
 
1919
        })
 
1920
        c.Assert(err, jc.ErrorIsNil)
 
1921
}
 
1922
 
 
1923
func newFullOpenstackService(mux *http.ServeMux, cred *identity.Credentials, auth identity.AuthMode) *novaservice.Nova {
 
1924
        service := openstackservice.New(cred, auth)
 
1925
        service.SetupHTTP(mux)
 
1926
        return service.Nova
 
1927
}
 
1928
 
 
1929
func newNovaOnlyOpenstackService(mux *http.ServeMux, cred *identity.Credentials, auth identity.AuthMode) *novaservice.Nova {
 
1930
        var identityService, fallbackService identityservice.IdentityService
 
1931
        if auth == identity.AuthKeyPair {
 
1932
                identityService = identityservice.NewKeyPair()
 
1933
        } else {
 
1934
                identityService = identityservice.NewUserPass()
 
1935
                fallbackService = identityservice.NewV3UserPass()
 
1936
        }
 
1937
        userInfo := identityService.AddUser(cred.User, cred.Secrets, cred.TenantName)
 
1938
        if cred.TenantName == "" {
 
1939
                panic("Openstack service double requires a tenant to be specified.")
 
1940
        }
 
1941
        novaService := novaservice.New(cred.URL, "v2", userInfo.TenantId, cred.Region, identityService, fallbackService)
 
1942
        identityService.SetupHTTP(mux)
 
1943
        novaService.SetupHTTP(mux)
 
1944
        return novaService
 
1945
}
 
1946
 
 
1947
func bootstrapEnv(c *gc.C, env environs.Environ) error {
 
1948
        return bootstrap.Bootstrap(envtesting.BootstrapContext(c), env, bootstrap.BootstrapParams{
 
1949
                ControllerConfig: coretesting.FakeControllerConfig(),
 
1950
                AdminSecret:      testing.AdminSecret,
 
1951
                CAPrivateKey:     coretesting.CAKey,
 
1952
        })
 
1953
}