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