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