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