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