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