1
// Copyright 2016 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
4
package discoverspaces_test
10
jc "github.com/juju/testing/checkers"
11
gc "gopkg.in/check.v1"
12
"gopkg.in/juju/names.v2"
14
"github.com/juju/juju/api"
15
apidiscoverspaces "github.com/juju/juju/api/discoverspaces"
16
"github.com/juju/juju/apiserver/params"
17
"github.com/juju/juju/environs"
18
"github.com/juju/juju/juju/testing"
19
"github.com/juju/juju/network"
20
"github.com/juju/juju/provider/dummy"
21
"github.com/juju/juju/state"
22
"github.com/juju/juju/state/stateenvirons"
23
coretesting "github.com/juju/juju/testing"
24
"github.com/juju/juju/worker"
25
"github.com/juju/juju/worker/discoverspaces"
26
"github.com/juju/juju/worker/gate"
27
"github.com/juju/juju/worker/workertest"
30
type WorkerSuite struct {
31
// TODO(fwereade): we *really* should not be using
32
// JujuConnSuite in new code.
35
APIConnection api.Connection
38
numCreateSpaceCalls uint32
39
numAddSubnetsCalls uint32
42
type checkingFacade struct {
45
createSpacesCallback func()
46
addSubnetsCallback func()
49
func (cf *checkingFacade) CreateSpaces(args params.CreateSpacesParams) (results params.ErrorResults, err error) {
50
if cf.createSpacesCallback != nil {
51
cf.createSpacesCallback()
53
return cf.API.CreateSpaces(args)
56
func (cf *checkingFacade) AddSubnets(args params.AddSubnetsParams) (params.ErrorResults, error) {
57
if cf.addSubnetsCallback != nil {
58
cf.addSubnetsCallback()
60
return cf.API.AddSubnets(args)
63
var _ = gc.Suite(&WorkerSuite{})
65
func (s *WorkerSuite) SetUpTest(c *gc.C) {
66
s.JujuConnSuite.SetUpTest(c)
68
// Unbreak dummy provider methods.
69
s.AssertConfigParameterUpdated(c, "broken", "")
71
s.APIConnection, _ = s.OpenAPIAsNewMachine(c, state.JobManageModel)
73
realAPI := s.APIConnection.DiscoverSpaces()
74
s.API = &checkingFacade{
76
createSpacesCallback: func() {
77
atomic.AddUint32(&s.numCreateSpaceCalls, 1)
79
addSubnetsCallback: func() {
80
atomic.AddUint32(&s.numAddSubnetsCalls, 1)
85
func (s *WorkerSuite) TearDownTest(c *gc.C) {
86
if s.APIConnection != nil {
87
c.Check(s.APIConnection.Close(), jc.ErrorIsNil)
89
s.JujuConnSuite.TearDownTest(c)
92
func (s *WorkerSuite) TestSupportsSpaceDiscoveryBroken(c *gc.C) {
93
s.AssertConfigParameterUpdated(c, "broken", "SupportsSpaceDiscovery")
95
worker, lock := s.startWorker(c)
96
err := workertest.CheckKilled(c, worker)
97
c.Assert(err, gc.ErrorMatches, "dummy.SupportsSpaceDiscovery is broken")
100
case <-time.After(coretesting.ShortWait):
101
case <-lock.Unlocked():
102
c.Fatalf("gate unlocked despite worker failure")
106
func (s *WorkerSuite) TestSpacesBroken(c *gc.C) {
107
dummy.SetSupportsSpaceDiscovery(true)
108
s.AssertConfigParameterUpdated(c, "broken", "Spaces")
110
worker, lock := s.startWorker(c)
111
err := workertest.CheckKilled(c, worker)
112
c.Assert(err, gc.ErrorMatches, "dummy.Spaces is broken")
115
case <-time.After(coretesting.ShortWait):
116
case <-lock.Unlocked():
117
c.Fatalf("gate unlocked despite worker failure")
121
func (s *WorkerSuite) TestWorkerSupportsNetworkingFalse(c *gc.C) {
122
// We set SupportsSpaceDiscovery to true so that spaces *would* be
123
// discovered if networking was supported. So we know that if they're
124
// discovered it must be because networking is not supported.
125
dummy.SetSupportsSpaceDiscovery(true)
127
// TODO(fwereade): monkey-patching remote packages is even worse
128
// than monkey-patching local packages, please don't do it.
129
noNetworking := func(environs.Environ) (environs.NetworkingEnviron, bool) {
132
s.PatchValue(&environs.SupportsNetworking, noNetworking)
134
s.unlockCheck(c, s.assertDiscoveredNoSpaces)
137
func (s *WorkerSuite) TestWorkerSupportsSpaceDiscoveryFalse(c *gc.C) {
138
s.unlockCheck(c, s.assertDiscoveredNoSpaces)
141
func (s *WorkerSuite) TestWorkerDiscoversSpaces(c *gc.C) {
142
dummy.SetSupportsSpaceDiscovery(true)
143
s.unlockCheck(c, func(*gc.C) {
144
s.assertDiscoveredSpaces(c)
145
s.assertNumCalls(c, 1, 1)
149
func (s *WorkerSuite) TestWorkerIdempotent(c *gc.C) {
150
dummy.SetSupportsSpaceDiscovery(true)
151
s.unlockCheck(c, s.assertDiscoveredSpaces)
152
s.unlockCheck(c, func(*gc.C) {
153
s.assertDiscoveredSpaces(c)
154
s.assertNumCalls(c, 2, 2)
158
func (s *WorkerSuite) TestWorkerIgnoresExistingSpacesAndSubnets(c *gc.C) {
159
dummy.SetSupportsSpaceDiscovery(true)
160
spaceTag := names.NewSpaceTag("foo")
161
args := params.CreateSpacesParams{
162
Spaces: []params.CreateSpaceParams{{
164
SpaceTag: spaceTag.String(),
167
result, err := s.API.CreateSpaces(args)
168
c.Assert(err, jc.ErrorIsNil)
169
c.Assert(result.Results, gc.HasLen, 1)
170
c.Assert(result.Results[0].Error, gc.IsNil)
172
subnetArgs := params.AddSubnetsParams{
173
Subnets: []params.AddSubnetParams{{
174
SubnetProviderId: "1",
175
SpaceTag: spaceTag.String(),
176
Zones: []string{"zone1"},
178
subnetResult, err := s.API.AddSubnets(subnetArgs)
179
c.Assert(err, jc.ErrorIsNil)
180
c.Assert(subnetResult.Results, gc.HasLen, 1)
181
c.Assert(subnetResult.Results[0].Error, gc.IsNil)
183
s.unlockCheck(c, func(c *gc.C) {
184
spaces, err := s.State.AllSpaces()
185
c.Assert(err, jc.ErrorIsNil)
186
c.Assert(spaces, gc.HasLen, 5)
190
func (s *WorkerSuite) startWorker(c *gc.C) (worker.Worker, gate.Lock) {
191
// create fresh environ to see any injected broken-ness
192
environ, err := stateenvirons.GetNewEnvironFunc(environs.New)(s.State)
193
c.Assert(err, jc.ErrorIsNil)
195
lock := gate.NewLock()
196
worker, err := discoverspaces.NewWorker(discoverspaces.Config{
199
NewName: network.ConvertSpaceName,
202
c.Assert(err, jc.ErrorIsNil)
206
func (s *WorkerSuite) unlockCheck(c *gc.C, check func(c *gc.C)) {
207
worker, lock := s.startWorker(c)
208
defer workertest.CleanKill(c, worker)
210
case <-time.After(coretesting.LongWait):
211
c.Fatalf("discovery never completed")
212
case <-lock.Unlocked():
215
workertest.CheckAlive(c, worker)
218
func (s *WorkerSuite) assertDiscoveredNoSpaces(c *gc.C) {
219
spaces, err := s.State.AllSpaces()
220
c.Assert(err, jc.ErrorIsNil)
221
c.Check(spaces, gc.HasLen, 0)
224
func (s *WorkerSuite) assertDiscoveredSpaces(c *gc.C) {
225
spaces, err := s.State.AllSpaces()
226
c.Assert(err, jc.ErrorIsNil)
227
c.Assert(spaces, gc.HasLen, 4)
228
expectedSpaces := []network.SpaceInfo{{
230
ProviderId: network.Id("0"),
231
Subnets: []network.SubnetInfo{{
232
ProviderId: network.Id("1"),
233
CIDR: "192.168.1.0/24",
234
AvailabilityZones: []string{"zone1"},
236
ProviderId: network.Id("2"),
237
CIDR: "192.168.2.0/24",
238
AvailabilityZones: []string{"zone1"},
240
Name: "another-foo-99",
241
ProviderId: network.Id("1"),
242
Subnets: []network.SubnetInfo{{
243
ProviderId: network.Id("3"),
244
CIDR: "192.168.3.0/24",
245
AvailabilityZones: []string{"zone1"},
248
ProviderId: network.Id("2"),
249
Subnets: []network.SubnetInfo{{
250
ProviderId: network.Id("4"),
251
CIDR: "192.168.4.0/24",
252
AvailabilityZones: []string{"zone1"},
255
ProviderId: network.Id("3"),
256
Subnets: []network.SubnetInfo{{
257
ProviderId: network.Id("5"),
258
CIDR: "192.168.5.0/24",
259
AvailabilityZones: []string{"zone1"},
261
expectedSpaceMap := make(map[string]network.SpaceInfo)
262
for _, space := range expectedSpaces {
263
expectedSpaceMap[space.Name] = space
265
for _, space := range spaces {
266
expected, ok := expectedSpaceMap[space.Name()]
267
if !c.Check(ok, jc.IsTrue) {
270
c.Check(space.ProviderId(), gc.Equals, expected.ProviderId)
271
subnets, err := space.Subnets()
272
if !c.Check(err, jc.ErrorIsNil) {
275
if !c.Check(len(subnets), gc.Equals, len(expected.Subnets)) {
278
for i, subnet := range subnets {
279
expectedSubnet := expected.Subnets[i]
280
c.Check(subnet.ProviderId(), gc.Equals, expectedSubnet.ProviderId)
281
c.Check([]string{subnet.AvailabilityZone()}, jc.DeepEquals, expectedSubnet.AvailabilityZones)
282
c.Check(subnet.CIDR(), gc.Equals, expectedSubnet.CIDR)
287
func (s *WorkerSuite) assertNumCalls(c *gc.C, expectedNumCreateSpaceCalls, expectedNumAddSubnetsCalls int) {
288
c.Check(atomic.LoadUint32(&s.numCreateSpaceCalls), gc.Equals, uint32(expectedNumCreateSpaceCalls))
289
c.Check(atomic.LoadUint32(&s.numAddSubnetsCalls), gc.Equals, uint32(expectedNumAddSubnetsCalls))