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