launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/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 gc "launchpad.net/gocheck" 8 9 "launchpad.net/juju-core/charm" 10 "launchpad.net/juju-core/errors" 11 "launchpad.net/juju-core/state" 12 jc "launchpad.net/juju-core/testing/checkers" 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, gc.IsNil) 25 mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) 26 mysqlEP, err := mysql.Endpoint("server") 27 c.Assert(err, gc.IsNil) 28 riak := s.AddTestingService(c, "riak", s.AddTestingCharm(c, "riak")) 29 riakEP, err := riak.Endpoint("ring") 30 c.Assert(err, gc.IsNil) 31 32 // Check we can't add a relation with services that don't exist. 33 yoursqlEP := mysqlEP 34 yoursqlEP.ServiceName = "yoursql" 35 _, err = s.State.AddRelation(yoursqlEP, wordpressEP) 36 c.Assert(err, gc.ErrorMatches, `cannot add relation "wordpress:db yoursql:server": service "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, gc.IsNil) 73 err = wordpress.Destroy() 74 c.Assert(err, gc.IsNil) 75 _, err = s.State.AddRelation(mysqlEP, wordpressEP) 76 c.Assert(err, gc.ErrorMatches, `cannot add relation "wordpress:db mysql:server": service "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, gc.IsNil) 85 mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) 86 mysqlEP, err := mysql.Endpoint("server") 87 c.Assert(err, gc.IsNil) 88 expect, err := s.State.AddRelation(wordpressEP, mysqlEP) 89 c.Assert(err, gc.IsNil) 90 rel, err := s.State.EndpointsRelation(wordpressEP, mysqlEP) 91 check := func() { 92 c.Assert(err, gc.IsNil) 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 ServiceName: "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 ServiceName: "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.IsNotFoundError) 125 126 _, err = s.State.Relation(999) 127 c.Assert(err, gc.ErrorMatches, `relation 999 not found`) 128 c.Assert(err, jc.Satisfies, errors.IsNotFoundError) 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, gc.IsNil) 136 mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) 137 mysqlEP, err := mysql.Endpoint("server") 138 c.Assert(err, gc.IsNil) 139 _, err = s.State.AddRelation(wordpressEP, mysqlEP) 140 c.Assert(err, gc.IsNil) 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, gc.IsNil) 157 mysql := s.AddTestingService(c, "mysql", s.AddSeriesCharm(c, "mysql", "otherseries")) 158 mysqlEP, err := mysql.Endpoint("server") 159 c.Assert(err, gc.IsNil) 160 _, err = s.State.AddRelation(wordpressEP, mysqlEP) 161 c.Assert(err, gc.IsNil) 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, gc.IsNil) 171 logging := s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging")) 172 loggingEP, err := logging.Endpoint("info") 173 c.Assert(err, gc.IsNil) 174 _, err = s.State.AddRelation(wordpressEP, loggingEP) 175 c.Assert(err, gc.IsNil) 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, gc.IsNil) 195 logging := s.AddTestingService(c, "logging", s.AddSeriesCharm(c, "logging", "otherseries")) 196 loggingEP, err := logging.Endpoint("info") 197 c.Assert(err, gc.IsNil) 198 _, err = s.State.AddRelation(wordpressEP, loggingEP) 199 c.Assert(err, gc.ErrorMatches, `cannot add relation "logging:info wordpress:juju-info": principal and subordinate services' series must match`) 200 } 201 202 func (s *RelationSuite) TestDestroyRelation(c *gc.C) { 203 wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 204 mysql := s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql")) 205 eps, err := s.State.InferEndpoints([]string{"wordpress", "mysql"}) 206 c.Assert(err, gc.IsNil) 207 rel, err := s.State.AddRelation(eps...) 208 c.Assert(err, gc.IsNil) 209 210 // Test that the relation can be destroyed. 211 err = rel.Destroy() 212 c.Assert(err, gc.IsNil) 213 err = rel.Refresh() 214 c.Assert(err, jc.Satisfies, errors.IsNotFoundError) 215 assertNoRelations(c, wordpress) 216 assertNoRelations(c, mysql) 217 218 // Check that a second destroy is a no-op. 219 err = rel.Destroy() 220 c.Assert(err, gc.IsNil) 221 222 // Create a new relation and check that refreshing the old does not find 223 // the new. 224 _, err = s.State.AddRelation(eps...) 225 c.Assert(err, gc.IsNil) 226 err = rel.Refresh() 227 c.Assert(err, jc.Satisfies, errors.IsNotFoundError) 228 } 229 230 func (s *RelationSuite) TestDestroyPeerRelation(c *gc.C) { 231 // Check that a peer relation cannot be destroyed directly. 232 riakch := s.AddTestingCharm(c, "riak") 233 riak := s.AddTestingService(c, "riak", riakch) 234 riakEP, err := riak.Endpoint("ring") 235 c.Assert(err, gc.IsNil) 236 rel := assertOneRelation(c, riak, 0, riakEP) 237 err = rel.Destroy() 238 c.Assert(err, gc.ErrorMatches, `cannot destroy relation "riak:ring": is a peer relation`) 239 assertOneRelation(c, riak, 0, riakEP) 240 241 // Check that it is destroyed when the service is destroyed. 242 err = riak.Destroy() 243 c.Assert(err, gc.IsNil) 244 assertNoRelations(c, riak) 245 err = rel.Refresh() 246 c.Assert(err, jc.Satisfies, errors.IsNotFoundError) 247 248 // Create a new service (and hence a new relation in the background); check 249 // that refreshing the old one does not accidentally get the new one. 250 newriak := s.AddTestingService(c, "riak", riakch) 251 assertOneRelation(c, newriak, 1, riakEP) 252 err = rel.Refresh() 253 c.Assert(err, jc.Satisfies, errors.IsNotFoundError) 254 } 255 256 func assertNoRelations(c *gc.C, srv *state.Service) { 257 rels, err := srv.Relations() 258 c.Assert(err, gc.IsNil) 259 c.Assert(rels, gc.HasLen, 0) 260 } 261 262 func assertOneRelation(c *gc.C, srv *state.Service, relId int, endpoints ...state.Endpoint) *state.Relation { 263 rels, err := srv.Relations() 264 c.Assert(err, gc.IsNil) 265 c.Assert(rels, gc.HasLen, 1) 266 rel := rels[0] 267 c.Assert(rel.Id(), gc.Equals, relId) 268 name := srv.Name() 269 expectEp := endpoints[0] 270 ep, err := rel.Endpoint(name) 271 c.Assert(err, gc.IsNil) 272 c.Assert(ep, gc.DeepEquals, expectEp) 273 if len(endpoints) == 2 { 274 expectEp = endpoints[1] 275 } 276 eps, err := rel.RelatedEndpoints(name) 277 c.Assert(err, gc.IsNil) 278 c.Assert(eps, gc.DeepEquals, []state.Endpoint{expectEp}) 279 return rel 280 }