launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/worker/deployer/deployer_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package deployer_test 5 6 import ( 7 gc "launchpad.net/gocheck" 8 "sort" 9 "strings" 10 stdtesting "testing" 11 "time" 12 13 "launchpad.net/juju-core/errors" 14 jujutesting "launchpad.net/juju-core/juju/testing" 15 "launchpad.net/juju-core/state" 16 "launchpad.net/juju-core/state/api" 17 apideployer "launchpad.net/juju-core/state/api/deployer" 18 "launchpad.net/juju-core/state/api/params" 19 coretesting "launchpad.net/juju-core/testing" 20 "launchpad.net/juju-core/worker" 21 "launchpad.net/juju-core/worker/deployer" 22 ) 23 24 func TestPackage(t *stdtesting.T) { 25 coretesting.MgoTestPackage(t) 26 } 27 28 type deployerSuite struct { 29 jujutesting.JujuConnSuite 30 SimpleToolsFixture 31 32 machine *state.Machine 33 stateAPI *api.State 34 deployerState *apideployer.State 35 } 36 37 var _ = gc.Suite(&deployerSuite{}) 38 39 var _ worker.StringsWatchHandler = (*deployer.Deployer)(nil) 40 41 func (s *deployerSuite) SetUpTest(c *gc.C) { 42 s.JujuConnSuite.SetUpTest(c) 43 s.SimpleToolsFixture.SetUp(c, s.DataDir()) 44 s.stateAPI, s.machine = s.OpenAPIAsNewMachine(c) 45 // Create the deployer facade. 46 s.deployerState = s.stateAPI.Deployer() 47 c.Assert(s.deployerState, gc.NotNil) 48 } 49 50 func (s *deployerSuite) TearDownTest(c *gc.C) { 51 s.SimpleToolsFixture.TearDown(c) 52 s.JujuConnSuite.TearDownTest(c) 53 } 54 55 func (s *deployerSuite) makeDeployerAndContext(c *gc.C) (worker.Worker, deployer.Context) { 56 // Create a deployer acting on behalf of the machine. 57 ctx := s.getContextForMachine(c, s.machine.Tag()) 58 return deployer.NewDeployer(s.deployerState, ctx), ctx 59 } 60 61 func (s *deployerSuite) TestDeployRecallRemovePrincipals(c *gc.C) { 62 // Create a machine, and a couple of units. 63 svc := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 64 u0, err := svc.AddUnit() 65 c.Assert(err, gc.IsNil) 66 u1, err := svc.AddUnit() 67 c.Assert(err, gc.IsNil) 68 69 dep, ctx := s.makeDeployerAndContext(c) 70 defer stop(c, dep) 71 72 // Assign one unit, and wait for it to be deployed. 73 err = u0.AssignToMachine(s.machine) 74 c.Assert(err, gc.IsNil) 75 s.waitFor(c, isDeployed(ctx, u0.Name())) 76 77 // Assign another unit, and wait for that to be deployed. 78 err = u1.AssignToMachine(s.machine) 79 c.Assert(err, gc.IsNil) 80 s.waitFor(c, isDeployed(ctx, u0.Name(), u1.Name())) 81 82 // Cause a unit to become Dying, and check no change. 83 err = u1.SetStatus(params.StatusInstalled, "", nil) 84 c.Assert(err, gc.IsNil) 85 err = u1.Destroy() 86 c.Assert(err, gc.IsNil) 87 s.waitFor(c, isDeployed(ctx, u0.Name(), u1.Name())) 88 89 // Cause a unit to become Dead, and check that it is both recalled and 90 // removed from state. 91 err = u0.EnsureDead() 92 c.Assert(err, gc.IsNil) 93 s.waitFor(c, isRemoved(s.State, u0.Name())) 94 s.waitFor(c, isDeployed(ctx, u1.Name())) 95 96 // Remove the Dying unit from the machine, and check that it is recalled... 97 err = u1.UnassignFromMachine() 98 c.Assert(err, gc.IsNil) 99 s.waitFor(c, isDeployed(ctx)) 100 101 // ...and that the deployer, no longer bearing any responsibility for the 102 // Dying unit, does nothing further to it. 103 err = u1.Refresh() 104 c.Assert(err, gc.IsNil) 105 c.Assert(u1.Life(), gc.Equals, state.Dying) 106 } 107 108 func (s *deployerSuite) TestRemoveNonAlivePrincipals(c *gc.C) { 109 // Create a service, and a couple of units. 110 svc := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 111 u0, err := svc.AddUnit() 112 c.Assert(err, gc.IsNil) 113 u1, err := svc.AddUnit() 114 c.Assert(err, gc.IsNil) 115 116 // Assign the units to the machine, and set them to Dying/Dead. 117 err = u0.AssignToMachine(s.machine) 118 c.Assert(err, gc.IsNil) 119 err = u0.EnsureDead() 120 c.Assert(err, gc.IsNil) 121 err = u1.AssignToMachine(s.machine) 122 c.Assert(err, gc.IsNil) 123 // note: this is not a sane state; for the unit to have a status it must 124 // have been deployed. But it's instructive to check that the right thing 125 // would happen if it were possible to have a dying unit in this situation. 126 err = u1.SetStatus(params.StatusInstalled, "", nil) 127 c.Assert(err, gc.IsNil) 128 err = u1.Destroy() 129 c.Assert(err, gc.IsNil) 130 131 // When the deployer is started, in each case (1) no unit agent is deployed 132 // and (2) the non-Alive unit is been removed from state. 133 dep, ctx := s.makeDeployerAndContext(c) 134 defer stop(c, dep) 135 s.waitFor(c, isRemoved(s.State, u0.Name())) 136 s.waitFor(c, isRemoved(s.State, u1.Name())) 137 s.waitFor(c, isDeployed(ctx)) 138 } 139 140 func (s *deployerSuite) prepareSubordinates(c *gc.C) (*state.Unit, []*state.RelationUnit) { 141 svc := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 142 u, err := svc.AddUnit() 143 c.Assert(err, gc.IsNil) 144 err = u.AssignToMachine(s.machine) 145 c.Assert(err, gc.IsNil) 146 rus := []*state.RelationUnit{} 147 logging := s.AddTestingCharm(c, "logging") 148 for _, name := range []string{"subsvc0", "subsvc1"} { 149 s.AddTestingService(c, name, logging) 150 eps, err := s.State.InferEndpoints([]string{"wordpress", name}) 151 c.Assert(err, gc.IsNil) 152 rel, err := s.State.AddRelation(eps...) 153 c.Assert(err, gc.IsNil) 154 ru, err := rel.Unit(u) 155 c.Assert(err, gc.IsNil) 156 rus = append(rus, ru) 157 } 158 return u, rus 159 } 160 161 func (s *deployerSuite) TestDeployRecallRemoveSubordinates(c *gc.C) { 162 // Create a deployer acting on behalf of the principal. 163 u, rus := s.prepareSubordinates(c) 164 dep, ctx := s.makeDeployerAndContext(c) 165 defer stop(c, dep) 166 167 // Add a subordinate, and wait for it to be deployed. 168 err := rus[0].EnterScope(nil) 169 c.Assert(err, gc.IsNil) 170 sub0, err := s.State.Unit("subsvc0/0") 171 c.Assert(err, gc.IsNil) 172 // Make sure the principal is deployed first, then the subordinate 173 s.waitFor(c, isDeployed(ctx, u.Name(), sub0.Name())) 174 175 // And another. 176 err = rus[1].EnterScope(nil) 177 c.Assert(err, gc.IsNil) 178 sub1, err := s.State.Unit("subsvc1/0") 179 c.Assert(err, gc.IsNil) 180 s.waitFor(c, isDeployed(ctx, u.Name(), sub0.Name(), sub1.Name())) 181 182 // Set one to Dying; check nothing happens. 183 err = sub1.Destroy() 184 c.Assert(err, gc.IsNil) 185 s.State.StartSync() 186 c.Assert(isRemoved(s.State, sub1.Name())(c), gc.Equals, false) 187 s.waitFor(c, isDeployed(ctx, u.Name(), sub0.Name(), sub1.Name())) 188 189 // Set the other to Dead; check it's recalled and removed. 190 err = sub0.EnsureDead() 191 c.Assert(err, gc.IsNil) 192 s.waitFor(c, isDeployed(ctx, u.Name(), sub1.Name())) 193 s.waitFor(c, isRemoved(s.State, sub0.Name())) 194 } 195 196 func (s *deployerSuite) TestNonAliveSubordinates(c *gc.C) { 197 // Add two subordinate units and set them to Dead/Dying respectively. 198 _, rus := s.prepareSubordinates(c) 199 err := rus[0].EnterScope(nil) 200 c.Assert(err, gc.IsNil) 201 sub0, err := s.State.Unit("subsvc0/0") 202 c.Assert(err, gc.IsNil) 203 err = sub0.EnsureDead() 204 c.Assert(err, gc.IsNil) 205 err = rus[1].EnterScope(nil) 206 c.Assert(err, gc.IsNil) 207 sub1, err := s.State.Unit("subsvc1/0") 208 c.Assert(err, gc.IsNil) 209 err = sub1.Destroy() 210 c.Assert(err, gc.IsNil) 211 212 // When we start a new deployer, neither unit will be deployed and 213 // both will be removed. 214 dep, _ := s.makeDeployerAndContext(c) 215 defer stop(c, dep) 216 s.waitFor(c, isRemoved(s.State, sub0.Name())) 217 s.waitFor(c, isRemoved(s.State, sub1.Name())) 218 } 219 220 func (s *deployerSuite) waitFor(c *gc.C, t func(c *gc.C) bool) { 221 s.BackingState.StartSync() 222 if t(c) { 223 return 224 } 225 timeout := time.After(coretesting.LongWait) 226 for { 227 select { 228 case <-timeout: 229 c.Fatalf("timeout") 230 case <-time.After(coretesting.ShortWait): 231 if t(c) { 232 return 233 } 234 } 235 } 236 } 237 238 func isDeployed(ctx deployer.Context, expected ...string) func(*gc.C) bool { 239 return func(c *gc.C) bool { 240 sort.Strings(expected) 241 current, err := ctx.DeployedUnits() 242 c.Assert(err, gc.IsNil) 243 sort.Strings(current) 244 return strings.Join(expected, ":") == strings.Join(current, ":") 245 } 246 } 247 248 func isRemoved(st *state.State, name string) func(*gc.C) bool { 249 return func(c *gc.C) bool { 250 _, err := st.Unit(name) 251 if errors.IsNotFoundError(err) { 252 return true 253 } 254 c.Assert(err, gc.IsNil) 255 return false 256 } 257 } 258 259 func stop(c *gc.C, w worker.Worker) { 260 c.Assert(worker.Stop(w), gc.IsNil) 261 }