github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/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 "github.com/juju/juju/apiserver/highavailability" 15 "github.com/juju/juju/apiserver/params" 16 apiservertesting "github.com/juju/juju/apiserver/testing" 17 "github.com/juju/juju/constraints" 18 "github.com/juju/juju/juju/testing" 19 "github.com/juju/juju/state" 20 "github.com/juju/juju/state/presence" 21 coretesting "github.com/juju/juju/testing" 22 ) 23 24 func TestAll(t *stdtesting.T) { 25 coretesting.MgoTestPackage(t) 26 } 27 28 type clientSuite struct { 29 testing.JujuConnSuite 30 31 resources *common.Resources 32 authoriser apiservertesting.FakeAuthorizer 33 haServer *highavailability.HighAvailabilityAPI 34 pinger *presence.Pinger 35 } 36 37 type Killer interface { 38 Kill() error 39 } 40 41 var _ = gc.Suite(&clientSuite{}) 42 43 func assertKill(c *gc.C, killer Killer) { 44 c.Assert(killer.Kill(), gc.IsNil) 45 } 46 47 var ( 48 emptyCons = constraints.Value{} 49 defaultSeries = "" 50 ) 51 52 func (s *clientSuite) SetUpTest(c *gc.C) { 53 s.JujuConnSuite.SetUpTest(c) 54 s.resources = common.NewResources() 55 s.AddCleanup(func(_ *gc.C) { s.resources.StopAll() }) 56 57 s.authoriser = apiservertesting.FakeAuthorizer{ 58 Tag: s.AdminUserTag(c), 59 EnvironManager: true, 60 } 61 62 var err error 63 s.haServer, err = highavailability.NewHighAvailabilityAPI(s.State, s.resources, s.authoriser) 64 c.Assert(err, jc.ErrorIsNil) 65 66 _, err = s.State.AddMachine("quantal", state.JobManageEnviron) 67 c.Assert(err, jc.ErrorIsNil) 68 // We have to ensure the agents are alive, or EnsureAvailability will 69 // create more to replace them. 70 s.pinger = s.setAgentPresence(c, "0") 71 } 72 73 func (s *clientSuite) TearDownTest(c *gc.C) { 74 assertKill(c, s.pinger) 75 s.JujuConnSuite.TearDownTest(c) 76 } 77 78 func (s *clientSuite) setAgentPresence(c *gc.C, machineId string) *presence.Pinger { 79 m, err := s.State.Machine(machineId) 80 c.Assert(err, jc.ErrorIsNil) 81 pinger, err := m.SetAgentPresence() 82 c.Assert(err, jc.ErrorIsNil) 83 s.State.StartSync() 84 err = m.WaitAgentPresence(coretesting.LongWait) 85 c.Assert(err, jc.ErrorIsNil) 86 return pinger 87 } 88 89 func (s *clientSuite) ensureAvailability( 90 c *gc.C, numStateServers int, cons constraints.Value, series string, placement []string, 91 ) (params.StateServersChanges, error) { 92 93 arg := params.StateServersSpecs{ 94 Specs: []params.StateServersSpec{{ 95 NumStateServers: numStateServers, 96 Constraints: cons, 97 Series: series, 98 Placement: placement, 99 }}} 100 results, err := s.haServer.EnsureAvailability(arg) 101 c.Assert(err, jc.ErrorIsNil) 102 c.Assert(results.Results, gc.HasLen, 1) 103 result := results.Results[0] 104 // We explicitly return nil here so we can do typed nil checking 105 // of the result like normal. 106 err = nil 107 if result.Error != nil { 108 err = result.Error 109 } 110 return result.Result, err 111 } 112 113 func (s *clientSuite) TestEnsureAvailabilitySeries(c *gc.C) { 114 machines, err := s.State.AllMachines() 115 c.Assert(err, jc.ErrorIsNil) 116 c.Assert(machines, gc.HasLen, 1) 117 c.Assert(machines[0].Series(), gc.Equals, "quantal") 118 119 ensureAvailabilityResult, err := s.ensureAvailability(c, 3, emptyCons, defaultSeries, nil) 120 c.Assert(err, jc.ErrorIsNil) 121 c.Assert(ensureAvailabilityResult.Maintained, gc.DeepEquals, []string{"machine-0"}) 122 c.Assert(ensureAvailabilityResult.Added, gc.DeepEquals, []string{"machine-1", "machine-2"}) 123 c.Assert(ensureAvailabilityResult.Removed, gc.HasLen, 0) 124 125 machines, err = s.State.AllMachines() 126 c.Assert(err, jc.ErrorIsNil) 127 c.Assert(machines, gc.HasLen, 3) 128 c.Assert(machines[0].Series(), gc.Equals, "quantal") 129 c.Assert(machines[1].Series(), gc.Equals, "quantal") 130 c.Assert(machines[2].Series(), gc.Equals, "quantal") 131 132 pingerB := s.setAgentPresence(c, "1") 133 defer assertKill(c, pingerB) 134 135 pingerC := s.setAgentPresence(c, "2") 136 defer assertKill(c, pingerC) 137 138 ensureAvailabilityResult, err = s.ensureAvailability(c, 5, emptyCons, "non-default", nil) 139 c.Assert(err, jc.ErrorIsNil) 140 c.Assert(ensureAvailabilityResult.Maintained, gc.DeepEquals, []string{"machine-0", "machine-1", "machine-2"}) 141 c.Assert(ensureAvailabilityResult.Added, gc.DeepEquals, []string{"machine-3", "machine-4"}) 142 c.Assert(ensureAvailabilityResult.Removed, gc.HasLen, 0) 143 144 c.Assert(err, jc.ErrorIsNil) 145 machines, err = s.State.AllMachines() 146 c.Assert(err, jc.ErrorIsNil) 147 c.Assert(machines, gc.HasLen, 5) 148 c.Assert(machines[0].Series(), gc.Equals, "quantal") 149 c.Assert(machines[1].Series(), gc.Equals, "quantal") 150 c.Assert(machines[2].Series(), gc.Equals, "quantal") 151 c.Assert(machines[3].Series(), gc.Equals, "non-default") 152 c.Assert(machines[4].Series(), gc.Equals, "non-default") 153 } 154 155 func (s *clientSuite) TestEnsureAvailabilityConstraints(c *gc.C) { 156 ensureAvailabilityResult, err := s.ensureAvailability(c, 3, constraints.MustParse("mem=4G"), defaultSeries, nil) 157 c.Assert(err, jc.ErrorIsNil) 158 c.Assert(ensureAvailabilityResult.Maintained, gc.DeepEquals, []string{"machine-0"}) 159 c.Assert(ensureAvailabilityResult.Added, gc.DeepEquals, []string{"machine-1", "machine-2"}) 160 c.Assert(ensureAvailabilityResult.Removed, gc.HasLen, 0) 161 162 machines, err := s.State.AllMachines() 163 c.Assert(err, jc.ErrorIsNil) 164 c.Assert(machines, gc.HasLen, 3) 165 expectedCons := []constraints.Value{ 166 {}, 167 constraints.MustParse("mem=4G"), 168 constraints.MustParse("mem=4G"), 169 } 170 for i, m := range machines { 171 cons, err := m.Constraints() 172 c.Assert(err, jc.ErrorIsNil) 173 c.Check(cons, gc.DeepEquals, expectedCons[i]) 174 } 175 } 176 177 func (s *clientSuite) TestBlockEnsureAvailability(c *gc.C) { 178 // Block all changes. 179 err := s.State.UpdateEnvironConfig(map[string]interface{}{"block-all-changes": true}, nil, nil) 180 c.Assert(err, jc.ErrorIsNil) 181 182 ensureAvailabilityResult, err := s.ensureAvailability(c, 3, constraints.MustParse("mem=4G"), defaultSeries, nil) 183 c.Assert(errors.Cause(err), gc.DeepEquals, common.ErrOperationBlocked) 184 185 c.Assert(ensureAvailabilityResult.Maintained, gc.HasLen, 0) 186 c.Assert(ensureAvailabilityResult.Added, gc.HasLen, 0) 187 c.Assert(ensureAvailabilityResult.Removed, gc.HasLen, 0) 188 189 machines, err := s.State.AllMachines() 190 c.Assert(err, jc.ErrorIsNil) 191 c.Assert(machines, gc.HasLen, 1) 192 } 193 194 func (s *clientSuite) TestEnsureAvailabilityPlacement(c *gc.C) { 195 placement := []string{"valid"} 196 ensureAvailabilityResult, err := s.ensureAvailability(c, 3, constraints.MustParse("mem=4G"), defaultSeries, placement) 197 c.Assert(err, jc.ErrorIsNil) 198 c.Assert(ensureAvailabilityResult.Maintained, gc.DeepEquals, []string{"machine-0"}) 199 c.Assert(ensureAvailabilityResult.Added, gc.DeepEquals, []string{"machine-1", "machine-2"}) 200 c.Assert(ensureAvailabilityResult.Removed, gc.HasLen, 0) 201 202 machines, err := s.State.AllMachines() 203 c.Assert(err, jc.ErrorIsNil) 204 c.Assert(machines, gc.HasLen, 3) 205 expectedCons := []constraints.Value{ 206 {}, 207 constraints.MustParse("mem=4G"), 208 constraints.MustParse("mem=4G"), 209 } 210 expectedPlacement := []string{"", "valid", ""} 211 for i, m := range machines { 212 cons, err := m.Constraints() 213 c.Assert(err, jc.ErrorIsNil) 214 c.Check(cons, gc.DeepEquals, expectedCons[i]) 215 c.Check(m.Placement(), gc.Equals, expectedPlacement[i]) 216 } 217 } 218 219 func (s *clientSuite) TestEnsureAvailability0Preserves(c *gc.C) { 220 // A value of 0 says either "if I'm not HA, make me HA" or "preserve my 221 // current HA settings". 222 ensureAvailabilityResult, err := s.ensureAvailability(c, 0, emptyCons, defaultSeries, nil) 223 c.Assert(err, jc.ErrorIsNil) 224 c.Assert(ensureAvailabilityResult.Maintained, gc.DeepEquals, []string{"machine-0"}) 225 c.Assert(ensureAvailabilityResult.Added, gc.DeepEquals, []string{"machine-1", "machine-2"}) 226 c.Assert(ensureAvailabilityResult.Removed, gc.HasLen, 0) 227 228 machines, err := s.State.AllMachines() 229 c.Assert(machines, gc.HasLen, 3) 230 231 pingerB := s.setAgentPresence(c, "1") 232 defer assertKill(c, pingerB) 233 234 // Now, we keep agent 1 alive, but not agent 2, calling 235 // EnsureAvailability(0) again will cause us to start another machine 236 ensureAvailabilityResult, err = s.ensureAvailability(c, 0, emptyCons, defaultSeries, nil) 237 c.Assert(err, jc.ErrorIsNil) 238 c.Assert(ensureAvailabilityResult.Maintained, gc.DeepEquals, []string{"machine-0", "machine-1"}) 239 c.Assert(ensureAvailabilityResult.Added, gc.DeepEquals, []string{"machine-3"}) 240 c.Assert(ensureAvailabilityResult.Removed, gc.HasLen, 0) 241 242 machines, err = s.State.AllMachines() 243 c.Assert(machines, gc.HasLen, 4) 244 } 245 246 func (s *clientSuite) TestEnsureAvailability0Preserves5(c *gc.C) { 247 // Start off with 5 servers 248 ensureAvailabilityResult, err := s.ensureAvailability(c, 5, emptyCons, defaultSeries, nil) 249 c.Assert(err, jc.ErrorIsNil) 250 c.Assert(ensureAvailabilityResult.Maintained, gc.DeepEquals, []string{"machine-0"}) 251 c.Assert(ensureAvailabilityResult.Added, gc.DeepEquals, []string{"machine-1", "machine-2", "machine-3", "machine-4"}) 252 c.Assert(ensureAvailabilityResult.Removed, gc.HasLen, 0) 253 254 machines, err := s.State.AllMachines() 255 c.Assert(machines, gc.HasLen, 5) 256 pingerB := s.setAgentPresence(c, "1") 257 defer assertKill(c, pingerB) 258 259 pingerC := s.setAgentPresence(c, "2") 260 defer assertKill(c, pingerC) 261 262 pingerD := s.setAgentPresence(c, "3") 263 defer assertKill(c, pingerD) 264 // Keeping all alive but one, will bring up 1 more server to preserve 5 265 ensureAvailabilityResult, err = s.ensureAvailability(c, 0, emptyCons, defaultSeries, nil) 266 c.Assert(err, jc.ErrorIsNil) 267 c.Assert(ensureAvailabilityResult.Maintained, gc.DeepEquals, []string{"machine-0", "machine-1", 268 "machine-2", "machine-3"}) 269 c.Assert(ensureAvailabilityResult.Added, gc.DeepEquals, []string{"machine-5"}) 270 c.Assert(ensureAvailabilityResult.Removed, gc.HasLen, 0) 271 272 machines, err = s.State.AllMachines() 273 c.Assert(machines, gc.HasLen, 6) 274 c.Assert(err, jc.ErrorIsNil) 275 } 276 277 func (s *clientSuite) TestEnsureAvailabilityErrors(c *gc.C) { 278 ensureAvailabilityResult, err := s.ensureAvailability(c, -1, emptyCons, defaultSeries, nil) 279 c.Assert(err, gc.ErrorMatches, "number of state servers must be odd and non-negative") 280 281 ensureAvailabilityResult, err = s.ensureAvailability(c, 3, emptyCons, defaultSeries, nil) 282 c.Assert(err, jc.ErrorIsNil) 283 c.Assert(ensureAvailabilityResult.Maintained, gc.DeepEquals, []string{"machine-0"}) 284 c.Assert(ensureAvailabilityResult.Added, gc.DeepEquals, []string{"machine-1", "machine-2"}) 285 c.Assert(ensureAvailabilityResult.Removed, gc.HasLen, 0) 286 287 _, err = s.ensureAvailability(c, 1, emptyCons, defaultSeries, nil) 288 c.Assert(err, gc.ErrorMatches, "failed to create new state server machines: cannot reduce state server count") 289 }