github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/state/relation_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 "github.com/juju/errors" 8 jc "github.com/juju/testing/checkers" 9 gc "gopkg.in/check.v1" 10 "gopkg.in/juju/charm.v6-unstable" 11 12 "github.com/juju/juju/state" 13 ) 14 15 type RelationSuite struct { 16 ConnSuite 17 } 18 19 var _ = gc.Suite(&RelationSuite{}) 20 21 func (s *RelationSuite) TestAddRelationErrors(c *gc.C) { 22 wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 23 wordpressEP, err := wordpress.Endpoint("db") 24 c.Assert(err, jc.ErrorIsNil) 25 mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) 26 mysqlEP, err := mysql.Endpoint("server") 27 c.Assert(err, jc.ErrorIsNil) 28 riak := s.AddTestingService(c, "riak", s.AddTestingCharm(c, "riak")) 29 riakEP, err := riak.Endpoint("ring") 30 c.Assert(err, jc.ErrorIsNil) 31 32 // Check we can't add a relation with services that don't exist. 33 yoursqlEP := mysqlEP 34 yoursqlEP.ApplicationName = "yoursql" 35 _, err = s.State.AddRelation(yoursqlEP, wordpressEP) 36 c.Assert(err, gc.ErrorMatches, `cannot add relation "wordpress:db yoursql:server": application "yoursql" does not exist`) 37 assertNoRelations(c, wordpress) 38 assertNoRelations(c, mysql) 39 40 // Check that interfaces have to match. 41 msep3 := mysqlEP 42 msep3.Interface = "roflcopter" 43 _, err = s.State.AddRelation(msep3, wordpressEP) 44 c.Assert(err, gc.ErrorMatches, `cannot add relation "wordpress:db mysql:server": endpoints do not relate`) 45 assertNoRelations(c, wordpress) 46 assertNoRelations(c, mysql) 47 48 // Check a variety of surprising endpoint combinations. 49 _, err = s.State.AddRelation(wordpressEP) 50 c.Assert(err, gc.ErrorMatches, `cannot add relation "wordpress:db": relation must have two endpoints`) 51 assertNoRelations(c, wordpress) 52 53 _, err = s.State.AddRelation(riakEP, wordpressEP) 54 c.Assert(err, gc.ErrorMatches, `cannot add relation "wordpress:db riak:ring": endpoints do not relate`) 55 assertOneRelation(c, riak, 0, riakEP) 56 assertNoRelations(c, wordpress) 57 58 _, err = s.State.AddRelation(riakEP, riakEP) 59 c.Assert(err, gc.ErrorMatches, `cannot add relation "riak:ring riak:ring": endpoints do not relate`) 60 assertOneRelation(c, riak, 0, riakEP) 61 62 _, err = s.State.AddRelation() 63 c.Assert(err, gc.ErrorMatches, `cannot add relation "": relation must have two endpoints`) 64 _, err = s.State.AddRelation(mysqlEP, wordpressEP, riakEP) 65 c.Assert(err, gc.ErrorMatches, `cannot add relation "wordpress:db mysql:server riak:ring": relation must have two endpoints`) 66 assertOneRelation(c, riak, 0, riakEP) 67 assertNoRelations(c, wordpress) 68 assertNoRelations(c, mysql) 69 70 // Check that a relation can't be added to a Dying service. 71 _, err = wordpress.AddUnit() 72 c.Assert(err, jc.ErrorIsNil) 73 err = wordpress.Destroy() 74 c.Assert(err, jc.ErrorIsNil) 75 _, err = s.State.AddRelation(mysqlEP, wordpressEP) 76 c.Assert(err, gc.ErrorMatches, `cannot add relation "wordpress:db mysql:server": application "wordpress" is not alive`) 77 assertNoRelations(c, wordpress) 78 assertNoRelations(c, mysql) 79 } 80 81 func (s *RelationSuite) TestRetrieveSuccess(c *gc.C) { 82 wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 83 wordpressEP, err := wordpress.Endpoint("db") 84 c.Assert(err, jc.ErrorIsNil) 85 mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) 86 mysqlEP, err := mysql.Endpoint("server") 87 c.Assert(err, jc.ErrorIsNil) 88 expect, err := s.State.AddRelation(wordpressEP, mysqlEP) 89 c.Assert(err, jc.ErrorIsNil) 90 rel, err := s.State.EndpointsRelation(wordpressEP, mysqlEP) 91 check := func() { 92 c.Assert(err, jc.ErrorIsNil) 93 c.Assert(rel.Id(), gc.Equals, expect.Id()) 94 c.Assert(rel.String(), gc.Equals, expect.String()) 95 } 96 check() 97 rel, err = s.State.EndpointsRelation(mysqlEP, wordpressEP) 98 check() 99 rel, err = s.State.Relation(expect.Id()) 100 check() 101 } 102 103 func (s *RelationSuite) TestRetrieveNotFound(c *gc.C) { 104 subway := state.Endpoint{ 105 ApplicationName: "subway", 106 Relation: charm.Relation{ 107 Name: "db", 108 Interface: "mongodb", 109 Role: charm.RoleRequirer, 110 Scope: charm.ScopeGlobal, 111 }, 112 } 113 mongo := state.Endpoint{ 114 ApplicationName: "mongo", 115 Relation: charm.Relation{ 116 Name: "server", 117 Interface: "mongodb", 118 Role: charm.RoleProvider, 119 Scope: charm.ScopeGlobal, 120 }, 121 } 122 _, err := s.State.EndpointsRelation(subway, mongo) 123 c.Assert(err, gc.ErrorMatches, `relation "subway:db mongo:server" not found`) 124 c.Assert(err, jc.Satisfies, errors.IsNotFound) 125 126 _, err = s.State.Relation(999) 127 c.Assert(err, gc.ErrorMatches, `relation 999 not found`) 128 c.Assert(err, jc.Satisfies, errors.IsNotFound) 129 } 130 131 func (s *RelationSuite) TestAddRelation(c *gc.C) { 132 // Add a relation. 133 wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 134 wordpressEP, err := wordpress.Endpoint("db") 135 c.Assert(err, jc.ErrorIsNil) 136 mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) 137 mysqlEP, err := mysql.Endpoint("server") 138 c.Assert(err, jc.ErrorIsNil) 139 _, err = s.State.AddRelation(wordpressEP, mysqlEP) 140 c.Assert(err, jc.ErrorIsNil) 141 assertOneRelation(c, mysql, 0, mysqlEP, wordpressEP) 142 assertOneRelation(c, wordpress, 0, wordpressEP, mysqlEP) 143 144 // Check we cannot re-add the same relation, regardless of endpoint ordering. 145 _, err = s.State.AddRelation(mysqlEP, wordpressEP) 146 c.Assert(err, gc.ErrorMatches, `cannot add relation "wordpress:db mysql:server": relation already exists`) 147 _, err = s.State.AddRelation(wordpressEP, mysqlEP) 148 c.Assert(err, gc.ErrorMatches, `cannot add relation "wordpress:db mysql:server": relation already exists`) 149 assertOneRelation(c, mysql, 0, mysqlEP, wordpressEP) 150 assertOneRelation(c, wordpress, 0, wordpressEP, mysqlEP) 151 } 152 153 func (s *RelationSuite) TestAddRelationSeriesNeedNotMatch(c *gc.C) { 154 wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 155 wordpressEP, err := wordpress.Endpoint("db") 156 c.Assert(err, jc.ErrorIsNil) 157 mysql := s.AddTestingService(c, "mysql", s.AddSeriesCharm(c, "mysql", "otherseries")) 158 mysqlEP, err := mysql.Endpoint("server") 159 c.Assert(err, jc.ErrorIsNil) 160 _, err = s.State.AddRelation(wordpressEP, mysqlEP) 161 c.Assert(err, jc.ErrorIsNil) 162 assertOneRelation(c, mysql, 0, mysqlEP, wordpressEP) 163 assertOneRelation(c, wordpress, 0, wordpressEP, mysqlEP) 164 } 165 166 func (s *RelationSuite) TestAddContainerRelation(c *gc.C) { 167 // Add a relation. 168 wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 169 wordpressEP, err := wordpress.Endpoint("juju-info") 170 c.Assert(err, jc.ErrorIsNil) 171 logging := s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging")) 172 loggingEP, err := logging.Endpoint("info") 173 c.Assert(err, jc.ErrorIsNil) 174 _, err = s.State.AddRelation(wordpressEP, loggingEP) 175 c.Assert(err, jc.ErrorIsNil) 176 177 // Check that the endpoints both have container scope. 178 wordpressEP.Scope = charm.ScopeContainer 179 assertOneRelation(c, logging, 0, loggingEP, wordpressEP) 180 assertOneRelation(c, wordpress, 0, wordpressEP, loggingEP) 181 182 // Check we cannot re-add the same relation, regardless of endpoint ordering. 183 _, err = s.State.AddRelation(loggingEP, wordpressEP) 184 c.Assert(err, gc.ErrorMatches, `cannot add relation "logging:info wordpress:juju-info": relation already exists`) 185 _, err = s.State.AddRelation(wordpressEP, loggingEP) 186 c.Assert(err, gc.ErrorMatches, `cannot add relation "logging:info wordpress:juju-info": relation already exists`) 187 assertOneRelation(c, logging, 0, loggingEP, wordpressEP) 188 assertOneRelation(c, wordpress, 0, wordpressEP, loggingEP) 189 } 190 191 func (s *RelationSuite) TestAddContainerRelationSeriesMustMatch(c *gc.C) { 192 wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 193 wordpressEP, err := wordpress.Endpoint("juju-info") 194 c.Assert(err, jc.ErrorIsNil) 195 logging := s.AddTestingService(c, "logging", s.AddSeriesCharm(c, "logging", "otherseries")) 196 loggingEP, err := logging.Endpoint("info") 197 c.Assert(err, jc.ErrorIsNil) 198 _, err = s.State.AddRelation(wordpressEP, loggingEP) 199 c.Assert(err, gc.ErrorMatches, `cannot add relation "logging:info wordpress:juju-info": principal and subordinate applications' series must match`) 200 } 201 202 func (s *RelationSuite) TestAddContainerRelationWithNoSubordinate(c *gc.C) { 203 wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 204 wordpressSubEP, err := wordpress.Endpoint("db") 205 c.Assert(err, jc.ErrorIsNil) 206 wordpressSubEP.Scope = charm.ScopeContainer 207 mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) 208 mysqlEP, err := mysql.Endpoint("server") 209 c.Assert(err, jc.ErrorIsNil) 210 211 _, err = s.State.AddRelation(mysqlEP, wordpressSubEP) 212 c.Assert(err, gc.ErrorMatches, 213 `cannot add relation "wordpress:db mysql:server": container scoped relation requires at least one subordinate application`) 214 assertNoRelations(c, wordpress) 215 assertNoRelations(c, mysql) 216 } 217 218 func (s *RelationSuite) TestAddContainerRelationWithTwoSubordinates(c *gc.C) { 219 loggingCharm := s.AddTestingCharm(c, "logging") 220 logging1 := s.AddTestingService(c, "logging1", loggingCharm) 221 logging1EP, err := logging1.Endpoint("juju-info") 222 c.Assert(err, jc.ErrorIsNil) 223 logging2 := s.AddTestingService(c, "logging2", loggingCharm) 224 logging2EP, err := logging2.Endpoint("info") 225 c.Assert(err, jc.ErrorIsNil) 226 227 _, err = s.State.AddRelation(logging1EP, logging2EP) 228 c.Assert(err, jc.ErrorIsNil) 229 // AddRelation changes the scope on the endpoint if relation is container scoped. 230 logging1EP.Scope = charm.ScopeContainer 231 assertOneRelation(c, logging1, 0, logging1EP, logging2EP) 232 assertOneRelation(c, logging2, 0, logging2EP, logging1EP) 233 } 234 235 func (s *RelationSuite) TestDestroyRelation(c *gc.C) { 236 wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 237 mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) 238 eps, err := s.State.InferEndpoints("wordpress", "mysql") 239 c.Assert(err, jc.ErrorIsNil) 240 rel, err := s.State.AddRelation(eps...) 241 c.Assert(err, jc.ErrorIsNil) 242 243 // Test that the relation can be destroyed. 244 err = rel.Destroy() 245 c.Assert(err, jc.ErrorIsNil) 246 err = rel.Refresh() 247 c.Assert(err, jc.Satisfies, errors.IsNotFound) 248 assertNoRelations(c, wordpress) 249 assertNoRelations(c, mysql) 250 251 // Check that a second destroy is a no-op. 252 err = rel.Destroy() 253 c.Assert(err, jc.ErrorIsNil) 254 255 // Create a new relation and check that refreshing the old does not find 256 // the new. 257 _, err = s.State.AddRelation(eps...) 258 c.Assert(err, jc.ErrorIsNil) 259 err = rel.Refresh() 260 c.Assert(err, jc.Satisfies, errors.IsNotFound) 261 } 262 263 func (s *RelationSuite) TestDestroyPeerRelation(c *gc.C) { 264 // Check that a peer relation cannot be destroyed directly. 265 riakch := s.AddTestingCharm(c, "riak") 266 riak := s.AddTestingService(c, "riak", riakch) 267 riakEP, err := riak.Endpoint("ring") 268 c.Assert(err, jc.ErrorIsNil) 269 rel := assertOneRelation(c, riak, 0, riakEP) 270 err = rel.Destroy() 271 c.Assert(err, gc.ErrorMatches, `cannot destroy relation "riak:ring": is a peer relation`) 272 assertOneRelation(c, riak, 0, riakEP) 273 274 // Check that it is destroyed when the service is destroyed. 275 err = riak.Destroy() 276 c.Assert(err, jc.ErrorIsNil) 277 assertNoRelations(c, riak) 278 err = rel.Refresh() 279 c.Assert(err, jc.Satisfies, errors.IsNotFound) 280 281 // Create a new service (and hence a new relation in the background); check 282 // that refreshing the old one does not accidentally get the new one. 283 newriak := s.AddTestingService(c, "riak", riakch) 284 assertOneRelation(c, newriak, 1, riakEP) 285 err = rel.Refresh() 286 c.Assert(err, jc.Satisfies, errors.IsNotFound) 287 } 288 289 func assertNoRelations(c *gc.C, srv *state.Application) { 290 rels, err := srv.Relations() 291 c.Assert(err, jc.ErrorIsNil) 292 c.Assert(rels, gc.HasLen, 0) 293 } 294 295 func assertOneRelation(c *gc.C, srv *state.Application, relId int, endpoints ...state.Endpoint) *state.Relation { 296 rels, err := srv.Relations() 297 c.Assert(err, jc.ErrorIsNil) 298 c.Assert(rels, gc.HasLen, 1) 299 300 rel := rels[0] 301 c.Assert(rel.Id(), gc.Equals, relId) 302 303 c.Assert(rel.Endpoints(), jc.SameContents, endpoints) 304 305 name := srv.Name() 306 expectEp := endpoints[0] 307 ep, err := rel.Endpoint(name) 308 c.Assert(err, jc.ErrorIsNil) 309 c.Assert(ep, gc.DeepEquals, expectEp) 310 if len(endpoints) == 2 { 311 expectEp = endpoints[1] 312 } 313 eps, err := rel.RelatedEndpoints(name) 314 c.Assert(err, jc.ErrorIsNil) 315 c.Assert(eps, gc.DeepEquals, []state.Endpoint{expectEp}) 316 return rel 317 }