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  }