github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/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 now := time.Now() 81 sInfo := status.StatusInfo{ 82 Status: status.Idle, 83 Message: "", 84 Since: &now, 85 } 86 err = u1.SetAgentStatus(sInfo) 87 c.Assert(err, jc.ErrorIsNil) 88 err = u1.Destroy() 89 c.Assert(err, jc.ErrorIsNil) 90 s.waitFor(c, isDeployed(ctx, u0.Name(), u1.Name())) 91 92 // Cause a unit to become Dead, and check that it is both recalled and 93 // removed from state. 94 err = u0.EnsureDead() 95 c.Assert(err, jc.ErrorIsNil) 96 s.waitFor(c, isRemoved(s.State, u0.Name())) 97 s.waitFor(c, isDeployed(ctx, u1.Name())) 98 99 // Remove the Dying unit from the machine, and check that it is recalled... 100 err = u1.UnassignFromMachine() 101 c.Assert(err, jc.ErrorIsNil) 102 s.waitFor(c, isDeployed(ctx)) 103 104 // ...and that the deployer, no longer bearing any responsibility for the 105 // Dying unit, does nothing further to it. 106 err = u1.Refresh() 107 c.Assert(err, jc.ErrorIsNil) 108 c.Assert(u1.Life(), gc.Equals, state.Dying) 109 } 110 111 func (s *deployerSuite) TestInitialStatusMessages(c *gc.C) { 112 svc := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 113 u0, err := svc.AddUnit() 114 c.Assert(err, jc.ErrorIsNil) 115 116 dep, _ := s.makeDeployerAndContext(c) 117 defer stop(c, dep) 118 err = u0.AssignToMachine(s.machine) 119 c.Assert(err, jc.ErrorIsNil) 120 s.waitFor(c, unitStatus(u0, status.StatusInfo{ 121 Status: status.Waiting, 122 Message: "installing agent", 123 })) 124 } 125 126 func (s *deployerSuite) TestRemoveNonAlivePrincipals(c *gc.C) { 127 // Create a service, and a couple of units. 128 svc := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 129 u0, err := svc.AddUnit() 130 c.Assert(err, jc.ErrorIsNil) 131 u1, err := svc.AddUnit() 132 c.Assert(err, jc.ErrorIsNil) 133 134 // Assign the units to the machine, and set them to Dying/Dead. 135 err = u0.AssignToMachine(s.machine) 136 c.Assert(err, jc.ErrorIsNil) 137 err = u0.EnsureDead() 138 c.Assert(err, jc.ErrorIsNil) 139 err = u1.AssignToMachine(s.machine) 140 c.Assert(err, jc.ErrorIsNil) 141 // note: this is not a sane state; for the unit to have a status it must 142 // have been deployed. But it's instructive to check that the right thing 143 // would happen if it were possible to have a dying unit in this situation. 144 now := time.Now() 145 sInfo := status.StatusInfo{ 146 Status: status.Idle, 147 Message: "", 148 Since: &now, 149 } 150 err = u1.SetAgentStatus(sInfo) 151 c.Assert(err, jc.ErrorIsNil) 152 err = u1.Destroy() 153 c.Assert(err, jc.ErrorIsNil) 154 155 // When the deployer is started, in each case (1) no unit agent is deployed 156 // and (2) the non-Alive unit is been removed from state. 157 dep, ctx := s.makeDeployerAndContext(c) 158 defer stop(c, dep) 159 s.waitFor(c, isRemoved(s.State, u0.Name())) 160 s.waitFor(c, isRemoved(s.State, u1.Name())) 161 s.waitFor(c, isDeployed(ctx)) 162 } 163 164 func (s *deployerSuite) prepareSubordinates(c *gc.C) (*state.Unit, []*state.RelationUnit) { 165 svc := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 166 u, err := svc.AddUnit() 167 c.Assert(err, jc.ErrorIsNil) 168 err = u.AssignToMachine(s.machine) 169 c.Assert(err, jc.ErrorIsNil) 170 rus := []*state.RelationUnit{} 171 logging := s.AddTestingCharm(c, "logging") 172 for _, name := range []string{"subsvc0", "subsvc1"} { 173 s.AddTestingService(c, name, logging) 174 eps, err := s.State.InferEndpoints("wordpress", name) 175 c.Assert(err, jc.ErrorIsNil) 176 rel, err := s.State.AddRelation(eps...) 177 c.Assert(err, jc.ErrorIsNil) 178 ru, err := rel.Unit(u) 179 c.Assert(err, jc.ErrorIsNil) 180 rus = append(rus, ru) 181 } 182 return u, rus 183 } 184 185 func (s *deployerSuite) TestDeployRecallRemoveSubordinates(c *gc.C) { 186 // Create a deployer acting on behalf of the principal. 187 u, rus := s.prepareSubordinates(c) 188 dep, ctx := s.makeDeployerAndContext(c) 189 defer stop(c, dep) 190 191 // Add a subordinate, and wait for it to be deployed. 192 err := rus[0].EnterScope(nil) 193 c.Assert(err, jc.ErrorIsNil) 194 sub0, err := s.State.Unit("subsvc0/0") 195 c.Assert(err, jc.ErrorIsNil) 196 // Make sure the principal is deployed first, then the subordinate 197 s.waitFor(c, isDeployed(ctx, u.Name(), sub0.Name())) 198 199 // And another. 200 err = rus[1].EnterScope(nil) 201 c.Assert(err, jc.ErrorIsNil) 202 sub1, err := s.State.Unit("subsvc1/0") 203 c.Assert(err, jc.ErrorIsNil) 204 s.waitFor(c, isDeployed(ctx, u.Name(), sub0.Name(), sub1.Name())) 205 206 // Set one to Dying; check nothing happens. 207 err = sub1.Destroy() 208 c.Assert(err, jc.ErrorIsNil) 209 s.State.StartSync() 210 c.Assert(isRemoved(s.State, sub1.Name())(c), jc.IsFalse) 211 s.waitFor(c, isDeployed(ctx, u.Name(), sub0.Name(), sub1.Name())) 212 213 // Set the other to Dead; check it's recalled and removed. 214 err = sub0.EnsureDead() 215 c.Assert(err, jc.ErrorIsNil) 216 s.waitFor(c, isDeployed(ctx, u.Name(), sub1.Name())) 217 s.waitFor(c, isRemoved(s.State, sub0.Name())) 218 } 219 220 func (s *deployerSuite) TestNonAliveSubordinates(c *gc.C) { 221 // Add two subordinate units and set them to Dead/Dying respectively. 222 _, rus := s.prepareSubordinates(c) 223 err := rus[0].EnterScope(nil) 224 c.Assert(err, jc.ErrorIsNil) 225 sub0, err := s.State.Unit("subsvc0/0") 226 c.Assert(err, jc.ErrorIsNil) 227 err = sub0.EnsureDead() 228 c.Assert(err, jc.ErrorIsNil) 229 err = rus[1].EnterScope(nil) 230 c.Assert(err, jc.ErrorIsNil) 231 sub1, err := s.State.Unit("subsvc1/0") 232 c.Assert(err, jc.ErrorIsNil) 233 err = sub1.Destroy() 234 c.Assert(err, jc.ErrorIsNil) 235 236 // When we start a new deployer, neither unit will be deployed and 237 // both will be removed. 238 dep, _ := s.makeDeployerAndContext(c) 239 defer stop(c, dep) 240 s.waitFor(c, isRemoved(s.State, sub0.Name())) 241 s.waitFor(c, isRemoved(s.State, sub1.Name())) 242 } 243 244 func (s *deployerSuite) waitFor(c *gc.C, t func(c *gc.C) bool) { 245 s.BackingState.StartSync() 246 if t(c) { 247 return 248 } 249 timeout := time.After(coretesting.LongWait) 250 for { 251 select { 252 case <-timeout: 253 c.Fatalf("timeout") 254 case <-time.After(coretesting.ShortWait): 255 if t(c) { 256 return 257 } 258 } 259 } 260 } 261 262 func isDeployed(ctx deployer.Context, expected ...string) func(*gc.C) bool { 263 return func(c *gc.C) bool { 264 sort.Strings(expected) 265 current, err := ctx.DeployedUnits() 266 c.Assert(err, jc.ErrorIsNil) 267 sort.Strings(current) 268 return strings.Join(expected, ":") == strings.Join(current, ":") 269 } 270 } 271 272 func isRemoved(st *state.State, name string) func(*gc.C) bool { 273 return func(c *gc.C) bool { 274 _, err := st.Unit(name) 275 if errors.IsNotFound(err) { 276 return true 277 } 278 c.Assert(err, jc.ErrorIsNil) 279 return false 280 } 281 } 282 283 func unitStatus(u *state.Unit, statusInfo status.StatusInfo) func(*gc.C) bool { 284 return func(c *gc.C) bool { 285 sInfo, err := u.Status() 286 c.Assert(err, jc.ErrorIsNil) 287 return sInfo.Status == statusInfo.Status && sInfo.Message == statusInfo.Message 288 } 289 } 290 291 func stop(c *gc.C, w worker.Worker) { 292 c.Assert(worker.Stop(w), gc.IsNil) 293 }