github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/api/uniter/unit_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  	"fmt"
     8  	"time"
     9  
    10  	"github.com/juju/errors"
    11  	"github.com/juju/names"
    12  	jc "github.com/juju/testing/checkers"
    13  	gc "gopkg.in/check.v1"
    14  	"gopkg.in/juju/charm.v4"
    15  
    16  	"github.com/juju/juju/api/base"
    17  	"github.com/juju/juju/api/uniter"
    18  	"github.com/juju/juju/apiserver/params"
    19  	"github.com/juju/juju/network"
    20  	"github.com/juju/juju/state"
    21  	statetesting "github.com/juju/juju/state/testing"
    22  )
    23  
    24  type unitSuite struct {
    25  	uniterSuite
    26  
    27  	apiUnit *uniter.Unit
    28  }
    29  
    30  var _ = gc.Suite(&unitSuite{})
    31  
    32  func (s *unitSuite) SetUpTest(c *gc.C) {
    33  	s.uniterSuite.SetUpTest(c)
    34  
    35  	var err error
    36  	s.apiUnit, err = s.uniter.Unit(s.wordpressUnit.Tag().(names.UnitTag))
    37  	c.Assert(err, jc.ErrorIsNil)
    38  }
    39  
    40  func (s *unitSuite) TestRequestReboot(c *gc.C) {
    41  	err := s.apiUnit.RequestReboot()
    42  	c.Assert(err, jc.ErrorIsNil)
    43  	rFlag, err := s.wordpressMachine.GetRebootFlag()
    44  	c.Assert(err, jc.ErrorIsNil)
    45  	c.Assert(rFlag, jc.IsTrue)
    46  }
    47  
    48  func (s *unitSuite) TestUnitAndUnitTag(c *gc.C) {
    49  	apiUnitFoo, err := s.uniter.Unit(names.NewUnitTag("foo/42"))
    50  	c.Assert(err, gc.ErrorMatches, "permission denied")
    51  	c.Assert(err, jc.Satisfies, params.IsCodeUnauthorized)
    52  	c.Assert(apiUnitFoo, gc.IsNil)
    53  
    54  	c.Assert(s.apiUnit.Tag(), gc.Equals, s.wordpressUnit.Tag().(names.UnitTag))
    55  }
    56  
    57  func (s *unitSuite) TestSetStatus(c *gc.C) {
    58  	status, info, data, err := s.wordpressUnit.Status()
    59  	c.Assert(err, jc.ErrorIsNil)
    60  	c.Assert(status, gc.Equals, state.StatusAllocating)
    61  	c.Assert(info, gc.Equals, "")
    62  	c.Assert(data, gc.HasLen, 0)
    63  
    64  	err = s.apiUnit.SetStatus(params.StatusActive, "blah", nil)
    65  	c.Assert(err, jc.ErrorIsNil)
    66  
    67  	status, info, data, err = s.wordpressUnit.Status()
    68  	c.Assert(err, jc.ErrorIsNil)
    69  	c.Assert(status, gc.Equals, state.StatusActive)
    70  	c.Assert(info, gc.Equals, "blah")
    71  	c.Assert(data, gc.HasLen, 0)
    72  }
    73  
    74  func (s *unitSuite) TestEnsureDead(c *gc.C) {
    75  	c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Alive)
    76  
    77  	err := s.apiUnit.EnsureDead()
    78  	c.Assert(err, jc.ErrorIsNil)
    79  
    80  	err = s.wordpressUnit.Refresh()
    81  	c.Assert(err, jc.ErrorIsNil)
    82  	c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Dead)
    83  
    84  	err = s.apiUnit.EnsureDead()
    85  	c.Assert(err, jc.ErrorIsNil)
    86  	err = s.wordpressUnit.Refresh()
    87  	c.Assert(err, jc.ErrorIsNil)
    88  	c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Dead)
    89  
    90  	err = s.wordpressUnit.Remove()
    91  	c.Assert(err, jc.ErrorIsNil)
    92  	err = s.wordpressUnit.Refresh()
    93  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
    94  
    95  	err = s.apiUnit.EnsureDead()
    96  	c.Assert(err, gc.ErrorMatches, `unit "wordpress/0" not found`)
    97  	c.Assert(err, jc.Satisfies, params.IsCodeNotFound)
    98  }
    99  
   100  func (s *unitSuite) TestDestroy(c *gc.C) {
   101  	c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Alive)
   102  
   103  	err := s.apiUnit.Destroy()
   104  	c.Assert(err, jc.ErrorIsNil)
   105  
   106  	err = s.wordpressUnit.Refresh()
   107  	c.Assert(err, gc.ErrorMatches, `unit "wordpress/0" not found`)
   108  }
   109  
   110  func (s *unitSuite) TestDestroyAllSubordinates(c *gc.C) {
   111  	c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Alive)
   112  
   113  	// Call without subordinates - no change.
   114  	err := s.apiUnit.DestroyAllSubordinates()
   115  	c.Assert(err, jc.ErrorIsNil)
   116  
   117  	// Add a couple of subordinates and try again.
   118  	_, _, loggingSub := s.addRelatedService(c, "wordpress", "logging", s.wordpressUnit)
   119  	_, _, monitoringSub := s.addRelatedService(c, "wordpress", "monitoring", s.wordpressUnit)
   120  	c.Assert(loggingSub.Life(), gc.Equals, state.Alive)
   121  	c.Assert(monitoringSub.Life(), gc.Equals, state.Alive)
   122  
   123  	err = s.apiUnit.DestroyAllSubordinates()
   124  	c.Assert(err, jc.ErrorIsNil)
   125  
   126  	// Verify they got destroyed.
   127  	err = loggingSub.Refresh()
   128  	c.Assert(err, jc.ErrorIsNil)
   129  	c.Assert(loggingSub.Life(), gc.Equals, state.Dying)
   130  	err = monitoringSub.Refresh()
   131  	c.Assert(err, jc.ErrorIsNil)
   132  	c.Assert(monitoringSub.Life(), gc.Equals, state.Dying)
   133  }
   134  
   135  func (s *unitSuite) TestRefresh(c *gc.C) {
   136  	c.Assert(s.apiUnit.Life(), gc.Equals, params.Alive)
   137  
   138  	err := s.apiUnit.EnsureDead()
   139  	c.Assert(err, jc.ErrorIsNil)
   140  	c.Assert(s.apiUnit.Life(), gc.Equals, params.Alive)
   141  
   142  	err = s.apiUnit.Refresh()
   143  	c.Assert(err, jc.ErrorIsNil)
   144  	c.Assert(s.apiUnit.Life(), gc.Equals, params.Dead)
   145  }
   146  
   147  func (s *unitSuite) TestWatch(c *gc.C) {
   148  	c.Assert(s.apiUnit.Life(), gc.Equals, params.Alive)
   149  
   150  	w, err := s.apiUnit.Watch()
   151  	c.Assert(err, jc.ErrorIsNil)
   152  	defer statetesting.AssertStop(c, w)
   153  	wc := statetesting.NewNotifyWatcherC(c, s.BackingState, w)
   154  
   155  	// Initial event.
   156  	wc.AssertOneChange()
   157  
   158  	// Change something other than the lifecycle and make sure it's
   159  	// not detected.
   160  	err = s.apiUnit.SetStatus(params.StatusActive, "not really", nil)
   161  	c.Assert(err, jc.ErrorIsNil)
   162  	wc.AssertNoChange()
   163  
   164  	// Make the unit dead and check it's detected.
   165  	err = s.apiUnit.EnsureDead()
   166  	c.Assert(err, jc.ErrorIsNil)
   167  	wc.AssertOneChange()
   168  
   169  	statetesting.AssertStop(c, w)
   170  	wc.AssertClosed()
   171  }
   172  
   173  func (s *unitSuite) TestResolve(c *gc.C) {
   174  	err := s.wordpressUnit.SetResolved(state.ResolvedRetryHooks)
   175  	c.Assert(err, jc.ErrorIsNil)
   176  
   177  	mode, err := s.apiUnit.Resolved()
   178  	c.Assert(err, jc.ErrorIsNil)
   179  	c.Assert(mode, gc.Equals, params.ResolvedRetryHooks)
   180  
   181  	err = s.apiUnit.ClearResolved()
   182  	c.Assert(err, jc.ErrorIsNil)
   183  
   184  	mode, err = s.apiUnit.Resolved()
   185  	c.Assert(err, jc.ErrorIsNil)
   186  	c.Assert(mode, gc.Equals, params.ResolvedNone)
   187  }
   188  
   189  func (s *unitSuite) TestAssignedMachineV0NotImplemented(c *gc.C) {
   190  	s.patchNewState(c, uniter.NewStateV0)
   191  
   192  	_, err := s.apiUnit.AssignedMachine()
   193  	c.Assert(err, jc.Satisfies, errors.IsNotImplemented)
   194  	c.Assert(err.Error(), gc.Equals, "unit.AssignedMachine() (need V1+) not implemented")
   195  }
   196  
   197  func (s *unitSuite) TestAssignedMachineV1(c *gc.C) {
   198  	s.patchNewState(c, uniter.NewStateV1)
   199  
   200  	machineTag, err := s.apiUnit.AssignedMachine()
   201  	c.Assert(err, jc.ErrorIsNil)
   202  	c.Assert(machineTag, gc.Equals, s.wordpressMachine.Tag())
   203  }
   204  
   205  func (s *unitSuite) TestIsPrincipal(c *gc.C) {
   206  	ok, err := s.apiUnit.IsPrincipal()
   207  	c.Assert(err, jc.ErrorIsNil)
   208  	c.Assert(ok, jc.IsTrue)
   209  }
   210  
   211  func (s *unitSuite) TestHasSubordinates(c *gc.C) {
   212  	found, err := s.apiUnit.HasSubordinates()
   213  	c.Assert(err, jc.ErrorIsNil)
   214  	c.Assert(found, jc.IsFalse)
   215  
   216  	// Add a couple of subordinates and try again.
   217  	s.addRelatedService(c, "wordpress", "logging", s.wordpressUnit)
   218  	s.addRelatedService(c, "wordpress", "monitoring", s.wordpressUnit)
   219  
   220  	found, err = s.apiUnit.HasSubordinates()
   221  	c.Assert(err, jc.ErrorIsNil)
   222  	c.Assert(found, jc.IsTrue)
   223  }
   224  
   225  func (s *unitSuite) TestPublicAddress(c *gc.C) {
   226  	address, err := s.apiUnit.PublicAddress()
   227  	c.Assert(err, gc.ErrorMatches, `"unit-wordpress-0" has no public address set`)
   228  
   229  	err = s.wordpressMachine.SetAddresses(network.NewAddress("1.2.3.4", network.ScopePublic))
   230  	c.Assert(err, jc.ErrorIsNil)
   231  
   232  	address, err = s.apiUnit.PublicAddress()
   233  	c.Assert(err, jc.ErrorIsNil)
   234  	c.Assert(address, gc.Equals, "1.2.3.4")
   235  }
   236  
   237  func (s *unitSuite) TestPrivateAddress(c *gc.C) {
   238  	address, err := s.apiUnit.PrivateAddress()
   239  	c.Assert(err, gc.ErrorMatches, `"unit-wordpress-0" has no private address set`)
   240  
   241  	err = s.wordpressMachine.SetAddresses(network.NewAddress("1.2.3.4", network.ScopeCloudLocal))
   242  	c.Assert(err, jc.ErrorIsNil)
   243  
   244  	address, err = s.apiUnit.PrivateAddress()
   245  	c.Assert(err, jc.ErrorIsNil)
   246  	c.Assert(address, gc.Equals, "1.2.3.4")
   247  }
   248  
   249  func (s *unitSuite) TestAvailabilityZone(c *gc.C) {
   250  	uniter.PatchUnitResponse(s, s.apiUnit, "AvailabilityZone",
   251  		func(result interface{}) error {
   252  			if results, ok := result.(*params.StringResults); ok {
   253  				results.Results = []params.StringResult{{
   254  					Result: "a-zone",
   255  				}}
   256  			}
   257  			return nil
   258  		},
   259  	)
   260  
   261  	zone, err := s.apiUnit.AvailabilityZone()
   262  	c.Assert(err, jc.ErrorIsNil)
   263  
   264  	c.Check(zone, gc.Equals, "a-zone")
   265  }
   266  
   267  func (s *unitSuite) TestOpenClosePortRanges(c *gc.C) {
   268  	ports, err := s.wordpressUnit.OpenedPorts()
   269  	c.Assert(err, jc.ErrorIsNil)
   270  	c.Assert(ports, gc.HasLen, 0)
   271  
   272  	err = s.apiUnit.OpenPorts("tcp", 1234, 1400)
   273  	c.Assert(err, jc.ErrorIsNil)
   274  	err = s.apiUnit.OpenPorts("udp", 4321, 5000)
   275  	c.Assert(err, jc.ErrorIsNil)
   276  
   277  	ports, err = s.wordpressUnit.OpenedPorts()
   278  	c.Assert(err, jc.ErrorIsNil)
   279  	// OpenedPorts returns a sorted slice.
   280  	c.Assert(ports, gc.DeepEquals, []network.PortRange{
   281  		{Protocol: "tcp", FromPort: 1234, ToPort: 1400},
   282  		{Protocol: "udp", FromPort: 4321, ToPort: 5000},
   283  	})
   284  
   285  	err = s.apiUnit.ClosePorts("udp", 4321, 5000)
   286  	c.Assert(err, jc.ErrorIsNil)
   287  
   288  	ports, err = s.wordpressUnit.OpenedPorts()
   289  	c.Assert(err, jc.ErrorIsNil)
   290  	// OpenedPorts returns a sorted slice.
   291  	c.Assert(ports, gc.DeepEquals, []network.PortRange{
   292  		{Protocol: "tcp", FromPort: 1234, ToPort: 1400},
   293  	})
   294  
   295  	err = s.apiUnit.ClosePorts("tcp", 1234, 1400)
   296  	c.Assert(err, jc.ErrorIsNil)
   297  
   298  	ports, err = s.wordpressUnit.OpenedPorts()
   299  	c.Assert(err, jc.ErrorIsNil)
   300  	c.Assert(ports, gc.HasLen, 0)
   301  }
   302  
   303  func (s *unitSuite) TestOpenClosePort(c *gc.C) {
   304  	ports, err := s.wordpressUnit.OpenedPorts()
   305  	c.Assert(err, jc.ErrorIsNil)
   306  	c.Assert(ports, gc.HasLen, 0)
   307  
   308  	err = s.apiUnit.OpenPort("tcp", 1234)
   309  	c.Assert(err, jc.ErrorIsNil)
   310  	err = s.apiUnit.OpenPort("tcp", 4321)
   311  	c.Assert(err, jc.ErrorIsNil)
   312  
   313  	ports, err = s.wordpressUnit.OpenedPorts()
   314  	c.Assert(err, jc.ErrorIsNil)
   315  	// OpenedPorts returns a sorted slice.
   316  	c.Assert(ports, gc.DeepEquals, []network.PortRange{
   317  		{Protocol: "tcp", FromPort: 1234, ToPort: 1234},
   318  		{Protocol: "tcp", FromPort: 4321, ToPort: 4321},
   319  	})
   320  
   321  	err = s.apiUnit.ClosePort("tcp", 4321)
   322  	c.Assert(err, jc.ErrorIsNil)
   323  
   324  	ports, err = s.wordpressUnit.OpenedPorts()
   325  	c.Assert(err, jc.ErrorIsNil)
   326  	// OpenedPorts returns a sorted slice.
   327  	c.Assert(ports, gc.DeepEquals, []network.PortRange{
   328  		{Protocol: "tcp", FromPort: 1234, ToPort: 1234},
   329  	})
   330  
   331  	err = s.apiUnit.ClosePort("tcp", 1234)
   332  	c.Assert(err, jc.ErrorIsNil)
   333  
   334  	ports, err = s.wordpressUnit.OpenedPorts()
   335  	c.Assert(err, jc.ErrorIsNil)
   336  	c.Assert(ports, gc.HasLen, 0)
   337  }
   338  
   339  func (s *unitSuite) TestGetSetCharmURL(c *gc.C) {
   340  	// No charm URL set yet.
   341  	curl, ok := s.wordpressUnit.CharmURL()
   342  	c.Assert(curl, gc.IsNil)
   343  	c.Assert(ok, jc.IsFalse)
   344  
   345  	// Now check the same through the API.
   346  	_, err := s.apiUnit.CharmURL()
   347  	c.Assert(err, gc.Equals, uniter.ErrNoCharmURLSet)
   348  
   349  	err = s.apiUnit.SetCharmURL(s.wordpressCharm.URL())
   350  	c.Assert(err, jc.ErrorIsNil)
   351  
   352  	curl, err = s.apiUnit.CharmURL()
   353  	c.Assert(err, jc.ErrorIsNil)
   354  	c.Assert(curl, gc.NotNil)
   355  	c.Assert(curl.String(), gc.Equals, s.wordpressCharm.String())
   356  }
   357  
   358  func (s *unitSuite) TestConfigSettings(c *gc.C) {
   359  	// Make sure ConfigSettings returns an error when
   360  	// no charm URL is set, as its state counterpart does.
   361  	settings, err := s.apiUnit.ConfigSettings()
   362  	c.Assert(err, gc.ErrorMatches, "unit charm not set")
   363  
   364  	// Now set the charm and try again.
   365  	err = s.apiUnit.SetCharmURL(s.wordpressCharm.URL())
   366  	c.Assert(err, jc.ErrorIsNil)
   367  
   368  	settings, err = s.apiUnit.ConfigSettings()
   369  	c.Assert(err, jc.ErrorIsNil)
   370  	c.Assert(settings, gc.DeepEquals, charm.Settings{
   371  		"blog-title": "My Title",
   372  	})
   373  
   374  	// Update the config and check we get the changes on the next call.
   375  	err = s.wordpressService.UpdateConfigSettings(charm.Settings{
   376  		"blog-title": "superhero paparazzi",
   377  	})
   378  	c.Assert(err, jc.ErrorIsNil)
   379  
   380  	settings, err = s.apiUnit.ConfigSettings()
   381  	c.Assert(err, jc.ErrorIsNil)
   382  	c.Assert(settings, gc.DeepEquals, charm.Settings{
   383  		"blog-title": "superhero paparazzi",
   384  	})
   385  }
   386  
   387  func (s *unitSuite) TestWatchConfigSettings(c *gc.C) {
   388  	// Make sure WatchConfigSettings returns an error when
   389  	// no charm URL is set, as its state counterpart does.
   390  	w, err := s.apiUnit.WatchConfigSettings()
   391  	c.Assert(err, gc.ErrorMatches, "unit charm not set")
   392  
   393  	// Now set the charm and try again.
   394  	err = s.apiUnit.SetCharmURL(s.wordpressCharm.URL())
   395  	c.Assert(err, jc.ErrorIsNil)
   396  
   397  	w, err = s.apiUnit.WatchConfigSettings()
   398  	defer statetesting.AssertStop(c, w)
   399  	wc := statetesting.NewNotifyWatcherC(c, s.BackingState, w)
   400  
   401  	// Initial event.
   402  	wc.AssertOneChange()
   403  
   404  	// Update config a couple of times, check a single event.
   405  	err = s.wordpressService.UpdateConfigSettings(charm.Settings{
   406  		"blog-title": "superhero paparazzi",
   407  	})
   408  	c.Assert(err, jc.ErrorIsNil)
   409  	err = s.wordpressService.UpdateConfigSettings(charm.Settings{
   410  		"blog-title": "sauceror central",
   411  	})
   412  	c.Assert(err, jc.ErrorIsNil)
   413  	wc.AssertOneChange()
   414  
   415  	// Non-change is not reported.
   416  	err = s.wordpressService.UpdateConfigSettings(charm.Settings{
   417  		"blog-title": "sauceror central",
   418  	})
   419  	c.Assert(err, jc.ErrorIsNil)
   420  	wc.AssertNoChange()
   421  
   422  	// NOTE: This test is not as exhaustive as the one in state,
   423  	// because the watcher is already tested there. Here we just
   424  	// ensure we get the events when we expect them and don't get
   425  	// them when they're not expected.
   426  
   427  	statetesting.AssertStop(c, w)
   428  	wc.AssertClosed()
   429  }
   430  
   431  func (s *unitSuite) TestWatchActionNotifications(c *gc.C) {
   432  	w, err := s.apiUnit.WatchActionNotifications()
   433  	c.Assert(err, jc.ErrorIsNil)
   434  
   435  	defer statetesting.AssertStop(c, w)
   436  	wc := statetesting.NewStringsWatcherC(c, s.BackingState, w)
   437  
   438  	// Initial event.
   439  	wc.AssertChange()
   440  
   441  	// Add a couple of actions and make sure the changes are detected.
   442  	action, err := s.wordpressUnit.AddAction("fakeaction", map[string]interface{}{
   443  		"outfile": "foo.txt",
   444  	})
   445  	c.Assert(err, jc.ErrorIsNil)
   446  	wc.AssertChange(action.Id())
   447  
   448  	action, err = s.wordpressUnit.AddAction("fakeaction", map[string]interface{}{
   449  		"outfile": "foo.bz2",
   450  		"compression": map[string]interface{}{
   451  			"kind":    "bzip",
   452  			"quality": float64(5.0),
   453  		},
   454  	})
   455  	c.Assert(err, jc.ErrorIsNil)
   456  	wc.AssertChange(action.Id())
   457  
   458  	statetesting.AssertStop(c, w)
   459  	wc.AssertClosed()
   460  }
   461  
   462  func (s *unitSuite) TestWatchActionNotificationsError(c *gc.C) {
   463  	uniter.PatchUnitResponse(s, s.apiUnit, "WatchActionNotifications",
   464  		func(result interface{}) error {
   465  			return fmt.Errorf("Test error")
   466  		},
   467  	)
   468  
   469  	_, err := s.apiUnit.WatchActionNotifications()
   470  	c.Assert(err.Error(), gc.Equals, "Test error")
   471  }
   472  
   473  func (s *unitSuite) TestWatchActionNotificationsErrorResults(c *gc.C) {
   474  	uniter.PatchUnitResponse(s, s.apiUnit, "WatchActionNotifications",
   475  		func(results interface{}) error {
   476  			if results, ok := results.(*params.StringsWatchResults); ok {
   477  				results.Results = make([]params.StringsWatchResult, 1)
   478  				results.Results[0] = params.StringsWatchResult{
   479  					Error: &params.Error{
   480  						Message: "An error in the watch result.",
   481  						Code:    params.CodeNotAssigned,
   482  					},
   483  				}
   484  			}
   485  			return nil
   486  		},
   487  	)
   488  
   489  	_, err := s.apiUnit.WatchActionNotifications()
   490  	c.Assert(err.Error(), gc.Equals, "An error in the watch result.")
   491  }
   492  
   493  func (s *unitSuite) TestWatchActionNotificationsNoResults(c *gc.C) {
   494  	uniter.PatchUnitResponse(s, s.apiUnit, "WatchActionNotifications",
   495  		func(results interface{}) error {
   496  			return nil
   497  		},
   498  	)
   499  
   500  	_, err := s.apiUnit.WatchActionNotifications()
   501  	c.Assert(err.Error(), gc.Equals, "expected 1 result, got 0")
   502  }
   503  
   504  func (s *unitSuite) TestWatchActionNotificationsMoreResults(c *gc.C) {
   505  	uniter.PatchUnitResponse(s, s.apiUnit, "WatchActionNotifications",
   506  		func(results interface{}) error {
   507  			if results, ok := results.(*params.StringsWatchResults); ok {
   508  				results.Results = make([]params.StringsWatchResult, 2)
   509  			}
   510  			return nil
   511  		},
   512  	)
   513  
   514  	_, err := s.apiUnit.WatchActionNotifications()
   515  	c.Assert(err.Error(), gc.Equals, "expected 1 result, got 2")
   516  }
   517  
   518  func (s *unitSuite) TestServiceNameAndTag(c *gc.C) {
   519  	c.Assert(s.apiUnit.ServiceName(), gc.Equals, s.wordpressService.Name())
   520  	c.Assert(s.apiUnit.ServiceTag(), gc.Equals, s.wordpressService.Tag())
   521  }
   522  
   523  func (s *unitSuite) TestJoinedRelations(c *gc.C) {
   524  	joinedRelations, err := s.apiUnit.JoinedRelations()
   525  	c.Assert(err, jc.ErrorIsNil)
   526  	c.Assert(joinedRelations, gc.HasLen, 0)
   527  
   528  	rel1, _, _ := s.addRelatedService(c, "wordpress", "monitoring", s.wordpressUnit)
   529  	joinedRelations, err = s.apiUnit.JoinedRelations()
   530  	c.Assert(err, jc.ErrorIsNil)
   531  	c.Assert(joinedRelations, gc.DeepEquals, []names.RelationTag{
   532  		rel1.Tag().(names.RelationTag),
   533  	})
   534  
   535  	rel2, _, _ := s.addRelatedService(c, "wordpress", "logging", s.wordpressUnit)
   536  	joinedRelations, err = s.apiUnit.JoinedRelations()
   537  	c.Assert(err, jc.ErrorIsNil)
   538  	c.Assert(joinedRelations, jc.SameContents, []names.RelationTag{
   539  		rel1.Tag().(names.RelationTag),
   540  		rel2.Tag().(names.RelationTag),
   541  	})
   542  }
   543  
   544  func (s *unitSuite) TestWatchAddresses(c *gc.C) {
   545  	w, err := s.apiUnit.WatchAddresses()
   546  	defer statetesting.AssertStop(c, w)
   547  	wc := statetesting.NewNotifyWatcherC(c, s.BackingState, w)
   548  
   549  	// Initial event.
   550  	wc.AssertOneChange()
   551  
   552  	// Update config a couple of times, check a single event.
   553  	err = s.wordpressMachine.SetAddresses(network.NewAddress("0.1.2.3", network.ScopeUnknown))
   554  	c.Assert(err, jc.ErrorIsNil)
   555  	err = s.wordpressMachine.SetAddresses(network.NewAddress("0.1.2.4", network.ScopeUnknown))
   556  	c.Assert(err, jc.ErrorIsNil)
   557  	wc.AssertOneChange()
   558  
   559  	// Non-change is not reported.
   560  	err = s.wordpressMachine.SetAddresses(network.NewAddress("0.1.2.4", network.ScopeUnknown))
   561  	c.Assert(err, jc.ErrorIsNil)
   562  	wc.AssertNoChange()
   563  
   564  	// NOTE: This test is not as exhaustive as the one in state,
   565  	// because the watcher is already tested there. Here we just
   566  	// ensure we get the events when we expect them and don't get
   567  	// them when they're not expected.
   568  
   569  	statetesting.AssertStop(c, w)
   570  	wc.AssertClosed()
   571  }
   572  
   573  func (s *unitSuite) TestWatchAddressesErrors(c *gc.C) {
   574  	err := s.wordpressUnit.UnassignFromMachine()
   575  	c.Assert(err, jc.ErrorIsNil)
   576  	_, err = s.apiUnit.WatchAddresses()
   577  	c.Assert(err, jc.Satisfies, params.IsCodeNotAssigned)
   578  }
   579  
   580  func (s *unitSuite) TestAddMetrics(c *gc.C) {
   581  	uniter.PatchUnitResponse(s, s.apiUnit, "AddMetrics",
   582  		func(results interface{}) error {
   583  			result := results.(*params.ErrorResults)
   584  			result.Results = make([]params.ErrorResult, 1)
   585  			return nil
   586  		},
   587  	)
   588  	metrics := []params.Metric{{"A", "23", time.Now()}, {"B", "27.0", time.Now()}}
   589  	err := s.apiUnit.AddMetrics(metrics)
   590  	c.Assert(err, jc.ErrorIsNil)
   591  }
   592  
   593  func (s *unitSuite) TestAddMetricsError(c *gc.C) {
   594  	uniter.PatchUnitResponse(s, s.apiUnit, "AddMetrics",
   595  		func(results interface{}) error {
   596  			result := results.(*params.ErrorResults)
   597  			result.Results = make([]params.ErrorResult, 1)
   598  			return fmt.Errorf("test error")
   599  		},
   600  	)
   601  	metrics := []params.Metric{{"A", "23", time.Now()}, {"B", "27.0", time.Now()}}
   602  	err := s.apiUnit.AddMetrics(metrics)
   603  	c.Assert(err, gc.ErrorMatches, "unable to add metric: test error")
   604  }
   605  
   606  func (s *unitSuite) TestAddMetricsResultError(c *gc.C) {
   607  	uniter.PatchUnitResponse(s, s.apiUnit, "AddMetrics",
   608  		func(results interface{}) error {
   609  			result := results.(*params.ErrorResults)
   610  			result.Results = make([]params.ErrorResult, 1)
   611  			result.Results[0].Error = &params.Error{
   612  				Message: "error adding metrics",
   613  				Code:    params.CodeNotAssigned,
   614  			}
   615  			return nil
   616  		},
   617  	)
   618  	metrics := []params.Metric{{"A", "23", time.Now()}, {"B", "27.0", time.Now()}}
   619  	err := s.apiUnit.AddMetrics(metrics)
   620  	c.Assert(err, gc.ErrorMatches, "error adding metrics")
   621  }
   622  
   623  func (s *unitSuite) TestMeterStatus(c *gc.C) {
   624  	uniter.PatchUnitResponse(s, s.apiUnit, "GetMeterStatus",
   625  		func(results interface{}) error {
   626  			result := results.(*params.MeterStatusResults)
   627  			result.Results = make([]params.MeterStatusResult, 1)
   628  			result.Results[0].Code = "GREEN"
   629  			result.Results[0].Info = "All ok."
   630  			return nil
   631  		},
   632  	)
   633  	statusCode, statusInfo, err := s.apiUnit.MeterStatus()
   634  	c.Assert(err, jc.ErrorIsNil)
   635  	c.Assert(statusCode, gc.Equals, "GREEN")
   636  	c.Assert(statusInfo, gc.Equals, "All ok.")
   637  }
   638  
   639  func (s *unitSuite) TestMeterStatusError(c *gc.C) {
   640  	uniter.PatchUnitResponse(s, s.apiUnit, "GetMeterStatus",
   641  		func(results interface{}) error {
   642  			result := results.(*params.MeterStatusResults)
   643  			result.Results = make([]params.MeterStatusResult, 1)
   644  			return fmt.Errorf("boo")
   645  		},
   646  	)
   647  	statusCode, statusInfo, err := s.apiUnit.MeterStatus()
   648  	c.Assert(err, gc.ErrorMatches, "boo")
   649  	c.Assert(statusCode, gc.Equals, "")
   650  	c.Assert(statusInfo, gc.Equals, "")
   651  }
   652  
   653  func (s *unitSuite) TestMeterStatusResultError(c *gc.C) {
   654  	uniter.PatchUnitResponse(s, s.apiUnit, "GetMeterStatus",
   655  		func(results interface{}) error {
   656  			result := results.(*params.MeterStatusResults)
   657  			result.Results = make([]params.MeterStatusResult, 1)
   658  			result.Results[0].Error = &params.Error{
   659  				Message: "error getting meter status",
   660  				Code:    params.CodeNotAssigned,
   661  			}
   662  			return nil
   663  		},
   664  	)
   665  	statusCode, statusInfo, err := s.apiUnit.MeterStatus()
   666  	c.Assert(err, gc.ErrorMatches, "error getting meter status")
   667  	c.Assert(statusCode, gc.Equals, "")
   668  	c.Assert(statusInfo, gc.Equals, "")
   669  }
   670  
   671  func (s *unitSuite) TestWatchMeterStatus(c *gc.C) {
   672  	w, err := s.apiUnit.WatchMeterStatus()
   673  	defer statetesting.AssertStop(c, w)
   674  	wc := statetesting.NewNotifyWatcherC(c, s.BackingState, w)
   675  
   676  	// Initial event.
   677  	wc.AssertOneChange()
   678  
   679  	err = s.wordpressUnit.SetMeterStatus("GREEN", "ok")
   680  	c.Assert(err, jc.ErrorIsNil)
   681  	err = s.wordpressUnit.SetMeterStatus("AMBER", "ok")
   682  	c.Assert(err, jc.ErrorIsNil)
   683  	wc.AssertOneChange()
   684  
   685  	// Non-change is not reported.
   686  	err = s.wordpressUnit.SetMeterStatus("AMBER", "ok")
   687  	c.Assert(err, jc.ErrorIsNil)
   688  	wc.AssertNoChange()
   689  
   690  	statetesting.AssertStop(c, w)
   691  	wc.AssertClosed()
   692  }
   693  
   694  func (s *unitSuite) patchNewState(
   695  	c *gc.C,
   696  	patchFunc func(_ base.APICaller, _ names.UnitTag) *uniter.State,
   697  ) {
   698  	s.uniterSuite.patchNewState(c, patchFunc)
   699  	var err error
   700  	s.apiUnit, err = s.uniter.Unit(s.wordpressUnit.Tag().(names.UnitTag))
   701  	c.Assert(err, jc.ErrorIsNil)
   702  }