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