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