1
// Copyright 2015 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
13
jc "github.com/juju/testing/checkers"
14
gc "gopkg.in/check.v1"
15
goyaml "gopkg.in/yaml.v2"
17
"github.com/juju/juju/apiserver/common"
18
"github.com/juju/juju/apiserver/params"
19
"github.com/juju/juju/cmd/modelcmd"
20
"github.com/juju/juju/constraints"
21
"github.com/juju/juju/instance"
22
"github.com/juju/juju/juju/testing"
23
"github.com/juju/juju/state"
24
coretesting "github.com/juju/juju/testing"
25
"github.com/juju/juju/testing/factory"
28
type EnableHASuite struct {
29
// TODO (cherylj) change this back to a FakeJujuXDGDataHomeSuite to
30
// remove the mongo dependency once ensure-availability is
31
// moved under a supercommand again.
36
// invalidNumServers is a number of controllers that would
37
// never be generated by the enable-ha command.
38
const invalidNumServers = -2
40
func (s *EnableHASuite) SetUpTest(c *gc.C) {
41
s.JujuConnSuite.SetUpTest(c)
43
// Initialize numControllers to an invalid number to validate
44
// that enable-ha doesn't call into the API when its
46
s.fake = &fakeHAClient{numControllers: invalidNumServers}
49
type fakeHAClient struct {
51
cons constraints.Value
54
result params.ControllersChanges
57
func (f *fakeHAClient) Close() error {
61
func (f *fakeHAClient) EnableHA(numControllers int, cons constraints.Value, placement []string) (params.ControllersChanges, error) {
63
f.numControllers = numControllers
65
f.placement = placement
68
return f.result, f.err
71
if numControllers == 1 {
75
// In the real HAClient, specifying a numControllers value of 0
76
// indicates that the default value (3) should be used
77
if numControllers == 0 {
81
f.result.Maintained = append(f.result.Maintained, "machine-0")
83
for _, p := range placement {
84
m, err := instance.ParsePlacement(p)
85
if err == nil && m.Scope == instance.MachineScope {
86
f.result.Converted = append(f.result.Converted, "machine-"+m.Directive)
90
// We may need to pretend that we added some machines.
91
for i := len(f.result.Converted) + 1; i < numControllers; i++ {
92
f.result.Added = append(f.result.Added, fmt.Sprintf("machine-%d", i))
98
var _ = gc.Suite(&EnableHASuite{})
100
func (s *EnableHASuite) runEnableHA(c *gc.C, args ...string) (*cmd.Context, error) {
101
command := &enableHACommand{newHAClientFunc: func() (MakeHAClient, error) { return s.fake, nil }}
102
return coretesting.RunCommand(c, modelcmd.Wrap(command), args...)
105
func (s *EnableHASuite) TestEnableHA(c *gc.C) {
106
ctx, err := s.runEnableHA(c, "-n", "1")
107
c.Assert(err, jc.ErrorIsNil)
108
c.Assert(coretesting.Stdout(ctx), gc.Equals, "")
110
c.Assert(s.fake.numControllers, gc.Equals, 1)
111
c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty)
112
c.Assert(len(s.fake.placement), gc.Equals, 0)
115
func (s *EnableHASuite) TestBlockEnableHA(c *gc.C) {
116
s.fake.err = common.OperationBlockedError("TestBlockEnableHA")
117
_, err := s.runEnableHA(c, "-n", "1")
118
c.Assert(err, gc.ErrorMatches, cmd.ErrSilent.Error())
121
stripped := strings.Replace(c.GetTestLog(), "\n", "", -1)
122
c.Check(stripped, gc.Matches, ".*TestBlockEnableHA.*")
125
func (s *EnableHASuite) TestEnableHAFormatYaml(c *gc.C) {
126
expected := map[string][]string{
131
ctx, err := s.runEnableHA(c, "-n", "3", "--format", "yaml")
132
c.Assert(err, jc.ErrorIsNil)
134
c.Assert(s.fake.numControllers, gc.Equals, 3)
135
c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty)
136
c.Assert(len(s.fake.placement), gc.Equals, 0)
138
var result map[string][]string
139
err = goyaml.Unmarshal(ctx.Stdout.(*bytes.Buffer).Bytes(), &result)
140
c.Assert(err, jc.ErrorIsNil)
141
c.Assert(result, gc.DeepEquals, expected)
144
func (s *EnableHASuite) TestEnableHAFormatJson(c *gc.C) {
145
expected := map[string][]string{
150
ctx, err := s.runEnableHA(c, "-n", "3", "--format", "json")
151
c.Assert(err, jc.ErrorIsNil)
153
c.Assert(s.fake.numControllers, gc.Equals, 3)
154
c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty)
155
c.Assert(len(s.fake.placement), gc.Equals, 0)
157
var result map[string][]string
158
err = json.Unmarshal(ctx.Stdout.(*bytes.Buffer).Bytes(), &result)
159
c.Assert(err, jc.ErrorIsNil)
160
c.Assert(result, gc.DeepEquals, expected)
163
func (s *EnableHASuite) TestEnableHAWithFive(c *gc.C) {
164
// Also test with -n 5 to validate numbers other than 1 and 3
165
ctx, err := s.runEnableHA(c, "-n", "5")
166
c.Assert(err, jc.ErrorIsNil)
167
c.Assert(coretesting.Stdout(ctx), gc.Equals,
168
"maintaining machines: 0\n"+
169
"adding machines: 1, 2, 3, 4\n\n")
171
c.Assert(s.fake.numControllers, gc.Equals, 5)
172
c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty)
173
c.Assert(len(s.fake.placement), gc.Equals, 0)
176
func (s *EnableHASuite) TestEnableHAWithConstraints(c *gc.C) {
177
ctx, err := s.runEnableHA(c, "--constraints", "mem=4G", "-n", "3")
178
c.Assert(err, jc.ErrorIsNil)
179
c.Assert(coretesting.Stdout(ctx), gc.Equals,
180
"maintaining machines: 0\n"+
181
"adding machines: 1, 2\n\n")
183
c.Assert(s.fake.numControllers, gc.Equals, 3)
184
expectedCons := constraints.MustParse("mem=4G")
185
c.Assert(s.fake.cons, gc.DeepEquals, expectedCons)
186
c.Assert(len(s.fake.placement), gc.Equals, 0)
189
func (s *EnableHASuite) TestEnableHAWithPlacement(c *gc.C) {
190
ctx, err := s.runEnableHA(c, "--to", "valid", "-n", "3")
191
c.Assert(err, jc.ErrorIsNil)
192
c.Assert(coretesting.Stdout(ctx), gc.Equals,
193
"maintaining machines: 0\n"+
194
"adding machines: 1, 2\n\n")
196
c.Assert(s.fake.numControllers, gc.Equals, 3)
197
c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty)
198
expectedPlacement := []string{"valid"}
199
c.Assert(s.fake.placement, gc.DeepEquals, expectedPlacement)
202
func (s *EnableHASuite) TestEnableHAErrors(c *gc.C) {
203
for _, n := range []int{-1, 2} {
204
_, err := s.runEnableHA(c, "-n", fmt.Sprint(n))
205
c.Assert(err, gc.ErrorMatches, "must specify a number of controllers odd and non-negative")
208
// Verify that enable-ha didn't call into the API
209
c.Assert(s.fake.numControllers, gc.Equals, invalidNumServers)
212
func (s *EnableHASuite) TestEnableHAAllows0(c *gc.C) {
213
// If the number of controllers is specified as "0", the API will
214
// then use the default number of 3.
215
ctx, err := s.runEnableHA(c, "-n", "0")
216
c.Assert(err, jc.ErrorIsNil)
217
c.Assert(coretesting.Stdout(ctx), gc.Equals,
218
"maintaining machines: 0\n"+
219
"adding machines: 1, 2\n\n")
221
c.Assert(s.fake.numControllers, gc.Equals, 0)
222
c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty)
223
c.Assert(len(s.fake.placement), gc.Equals, 0)
226
func (s *EnableHASuite) TestEnableHADefaultsTo0(c *gc.C) {
227
// If the number of controllers is not specified, we pass in 0 to the
228
// API. The API will then use the default number of 3.
229
ctx, err := s.runEnableHA(c)
230
c.Assert(err, jc.ErrorIsNil)
231
c.Assert(coretesting.Stdout(ctx), gc.Equals,
232
"maintaining machines: 0\n"+
233
"adding machines: 1, 2\n\n")
235
c.Assert(s.fake.numControllers, gc.Equals, 0)
236
c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty)
237
c.Assert(len(s.fake.placement), gc.Equals, 0)
240
func (s *EnableHASuite) TestEnableHAEndToEnd(c *gc.C) {
241
s.Factory.MakeMachine(c, &factory.MachineParams{
242
Jobs: []state.MachineJob{state.JobManageModel},
244
ctx, err := coretesting.RunCommand(c, newEnableHACommand(), "-n", "3")
245
c.Assert(err, jc.ErrorIsNil)
247
// Machine 0 is demoted because it hasn't reported its presence
248
c.Assert(coretesting.Stdout(ctx), gc.Equals,
249
"adding machines: 1, 2, 3\n"+
250
"demoting machines: 0\n\n")
253
func (s *EnableHASuite) TestEnableHAToExisting(c *gc.C) {
254
ctx, err := s.runEnableHA(c, "--to", "1,2")
255
c.Assert(err, jc.ErrorIsNil)
256
c.Check(coretesting.Stdout(ctx), gc.Equals, `
257
maintaining machines: 0
258
converting machines: 1, 2
262
c.Check(s.fake.numControllers, gc.Equals, 0)
263
c.Check(&s.fake.cons, jc.Satisfies, constraints.IsEmpty)
264
c.Check(len(s.fake.placement), gc.Equals, 2)
267
func (s *EnableHASuite) TestEnableHADisallowsSeries(c *gc.C) {
268
// We don't allow --series as an argument. This test ensures it is not
269
// inadvertantly added back.
270
ctx, err := s.runEnableHA(c, "-n", "0", "--series", "xenian")
271
c.Assert(err, gc.ErrorMatches, "flag provided but not defined: --series")
272
c.Assert(coretesting.Stdout(ctx), gc.Equals, "")