github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/state/life_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state_test 5 6 import ( 7 jc "github.com/juju/testing/checkers" 8 gc "gopkg.in/check.v1" 9 "gopkg.in/mgo.v2/bson" 10 11 "github.com/juju/juju/state" 12 ) 13 14 type LifeSuite struct { 15 ConnSuite 16 charm *state.Charm 17 svc *state.Service 18 } 19 20 func (s *LifeSuite) SetUpTest(c *gc.C) { 21 s.ConnSuite.SetUpTest(c) 22 s.charm = s.AddTestingCharm(c, "dummy") 23 s.svc = s.AddTestingService(c, "dummysvc", s.charm) 24 } 25 26 var _ = gc.Suite(&LifeSuite{}) 27 28 var stateChanges = []struct { 29 cached, desired state.Life 30 dbinitial, dbfinal state.Life 31 }{ 32 { 33 state.Alive, state.Dying, 34 state.Alive, state.Dying, 35 }, 36 { 37 state.Alive, state.Dying, 38 state.Dying, state.Dying, 39 }, 40 { 41 state.Alive, state.Dying, 42 state.Dead, state.Dead, 43 }, 44 { 45 state.Alive, state.Dead, 46 state.Alive, state.Dead, 47 }, 48 { 49 state.Alive, state.Dead, 50 state.Dying, state.Dead, 51 }, 52 { 53 state.Alive, state.Dead, 54 state.Dead, state.Dead, 55 }, 56 { 57 state.Dying, state.Dying, 58 state.Dying, state.Dying, 59 }, 60 { 61 state.Dying, state.Dying, 62 state.Dead, state.Dead, 63 }, 64 { 65 state.Dying, state.Dead, 66 state.Dying, state.Dead, 67 }, 68 { 69 state.Dying, state.Dead, 70 state.Dead, state.Dead, 71 }, 72 { 73 state.Dead, state.Dying, 74 state.Dead, state.Dead, 75 }, 76 { 77 state.Dead, state.Dead, 78 state.Dead, state.Dead, 79 }, 80 } 81 82 type lifeFixture interface { 83 id() (coll string, id interface{}) 84 setup(s *LifeSuite, c *gc.C) state.AgentLiving 85 } 86 87 type unitLife struct { 88 unit *state.Unit 89 st *state.State 90 } 91 92 func (l *unitLife) id() (coll string, id interface{}) { 93 return "units", state.DocID(l.st, l.unit.Name()) 94 } 95 96 func (l *unitLife) setup(s *LifeSuite, c *gc.C) state.AgentLiving { 97 unit, err := s.svc.AddUnit() 98 c.Assert(err, jc.ErrorIsNil) 99 preventUnitDestroyRemove(c, unit) 100 l.unit = unit 101 return l.unit 102 } 103 104 type machineLife struct { 105 machine *state.Machine 106 st *state.State 107 } 108 109 func (l *machineLife) id() (coll string, id interface{}) { 110 return "machines", state.DocID(l.st, l.machine.Id()) 111 } 112 113 func (l *machineLife) setup(s *LifeSuite, c *gc.C) state.AgentLiving { 114 var err error 115 l.machine, err = s.State.AddMachine("quantal", state.JobHostUnits) 116 c.Assert(err, jc.ErrorIsNil) 117 return l.machine 118 } 119 120 func (s *LifeSuite) prepareFixture(living state.Living, lfix lifeFixture, cached, dbinitial state.Life, c *gc.C) { 121 collName, id := lfix.id() 122 coll := s.MgoSuite.Session.DB("juju").C(collName) 123 124 err := coll.UpdateId(id, bson.D{{"$set", bson.D{ 125 {"life", cached}, 126 }}}) 127 c.Assert(err, jc.ErrorIsNil) 128 err = living.Refresh() 129 c.Assert(err, jc.ErrorIsNil) 130 131 err = coll.UpdateId(id, bson.D{{"$set", bson.D{ 132 {"life", dbinitial}, 133 }}}) 134 c.Assert(err, jc.ErrorIsNil) 135 } 136 137 func (s *LifeSuite) TestLifecycleStateChanges(c *gc.C) { 138 for i, lfix := range []lifeFixture{&unitLife{st: s.State}, &machineLife{st: s.State}} { 139 c.Logf("fixture %d", i) 140 for j, v := range stateChanges { 141 c.Logf("sequence %d", j) 142 living := lfix.setup(s, c) 143 s.prepareFixture(living, lfix, v.cached, v.dbinitial, c) 144 switch v.desired { 145 case state.Dying: 146 err := living.Destroy() 147 c.Assert(err, jc.ErrorIsNil) 148 case state.Dead: 149 err := living.EnsureDead() 150 c.Assert(err, jc.ErrorIsNil) 151 default: 152 panic("desired lifecycle can only be dying or dead") 153 } 154 err := living.Refresh() 155 c.Assert(err, jc.ErrorIsNil) 156 c.Assert(living.Life(), gc.Equals, v.dbfinal) 157 err = living.EnsureDead() 158 c.Assert(err, jc.ErrorIsNil) 159 err = living.Remove() 160 c.Assert(err, jc.ErrorIsNil) 161 } 162 } 163 } 164 165 func (s *LifeSuite) TestLifeString(c *gc.C) { 166 var tests = []struct { 167 life state.Life 168 want string 169 }{ 170 {state.Alive, "alive"}, 171 {state.Dying, "dying"}, 172 {state.Dead, "dead"}, 173 {42, "unknown"}, 174 } 175 for _, test := range tests { 176 got := test.life.String() 177 c.Assert(got, gc.Equals, test.want) 178 } 179 } 180 181 const ( 182 notAliveErr = ".*: not found or not alive" 183 deadErr = ".*: not found or dead" 184 noErr = "" 185 ) 186 187 type lifer interface { 188 EnsureDead() error 189 Destroy() error 190 Life() state.Life 191 } 192 193 func runLifeChecks(c *gc.C, obj lifer, expectErr string, checks []func() error) { 194 for i, check := range checks { 195 c.Logf("check %d when %v", i, obj.Life()) 196 err := check() 197 if expectErr == noErr { 198 c.Assert(err, jc.ErrorIsNil) 199 } else { 200 c.Assert(err, gc.ErrorMatches, expectErr) 201 } 202 } 203 } 204 205 // testWhenDying sets obj to Dying and Dead in turn, and asserts 206 // that the errors from the given checks match aliveErr, dyingErr and deadErr 207 // in each respective life state. 208 func testWhenDying(c *gc.C, obj lifer, dyingErr, deadErr string, checks ...func() error) { 209 c.Logf("checking life of %v (%T)", obj, obj) 210 err := obj.Destroy() 211 c.Assert(err, jc.ErrorIsNil) 212 runLifeChecks(c, obj, dyingErr, checks) 213 err = obj.EnsureDead() 214 c.Assert(err, jc.ErrorIsNil) 215 runLifeChecks(c, obj, deadErr, checks) 216 }