github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/state/api/uniter/relationunit_test.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package uniter_test 5 6 import ( 7 gc "launchpad.net/gocheck" 8 9 "launchpad.net/juju-core/charm" 10 "launchpad.net/juju-core/state" 11 "launchpad.net/juju-core/state/api/params" 12 "launchpad.net/juju-core/state/api/uniter" 13 statetesting "launchpad.net/juju-core/state/testing" 14 jc "launchpad.net/juju-core/testing/checkers" 15 ) 16 17 // commonRelationSuiteMixin contains fields used by both relationSuite 18 // and relationUnitSuite. We're not just embeddnig relationUnitSuite 19 // into relationSuite to avoid running the former's tests twice. 20 type commonRelationSuiteMixin struct { 21 mysqlMachine *state.Machine 22 mysqlService *state.Service 23 mysqlCharm *state.Charm 24 mysqlUnit *state.Unit 25 26 stateRelation *state.Relation 27 } 28 29 type relationUnitSuite struct { 30 uniterSuite 31 commonRelationSuiteMixin 32 } 33 34 var _ = gc.Suite(&relationUnitSuite{}) 35 36 func (m *commonRelationSuiteMixin) SetUpTest(c *gc.C, s uniterSuite) { 37 // Create another machine, service and unit, so we can 38 // test relations and relation units. 39 m.mysqlMachine, m.mysqlService, m.mysqlCharm, m.mysqlUnit = s.addMachineServiceCharmAndUnit(c, "mysql") 40 41 // Add a relation, used by both this suite and relationSuite. 42 m.stateRelation = s.addRelation(c, "wordpress", "mysql") 43 } 44 45 func (s *relationUnitSuite) SetUpTest(c *gc.C) { 46 s.uniterSuite.SetUpTest(c) 47 s.commonRelationSuiteMixin.SetUpTest(c, s.uniterSuite) 48 } 49 50 func (s *relationUnitSuite) TearDownTest(c *gc.C) { 51 s.uniterSuite.TearDownTest(c) 52 } 53 54 func (s *relationUnitSuite) getRelationUnits(c *gc.C) (*state.RelationUnit, *uniter.RelationUnit) { 55 wpRelUnit, err := s.stateRelation.Unit(s.wordpressUnit) 56 c.Assert(err, gc.IsNil) 57 apiRelation, err := s.uniter.Relation(s.stateRelation.Tag()) 58 c.Assert(err, gc.IsNil) 59 apiUnit, err := s.uniter.Unit(s.wordpressUnit.Tag()) 60 c.Assert(err, gc.IsNil) 61 apiRelUnit, err := apiRelation.Unit(apiUnit) 62 c.Assert(err, gc.IsNil) 63 return wpRelUnit, apiRelUnit 64 } 65 66 func (s *relationUnitSuite) TestRelation(c *gc.C) { 67 _, apiRelUnit := s.getRelationUnits(c) 68 69 apiRel := apiRelUnit.Relation() 70 c.Assert(apiRel, gc.NotNil) 71 c.Assert(apiRel.String(), gc.Equals, "wordpress:db mysql:server") 72 } 73 74 func (s *relationUnitSuite) TestEndpoint(c *gc.C) { 75 _, apiRelUnit := s.getRelationUnits(c) 76 77 apiEndpoint := apiRelUnit.Endpoint() 78 c.Assert(apiEndpoint, gc.DeepEquals, uniter.Endpoint{ 79 charm.Relation{ 80 Name: "db", 81 Role: "requirer", 82 Interface: "mysql", 83 Optional: false, 84 Limit: 1, 85 Scope: "global", 86 }, 87 }) 88 } 89 90 func (s *relationUnitSuite) TestPrivateAddress(c *gc.C) { 91 _, apiRelUnit := s.getRelationUnits(c) 92 93 // Try getting it first without an address set. 94 address, err := apiRelUnit.PrivateAddress() 95 c.Assert(err, gc.ErrorMatches, `"unit-wordpress-0" has no private address set`) 96 97 // Set an address and try again. 98 err = s.wordpressUnit.SetPrivateAddress("1.2.3.4") 99 c.Assert(err, gc.IsNil) 100 address, err = apiRelUnit.PrivateAddress() 101 c.Assert(err, gc.IsNil) 102 c.Assert(address, gc.Equals, "1.2.3.4") 103 } 104 105 func (s *relationUnitSuite) TestEnterScopeSuccessfully(c *gc.C) { 106 // NOTE: This test is not as exhaustive as the ones in state. 107 // Here, we just check the success case, while the two error 108 // cases are tested separately. 109 wpRelUnit, apiRelUnit := s.getRelationUnits(c) 110 s.assertInScope(c, wpRelUnit, false) 111 112 err := apiRelUnit.EnterScope() 113 c.Assert(err, gc.IsNil) 114 s.assertInScope(c, wpRelUnit, true) 115 } 116 117 func (s *relationUnitSuite) TestEnterScopeErrCannotEnterScope(c *gc.C) { 118 // Test the ErrCannotEnterScope gets forwarded correctly. 119 // We need to enter the scope wit the other unit first. 120 myRelUnit, err := s.stateRelation.Unit(s.mysqlUnit) 121 c.Assert(err, gc.IsNil) 122 err = myRelUnit.EnterScope(nil) 123 c.Assert(err, gc.IsNil) 124 s.assertInScope(c, myRelUnit, true) 125 126 // Now we destroy mysqlService, so the relation is be set to 127 // dying. 128 err = s.mysqlService.Destroy() 129 c.Assert(err, gc.IsNil) 130 err = s.stateRelation.Refresh() 131 c.Assert(err, gc.IsNil) 132 c.Assert(s.stateRelation.Life(), gc.Equals, state.Dying) 133 134 // Enter the scope with wordpressUnit. 135 wpRelUnit, apiRelUnit := s.getRelationUnits(c) 136 s.assertInScope(c, wpRelUnit, false) 137 err = apiRelUnit.EnterScope() 138 c.Assert(err, gc.NotNil) 139 c.Check(err, jc.Satisfies, params.IsCodeCannotEnterScope) 140 c.Check(err, gc.ErrorMatches, "cannot enter scope: unit or relation is not alive") 141 } 142 143 func (s *relationUnitSuite) TestEnterScopeErrCannotEnterScopeYet(c *gc.C) { 144 // Test the ErrCannotEnterScopeYet gets forwarded correctly. 145 // First we need to destroy the stateRelation. 146 err := s.stateRelation.Destroy() 147 c.Assert(err, gc.IsNil) 148 149 // Now we create a subordinate of wordpressUnit and enter scope. 150 subRel, _, loggingSub := s.addRelatedService(c, "wordpress", "logging", s.wordpressUnit) 151 wpRelUnit, err := subRel.Unit(s.wordpressUnit) 152 c.Assert(err, gc.IsNil) 153 s.assertInScope(c, wpRelUnit, true) 154 155 // Leave scope, destroy the subordinate and try entering again. 156 err = wpRelUnit.LeaveScope() 157 c.Assert(err, gc.IsNil) 158 s.assertInScope(c, wpRelUnit, false) 159 err = loggingSub.Destroy() 160 c.Assert(err, gc.IsNil) 161 162 apiUnit, err := s.uniter.Unit(s.wordpressUnit.Tag()) 163 c.Assert(err, gc.IsNil) 164 apiRel, err := s.uniter.Relation(subRel.Tag()) 165 c.Assert(err, gc.IsNil) 166 apiRelUnit, err := apiRel.Unit(apiUnit) 167 c.Assert(err, gc.IsNil) 168 err = apiRelUnit.EnterScope() 169 c.Assert(err, gc.NotNil) 170 c.Check(err, jc.Satisfies, params.IsCodeCannotEnterScopeYet) 171 c.Check(err, gc.ErrorMatches, "cannot enter scope yet: non-alive subordinate unit has not been removed") 172 } 173 174 func (s *relationUnitSuite) TestLeaveScope(c *gc.C) { 175 wpRelUnit, apiRelUnit := s.getRelationUnits(c) 176 s.assertInScope(c, wpRelUnit, false) 177 178 err := wpRelUnit.EnterScope(nil) 179 c.Assert(err, gc.IsNil) 180 s.assertInScope(c, wpRelUnit, true) 181 182 err = apiRelUnit.LeaveScope() 183 c.Assert(err, gc.IsNil) 184 s.assertInScope(c, wpRelUnit, false) 185 } 186 187 func (s *relationUnitSuite) TestSettings(c *gc.C) { 188 wpRelUnit, apiRelUnit := s.getRelationUnits(c) 189 settings := map[string]interface{}{ 190 "some": "settings", 191 "other": "things", 192 } 193 err := wpRelUnit.EnterScope(settings) 194 c.Assert(err, gc.IsNil) 195 s.assertInScope(c, wpRelUnit, true) 196 197 gotSettings, err := apiRelUnit.Settings() 198 c.Assert(err, gc.IsNil) 199 c.Assert(gotSettings.Map(), gc.DeepEquals, params.RelationSettings{ 200 "some": "settings", 201 "other": "things", 202 }) 203 } 204 205 func (s *relationUnitSuite) TestReadSettings(c *gc.C) { 206 // First try to read the settings which are not set. 207 myRelUnit, err := s.stateRelation.Unit(s.mysqlUnit) 208 c.Assert(err, gc.IsNil) 209 err = myRelUnit.EnterScope(nil) 210 c.Assert(err, gc.IsNil) 211 s.assertInScope(c, myRelUnit, true) 212 213 // Try reading - should be ok. 214 wpRelUnit, apiRelUnit := s.getRelationUnits(c) 215 s.assertInScope(c, wpRelUnit, false) 216 gotSettings, err := apiRelUnit.ReadSettings("mysql/0") 217 c.Assert(err, gc.IsNil) 218 c.Assert(gotSettings, gc.HasLen, 0) 219 220 // Now leave and re-enter scope with some settings. 221 settings := map[string]interface{}{ 222 "some": "settings", 223 "other": "things", 224 } 225 err = myRelUnit.LeaveScope() 226 c.Assert(err, gc.IsNil) 227 s.assertInScope(c, myRelUnit, false) 228 err = myRelUnit.EnterScope(settings) 229 c.Assert(err, gc.IsNil) 230 s.assertInScope(c, myRelUnit, true) 231 gotSettings, err = apiRelUnit.ReadSettings("mysql/0") 232 c.Assert(err, gc.IsNil) 233 c.Assert(gotSettings, gc.DeepEquals, params.RelationSettings{ 234 "some": "settings", 235 "other": "things", 236 }) 237 } 238 239 func (s *relationUnitSuite) TestWatchRelationUnits(c *gc.C) { 240 // Enter scope with mysqlUnit. 241 myRelUnit, err := s.stateRelation.Unit(s.mysqlUnit) 242 c.Assert(err, gc.IsNil) 243 err = myRelUnit.EnterScope(nil) 244 c.Assert(err, gc.IsNil) 245 s.assertInScope(c, myRelUnit, true) 246 247 apiRel, err := s.uniter.Relation(s.stateRelation.Tag()) 248 c.Assert(err, gc.IsNil) 249 apiUnit, err := s.uniter.Unit("unit-wordpress-0") 250 c.Assert(err, gc.IsNil) 251 apiRelUnit, err := apiRel.Unit(apiUnit) 252 c.Assert(err, gc.IsNil) 253 254 w, err := apiRelUnit.Watch() 255 defer statetesting.AssertStop(c, w) 256 wc := statetesting.NewRelationUnitsWatcherC(c, s.BackingState, w) 257 258 // Initial event. 259 wc.AssertChange([]string{"mysql/0"}, nil) 260 261 // Leave scope with mysqlUnit, check it's detected. 262 err = myRelUnit.LeaveScope() 263 c.Assert(err, gc.IsNil) 264 s.assertInScope(c, myRelUnit, false) 265 wc.AssertChange(nil, []string{"mysql/0"}) 266 267 // Non-change is not reported. 268 err = myRelUnit.LeaveScope() 269 c.Assert(err, gc.IsNil) 270 wc.AssertNoChange() 271 272 // NOTE: This test is not as exhaustive as the one in state, 273 // because the watcher is already tested there. Here we just 274 // ensure we get the events when we expect them and don't get 275 // them when they're not expected. 276 277 statetesting.AssertStop(c, w) 278 wc.AssertClosed() 279 }