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  }