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