github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/cmd/juju/commands/enableha_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package commands 5 6 import ( 7 "bytes" 8 "encoding/json" 9 "fmt" 10 11 "github.com/juju/cmd" 12 jc "github.com/juju/testing/checkers" 13 gc "gopkg.in/check.v1" 14 goyaml "gopkg.in/yaml.v2" 15 16 "github.com/juju/juju/apiserver/common" 17 "github.com/juju/juju/apiserver/params" 18 "github.com/juju/juju/cmd/modelcmd" 19 "github.com/juju/juju/constraints" 20 "github.com/juju/juju/instance" 21 "github.com/juju/juju/juju/testing" 22 "github.com/juju/juju/state" 23 coretesting "github.com/juju/juju/testing" 24 "github.com/juju/juju/testing/factory" 25 ) 26 27 type EnableHASuite struct { 28 // TODO (cherylj) change this back to a FakeJujuXDGDataHomeSuite to 29 // remove the mongo dependency once ensure-availability is 30 // moved under a supercommand again. 31 testing.JujuConnSuite 32 fake *fakeHAClient 33 } 34 35 // invalidNumServers is a number of controllers that would 36 // never be generated by the enable-ha command. 37 const invalidNumServers = -2 38 39 func (s *EnableHASuite) SetUpTest(c *gc.C) { 40 s.JujuConnSuite.SetUpTest(c) 41 42 // Initialize numControllers to an invalid number to validate 43 // that enable-ha doesn't call into the API when its 44 // pre-checks fail 45 s.fake = &fakeHAClient{numControllers: invalidNumServers} 46 } 47 48 type fakeHAClient struct { 49 numControllers int 50 cons constraints.Value 51 err error 52 placement []string 53 result params.ControllersChanges 54 } 55 56 func (f *fakeHAClient) Close() error { 57 return nil 58 } 59 60 func (f *fakeHAClient) EnableHA(numControllers int, cons constraints.Value, placement []string) (params.ControllersChanges, error) { 61 62 f.numControllers = numControllers 63 f.cons = cons 64 f.placement = placement 65 66 if f.err != nil { 67 return f.result, f.err 68 } 69 70 if numControllers == 1 { 71 return f.result, nil 72 } 73 74 // In the real HAClient, specifying a numControllers value of 0 75 // indicates that the default value (3) should be used 76 if numControllers == 0 { 77 numControllers = 3 78 } 79 80 f.result.Maintained = append(f.result.Maintained, "machine-0") 81 82 for _, p := range placement { 83 m, err := instance.ParsePlacement(p) 84 if err == nil && m.Scope == instance.MachineScope { 85 f.result.Converted = append(f.result.Converted, "machine-"+m.Directive) 86 } 87 } 88 89 // We may need to pretend that we added some machines. 90 for i := len(f.result.Converted) + 1; i < numControllers; i++ { 91 f.result.Added = append(f.result.Added, fmt.Sprintf("machine-%d", i)) 92 } 93 94 return f.result, nil 95 } 96 97 var _ = gc.Suite(&EnableHASuite{}) 98 99 func (s *EnableHASuite) runEnableHA(c *gc.C, args ...string) (*cmd.Context, error) { 100 command := &enableHACommand{newHAClientFunc: func() (MakeHAClient, error) { return s.fake, nil }} 101 return coretesting.RunCommand(c, modelcmd.Wrap(command), args...) 102 } 103 104 func (s *EnableHASuite) TestEnableHA(c *gc.C) { 105 ctx, err := s.runEnableHA(c, "-n", "1") 106 c.Assert(err, jc.ErrorIsNil) 107 c.Assert(coretesting.Stdout(ctx), gc.Equals, "\n") 108 109 c.Assert(s.fake.numControllers, gc.Equals, 1) 110 c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty) 111 c.Assert(len(s.fake.placement), gc.Equals, 0) 112 } 113 114 func (s *EnableHASuite) TestBlockEnableHA(c *gc.C) { 115 s.fake.err = common.OperationBlockedError("TestBlockEnableHA") 116 _, err := s.runEnableHA(c, "-n", "1") 117 coretesting.AssertOperationWasBlocked(c, err, ".*TestBlockEnableHA.*") 118 } 119 120 func (s *EnableHASuite) TestEnableHAFormatYaml(c *gc.C) { 121 expected := map[string][]string{ 122 "maintained": {"0"}, 123 "added": {"1", "2"}, 124 } 125 126 ctx, err := s.runEnableHA(c, "-n", "3", "--format", "yaml") 127 c.Assert(err, jc.ErrorIsNil) 128 129 c.Assert(s.fake.numControllers, gc.Equals, 3) 130 c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty) 131 c.Assert(len(s.fake.placement), gc.Equals, 0) 132 133 var result map[string][]string 134 err = goyaml.Unmarshal(ctx.Stdout.(*bytes.Buffer).Bytes(), &result) 135 c.Assert(err, jc.ErrorIsNil) 136 c.Assert(result, gc.DeepEquals, expected) 137 } 138 139 func (s *EnableHASuite) TestEnableHAFormatJson(c *gc.C) { 140 expected := map[string][]string{ 141 "maintained": {"0"}, 142 "added": {"1", "2"}, 143 } 144 145 ctx, err := s.runEnableHA(c, "-n", "3", "--format", "json") 146 c.Assert(err, jc.ErrorIsNil) 147 148 c.Assert(s.fake.numControllers, gc.Equals, 3) 149 c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty) 150 c.Assert(len(s.fake.placement), gc.Equals, 0) 151 152 var result map[string][]string 153 err = json.Unmarshal(ctx.Stdout.(*bytes.Buffer).Bytes(), &result) 154 c.Assert(err, jc.ErrorIsNil) 155 c.Assert(result, gc.DeepEquals, expected) 156 } 157 158 func (s *EnableHASuite) TestEnableHAWithFive(c *gc.C) { 159 // Also test with -n 5 to validate numbers other than 1 and 3 160 ctx, err := s.runEnableHA(c, "-n", "5") 161 c.Assert(err, jc.ErrorIsNil) 162 c.Assert(coretesting.Stdout(ctx), gc.Equals, 163 "maintaining machines: 0\n"+ 164 "adding machines: 1, 2, 3, 4\n\n") 165 166 c.Assert(s.fake.numControllers, gc.Equals, 5) 167 c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty) 168 c.Assert(len(s.fake.placement), gc.Equals, 0) 169 } 170 171 func (s *EnableHASuite) TestEnableHAWithConstraints(c *gc.C) { 172 ctx, err := s.runEnableHA(c, "--constraints", "mem=4G", "-n", "3") 173 c.Assert(err, jc.ErrorIsNil) 174 c.Assert(coretesting.Stdout(ctx), gc.Equals, 175 "maintaining machines: 0\n"+ 176 "adding machines: 1, 2\n\n") 177 178 c.Assert(s.fake.numControllers, gc.Equals, 3) 179 expectedCons := constraints.MustParse("mem=4G") 180 c.Assert(s.fake.cons, gc.DeepEquals, expectedCons) 181 c.Assert(len(s.fake.placement), gc.Equals, 0) 182 } 183 184 func (s *EnableHASuite) TestEnableHAWithPlacement(c *gc.C) { 185 ctx, err := s.runEnableHA(c, "--to", "valid", "-n", "3") 186 c.Assert(err, jc.ErrorIsNil) 187 c.Assert(coretesting.Stdout(ctx), gc.Equals, 188 "maintaining machines: 0\n"+ 189 "adding machines: 1, 2\n\n") 190 191 c.Assert(s.fake.numControllers, gc.Equals, 3) 192 c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty) 193 expectedPlacement := []string{"valid"} 194 c.Assert(s.fake.placement, gc.DeepEquals, expectedPlacement) 195 } 196 197 func (s *EnableHASuite) TestEnableHAErrors(c *gc.C) { 198 for _, n := range []int{-1, 2} { 199 _, err := s.runEnableHA(c, "-n", fmt.Sprint(n)) 200 c.Assert(err, gc.ErrorMatches, "must specify a number of controllers odd and non-negative") 201 } 202 203 // Verify that enable-ha didn't call into the API 204 c.Assert(s.fake.numControllers, gc.Equals, invalidNumServers) 205 } 206 207 func (s *EnableHASuite) TestEnableHAAllows0(c *gc.C) { 208 // If the number of controllers is specified as "0", the API will 209 // then use the default number of 3. 210 ctx, err := s.runEnableHA(c, "-n", "0") 211 c.Assert(err, jc.ErrorIsNil) 212 c.Assert(coretesting.Stdout(ctx), gc.Equals, 213 "maintaining machines: 0\n"+ 214 "adding machines: 1, 2\n\n") 215 216 c.Assert(s.fake.numControllers, gc.Equals, 0) 217 c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty) 218 c.Assert(len(s.fake.placement), gc.Equals, 0) 219 } 220 221 func (s *EnableHASuite) TestEnableHADefaultsTo0(c *gc.C) { 222 // If the number of controllers is not specified, we pass in 0 to the 223 // API. The API will then use the default number of 3. 224 ctx, err := s.runEnableHA(c) 225 c.Assert(err, jc.ErrorIsNil) 226 c.Assert(coretesting.Stdout(ctx), gc.Equals, 227 "maintaining machines: 0\n"+ 228 "adding machines: 1, 2\n\n") 229 230 c.Assert(s.fake.numControllers, gc.Equals, 0) 231 c.Assert(&s.fake.cons, jc.Satisfies, constraints.IsEmpty) 232 c.Assert(len(s.fake.placement), gc.Equals, 0) 233 } 234 235 func (s *EnableHASuite) TestEnableHAEndToEnd(c *gc.C) { 236 s.Factory.MakeMachine(c, &factory.MachineParams{ 237 Jobs: []state.MachineJob{state.JobManageModel}, 238 }) 239 ctx, err := coretesting.RunCommand(c, newEnableHACommand(), "-n", "3") 240 c.Assert(err, jc.ErrorIsNil) 241 242 // Machine 0 is demoted because it hasn't reported its presence 243 c.Assert(coretesting.Stdout(ctx), gc.Equals, 244 "adding machines: 1, 2, 3\n"+ 245 "demoting machines: 0\n\n") 246 } 247 248 func (s *EnableHASuite) TestEnableHAToExisting(c *gc.C) { 249 ctx, err := s.runEnableHA(c, "--to", "1,2") 250 c.Assert(err, jc.ErrorIsNil) 251 c.Check(coretesting.Stdout(ctx), gc.Equals, ` 252 maintaining machines: 0 253 converting machines: 1, 2 254 255 `[1:]) 256 257 c.Check(s.fake.numControllers, gc.Equals, 0) 258 c.Check(&s.fake.cons, jc.Satisfies, constraints.IsEmpty) 259 c.Check(len(s.fake.placement), gc.Equals, 2) 260 } 261 262 func (s *EnableHASuite) TestEnableHADisallowsSeries(c *gc.C) { 263 // We don't allow --series as an argument. This test ensures it is not 264 // inadvertantly added back. 265 ctx, err := s.runEnableHA(c, "-n", "0", "--series", "xenian") 266 c.Assert(err, gc.ErrorMatches, "flag provided but not defined: --series") 267 c.Assert(coretesting.Stdout(ctx), gc.Equals, "") 268 }