github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/apiserver/highavailability/highavailability_test.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package highavailability_test 5 6 import ( 7 stdtesting "testing" 8 9 "github.com/juju/errors" 10 jc "github.com/juju/testing/checkers" 11 gc "gopkg.in/check.v1" 12 13 "github.com/juju/juju/apiserver/common" 14 commontesting "github.com/juju/juju/apiserver/common/testing" 15 "github.com/juju/juju/apiserver/highavailability" 16 "github.com/juju/juju/apiserver/params" 17 apiservertesting "github.com/juju/juju/apiserver/testing" 18 "github.com/juju/juju/constraints" 19 "github.com/juju/juju/juju/testing" 20 "github.com/juju/juju/state" 21 "github.com/juju/juju/state/presence" 22 coretesting "github.com/juju/juju/testing" 23 "github.com/juju/juju/testing/factory" 24 ) 25 26 func TestAll(t *stdtesting.T) { 27 coretesting.MgoTestPackage(t) 28 } 29 30 type clientSuite struct { 31 testing.JujuConnSuite 32 33 resources *common.Resources 34 authoriser apiservertesting.FakeAuthorizer 35 haServer *highavailability.HighAvailabilityAPI 36 pingers []*presence.Pinger 37 38 commontesting.BlockHelper 39 } 40 41 type KillerForTesting interface { 42 KillForTesting() error 43 } 44 45 var _ = gc.Suite(&clientSuite{}) 46 47 func assertKill(c *gc.C, killer KillerForTesting) { 48 c.Assert(killer.KillForTesting(), gc.IsNil) 49 } 50 51 var ( 52 emptyCons = constraints.Value{} 53 controllerCons = constraints.MustParse("mem=16G cpu-cores=16") 54 defaultSeries = "" 55 ) 56 57 func (s *clientSuite) SetUpTest(c *gc.C) { 58 s.JujuConnSuite.SetUpTest(c) 59 s.resources = common.NewResources() 60 s.AddCleanup(func(_ *gc.C) { s.resources.StopAll() }) 61 62 s.authoriser = apiservertesting.FakeAuthorizer{ 63 Tag: s.AdminUserTag(c), 64 EnvironManager: true, 65 } 66 67 var err error 68 s.haServer, err = highavailability.NewHighAvailabilityAPI(s.State, s.resources, s.authoriser) 69 c.Assert(err, jc.ErrorIsNil) 70 71 _, err = s.State.AddMachines(state.MachineTemplate{ 72 Series: "quantal", 73 Jobs: []state.MachineJob{state.JobManageModel}, 74 Constraints: controllerCons, 75 }) 76 c.Assert(err, jc.ErrorIsNil) 77 // We have to ensure the agents are alive, or EnableHA will 78 // create more to replace them. 79 s.pingers = []*presence.Pinger{s.setAgentPresence(c, "0")} 80 s.BlockHelper = commontesting.NewBlockHelper(s.APIState) 81 s.AddCleanup(func(*gc.C) { s.BlockHelper.Close() }) 82 } 83 84 func (s *clientSuite) TearDownTest(c *gc.C) { 85 for _, pinger := range s.pingers { 86 assertKill(c, pinger) 87 } 88 s.JujuConnSuite.TearDownTest(c) 89 } 90 91 func (s *clientSuite) setAgentPresence(c *gc.C, machineId string) *presence.Pinger { 92 m, err := s.State.Machine(machineId) 93 c.Assert(err, jc.ErrorIsNil) 94 pinger, err := m.SetAgentPresence() 95 c.Assert(err, jc.ErrorIsNil) 96 s.State.StartSync() 97 err = m.WaitAgentPresence(coretesting.LongWait) 98 c.Assert(err, jc.ErrorIsNil) 99 return pinger 100 } 101 102 func (s *clientSuite) enableHA( 103 c *gc.C, numControllers int, cons constraints.Value, series string, placement []string, 104 ) (params.ControllersChanges, error) { 105 return enableHA(c, s.haServer, numControllers, cons, series, placement) 106 } 107 108 func enableHA( 109 c *gc.C, haServer *highavailability.HighAvailabilityAPI, numControllers int, cons constraints.Value, series string, placement []string, 110 ) (params.ControllersChanges, error) { 111 arg := params.ControllersSpecs{ 112 Specs: []params.ControllersSpec{{ 113 NumControllers: numControllers, 114 Constraints: cons, 115 Series: series, 116 Placement: placement, 117 }}} 118 results, err := haServer.EnableHA(arg) 119 c.Assert(err, jc.ErrorIsNil) 120 c.Assert(results.Results, gc.HasLen, 1) 121 result := results.Results[0] 122 // We explicitly return nil here so we can do typed nil checking 123 // of the result like normal. 124 err = nil 125 if result.Error != nil { 126 err = result.Error 127 } 128 return result.Result, err 129 } 130 131 func (s *clientSuite) TestEnableHASeries(c *gc.C) { 132 machines, err := s.State.AllMachines() 133 c.Assert(err, jc.ErrorIsNil) 134 c.Assert(machines, gc.HasLen, 1) 135 c.Assert(machines[0].Series(), gc.Equals, "quantal") 136 137 enableHAResult, err := s.enableHA(c, 3, emptyCons, defaultSeries, nil) 138 c.Assert(err, jc.ErrorIsNil) 139 c.Assert(enableHAResult.Maintained, gc.DeepEquals, []string{"machine-0"}) 140 c.Assert(enableHAResult.Added, gc.DeepEquals, []string{"machine-1", "machine-2"}) 141 c.Assert(enableHAResult.Removed, gc.HasLen, 0) 142 c.Assert(enableHAResult.Converted, gc.HasLen, 0) 143 144 machines, err = s.State.AllMachines() 145 c.Assert(err, jc.ErrorIsNil) 146 c.Assert(machines, gc.HasLen, 3) 147 c.Assert(machines[0].Series(), gc.Equals, "quantal") 148 c.Assert(machines[1].Series(), gc.Equals, "quantal") 149 c.Assert(machines[2].Series(), gc.Equals, "quantal") 150 151 pingerB := s.setAgentPresence(c, "1") 152 defer assertKill(c, pingerB) 153 154 pingerC := s.setAgentPresence(c, "2") 155 defer assertKill(c, pingerC) 156 157 enableHAResult, err = s.enableHA(c, 5, emptyCons, "non-default", nil) 158 c.Assert(err, jc.ErrorIsNil) 159 c.Assert(enableHAResult.Maintained, gc.DeepEquals, []string{"machine-0", "machine-1", "machine-2"}) 160 c.Assert(enableHAResult.Added, gc.DeepEquals, []string{"machine-3", "machine-4"}) 161 c.Assert(enableHAResult.Removed, gc.HasLen, 0) 162 c.Assert(enableHAResult.Converted, gc.HasLen, 0) 163 164 c.Assert(err, jc.ErrorIsNil) 165 machines, err = s.State.AllMachines() 166 c.Assert(err, jc.ErrorIsNil) 167 c.Assert(machines, gc.HasLen, 5) 168 c.Assert(machines[0].Series(), gc.Equals, "quantal") 169 c.Assert(machines[1].Series(), gc.Equals, "quantal") 170 c.Assert(machines[2].Series(), gc.Equals, "quantal") 171 c.Assert(machines[3].Series(), gc.Equals, "non-default") 172 c.Assert(machines[4].Series(), gc.Equals, "non-default") 173 } 174 175 func (s *clientSuite) TestEnableHAConstraints(c *gc.C) { 176 enableHAResult, err := s.enableHA(c, 3, constraints.MustParse("mem=4G"), defaultSeries, nil) 177 c.Assert(err, jc.ErrorIsNil) 178 c.Assert(enableHAResult.Maintained, gc.DeepEquals, []string{"machine-0"}) 179 c.Assert(enableHAResult.Added, gc.DeepEquals, []string{"machine-1", "machine-2"}) 180 c.Assert(enableHAResult.Removed, gc.HasLen, 0) 181 c.Assert(enableHAResult.Converted, gc.HasLen, 0) 182 183 machines, err := s.State.AllMachines() 184 c.Assert(err, jc.ErrorIsNil) 185 c.Assert(machines, gc.HasLen, 3) 186 expectedCons := []constraints.Value{ 187 controllerCons, 188 constraints.MustParse("mem=4G"), 189 constraints.MustParse("mem=4G"), 190 } 191 for i, m := range machines { 192 cons, err := m.Constraints() 193 c.Assert(err, jc.ErrorIsNil) 194 c.Check(cons, gc.DeepEquals, expectedCons[i]) 195 } 196 } 197 198 func (s *clientSuite) TestEnableHAEmptyConstraints(c *gc.C) { 199 enableHAResult, err := s.enableHA(c, 3, emptyCons, defaultSeries, nil) 200 c.Assert(err, jc.ErrorIsNil) 201 c.Assert(enableHAResult.Maintained, gc.DeepEquals, []string{"machine-0"}) 202 c.Assert(enableHAResult.Added, gc.DeepEquals, []string{"machine-1", "machine-2"}) 203 c.Assert(enableHAResult.Removed, gc.HasLen, 0) 204 c.Assert(enableHAResult.Converted, gc.HasLen, 0) 205 206 machines, err := s.State.AllMachines() 207 c.Assert(err, jc.ErrorIsNil) 208 c.Assert(machines, gc.HasLen, 3) 209 for _, m := range machines { 210 cons, err := m.Constraints() 211 c.Assert(err, jc.ErrorIsNil) 212 c.Check(cons, gc.DeepEquals, controllerCons) 213 } 214 } 215 216 func (s *clientSuite) TestBlockMakeHA(c *gc.C) { 217 // Block all changes. 218 s.BlockAllChanges(c, "TestBlockEnableHA") 219 220 enableHAResult, err := s.enableHA(c, 3, constraints.MustParse("mem=4G"), defaultSeries, nil) 221 s.AssertBlocked(c, err, "TestBlockEnableHA") 222 223 c.Assert(enableHAResult.Maintained, gc.HasLen, 0) 224 c.Assert(enableHAResult.Added, gc.HasLen, 0) 225 c.Assert(enableHAResult.Removed, gc.HasLen, 0) 226 c.Assert(enableHAResult.Converted, gc.HasLen, 0) 227 228 machines, err := s.State.AllMachines() 229 c.Assert(err, jc.ErrorIsNil) 230 c.Assert(machines, gc.HasLen, 1) 231 } 232 233 func (s *clientSuite) TestEnableHAPlacement(c *gc.C) { 234 placement := []string{"valid"} 235 enableHAResult, err := s.enableHA(c, 3, constraints.MustParse("mem=4G"), defaultSeries, placement) 236 c.Assert(err, jc.ErrorIsNil) 237 c.Assert(enableHAResult.Maintained, gc.DeepEquals, []string{"machine-0"}) 238 c.Assert(enableHAResult.Added, gc.DeepEquals, []string{"machine-1", "machine-2"}) 239 c.Assert(enableHAResult.Removed, gc.HasLen, 0) 240 c.Assert(enableHAResult.Converted, gc.HasLen, 0) 241 242 machines, err := s.State.AllMachines() 243 c.Assert(err, jc.ErrorIsNil) 244 c.Assert(machines, gc.HasLen, 3) 245 expectedCons := []constraints.Value{ 246 controllerCons, 247 constraints.MustParse("mem=4G"), 248 constraints.MustParse("mem=4G"), 249 } 250 expectedPlacement := []string{"", "valid", ""} 251 for i, m := range machines { 252 cons, err := m.Constraints() 253 c.Assert(err, jc.ErrorIsNil) 254 c.Check(cons, gc.DeepEquals, expectedCons[i]) 255 c.Check(m.Placement(), gc.Equals, expectedPlacement[i]) 256 } 257 } 258 259 func (s *clientSuite) TestEnableHAPlacementTo(c *gc.C) { 260 machine1Cons := constraints.MustParse("mem=8G") 261 _, err := s.State.AddMachines(state.MachineTemplate{ 262 Series: "quantal", 263 Jobs: []state.MachineJob{state.JobHostUnits}, 264 Constraints: machine1Cons, 265 }) 266 s.pingers = append(s.pingers, s.setAgentPresence(c, "1")) 267 268 _, err = s.State.AddMachine("quantal", state.JobHostUnits) 269 c.Assert(err, jc.ErrorIsNil) 270 s.pingers = append(s.pingers, s.setAgentPresence(c, "2")) 271 272 placement := []string{"1", "2"} 273 enableHAResult, err := s.enableHA(c, 3, emptyCons, defaultSeries, placement) 274 c.Assert(err, jc.ErrorIsNil) 275 c.Assert(enableHAResult.Maintained, gc.DeepEquals, []string{"machine-0"}) 276 c.Assert(enableHAResult.Added, gc.HasLen, 0) 277 c.Assert(enableHAResult.Removed, gc.HasLen, 0) 278 c.Assert(enableHAResult.Converted, gc.DeepEquals, []string{"machine-1", "machine-2"}) 279 280 machines, err := s.State.AllMachines() 281 c.Assert(err, jc.ErrorIsNil) 282 c.Assert(machines, gc.HasLen, 3) 283 expectedCons := []constraints.Value{ 284 controllerCons, 285 machine1Cons, 286 {}, 287 } 288 expectedPlacement := []string{"", "", ""} 289 for i, m := range machines { 290 cons, err := m.Constraints() 291 c.Assert(err, jc.ErrorIsNil) 292 c.Check(cons, gc.DeepEquals, expectedCons[i]) 293 c.Check(m.Placement(), gc.Equals, expectedPlacement[i]) 294 } 295 } 296 297 func (s *clientSuite) TestEnableHA0Preserves(c *gc.C) { 298 // A value of 0 says either "if I'm not HA, make me HA" or "preserve my 299 // current HA settings". 300 enableHAResult, err := s.enableHA(c, 0, emptyCons, defaultSeries, nil) 301 c.Assert(err, jc.ErrorIsNil) 302 c.Assert(enableHAResult.Maintained, gc.DeepEquals, []string{"machine-0"}) 303 c.Assert(enableHAResult.Added, gc.DeepEquals, []string{"machine-1", "machine-2"}) 304 c.Assert(enableHAResult.Removed, gc.HasLen, 0) 305 c.Assert(enableHAResult.Converted, gc.HasLen, 0) 306 307 machines, err := s.State.AllMachines() 308 c.Assert(machines, gc.HasLen, 3) 309 310 pingerB := s.setAgentPresence(c, "1") 311 defer assertKill(c, pingerB) 312 313 // Now, we keep agent 1 alive, but not agent 2, calling 314 // EnableHA(0) again will cause us to start another machine 315 enableHAResult, err = s.enableHA(c, 0, emptyCons, defaultSeries, nil) 316 c.Assert(err, jc.ErrorIsNil) 317 c.Assert(enableHAResult.Maintained, gc.DeepEquals, []string{"machine-0", "machine-1"}) 318 c.Assert(enableHAResult.Added, gc.DeepEquals, []string{"machine-3"}) 319 c.Assert(enableHAResult.Removed, gc.HasLen, 0) 320 c.Assert(enableHAResult.Converted, gc.HasLen, 0) 321 322 machines, err = s.State.AllMachines() 323 c.Assert(err, jc.ErrorIsNil) 324 c.Assert(machines, gc.HasLen, 4) 325 } 326 327 func (s *clientSuite) TestEnableHA0Preserves5(c *gc.C) { 328 // Start off with 5 servers 329 enableHAResult, err := s.enableHA(c, 5, emptyCons, defaultSeries, nil) 330 c.Assert(err, jc.ErrorIsNil) 331 c.Assert(enableHAResult.Maintained, gc.DeepEquals, []string{"machine-0"}) 332 c.Assert(enableHAResult.Added, gc.DeepEquals, []string{"machine-1", "machine-2", "machine-3", "machine-4"}) 333 c.Assert(enableHAResult.Removed, gc.HasLen, 0) 334 c.Assert(enableHAResult.Converted, gc.HasLen, 0) 335 336 machines, err := s.State.AllMachines() 337 c.Assert(machines, gc.HasLen, 5) 338 pingerB := s.setAgentPresence(c, "1") 339 defer assertKill(c, pingerB) 340 341 pingerC := s.setAgentPresence(c, "2") 342 defer assertKill(c, pingerC) 343 344 pingerD := s.setAgentPresence(c, "3") 345 defer assertKill(c, pingerD) 346 // Keeping all alive but one, will bring up 1 more server to preserve 5 347 enableHAResult, err = s.enableHA(c, 0, emptyCons, defaultSeries, nil) 348 c.Assert(err, jc.ErrorIsNil) 349 c.Assert(enableHAResult.Maintained, gc.DeepEquals, []string{"machine-0", "machine-1", 350 "machine-2", "machine-3"}) 351 c.Assert(enableHAResult.Added, gc.DeepEquals, []string{"machine-5"}) 352 c.Assert(enableHAResult.Removed, gc.HasLen, 0) 353 c.Assert(enableHAResult.Converted, gc.HasLen, 0) 354 355 machines, err = s.State.AllMachines() 356 c.Assert(machines, gc.HasLen, 6) 357 c.Assert(err, jc.ErrorIsNil) 358 } 359 360 func (s *clientSuite) TestEnableHAErrors(c *gc.C) { 361 enableHAResult, err := s.enableHA(c, -1, emptyCons, defaultSeries, nil) 362 c.Assert(err, gc.ErrorMatches, "number of controllers must be odd and non-negative") 363 364 enableHAResult, err = s.enableHA(c, 3, emptyCons, defaultSeries, nil) 365 c.Assert(err, jc.ErrorIsNil) 366 c.Assert(enableHAResult.Maintained, gc.DeepEquals, []string{"machine-0"}) 367 c.Assert(enableHAResult.Added, gc.DeepEquals, []string{"machine-1", "machine-2"}) 368 c.Assert(enableHAResult.Removed, gc.HasLen, 0) 369 c.Assert(enableHAResult.Converted, gc.HasLen, 0) 370 371 _, err = s.enableHA(c, 1, emptyCons, defaultSeries, nil) 372 c.Assert(err, gc.ErrorMatches, "failed to create new controller machines: cannot reduce controller count") 373 } 374 375 func (s *clientSuite) TestEnableHAHostedEnvErrors(c *gc.C) { 376 st2 := s.Factory.MakeModel(c, &factory.ModelParams{ConfigAttrs: coretesting.Attrs{"controller": false}}) 377 defer st2.Close() 378 379 haServer, err := highavailability.NewHighAvailabilityAPI(st2, s.resources, s.authoriser) 380 c.Assert(err, jc.ErrorIsNil) 381 382 enableHAResult, err := enableHA(c, haServer, 3, constraints.MustParse("mem=4G"), defaultSeries, nil) 383 c.Assert(errors.Cause(err), gc.ErrorMatches, "unsupported with hosted models") 384 385 c.Assert(enableHAResult.Maintained, gc.HasLen, 0) 386 c.Assert(enableHAResult.Added, gc.HasLen, 0) 387 c.Assert(enableHAResult.Removed, gc.HasLen, 0) 388 c.Assert(enableHAResult.Converted, gc.HasLen, 0) 389 390 machines, err := st2.AllMachines() 391 c.Assert(err, jc.ErrorIsNil) 392 c.Assert(machines, gc.HasLen, 0) 393 }