github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/apiserver/uniter/uniter_base_test.go (about)

     1  // Copyright 2014 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  	"github.com/juju/utils"
    14  	gc "gopkg.in/check.v1"
    15  	"gopkg.in/juju/charm.v4"
    16  
    17  	"github.com/juju/juju/apiserver/common"
    18  	"github.com/juju/juju/apiserver/params"
    19  	apiservertesting "github.com/juju/juju/apiserver/testing"
    20  	"github.com/juju/juju/apiserver/uniter"
    21  	"github.com/juju/juju/juju/testing"
    22  	"github.com/juju/juju/network"
    23  	"github.com/juju/juju/state"
    24  	"github.com/juju/juju/state/multiwatcher"
    25  	statetesting "github.com/juju/juju/state/testing"
    26  	coretesting "github.com/juju/juju/testing"
    27  	jujuFactory "github.com/juju/juju/testing/factory"
    28  )
    29  
    30  // uniterBaseSuite implements common testing suite for all API
    31  // versions. It's not intended to be used directly or registered as a
    32  // suite, but embedded.
    33  type uniterBaseSuite struct {
    34  	testing.JujuConnSuite
    35  
    36  	authorizer apiservertesting.FakeAuthorizer
    37  	resources  *common.Resources
    38  
    39  	machine0      *state.Machine
    40  	machine1      *state.Machine
    41  	wordpress     *state.Service
    42  	wpCharm       *state.Charm
    43  	mysql         *state.Service
    44  	wordpressUnit *state.Unit
    45  	mysqlUnit     *state.Unit
    46  	meteredUnit   *state.Unit
    47  }
    48  
    49  func (s *uniterBaseSuite) setUpTest(c *gc.C) {
    50  	s.JujuConnSuite.SetUpTest(c)
    51  
    52  	factory := jujuFactory.NewFactory(s.State)
    53  	// Create two machines, two services and add a unit to each service.
    54  	s.machine0 = factory.MakeMachine(c, &jujuFactory.MachineParams{
    55  		Series: "quantal",
    56  		Jobs:   []state.MachineJob{state.JobHostUnits, state.JobManageEnviron},
    57  	})
    58  	s.machine1 = factory.MakeMachine(c, &jujuFactory.MachineParams{
    59  		Series: "quantal",
    60  		Jobs:   []state.MachineJob{state.JobHostUnits},
    61  	})
    62  	s.wpCharm = factory.MakeCharm(c, &jujuFactory.CharmParams{
    63  		Name: "wordpress",
    64  		URL:  "cs:quantal/wordpress-3",
    65  	})
    66  	s.wordpress = factory.MakeService(c, &jujuFactory.ServiceParams{
    67  		Name:    "wordpress",
    68  		Charm:   s.wpCharm,
    69  		Creator: s.AdminUserTag(c),
    70  	})
    71  	mysqlCharm := factory.MakeCharm(c, &jujuFactory.CharmParams{
    72  		Name: "mysql",
    73  	})
    74  	s.mysql = factory.MakeService(c, &jujuFactory.ServiceParams{
    75  		Name:    "mysql",
    76  		Charm:   mysqlCharm,
    77  		Creator: s.AdminUserTag(c),
    78  	})
    79  	s.wordpressUnit = factory.MakeUnit(c, &jujuFactory.UnitParams{
    80  		Service: s.wordpress,
    81  		Machine: s.machine0,
    82  	})
    83  	s.mysqlUnit = factory.MakeUnit(c, &jujuFactory.UnitParams{
    84  		Service: s.mysql,
    85  		Machine: s.machine1,
    86  	})
    87  
    88  	meteredCharm := s.Factory.MakeCharm(c, &jujuFactory.CharmParams{
    89  		Name: "metered",
    90  		URL:  "cs:quantal/metered",
    91  	})
    92  	meteredService := s.Factory.MakeService(c, &jujuFactory.ServiceParams{
    93  		Charm: meteredCharm,
    94  	})
    95  	s.meteredUnit = s.Factory.MakeUnit(c, &jujuFactory.UnitParams{
    96  		Service:     meteredService,
    97  		SetCharmURL: true,
    98  	})
    99  
   100  	// Create a FakeAuthorizer so we can check permissions,
   101  	// set up assuming unit 0 has logged in.
   102  	s.authorizer = apiservertesting.FakeAuthorizer{
   103  		Tag: s.wordpressUnit.Tag(),
   104  	}
   105  
   106  	// Create the resource registry separately to track invocations to
   107  	// Register.
   108  	s.resources = common.NewResources()
   109  	s.AddCleanup(func(_ *gc.C) { s.resources.StopAll() })
   110  }
   111  
   112  func (s *uniterBaseSuite) testUniterFailsWithNonUnitAgentUser(
   113  	c *gc.C,
   114  	factory func(_ *state.State, _ *common.Resources, _ common.Authorizer) error,
   115  ) {
   116  	anAuthorizer := s.authorizer
   117  	anAuthorizer.Tag = names.NewMachineTag("9")
   118  	err := factory(s.State, s.resources, anAuthorizer)
   119  	c.Assert(err, gc.NotNil)
   120  	c.Assert(err, gc.ErrorMatches, "permission denied")
   121  }
   122  
   123  func (s *uniterBaseSuite) testSetStatus(
   124  	c *gc.C,
   125  	facade interface {
   126  		SetStatus(args params.SetStatus) (params.ErrorResults, error)
   127  	},
   128  ) {
   129  	err := s.wordpressUnit.SetStatus(state.StatusActive, "blah", nil)
   130  	c.Assert(err, jc.ErrorIsNil)
   131  	err = s.mysqlUnit.SetStatus(state.StatusStopping, "foo", nil)
   132  	c.Assert(err, jc.ErrorIsNil)
   133  
   134  	args := params.SetStatus{
   135  		Entities: []params.EntityStatus{
   136  			{Tag: "unit-mysql-0", Status: params.StatusError, Info: "not really"},
   137  			{Tag: "unit-wordpress-0", Status: params.StatusStopping, Info: "foobar"},
   138  			{Tag: "unit-foo-42", Status: params.StatusActive, Info: "blah"},
   139  		}}
   140  	result, err := facade.SetStatus(args)
   141  	c.Assert(err, jc.ErrorIsNil)
   142  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   143  		Results: []params.ErrorResult{
   144  			{apiservertesting.ErrUnauthorized},
   145  			{nil},
   146  			{apiservertesting.ErrUnauthorized},
   147  		},
   148  	})
   149  
   150  	// Verify mysqlUnit - no change.
   151  	status, info, _, err := s.mysqlUnit.Status()
   152  	c.Assert(err, jc.ErrorIsNil)
   153  	c.Assert(status, gc.Equals, state.StatusStopping)
   154  	c.Assert(info, gc.Equals, "foo")
   155  	// ...wordpressUnit is fine though.
   156  	status, info, _, err = s.wordpressUnit.Status()
   157  	c.Assert(err, jc.ErrorIsNil)
   158  	c.Assert(status, gc.Equals, state.StatusStopping)
   159  	c.Assert(info, gc.Equals, "foobar")
   160  }
   161  
   162  func (s *uniterBaseSuite) testLife(
   163  	c *gc.C,
   164  	facade interface {
   165  		Life(args params.Entities) (params.LifeResults, error)
   166  	},
   167  ) {
   168  	// Add a relation wordpress-mysql.
   169  	rel := s.addRelation(c, "wordpress", "mysql")
   170  	relUnit, err := rel.Unit(s.wordpressUnit)
   171  	c.Assert(err, jc.ErrorIsNil)
   172  	err = relUnit.EnterScope(nil)
   173  	c.Assert(err, jc.ErrorIsNil)
   174  	c.Assert(rel.Life(), gc.Equals, state.Alive)
   175  
   176  	// Make the wordpressUnit dead.
   177  	err = s.wordpressUnit.EnsureDead()
   178  	c.Assert(err, jc.ErrorIsNil)
   179  	err = s.wordpressUnit.Refresh()
   180  	c.Assert(err, jc.ErrorIsNil)
   181  	c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Dead)
   182  
   183  	// Add another unit, so the service will stay dying when we
   184  	// destroy it later.
   185  	extraUnit, err := s.wordpress.AddUnit()
   186  	c.Assert(err, jc.ErrorIsNil)
   187  	c.Assert(extraUnit, gc.NotNil)
   188  
   189  	// Make the wordpress service dying.
   190  	err = s.wordpress.Destroy()
   191  	c.Assert(err, jc.ErrorIsNil)
   192  	err = s.wordpress.Refresh()
   193  	c.Assert(err, jc.ErrorIsNil)
   194  	c.Assert(s.wordpress.Life(), gc.Equals, state.Dying)
   195  
   196  	args := params.Entities{Entities: []params.Entity{
   197  		{Tag: "unit-mysql-0"},
   198  		{Tag: "unit-wordpress-0"},
   199  		{Tag: "unit-foo-42"},
   200  		{Tag: "service-mysql"},
   201  		{Tag: "service-wordpress"},
   202  		{Tag: "machine-0"},
   203  		{Tag: "machine-1"},
   204  		{Tag: "machine-42"},
   205  		{Tag: "service-foo"},
   206  		// TODO(dfc) these aren't valid tags any more
   207  		// but I hope to restore this test when params.Entity takes
   208  		// tags, not strings, which is coming soon.
   209  		// {Tag: "just-foo"},
   210  		{Tag: rel.Tag().String()},
   211  		{Tag: "relation-svc1.rel1#svc2.rel2"},
   212  		// {Tag: "relation-blah"},
   213  	}}
   214  	result, err := facade.Life(args)
   215  	c.Assert(err, jc.ErrorIsNil)
   216  	c.Assert(result, gc.DeepEquals, params.LifeResults{
   217  		Results: []params.LifeResult{
   218  			{Error: apiservertesting.ErrUnauthorized},
   219  			{Life: "dead"},
   220  			{Error: apiservertesting.ErrUnauthorized},
   221  			{Error: apiservertesting.ErrUnauthorized},
   222  			{Life: "dying"},
   223  			{Error: apiservertesting.ErrUnauthorized},
   224  			{Error: apiservertesting.ErrUnauthorized},
   225  			{Error: apiservertesting.ErrUnauthorized},
   226  			{Error: apiservertesting.ErrUnauthorized},
   227  			// TODO(dfc) see above
   228  			// {Error: apiservertesting.ErrUnauthorized},
   229  			{Error: apiservertesting.ErrUnauthorized},
   230  			{Error: apiservertesting.ErrUnauthorized},
   231  			// {Error: apiservertesting.ErrUnauthorized},
   232  		},
   233  	})
   234  }
   235  
   236  func (s *uniterBaseSuite) testEnsureDead(
   237  	c *gc.C,
   238  	facade interface {
   239  		EnsureDead(args params.Entities) (params.ErrorResults, error)
   240  	},
   241  ) {
   242  	c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Alive)
   243  	c.Assert(s.mysqlUnit.Life(), gc.Equals, state.Alive)
   244  
   245  	args := params.Entities{Entities: []params.Entity{
   246  		{Tag: "unit-mysql-0"},
   247  		{Tag: "unit-wordpress-0"},
   248  		{Tag: "unit-foo-42"},
   249  	}}
   250  	result, err := facade.EnsureDead(args)
   251  	c.Assert(err, jc.ErrorIsNil)
   252  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   253  		Results: []params.ErrorResult{
   254  			{apiservertesting.ErrUnauthorized},
   255  			{nil},
   256  			{apiservertesting.ErrUnauthorized},
   257  		},
   258  	})
   259  
   260  	err = s.wordpressUnit.Refresh()
   261  	c.Assert(err, jc.ErrorIsNil)
   262  	c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Dead)
   263  	err = s.mysqlUnit.Refresh()
   264  	c.Assert(err, jc.ErrorIsNil)
   265  	c.Assert(s.mysqlUnit.Life(), gc.Equals, state.Alive)
   266  
   267  	// Try it again on a Dead unit; should work.
   268  	args = params.Entities{
   269  		Entities: []params.Entity{{Tag: "unit-wordpress-0"}},
   270  	}
   271  	result, err = facade.EnsureDead(args)
   272  	c.Assert(err, jc.ErrorIsNil)
   273  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   274  		Results: []params.ErrorResult{{nil}},
   275  	})
   276  
   277  	// Verify Life is unchanged.
   278  	err = s.wordpressUnit.Refresh()
   279  	c.Assert(err, jc.ErrorIsNil)
   280  	c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Dead)
   281  }
   282  
   283  func (s *uniterBaseSuite) testWatch(
   284  	c *gc.C,
   285  	facade interface {
   286  		Watch(args params.Entities) (params.NotifyWatchResults, error)
   287  	},
   288  ) {
   289  	c.Assert(s.resources.Count(), gc.Equals, 0)
   290  
   291  	args := params.Entities{Entities: []params.Entity{
   292  		{Tag: "unit-mysql-0"},
   293  		{Tag: "unit-wordpress-0"},
   294  		{Tag: "unit-foo-42"},
   295  		{Tag: "service-mysql"},
   296  		{Tag: "service-wordpress"},
   297  		{Tag: "service-foo"},
   298  		// TODO(dfc) these aren't valid tags any more
   299  		// but I hope to restore this test when params.Entity takes
   300  		// tags, not strings, which is coming soon.
   301  		// {Tag: "just-foo"},
   302  	}}
   303  	result, err := facade.Watch(args)
   304  	c.Assert(err, jc.ErrorIsNil)
   305  	c.Assert(result, gc.DeepEquals, params.NotifyWatchResults{
   306  		Results: []params.NotifyWatchResult{
   307  			{Error: apiservertesting.ErrUnauthorized},
   308  			{NotifyWatcherId: "1"},
   309  			{Error: apiservertesting.ErrUnauthorized},
   310  			{Error: apiservertesting.ErrUnauthorized},
   311  			{NotifyWatcherId: "2"},
   312  			{Error: apiservertesting.ErrUnauthorized},
   313  			// see above
   314  			// {Error: apiservertesting.ErrUnauthorized},
   315  		},
   316  	})
   317  
   318  	// Verify the resource was registered and stop when done
   319  	c.Assert(s.resources.Count(), gc.Equals, 2)
   320  	resource1 := s.resources.Get("1")
   321  	defer statetesting.AssertStop(c, resource1)
   322  	resource2 := s.resources.Get("2")
   323  	defer statetesting.AssertStop(c, resource2)
   324  
   325  	// Check that the Watch has consumed the initial event ("returned" in
   326  	// the Watch call)
   327  	wc := statetesting.NewNotifyWatcherC(c, s.State, resource1.(state.NotifyWatcher))
   328  	wc.AssertNoChange()
   329  	wc = statetesting.NewNotifyWatcherC(c, s.State, resource2.(state.NotifyWatcher))
   330  	wc.AssertNoChange()
   331  }
   332  
   333  func (s *uniterBaseSuite) testPublicAddress(
   334  	c *gc.C,
   335  	facade interface {
   336  		PublicAddress(args params.Entities) (params.StringResults, error)
   337  	},
   338  ) {
   339  	// Try first without setting an address.
   340  	args := params.Entities{Entities: []params.Entity{
   341  		{Tag: "unit-mysql-0"},
   342  		{Tag: "unit-wordpress-0"},
   343  		{Tag: "unit-foo-42"},
   344  	}}
   345  	expectErr := &params.Error{
   346  		Code:    params.CodeNoAddressSet,
   347  		Message: `"unit-wordpress-0" has no public address set`,
   348  	}
   349  	result, err := facade.PublicAddress(args)
   350  	c.Assert(err, jc.ErrorIsNil)
   351  	c.Assert(result, gc.DeepEquals, params.StringResults{
   352  		Results: []params.StringResult{
   353  			{Error: apiservertesting.ErrUnauthorized},
   354  			{Error: expectErr},
   355  			{Error: apiservertesting.ErrUnauthorized},
   356  		},
   357  	})
   358  
   359  	// Now set it an try again.
   360  	err = s.machine0.SetAddresses(network.NewAddress("1.2.3.4", network.ScopePublic))
   361  	c.Assert(err, jc.ErrorIsNil)
   362  	address, ok := s.wordpressUnit.PublicAddress()
   363  	c.Assert(address, gc.Equals, "1.2.3.4")
   364  	c.Assert(ok, jc.IsTrue)
   365  
   366  	result, err = facade.PublicAddress(args)
   367  	c.Assert(err, jc.ErrorIsNil)
   368  	c.Assert(result, gc.DeepEquals, params.StringResults{
   369  		Results: []params.StringResult{
   370  			{Error: apiservertesting.ErrUnauthorized},
   371  			{Result: "1.2.3.4"},
   372  			{Error: apiservertesting.ErrUnauthorized},
   373  		},
   374  	})
   375  }
   376  
   377  func (s *uniterBaseSuite) testPrivateAddress(
   378  	c *gc.C,
   379  	facade interface {
   380  		PrivateAddress(args params.Entities) (params.StringResults, error)
   381  	},
   382  ) {
   383  	args := params.Entities{Entities: []params.Entity{
   384  		{Tag: "unit-mysql-0"},
   385  		{Tag: "unit-wordpress-0"},
   386  		{Tag: "unit-foo-42"},
   387  	}}
   388  	expectErr := &params.Error{
   389  		Code:    params.CodeNoAddressSet,
   390  		Message: `"unit-wordpress-0" has no private address set`,
   391  	}
   392  	result, err := facade.PrivateAddress(args)
   393  	c.Assert(err, jc.ErrorIsNil)
   394  	c.Assert(result, gc.DeepEquals, params.StringResults{
   395  		Results: []params.StringResult{
   396  			{Error: apiservertesting.ErrUnauthorized},
   397  			{Error: expectErr},
   398  			{Error: apiservertesting.ErrUnauthorized},
   399  		},
   400  	})
   401  
   402  	// Now set it and try again.
   403  	err = s.machine0.SetAddresses(network.NewAddress("1.2.3.4", network.ScopeCloudLocal))
   404  	c.Assert(err, jc.ErrorIsNil)
   405  	address, ok := s.wordpressUnit.PrivateAddress()
   406  	c.Assert(address, gc.Equals, "1.2.3.4")
   407  	c.Assert(ok, jc.IsTrue)
   408  
   409  	result, err = facade.PrivateAddress(args)
   410  	c.Assert(err, jc.ErrorIsNil)
   411  	c.Assert(result, gc.DeepEquals, params.StringResults{
   412  		Results: []params.StringResult{
   413  			{Error: apiservertesting.ErrUnauthorized},
   414  			{Result: "1.2.3.4"},
   415  			{Error: apiservertesting.ErrUnauthorized},
   416  		},
   417  	})
   418  }
   419  
   420  func (s *uniterBaseSuite) testAvailabilityZone(
   421  	c *gc.C,
   422  	facade interface {
   423  		AvailabilityZone(args params.Entities) (params.StringResults, error)
   424  	},
   425  ) {
   426  	s.PatchValue(uniter.GetZone, func(st *state.State, tag names.Tag) (string, error) {
   427  		return "a_zone", nil
   428  	})
   429  
   430  	args := params.Entities{Entities: []params.Entity{
   431  		{Tag: "unit-wordpress-0"},
   432  	}}
   433  	result, err := facade.AvailabilityZone(args)
   434  	c.Assert(err, jc.ErrorIsNil)
   435  
   436  	c.Check(result, gc.DeepEquals, params.StringResults{
   437  		Results: []params.StringResult{
   438  			{Result: "a_zone"},
   439  		},
   440  	})
   441  }
   442  
   443  func (s *uniterBaseSuite) testResolved(
   444  	c *gc.C,
   445  	facade interface {
   446  		Resolved(args params.Entities) (params.ResolvedModeResults, error)
   447  	},
   448  ) {
   449  	err := s.wordpressUnit.SetResolved(state.ResolvedRetryHooks)
   450  	c.Assert(err, jc.ErrorIsNil)
   451  	mode := s.wordpressUnit.Resolved()
   452  	c.Assert(mode, gc.Equals, state.ResolvedRetryHooks)
   453  
   454  	args := params.Entities{Entities: []params.Entity{
   455  		{Tag: "unit-mysql-0"},
   456  		{Tag: "unit-wordpress-0"},
   457  		{Tag: "unit-foo-42"},
   458  	}}
   459  	result, err := facade.Resolved(args)
   460  	c.Assert(err, jc.ErrorIsNil)
   461  	c.Assert(result, gc.DeepEquals, params.ResolvedModeResults{
   462  		Results: []params.ResolvedModeResult{
   463  			{Error: apiservertesting.ErrUnauthorized},
   464  			{Mode: params.ResolvedMode(mode)},
   465  			{Error: apiservertesting.ErrUnauthorized},
   466  		},
   467  	})
   468  }
   469  
   470  func (s *uniterBaseSuite) testClearResolved(
   471  	c *gc.C,
   472  	facade interface {
   473  		ClearResolved(args params.Entities) (params.ErrorResults, error)
   474  	},
   475  ) {
   476  	err := s.wordpressUnit.SetResolved(state.ResolvedRetryHooks)
   477  	c.Assert(err, jc.ErrorIsNil)
   478  	mode := s.wordpressUnit.Resolved()
   479  	c.Assert(mode, gc.Equals, state.ResolvedRetryHooks)
   480  
   481  	args := params.Entities{Entities: []params.Entity{
   482  		{Tag: "unit-mysql-0"},
   483  		{Tag: "unit-wordpress-0"},
   484  		{Tag: "unit-foo-42"},
   485  	}}
   486  	result, err := facade.ClearResolved(args)
   487  	c.Assert(err, jc.ErrorIsNil)
   488  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   489  		Results: []params.ErrorResult{
   490  			{apiservertesting.ErrUnauthorized},
   491  			{nil},
   492  			{apiservertesting.ErrUnauthorized},
   493  		},
   494  	})
   495  
   496  	// Verify wordpressUnit's resolved mode has changed.
   497  	err = s.wordpressUnit.Refresh()
   498  	c.Assert(err, jc.ErrorIsNil)
   499  	mode = s.wordpressUnit.Resolved()
   500  	c.Assert(mode, gc.Equals, state.ResolvedNone)
   501  }
   502  
   503  type getPrincipal interface {
   504  	GetPrincipal(args params.Entities) (params.StringBoolResults, error)
   505  }
   506  
   507  func (s *uniterBaseSuite) testGetPrincipal(
   508  	c *gc.C,
   509  	facade getPrincipal,
   510  	factory func(_ *state.State, _ *common.Resources, _ common.Authorizer) (getPrincipal, error),
   511  ) {
   512  	// Add a subordinate to wordpressUnit.
   513  	_, _, subordinate := s.addRelatedService(c, "wordpress", "logging", s.wordpressUnit)
   514  
   515  	principal, ok := subordinate.PrincipalName()
   516  	c.Assert(principal, gc.Equals, s.wordpressUnit.Name())
   517  	c.Assert(ok, jc.IsTrue)
   518  
   519  	// First try it as wordpressUnit's agent.
   520  	args := params.Entities{Entities: []params.Entity{
   521  		{Tag: "unit-mysql-0"},
   522  		{Tag: "unit-wordpress-0"},
   523  		{Tag: subordinate.Tag().String()},
   524  		{Tag: "unit-foo-42"},
   525  	}}
   526  	result, err := facade.GetPrincipal(args)
   527  	c.Assert(err, jc.ErrorIsNil)
   528  	c.Assert(result, gc.DeepEquals, params.StringBoolResults{
   529  		Results: []params.StringBoolResult{
   530  			{Error: apiservertesting.ErrUnauthorized},
   531  			{Result: "", Ok: false, Error: nil},
   532  			{Error: apiservertesting.ErrUnauthorized},
   533  			{Error: apiservertesting.ErrUnauthorized},
   534  		},
   535  	})
   536  
   537  	// Now try as subordinate's agent.
   538  	subAuthorizer := s.authorizer
   539  	subAuthorizer.Tag = subordinate.Tag()
   540  	subUniter, err := factory(s.State, s.resources, subAuthorizer)
   541  	c.Assert(err, jc.ErrorIsNil)
   542  
   543  	result, err = subUniter.GetPrincipal(args)
   544  	c.Assert(err, jc.ErrorIsNil)
   545  	c.Assert(result, gc.DeepEquals, params.StringBoolResults{
   546  		Results: []params.StringBoolResult{
   547  			{Error: apiservertesting.ErrUnauthorized},
   548  			{Error: apiservertesting.ErrUnauthorized},
   549  			{Result: "unit-wordpress-0", Ok: true, Error: nil},
   550  			{Error: apiservertesting.ErrUnauthorized},
   551  		},
   552  	})
   553  }
   554  
   555  func (s *uniterBaseSuite) testHasSubordinates(
   556  	c *gc.C,
   557  	facade interface {
   558  		HasSubordinates(args params.Entities) (params.BoolResults, error)
   559  	},
   560  ) {
   561  	// Try first without any subordinates for wordpressUnit.
   562  	args := params.Entities{Entities: []params.Entity{
   563  		{Tag: "unit-mysql-0"},
   564  		{Tag: "unit-wordpress-0"},
   565  		{Tag: "unit-logging-0"},
   566  		{Tag: "unit-foo-42"},
   567  	}}
   568  	result, err := facade.HasSubordinates(args)
   569  	c.Assert(err, jc.ErrorIsNil)
   570  	c.Assert(result, gc.DeepEquals, params.BoolResults{
   571  		Results: []params.BoolResult{
   572  			{Error: apiservertesting.ErrUnauthorized},
   573  			{Result: false},
   574  			{Error: apiservertesting.ErrUnauthorized},
   575  			{Error: apiservertesting.ErrUnauthorized},
   576  		},
   577  	})
   578  
   579  	// Add two subordinates to wordpressUnit and try again.
   580  	s.addRelatedService(c, "wordpress", "logging", s.wordpressUnit)
   581  	s.addRelatedService(c, "wordpress", "monitoring", s.wordpressUnit)
   582  
   583  	result, err = facade.HasSubordinates(args)
   584  	c.Assert(err, jc.ErrorIsNil)
   585  	c.Assert(result, gc.DeepEquals, params.BoolResults{
   586  		Results: []params.BoolResult{
   587  			{Error: apiservertesting.ErrUnauthorized},
   588  			{Result: true},
   589  			{Error: apiservertesting.ErrUnauthorized},
   590  			{Error: apiservertesting.ErrUnauthorized},
   591  		},
   592  	})
   593  }
   594  
   595  func (s *uniterBaseSuite) testDestroy(
   596  	c *gc.C,
   597  	facade interface {
   598  		Destroy(args params.Entities) (params.ErrorResults, error)
   599  	},
   600  ) {
   601  	c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Alive)
   602  
   603  	args := params.Entities{Entities: []params.Entity{
   604  		{Tag: "unit-mysql-0"},
   605  		{Tag: "unit-wordpress-0"},
   606  		{Tag: "unit-foo-42"},
   607  	}}
   608  	result, err := facade.Destroy(args)
   609  	c.Assert(err, jc.ErrorIsNil)
   610  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   611  		Results: []params.ErrorResult{
   612  			{apiservertesting.ErrUnauthorized},
   613  			{nil},
   614  			{apiservertesting.ErrUnauthorized},
   615  		},
   616  	})
   617  
   618  	// Verify wordpressUnit is destroyed and removed.
   619  	err = s.wordpressUnit.Refresh()
   620  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   621  }
   622  
   623  func (s *uniterBaseSuite) testDestroyAllSubordinates(
   624  	c *gc.C,
   625  	facade interface {
   626  		DestroyAllSubordinates(args params.Entities) (params.ErrorResults, error)
   627  	},
   628  ) {
   629  	// Add two subordinates to wordpressUnit.
   630  	_, _, loggingSub := s.addRelatedService(c, "wordpress", "logging", s.wordpressUnit)
   631  	_, _, monitoringSub := s.addRelatedService(c, "wordpress", "monitoring", s.wordpressUnit)
   632  	c.Assert(loggingSub.Life(), gc.Equals, state.Alive)
   633  	c.Assert(monitoringSub.Life(), gc.Equals, state.Alive)
   634  
   635  	err := s.wordpressUnit.Refresh()
   636  	c.Assert(err, jc.ErrorIsNil)
   637  	subordinates := s.wordpressUnit.SubordinateNames()
   638  	c.Assert(subordinates, gc.DeepEquals, []string{"logging/0", "monitoring/0"})
   639  
   640  	args := params.Entities{Entities: []params.Entity{
   641  		{Tag: "unit-mysql-0"},
   642  		{Tag: "unit-wordpress-0"},
   643  		{Tag: "unit-foo-42"},
   644  	}}
   645  	result, err := facade.DestroyAllSubordinates(args)
   646  	c.Assert(err, jc.ErrorIsNil)
   647  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   648  		Results: []params.ErrorResult{
   649  			{apiservertesting.ErrUnauthorized},
   650  			{nil},
   651  			{apiservertesting.ErrUnauthorized},
   652  		},
   653  	})
   654  
   655  	// Verify wordpressUnit's subordinates were destroyed.
   656  	err = loggingSub.Refresh()
   657  	c.Assert(err, jc.ErrorIsNil)
   658  	c.Assert(loggingSub.Life(), gc.Equals, state.Dying)
   659  	err = monitoringSub.Refresh()
   660  	c.Assert(err, jc.ErrorIsNil)
   661  	c.Assert(monitoringSub.Life(), gc.Equals, state.Dying)
   662  }
   663  
   664  func (s *uniterBaseSuite) testCharmURL(
   665  	c *gc.C,
   666  	facade interface {
   667  		CharmURL(args params.Entities) (params.StringBoolResults, error)
   668  	},
   669  ) {
   670  	// Set wordpressUnit's charm URL first.
   671  	err := s.wordpressUnit.SetCharmURL(s.wpCharm.URL())
   672  	c.Assert(err, jc.ErrorIsNil)
   673  	curl, ok := s.wordpressUnit.CharmURL()
   674  	c.Assert(curl, gc.DeepEquals, s.wpCharm.URL())
   675  	c.Assert(ok, jc.IsTrue)
   676  
   677  	// Make sure wordpress service's charm is what we expect.
   678  	curl, force := s.wordpress.CharmURL()
   679  	c.Assert(curl, gc.DeepEquals, s.wpCharm.URL())
   680  	c.Assert(force, jc.IsFalse)
   681  
   682  	args := params.Entities{Entities: []params.Entity{
   683  		{Tag: "unit-mysql-0"},
   684  		{Tag: "unit-wordpress-0"},
   685  		{Tag: "unit-foo-42"},
   686  		{Tag: "service-mysql"},
   687  		{Tag: "service-wordpress"},
   688  		{Tag: "service-foo"},
   689  		// TODO(dfc) these aren't valid tags any more
   690  		// but I hope to restore this test when params.Entity takes
   691  		// tags, not strings, which is coming soon.
   692  		// {Tag: "just-foo"},
   693  	}}
   694  	result, err := facade.CharmURL(args)
   695  	c.Assert(err, jc.ErrorIsNil)
   696  	c.Assert(result, gc.DeepEquals, params.StringBoolResults{
   697  		Results: []params.StringBoolResult{
   698  			{Error: apiservertesting.ErrUnauthorized},
   699  			{Result: s.wpCharm.String(), Ok: ok},
   700  			{Error: apiservertesting.ErrUnauthorized},
   701  			{Error: apiservertesting.ErrUnauthorized},
   702  			{Result: s.wpCharm.String(), Ok: force},
   703  			{Error: apiservertesting.ErrUnauthorized},
   704  			// see above
   705  			// {Error: apiservertesting.ErrUnauthorized},
   706  		},
   707  	})
   708  }
   709  
   710  func (s *uniterBaseSuite) testSetCharmURL(
   711  	c *gc.C,
   712  	facade interface {
   713  		SetCharmURL(args params.EntitiesCharmURL) (params.ErrorResults, error)
   714  	},
   715  ) {
   716  	_, ok := s.wordpressUnit.CharmURL()
   717  	c.Assert(ok, jc.IsFalse)
   718  
   719  	args := params.EntitiesCharmURL{Entities: []params.EntityCharmURL{
   720  		{Tag: "unit-mysql-0", CharmURL: "cs:quantal/service-42"},
   721  		{Tag: "unit-wordpress-0", CharmURL: s.wpCharm.String()},
   722  		{Tag: "unit-foo-42", CharmURL: "cs:quantal/foo-321"},
   723  	}}
   724  	result, err := facade.SetCharmURL(args)
   725  	c.Assert(err, jc.ErrorIsNil)
   726  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   727  		Results: []params.ErrorResult{
   728  			{apiservertesting.ErrUnauthorized},
   729  			{nil},
   730  			{apiservertesting.ErrUnauthorized},
   731  		},
   732  	})
   733  
   734  	// Verify the charm URL was set.
   735  	err = s.wordpressUnit.Refresh()
   736  	c.Assert(err, jc.ErrorIsNil)
   737  	charmUrl, needsUpgrade := s.wordpressUnit.CharmURL()
   738  	c.Assert(charmUrl, gc.NotNil)
   739  	c.Assert(charmUrl.String(), gc.Equals, s.wpCharm.String())
   740  	c.Assert(needsUpgrade, jc.IsTrue)
   741  }
   742  
   743  func (s *uniterBaseSuite) testOpenPorts(
   744  	c *gc.C,
   745  	facade interface {
   746  		OpenPorts(args params.EntitiesPortRanges) (params.ErrorResults, error)
   747  	},
   748  ) {
   749  	openedPorts, err := s.wordpressUnit.OpenedPorts()
   750  	c.Assert(err, jc.ErrorIsNil)
   751  	c.Assert(openedPorts, gc.HasLen, 0)
   752  
   753  	args := params.EntitiesPortRanges{Entities: []params.EntityPortRange{
   754  		{Tag: "unit-mysql-0", Protocol: "tcp", FromPort: 1234, ToPort: 1400},
   755  		{Tag: "unit-wordpress-0", Protocol: "udp", FromPort: 4321, ToPort: 5000},
   756  		{Tag: "unit-foo-42", Protocol: "tcp", FromPort: 42, ToPort: 42},
   757  	}}
   758  	result, err := facade.OpenPorts(args)
   759  	c.Assert(err, jc.ErrorIsNil)
   760  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   761  		Results: []params.ErrorResult{
   762  			{apiservertesting.ErrUnauthorized},
   763  			{nil},
   764  			{apiservertesting.ErrUnauthorized},
   765  		},
   766  	})
   767  
   768  	// Verify the wordpressUnit's port is opened.
   769  	openedPorts, err = s.wordpressUnit.OpenedPorts()
   770  	c.Assert(err, jc.ErrorIsNil)
   771  	c.Assert(openedPorts, gc.DeepEquals, []network.PortRange{
   772  		{Protocol: "udp", FromPort: 4321, ToPort: 5000},
   773  	})
   774  }
   775  
   776  func (s *uniterBaseSuite) testClosePorts(
   777  	c *gc.C,
   778  	facade interface {
   779  		ClosePorts(args params.EntitiesPortRanges) (params.ErrorResults, error)
   780  	},
   781  ) {
   782  	// Open port udp:4321 in advance on wordpressUnit.
   783  	err := s.wordpressUnit.OpenPorts("udp", 4321, 5000)
   784  	c.Assert(err, jc.ErrorIsNil)
   785  	openedPorts, err := s.wordpressUnit.OpenedPorts()
   786  	c.Assert(err, jc.ErrorIsNil)
   787  	c.Assert(openedPorts, gc.DeepEquals, []network.PortRange{
   788  		{Protocol: "udp", FromPort: 4321, ToPort: 5000},
   789  	})
   790  
   791  	args := params.EntitiesPortRanges{Entities: []params.EntityPortRange{
   792  		{Tag: "unit-mysql-0", Protocol: "tcp", FromPort: 1234, ToPort: 1400},
   793  		{Tag: "unit-wordpress-0", Protocol: "udp", FromPort: 4321, ToPort: 5000},
   794  		{Tag: "unit-foo-42", Protocol: "tcp", FromPort: 42, ToPort: 42},
   795  	}}
   796  	result, err := facade.ClosePorts(args)
   797  	c.Assert(err, jc.ErrorIsNil)
   798  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   799  		Results: []params.ErrorResult{
   800  			{apiservertesting.ErrUnauthorized},
   801  			{nil},
   802  			{apiservertesting.ErrUnauthorized},
   803  		},
   804  	})
   805  
   806  	// Verify the wordpressUnit's port is closed.
   807  	openedPorts, err = s.wordpressUnit.OpenedPorts()
   808  	c.Assert(err, jc.ErrorIsNil)
   809  	c.Assert(openedPorts, gc.HasLen, 0)
   810  }
   811  
   812  func (s *uniterBaseSuite) testOpenPort(
   813  	c *gc.C,
   814  	facade interface {
   815  		OpenPort(args params.EntitiesPorts) (params.ErrorResults, error)
   816  	},
   817  ) {
   818  	openedPorts, err := s.wordpressUnit.OpenedPorts()
   819  	c.Assert(err, jc.ErrorIsNil)
   820  	c.Assert(openedPorts, gc.HasLen, 0)
   821  
   822  	args := params.EntitiesPorts{Entities: []params.EntityPort{
   823  		{Tag: "unit-mysql-0", Protocol: "tcp", Port: 1234},
   824  		{Tag: "unit-wordpress-0", Protocol: "udp", Port: 4321},
   825  		{Tag: "unit-foo-42", Protocol: "tcp", Port: 42},
   826  	}}
   827  	result, err := facade.OpenPort(args)
   828  	c.Assert(err, jc.ErrorIsNil)
   829  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   830  		Results: []params.ErrorResult{
   831  			{apiservertesting.ErrUnauthorized},
   832  			{nil},
   833  			{apiservertesting.ErrUnauthorized},
   834  		},
   835  	})
   836  
   837  	// Verify the wordpressUnit's port is opened.
   838  	openedPorts, err = s.wordpressUnit.OpenedPorts()
   839  	c.Assert(err, jc.ErrorIsNil)
   840  	c.Assert(openedPorts, gc.DeepEquals, []network.PortRange{
   841  		{Protocol: "udp", FromPort: 4321, ToPort: 4321},
   842  	})
   843  }
   844  
   845  func (s *uniterBaseSuite) testClosePort(
   846  	c *gc.C,
   847  	facade interface {
   848  		ClosePort(args params.EntitiesPorts) (params.ErrorResults, error)
   849  	},
   850  ) {
   851  	// Open port udp:4321 in advance on wordpressUnit.
   852  	err := s.wordpressUnit.OpenPort("udp", 4321)
   853  	c.Assert(err, jc.ErrorIsNil)
   854  	openedPorts, err := s.wordpressUnit.OpenedPorts()
   855  	c.Assert(err, jc.ErrorIsNil)
   856  	c.Assert(openedPorts, gc.DeepEquals, []network.PortRange{
   857  		{Protocol: "udp", FromPort: 4321, ToPort: 4321},
   858  	})
   859  
   860  	args := params.EntitiesPorts{Entities: []params.EntityPort{
   861  		{Tag: "unit-mysql-0", Protocol: "tcp", Port: 1234},
   862  		{Tag: "unit-wordpress-0", Protocol: "udp", Port: 4321},
   863  		{Tag: "unit-foo-42", Protocol: "tcp", Port: 42},
   864  	}}
   865  	result, err := facade.ClosePort(args)
   866  	c.Assert(err, jc.ErrorIsNil)
   867  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   868  		Results: []params.ErrorResult{
   869  			{apiservertesting.ErrUnauthorized},
   870  			{nil},
   871  			{apiservertesting.ErrUnauthorized},
   872  		},
   873  	})
   874  
   875  	// Verify the wordpressUnit's port is closed.
   876  	openedPorts, err = s.wordpressUnit.OpenedPorts()
   877  	c.Assert(err, jc.ErrorIsNil)
   878  	c.Assert(openedPorts, gc.HasLen, 0)
   879  }
   880  
   881  func (s *uniterBaseSuite) testWatchConfigSettings(
   882  	c *gc.C,
   883  	facade interface {
   884  		WatchConfigSettings(args params.Entities) (params.NotifyWatchResults, error)
   885  	},
   886  ) {
   887  	err := s.wordpressUnit.SetCharmURL(s.wpCharm.URL())
   888  	c.Assert(err, jc.ErrorIsNil)
   889  
   890  	c.Assert(s.resources.Count(), gc.Equals, 0)
   891  
   892  	args := params.Entities{Entities: []params.Entity{
   893  		{Tag: "unit-mysql-0"},
   894  		{Tag: "unit-wordpress-0"},
   895  		{Tag: "unit-foo-42"},
   896  	}}
   897  	result, err := facade.WatchConfigSettings(args)
   898  	c.Assert(err, jc.ErrorIsNil)
   899  	c.Assert(result, gc.DeepEquals, params.NotifyWatchResults{
   900  		Results: []params.NotifyWatchResult{
   901  			{Error: apiservertesting.ErrUnauthorized},
   902  			{NotifyWatcherId: "1"},
   903  			{Error: apiservertesting.ErrUnauthorized},
   904  		},
   905  	})
   906  
   907  	// Verify the resource was registered and stop when done
   908  	c.Assert(s.resources.Count(), gc.Equals, 1)
   909  	resource := s.resources.Get("1")
   910  	defer statetesting.AssertStop(c, resource)
   911  
   912  	// Check that the Watch has consumed the initial event ("returned" in
   913  	// the Watch call)
   914  	wc := statetesting.NewNotifyWatcherC(c, s.State, resource.(state.NotifyWatcher))
   915  	wc.AssertNoChange()
   916  }
   917  
   918  type watchActions interface {
   919  	WatchActionNotifications(args params.Entities) (params.StringsWatchResults, error)
   920  }
   921  
   922  func (s *uniterBaseSuite) testWatchActionNotifications(c *gc.C, facade watchActions) {
   923  	err := s.wordpressUnit.SetCharmURL(s.wpCharm.URL())
   924  	c.Assert(err, jc.ErrorIsNil)
   925  
   926  	c.Assert(s.resources.Count(), gc.Equals, 0)
   927  
   928  	args := params.Entities{Entities: []params.Entity{
   929  		{Tag: "unit-mysql-0"},
   930  		{Tag: "unit-wordpress-0"},
   931  		{Tag: "unit-foo-42"},
   932  	}}
   933  	result, err := facade.WatchActionNotifications(args)
   934  	c.Assert(err, jc.ErrorIsNil)
   935  	c.Assert(result, gc.DeepEquals, params.StringsWatchResults{
   936  		Results: []params.StringsWatchResult{
   937  			{Error: apiservertesting.ErrUnauthorized},
   938  			{StringsWatcherId: "1"},
   939  			{Error: apiservertesting.ErrUnauthorized},
   940  		},
   941  	})
   942  
   943  	// Verify the resource was registered and stop when done
   944  	c.Assert(s.resources.Count(), gc.Equals, 1)
   945  	resource := s.resources.Get("1")
   946  	defer statetesting.AssertStop(c, resource)
   947  
   948  	// Check that the Watch has consumed the initial event ("returned" in
   949  	// the Watch call)
   950  	wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher))
   951  	wc.AssertNoChange()
   952  
   953  	addedAction, err := s.wordpressUnit.AddAction("fakeaction", nil)
   954  
   955  	wc.AssertChange(addedAction.Id())
   956  	wc.AssertNoChange()
   957  }
   958  
   959  func (s *uniterBaseSuite) testWatchPreexistingActions(c *gc.C, facade watchActions) {
   960  	err := s.wordpressUnit.SetCharmURL(s.wpCharm.URL())
   961  	c.Assert(err, jc.ErrorIsNil)
   962  
   963  	c.Assert(s.resources.Count(), gc.Equals, 0)
   964  
   965  	action1, err := s.wordpressUnit.AddAction("fakeaction", nil)
   966  	c.Assert(err, jc.ErrorIsNil)
   967  	action2, err := s.wordpressUnit.AddAction("fakeaction", nil)
   968  	c.Assert(err, jc.ErrorIsNil)
   969  
   970  	args := params.Entities{Entities: []params.Entity{
   971  		{Tag: "unit-wordpress-0"},
   972  	}}
   973  
   974  	s.State.StartSync()
   975  	results, err := facade.WatchActionNotifications(args)
   976  	c.Assert(err, jc.ErrorIsNil)
   977  
   978  	checkUnorderedActionIdsEqual(c, []string{action1.Id(), action2.Id()}, results)
   979  
   980  	// Verify the resource was registered and stop when done
   981  	c.Assert(s.resources.Count(), gc.Equals, 1)
   982  	resource := s.resources.Get("1")
   983  	defer statetesting.AssertStop(c, resource)
   984  
   985  	// Check that the Watch has consumed the initial event ("returned" in
   986  	// the Watch call)
   987  	wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher))
   988  	wc.AssertNoChange()
   989  
   990  	addedAction, err := s.wordpressUnit.AddAction("fakeaction", nil)
   991  	c.Assert(err, jc.ErrorIsNil)
   992  	wc.AssertChange(addedAction.Id())
   993  	wc.AssertNoChange()
   994  }
   995  
   996  func (s *uniterBaseSuite) testWatchActionNotificationsMalformedTag(c *gc.C, facade watchActions) {
   997  	args := params.Entities{Entities: []params.Entity{
   998  		{Tag: "ewenit-mysql-0"},
   999  	}}
  1000  	_, err := facade.WatchActionNotifications(args)
  1001  	c.Assert(err, gc.NotNil)
  1002  	c.Assert(err.Error(), gc.Equals, `"ewenit-mysql-0" is not a valid tag`)
  1003  }
  1004  
  1005  func (s *uniterBaseSuite) testWatchActionNotificationsMalformedUnitName(c *gc.C, facade watchActions) {
  1006  	args := params.Entities{Entities: []params.Entity{
  1007  		{Tag: "unit-mysql-01"},
  1008  	}}
  1009  	_, err := facade.WatchActionNotifications(args)
  1010  	c.Assert(err, gc.NotNil)
  1011  	c.Assert(err.Error(), gc.Equals, `"unit-mysql-01" is not a valid unit tag`)
  1012  }
  1013  
  1014  func (s *uniterBaseSuite) testWatchActionNotificationsNotUnit(c *gc.C, facade watchActions) {
  1015  	action, err := s.mysqlUnit.AddAction("fakeaction", nil)
  1016  	c.Assert(err, jc.ErrorIsNil)
  1017  	args := params.Entities{Entities: []params.Entity{
  1018  		{Tag: action.Tag().String()},
  1019  	}}
  1020  	_, err = facade.WatchActionNotifications(args)
  1021  	c.Assert(err, gc.NotNil)
  1022  	c.Assert(err.Error(), gc.Equals, `"action-`+action.Id()+`" is not a valid unit tag`)
  1023  }
  1024  
  1025  func (s *uniterBaseSuite) testWatchActionNotificationsPermissionDenied(c *gc.C, facade watchActions) {
  1026  	args := params.Entities{Entities: []params.Entity{
  1027  		{Tag: "unit-nonexistentgarbage-0"},
  1028  	}}
  1029  	results, err := facade.WatchActionNotifications(args)
  1030  	c.Assert(err, jc.ErrorIsNil)
  1031  	c.Assert(results, gc.NotNil)
  1032  	c.Assert(len(results.Results), gc.Equals, 1)
  1033  	result := results.Results[0]
  1034  	c.Assert(result.Error, gc.NotNil)
  1035  	c.Assert(result.Error.Message, gc.Equals, "permission denied")
  1036  }
  1037  
  1038  func (s *uniterBaseSuite) testConfigSettings(
  1039  	c *gc.C,
  1040  	facade interface {
  1041  		ConfigSettings(args params.Entities) (params.ConfigSettingsResults, error)
  1042  	},
  1043  ) {
  1044  	err := s.wordpressUnit.SetCharmURL(s.wpCharm.URL())
  1045  	c.Assert(err, jc.ErrorIsNil)
  1046  	settings, err := s.wordpressUnit.ConfigSettings()
  1047  	c.Assert(err, jc.ErrorIsNil)
  1048  	c.Assert(settings, gc.DeepEquals, charm.Settings{"blog-title": "My Title"})
  1049  
  1050  	args := params.Entities{Entities: []params.Entity{
  1051  		{Tag: "unit-mysql-0"},
  1052  		{Tag: "unit-wordpress-0"},
  1053  		{Tag: "unit-foo-42"},
  1054  	}}
  1055  	result, err := facade.ConfigSettings(args)
  1056  	c.Assert(err, jc.ErrorIsNil)
  1057  	c.Assert(result, gc.DeepEquals, params.ConfigSettingsResults{
  1058  		Results: []params.ConfigSettingsResult{
  1059  			{Error: apiservertesting.ErrUnauthorized},
  1060  			{Settings: params.ConfigSettings{"blog-title": "My Title"}},
  1061  			{Error: apiservertesting.ErrUnauthorized},
  1062  		},
  1063  	})
  1064  }
  1065  
  1066  func (s *uniterBaseSuite) testWatchServiceRelations(
  1067  	c *gc.C,
  1068  	facade interface {
  1069  		WatchServiceRelations(args params.Entities) (params.StringsWatchResults, error)
  1070  	},
  1071  ) {
  1072  	c.Assert(s.resources.Count(), gc.Equals, 0)
  1073  
  1074  	args := params.Entities{Entities: []params.Entity{
  1075  		{Tag: "service-mysql"},
  1076  		{Tag: "service-wordpress"},
  1077  		{Tag: "service-foo"},
  1078  	}}
  1079  	result, err := facade.WatchServiceRelations(args)
  1080  	s.assertOneStringsWatcher(c, result, err)
  1081  }
  1082  
  1083  func (s *uniterBaseSuite) testCharmArchiveSha256(
  1084  	c *gc.C,
  1085  	facade interface {
  1086  		CharmArchiveSha256(args params.CharmURLs) (params.StringResults, error)
  1087  	},
  1088  ) {
  1089  	dummyCharm := s.AddTestingCharm(c, "dummy")
  1090  
  1091  	args := params.CharmURLs{URLs: []params.CharmURL{
  1092  		{URL: "something-invalid"},
  1093  		{URL: s.wpCharm.String()},
  1094  		{URL: dummyCharm.String()},
  1095  	}}
  1096  	result, err := facade.CharmArchiveSha256(args)
  1097  	c.Assert(err, jc.ErrorIsNil)
  1098  	c.Assert(result, gc.DeepEquals, params.StringResults{
  1099  		Results: []params.StringResult{
  1100  			{Error: apiservertesting.ErrUnauthorized},
  1101  			{Result: s.wpCharm.BundleSha256()},
  1102  			{Result: dummyCharm.BundleSha256()},
  1103  		},
  1104  	})
  1105  }
  1106  
  1107  func (s *uniterBaseSuite) testCharmArchiveURLs(
  1108  	c *gc.C,
  1109  	facade interface {
  1110  		CharmArchiveURLs(args params.CharmURLs) (params.StringsResults, error)
  1111  	},
  1112  ) {
  1113  	dummyCharm := s.AddTestingCharm(c, "dummy")
  1114  
  1115  	hostPorts := [][]network.HostPort{{{
  1116  		Address: network.NewAddress("1.2.3.4", network.ScopePublic),
  1117  		Port:    1234,
  1118  	}, {
  1119  		Address: network.NewAddress("0.1.2.3", network.ScopeCloudLocal),
  1120  		Port:    1234,
  1121  	}}, {{
  1122  		Address: network.NewAddress("1.2.3.5", network.ScopePublic),
  1123  		Port:    1234,
  1124  	}}}
  1125  	err := s.State.SetAPIHostPorts(hostPorts)
  1126  	c.Assert(err, jc.ErrorIsNil)
  1127  
  1128  	args := params.CharmURLs{URLs: []params.CharmURL{
  1129  		{URL: "something-invalid"},
  1130  		{URL: s.wpCharm.String()},
  1131  		{URL: dummyCharm.String()},
  1132  	}}
  1133  	result, err := facade.CharmArchiveURLs(args)
  1134  	c.Assert(err, jc.ErrorIsNil)
  1135  
  1136  	wordpressURLs := []string{
  1137  		fmt.Sprintf("https://0.1.2.3:1234/environment/%s/charms?file=%%2A&url=cs%%3Aquantal%%2Fwordpress-3", coretesting.EnvironmentTag.Id()),
  1138  		fmt.Sprintf("https://1.2.3.5:1234/environment/%s/charms?file=%%2A&url=cs%%3Aquantal%%2Fwordpress-3", coretesting.EnvironmentTag.Id()),
  1139  	}
  1140  	dummyURLs := []string{
  1141  		fmt.Sprintf("https://0.1.2.3:1234/environment/%s/charms?file=%%2A&url=local%%3Aquantal%%2Fdummy-1", coretesting.EnvironmentTag.Id()),
  1142  		fmt.Sprintf("https://1.2.3.5:1234/environment/%s/charms?file=%%2A&url=local%%3Aquantal%%2Fdummy-1", coretesting.EnvironmentTag.Id()),
  1143  	}
  1144  
  1145  	c.Assert(result, gc.DeepEquals, params.StringsResults{
  1146  		Results: []params.StringsResult{
  1147  			{Error: apiservertesting.ErrUnauthorized},
  1148  			{Result: wordpressURLs},
  1149  			{Result: dummyURLs},
  1150  		},
  1151  	})
  1152  }
  1153  
  1154  func (s *uniterBaseSuite) testCurrentEnvironUUID(
  1155  	c *gc.C,
  1156  	facade interface {
  1157  		CurrentEnvironUUID() (params.StringResult, error)
  1158  	},
  1159  ) {
  1160  	env, err := s.State.Environment()
  1161  	c.Assert(err, jc.ErrorIsNil)
  1162  
  1163  	result, err := facade.CurrentEnvironUUID()
  1164  	c.Assert(err, jc.ErrorIsNil)
  1165  	c.Assert(result, gc.DeepEquals, params.StringResult{Result: env.UUID()})
  1166  }
  1167  
  1168  func (s *uniterBaseSuite) testCurrentEnvironment(
  1169  	c *gc.C,
  1170  	facade interface {
  1171  		CurrentEnvironment() (params.EnvironmentResult, error)
  1172  	},
  1173  ) {
  1174  	env, err := s.State.Environment()
  1175  	c.Assert(err, jc.ErrorIsNil)
  1176  
  1177  	result, err := facade.CurrentEnvironment()
  1178  	c.Assert(err, jc.ErrorIsNil)
  1179  	expected := params.EnvironmentResult{
  1180  		Name: env.Name(),
  1181  		UUID: env.UUID(),
  1182  	}
  1183  	c.Assert(result, gc.DeepEquals, expected)
  1184  }
  1185  
  1186  type actions interface {
  1187  	Actions(args params.Entities) (params.ActionsQueryResults, error)
  1188  }
  1189  
  1190  func (s *uniterBaseSuite) testActions(c *gc.C, facade actions) {
  1191  	var actionTests = []struct {
  1192  		description string
  1193  		action      params.ActionResult
  1194  	}{{
  1195  		description: "A simple action.",
  1196  		action: params.ActionResult{
  1197  			Action: &params.Action{
  1198  				Name: "fakeaction",
  1199  				Parameters: map[string]interface{}{
  1200  					"outfile": "foo.txt",
  1201  				}},
  1202  		},
  1203  	}, {
  1204  		description: "An action with nested parameters.",
  1205  		action: params.ActionResult{
  1206  			Action: &params.Action{
  1207  				Name: "fakeaction",
  1208  				Parameters: map[string]interface{}{
  1209  					"outfile": "foo.bz2",
  1210  					"compression": map[string]interface{}{
  1211  						"kind":    "bzip",
  1212  						"quality": 5,
  1213  					},
  1214  				}},
  1215  		},
  1216  	}}
  1217  
  1218  	for i, actionTest := range actionTests {
  1219  		c.Logf("test %d: %s", i, actionTest.description)
  1220  
  1221  		a, err := s.wordpressUnit.AddAction(
  1222  			actionTest.action.Action.Name,
  1223  			actionTest.action.Action.Parameters)
  1224  		c.Assert(err, jc.ErrorIsNil)
  1225  		c.Assert(names.IsValidAction(a.Id()), gc.Equals, true)
  1226  		actionTag := names.NewActionTag(a.Id())
  1227  		c.Assert(a.ActionTag(), gc.Equals, actionTag)
  1228  
  1229  		args := params.Entities{
  1230  			Entities: []params.Entity{{
  1231  				Tag: actionTag.String(),
  1232  			}},
  1233  		}
  1234  		results, err := facade.Actions(args)
  1235  		c.Assert(err, jc.ErrorIsNil)
  1236  		c.Assert(results.Results, gc.HasLen, 1)
  1237  
  1238  		actionsQueryResult := results.Results[0]
  1239  
  1240  		c.Assert(actionsQueryResult.Error, gc.IsNil)
  1241  		c.Assert(actionsQueryResult.Action, jc.DeepEquals, actionTest.action)
  1242  	}
  1243  }
  1244  
  1245  func (s *uniterBaseSuite) testActionsNotPresent(c *gc.C, facade actions) {
  1246  	uuid, err := utils.NewUUID()
  1247  	c.Assert(err, jc.ErrorIsNil)
  1248  	args := params.Entities{
  1249  		Entities: []params.Entity{{
  1250  			Tag: names.NewActionTag(uuid.String()).String(),
  1251  		}},
  1252  	}
  1253  	results, err := facade.Actions(args)
  1254  	c.Assert(err, jc.ErrorIsNil)
  1255  
  1256  	c.Assert(results.Results, gc.HasLen, 1)
  1257  	actionsQueryResult := results.Results[0]
  1258  	c.Assert(actionsQueryResult.Error, gc.NotNil)
  1259  	c.Assert(actionsQueryResult.Error, gc.ErrorMatches, `action "[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}" not found`)
  1260  }
  1261  
  1262  func (s *uniterBaseSuite) testActionsWrongUnit(
  1263  	c *gc.C,
  1264  	factory func(_ *state.State, _ *common.Resources, _ common.Authorizer) (actions, error),
  1265  ) {
  1266  	// Action doesn't match unit.
  1267  	mysqlUnitAuthorizer := apiservertesting.FakeAuthorizer{
  1268  		Tag: s.mysqlUnit.Tag(),
  1269  	}
  1270  	mysqlUnitFacade, err := factory(s.State, s.resources, mysqlUnitAuthorizer)
  1271  	c.Assert(err, jc.ErrorIsNil)
  1272  
  1273  	action, err := s.wordpressUnit.AddAction("fakeaction", nil)
  1274  	c.Assert(err, jc.ErrorIsNil)
  1275  	args := params.Entities{
  1276  		Entities: []params.Entity{{
  1277  			Tag: action.Tag().String(),
  1278  		}},
  1279  	}
  1280  	actions, err := mysqlUnitFacade.Actions(args)
  1281  	c.Assert(err, jc.ErrorIsNil)
  1282  	c.Assert(len(actions.Results), gc.Equals, 1)
  1283  	c.Assert(actions.Results[0].Error, jc.Satisfies, params.IsCodeUnauthorized)
  1284  }
  1285  
  1286  func (s *uniterBaseSuite) testActionsPermissionDenied(c *gc.C, facade actions) {
  1287  	action, err := s.mysqlUnit.AddAction("fakeaction", nil)
  1288  	c.Assert(err, jc.ErrorIsNil)
  1289  	args := params.Entities{
  1290  		Entities: []params.Entity{{
  1291  			Tag: action.Tag().String(),
  1292  		}},
  1293  	}
  1294  	actions, err := facade.Actions(args)
  1295  	c.Assert(err, jc.ErrorIsNil)
  1296  	c.Assert(len(actions.Results), gc.Equals, 1)
  1297  	c.Assert(actions.Results[0].Error, jc.Satisfies, params.IsCodeUnauthorized)
  1298  }
  1299  
  1300  type finishActions interface {
  1301  	FinishActions(args params.ActionExecutionResults) (params.ErrorResults, error)
  1302  }
  1303  
  1304  func (s *uniterBaseSuite) testFinishActionsSuccess(c *gc.C, facade finishActions) {
  1305  	testName := "fakeaction"
  1306  	testOutput := map[string]interface{}{"output": "completed fakeaction successfully"}
  1307  
  1308  	results, err := s.wordpressUnit.CompletedActions()
  1309  	c.Assert(err, jc.ErrorIsNil)
  1310  	c.Assert(results, gc.DeepEquals, ([]*state.Action)(nil))
  1311  
  1312  	action, err := s.wordpressUnit.AddAction(testName, nil)
  1313  	c.Assert(err, jc.ErrorIsNil)
  1314  
  1315  	actionResults := params.ActionExecutionResults{
  1316  		Results: []params.ActionExecutionResult{{
  1317  			ActionTag: action.ActionTag().String(),
  1318  			Status:    params.ActionCompleted,
  1319  			Results:   testOutput,
  1320  		}},
  1321  	}
  1322  	res, err := facade.FinishActions(actionResults)
  1323  	c.Assert(err, jc.ErrorIsNil)
  1324  	c.Assert(res, gc.DeepEquals, params.ErrorResults{Results: []params.ErrorResult{{Error: nil}}})
  1325  
  1326  	results, err = s.wordpressUnit.CompletedActions()
  1327  	c.Assert(err, jc.ErrorIsNil)
  1328  	c.Assert(len(results), gc.Equals, 1)
  1329  	c.Assert(results[0].Status(), gc.Equals, state.ActionCompleted)
  1330  	res2, errstr := results[0].Results()
  1331  	c.Assert(errstr, gc.Equals, "")
  1332  	c.Assert(res2, gc.DeepEquals, testOutput)
  1333  	c.Assert(results[0].Name(), gc.Equals, testName)
  1334  }
  1335  
  1336  func (s *uniterBaseSuite) testFinishActionsFailure(c *gc.C, facade finishActions) {
  1337  	testName := "fakeaction"
  1338  	testError := "fakeaction was a dismal failure"
  1339  
  1340  	results, err := s.wordpressUnit.CompletedActions()
  1341  	c.Assert(err, jc.ErrorIsNil)
  1342  	c.Assert(results, gc.DeepEquals, ([]*state.Action)(nil))
  1343  
  1344  	action, err := s.wordpressUnit.AddAction(testName, nil)
  1345  	c.Assert(err, jc.ErrorIsNil)
  1346  
  1347  	actionResults := params.ActionExecutionResults{
  1348  		Results: []params.ActionExecutionResult{{
  1349  			ActionTag: action.ActionTag().String(),
  1350  			Status:    params.ActionFailed,
  1351  			Results:   nil,
  1352  			Message:   testError,
  1353  		}},
  1354  	}
  1355  	res, err := facade.FinishActions(actionResults)
  1356  	c.Assert(err, jc.ErrorIsNil)
  1357  	c.Assert(res, gc.DeepEquals, params.ErrorResults{Results: []params.ErrorResult{{Error: nil}}})
  1358  
  1359  	results, err = s.wordpressUnit.CompletedActions()
  1360  	c.Assert(err, jc.ErrorIsNil)
  1361  	c.Assert(len(results), gc.Equals, 1)
  1362  	c.Assert(results[0].Status(), gc.Equals, state.ActionFailed)
  1363  	res2, errstr := results[0].Results()
  1364  	c.Assert(errstr, gc.Equals, testError)
  1365  	c.Assert(res2, gc.DeepEquals, map[string]interface{}{})
  1366  	c.Assert(results[0].Name(), gc.Equals, testName)
  1367  }
  1368  
  1369  func (s *uniterBaseSuite) testFinishActionsAuthAccess(c *gc.C, facade finishActions) {
  1370  	good, err := s.wordpressUnit.AddAction("fakeaction", nil)
  1371  	c.Assert(err, jc.ErrorIsNil)
  1372  
  1373  	bad, err := s.mysqlUnit.AddAction("fakeaction", nil)
  1374  	c.Assert(err, jc.ErrorIsNil)
  1375  
  1376  	var tests = []struct {
  1377  		actionTag names.ActionTag
  1378  		err       error
  1379  	}{
  1380  		{actionTag: good.ActionTag(), err: nil},
  1381  		{actionTag: bad.ActionTag(), err: common.ErrPerm},
  1382  	}
  1383  
  1384  	// Queue up actions from tests
  1385  	actionResults := params.ActionExecutionResults{Results: make([]params.ActionExecutionResult, len(tests))}
  1386  	for i, test := range tests {
  1387  		actionResults.Results[i] = params.ActionExecutionResult{
  1388  			ActionTag: test.actionTag.String(),
  1389  			Status:    params.ActionCompleted,
  1390  			Results:   map[string]interface{}{},
  1391  		}
  1392  	}
  1393  
  1394  	// Invoke FinishActions
  1395  	res, err := facade.FinishActions(actionResults)
  1396  	c.Assert(err, jc.ErrorIsNil)
  1397  
  1398  	// Verify permissions errors for actions queued on different unit
  1399  	for i, result := range res.Results {
  1400  		expected := tests[i].err
  1401  		if expected != nil {
  1402  			c.Assert(result.Error, gc.NotNil)
  1403  			c.Assert(result.Error.Error(), gc.Equals, expected.Error())
  1404  		} else {
  1405  			c.Assert(result.Error, gc.IsNil)
  1406  		}
  1407  	}
  1408  }
  1409  
  1410  type beginActions interface {
  1411  	BeginActions(args params.Entities) (params.ErrorResults, error)
  1412  }
  1413  
  1414  func (s *uniterBaseSuite) testBeginActions(c *gc.C, facade beginActions) {
  1415  	ten_seconds_ago := time.Now().Add(-10 * time.Second)
  1416  	good, err := s.wordpressUnit.AddAction("fakeaction", nil)
  1417  	c.Assert(err, jc.ErrorIsNil)
  1418  
  1419  	running, err := s.wordpressUnit.RunningActions()
  1420  	c.Assert(err, jc.ErrorIsNil)
  1421  	c.Assert(len(running), gc.Equals, 0, gc.Commentf("expected no running actions, got %d", len(running)))
  1422  
  1423  	args := params.Entities{Entities: []params.Entity{{Tag: good.ActionTag().String()}}}
  1424  	res, err := facade.BeginActions(args)
  1425  	c.Assert(err, jc.ErrorIsNil)
  1426  	c.Assert(len(res.Results), gc.Equals, 1)
  1427  	c.Assert(res.Results[0].Error, gc.IsNil)
  1428  
  1429  	running, err = s.wordpressUnit.RunningActions()
  1430  	c.Assert(err, jc.ErrorIsNil)
  1431  	c.Assert(len(running), gc.Equals, 1, gc.Commentf("expected one running action, got %d", len(running)))
  1432  	c.Assert(running[0].ActionTag(), gc.Equals, good.ActionTag())
  1433  	enqueued, started := running[0].Enqueued(), running[0].Started()
  1434  	c.Assert(ten_seconds_ago.Before(enqueued), jc.IsTrue, gc.Commentf("enqueued time should be after 10 seconds ago"))
  1435  	c.Assert(ten_seconds_ago.Before(started), jc.IsTrue, gc.Commentf("started time should be after 10 seconds ago"))
  1436  	c.Assert(started.After(enqueued) || started.Equal(enqueued), jc.IsTrue, gc.Commentf("started should be after or equal to enqueued time"))
  1437  }
  1438  
  1439  func (s *uniterBaseSuite) testRelation(
  1440  	c *gc.C,
  1441  	facade interface {
  1442  		Relation(args params.RelationUnits) (params.RelationResults, error)
  1443  	},
  1444  ) {
  1445  	rel := s.addRelation(c, "wordpress", "mysql")
  1446  	wpEp, err := rel.Endpoint("wordpress")
  1447  	c.Assert(err, jc.ErrorIsNil)
  1448  
  1449  	args := params.RelationUnits{RelationUnits: []params.RelationUnit{
  1450  		{Relation: "relation-42", Unit: "unit-foo-0"},
  1451  		{Relation: rel.Tag().String(), Unit: "unit-wordpress-0"},
  1452  		{Relation: rel.Tag().String(), Unit: "unit-mysql-0"},
  1453  		{Relation: rel.Tag().String(), Unit: "unit-foo-0"},
  1454  		{Relation: "relation-blah", Unit: "unit-wordpress-0"},
  1455  		{Relation: "service-foo", Unit: "user-foo"},
  1456  		{Relation: "foo", Unit: "bar"},
  1457  		{Relation: "unit-wordpress-0", Unit: rel.Tag().String()},
  1458  	}}
  1459  	result, err := facade.Relation(args)
  1460  	c.Assert(err, jc.ErrorIsNil)
  1461  	c.Assert(result, gc.DeepEquals, params.RelationResults{
  1462  		Results: []params.RelationResult{
  1463  			{Error: apiservertesting.ErrUnauthorized},
  1464  			{
  1465  				Id:   rel.Id(),
  1466  				Key:  rel.String(),
  1467  				Life: params.Life(rel.Life().String()),
  1468  				Endpoint: multiwatcher.Endpoint{
  1469  					ServiceName: wpEp.ServiceName,
  1470  					Relation:    wpEp.Relation,
  1471  				},
  1472  			},
  1473  			{Error: apiservertesting.ErrUnauthorized},
  1474  			{Error: apiservertesting.ErrUnauthorized},
  1475  			{Error: apiservertesting.ErrUnauthorized},
  1476  			{Error: apiservertesting.ErrUnauthorized},
  1477  			{Error: apiservertesting.ErrUnauthorized},
  1478  			{Error: apiservertesting.ErrUnauthorized},
  1479  		},
  1480  	})
  1481  }
  1482  
  1483  func (s *uniterBaseSuite) testRelationById(
  1484  	c *gc.C,
  1485  	facade interface {
  1486  		RelationById(args params.RelationIds) (params.RelationResults, error)
  1487  	},
  1488  ) {
  1489  	rel := s.addRelation(c, "wordpress", "mysql")
  1490  	c.Assert(rel.Id(), gc.Equals, 0)
  1491  	wpEp, err := rel.Endpoint("wordpress")
  1492  	c.Assert(err, jc.ErrorIsNil)
  1493  
  1494  	// Add another relation to mysql service, so we can see we can't
  1495  	// get it.
  1496  	otherRel, _, _ := s.addRelatedService(c, "mysql", "logging", s.mysqlUnit)
  1497  
  1498  	args := params.RelationIds{
  1499  		RelationIds: []int{-1, rel.Id(), otherRel.Id(), 42, 234},
  1500  	}
  1501  	result, err := facade.RelationById(args)
  1502  	c.Assert(err, jc.ErrorIsNil)
  1503  	c.Assert(result, gc.DeepEquals, params.RelationResults{
  1504  		Results: []params.RelationResult{
  1505  			{Error: apiservertesting.ErrUnauthorized},
  1506  			{
  1507  				Id:   rel.Id(),
  1508  				Key:  rel.String(),
  1509  				Life: params.Life(rel.Life().String()),
  1510  				Endpoint: multiwatcher.Endpoint{
  1511  					ServiceName: wpEp.ServiceName,
  1512  					Relation:    wpEp.Relation,
  1513  				},
  1514  			},
  1515  			{Error: apiservertesting.ErrUnauthorized},
  1516  			{Error: apiservertesting.ErrUnauthorized},
  1517  			{Error: apiservertesting.ErrUnauthorized},
  1518  		},
  1519  	})
  1520  }
  1521  
  1522  func (s *uniterBaseSuite) testProviderType(
  1523  	c *gc.C,
  1524  	facade interface {
  1525  		ProviderType() (params.StringResult, error)
  1526  	},
  1527  ) {
  1528  	cfg, err := s.State.EnvironConfig()
  1529  	c.Assert(err, jc.ErrorIsNil)
  1530  
  1531  	result, err := facade.ProviderType()
  1532  	c.Assert(err, jc.ErrorIsNil)
  1533  	c.Assert(result, gc.DeepEquals, params.StringResult{Result: cfg.Type()})
  1534  }
  1535  
  1536  func (s *uniterBaseSuite) testEnterScope(
  1537  	c *gc.C,
  1538  	facade interface {
  1539  		EnterScope(args params.RelationUnits) (params.ErrorResults, error)
  1540  	},
  1541  ) {
  1542  	// Set wordpressUnit's private address first.
  1543  	err := s.machine0.SetAddresses(network.NewAddress("1.2.3.4", network.ScopeCloudLocal))
  1544  	c.Assert(err, jc.ErrorIsNil)
  1545  
  1546  	rel := s.addRelation(c, "wordpress", "mysql")
  1547  	relUnit, err := rel.Unit(s.wordpressUnit)
  1548  	c.Assert(err, jc.ErrorIsNil)
  1549  	s.assertInScope(c, relUnit, false)
  1550  
  1551  	args := params.RelationUnits{RelationUnits: []params.RelationUnit{
  1552  		{Relation: "relation-42", Unit: "unit-foo-0"},
  1553  		{Relation: rel.Tag().String(), Unit: "unit-wordpress-0"},
  1554  		{Relation: rel.Tag().String(), Unit: "unit-wordpress-0"},
  1555  		{Relation: "relation-42", Unit: "unit-wordpress-0"},
  1556  		{Relation: "relation-foo", Unit: "unit-wordpress-0"},
  1557  		{Relation: "service-wordpress", Unit: "unit-foo-0"},
  1558  		{Relation: "foo", Unit: "bar"},
  1559  		{Relation: rel.Tag().String(), Unit: "unit-mysql-0"},
  1560  		{Relation: rel.Tag().String(), Unit: "service-wordpress"},
  1561  		{Relation: rel.Tag().String(), Unit: "service-mysql"},
  1562  		{Relation: rel.Tag().String(), Unit: "user-foo"},
  1563  	}}
  1564  	result, err := facade.EnterScope(args)
  1565  	c.Assert(err, jc.ErrorIsNil)
  1566  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  1567  		Results: []params.ErrorResult{
  1568  			{apiservertesting.ErrUnauthorized},
  1569  			{nil},
  1570  			{nil},
  1571  			{apiservertesting.ErrUnauthorized},
  1572  			{apiservertesting.ErrUnauthorized},
  1573  			{apiservertesting.ErrUnauthorized},
  1574  			{apiservertesting.ErrUnauthorized},
  1575  			{apiservertesting.ErrUnauthorized},
  1576  			{apiservertesting.ErrUnauthorized},
  1577  			{apiservertesting.ErrUnauthorized},
  1578  			{apiservertesting.ErrUnauthorized},
  1579  		},
  1580  	})
  1581  
  1582  	// Verify the scope changes and settings.
  1583  	s.assertInScope(c, relUnit, true)
  1584  	readSettings, err := relUnit.ReadSettings(s.wordpressUnit.Name())
  1585  	c.Assert(err, jc.ErrorIsNil)
  1586  	c.Assert(readSettings, gc.DeepEquals, map[string]interface{}{
  1587  		"private-address": "1.2.3.4",
  1588  	})
  1589  }
  1590  
  1591  func (s *uniterBaseSuite) testLeaveScope(
  1592  	c *gc.C,
  1593  	facade interface {
  1594  		LeaveScope(args params.RelationUnits) (params.ErrorResults, error)
  1595  	},
  1596  ) {
  1597  	rel := s.addRelation(c, "wordpress", "mysql")
  1598  	relUnit, err := rel.Unit(s.wordpressUnit)
  1599  	c.Assert(err, jc.ErrorIsNil)
  1600  	settings := map[string]interface{}{
  1601  		"some": "settings",
  1602  	}
  1603  	err = relUnit.EnterScope(settings)
  1604  	c.Assert(err, jc.ErrorIsNil)
  1605  	s.assertInScope(c, relUnit, true)
  1606  
  1607  	args := params.RelationUnits{RelationUnits: []params.RelationUnit{
  1608  		{Relation: "relation-42", Unit: "unit-foo-0"},
  1609  		{Relation: rel.Tag().String(), Unit: "unit-wordpress-0"},
  1610  		{Relation: rel.Tag().String(), Unit: "unit-wordpress-0"},
  1611  		{Relation: "relation-42", Unit: "unit-wordpress-0"},
  1612  		{Relation: "relation-foo", Unit: "unit-wordpress-0"},
  1613  		{Relation: "service-wordpress", Unit: "unit-foo-0"},
  1614  		{Relation: "foo", Unit: "bar"},
  1615  		{Relation: rel.Tag().String(), Unit: "unit-mysql-0"},
  1616  		{Relation: rel.Tag().String(), Unit: "service-wordpress"},
  1617  		{Relation: rel.Tag().String(), Unit: "service-mysql"},
  1618  		{Relation: rel.Tag().String(), Unit: "user-foo"},
  1619  	}}
  1620  	result, err := facade.LeaveScope(args)
  1621  	c.Assert(err, jc.ErrorIsNil)
  1622  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  1623  		Results: []params.ErrorResult{
  1624  			{apiservertesting.ErrUnauthorized},
  1625  			{nil},
  1626  			{nil},
  1627  			{apiservertesting.ErrUnauthorized},
  1628  			{apiservertesting.ErrUnauthorized},
  1629  			{apiservertesting.ErrUnauthorized},
  1630  			{apiservertesting.ErrUnauthorized},
  1631  			{apiservertesting.ErrUnauthorized},
  1632  			{apiservertesting.ErrUnauthorized},
  1633  			{apiservertesting.ErrUnauthorized},
  1634  			{apiservertesting.ErrUnauthorized},
  1635  		},
  1636  	})
  1637  
  1638  	// Verify the scope changes.
  1639  	s.assertInScope(c, relUnit, false)
  1640  	readSettings, err := relUnit.ReadSettings(s.wordpressUnit.Name())
  1641  	c.Assert(err, jc.ErrorIsNil)
  1642  	c.Assert(readSettings, gc.DeepEquals, settings)
  1643  }
  1644  
  1645  func (s *uniterBaseSuite) testJoinedRelations(
  1646  	c *gc.C,
  1647  	facade interface {
  1648  		JoinedRelations(args params.Entities) (params.StringsResults, error)
  1649  	},
  1650  ) {
  1651  	rel := s.addRelation(c, "wordpress", "mysql")
  1652  	relUnit, err := rel.Unit(s.wordpressUnit)
  1653  	c.Assert(err, jc.ErrorIsNil)
  1654  	err = relUnit.EnterScope(nil)
  1655  	c.Assert(err, jc.ErrorIsNil)
  1656  
  1657  	args := params.Entities{
  1658  		Entities: []params.Entity{
  1659  			{s.wordpressUnit.Tag().String()},
  1660  			{s.mysqlUnit.Tag().String()},
  1661  			{"unit-unknown-1"},
  1662  			{"service-wordpress"},
  1663  			{"machine-0"},
  1664  			{rel.Tag().String()},
  1665  		},
  1666  	}
  1667  	expect := params.StringsResults{
  1668  		Results: []params.StringsResult{
  1669  			{Result: []string{rel.Tag().String()}},
  1670  			{Error: apiservertesting.ErrUnauthorized},
  1671  			{Error: apiservertesting.ErrUnauthorized},
  1672  			{Error: apiservertesting.ErrUnauthorized},
  1673  			{Error: apiservertesting.ErrUnauthorized},
  1674  			{Error: apiservertesting.ErrUnauthorized},
  1675  		},
  1676  	}
  1677  	check := func() {
  1678  		result, err := facade.JoinedRelations(args)
  1679  		c.Assert(err, jc.ErrorIsNil)
  1680  		c.Assert(result, gc.DeepEquals, expect)
  1681  	}
  1682  	check()
  1683  	err = relUnit.PrepareLeaveScope()
  1684  	c.Assert(err, jc.ErrorIsNil)
  1685  	check()
  1686  }
  1687  
  1688  type readSettings interface {
  1689  	ReadSettings(args params.RelationUnits) (params.SettingsResults, error)
  1690  }
  1691  
  1692  func (s *uniterBaseSuite) testReadSettings(c *gc.C, facade readSettings) {
  1693  	rel := s.addRelation(c, "wordpress", "mysql")
  1694  	relUnit, err := rel.Unit(s.wordpressUnit)
  1695  	c.Assert(err, jc.ErrorIsNil)
  1696  	settings := map[string]interface{}{
  1697  		"some": "settings",
  1698  	}
  1699  	err = relUnit.EnterScope(settings)
  1700  	c.Assert(err, jc.ErrorIsNil)
  1701  	s.assertInScope(c, relUnit, true)
  1702  
  1703  	args := params.RelationUnits{RelationUnits: []params.RelationUnit{
  1704  		{Relation: "relation-42", Unit: "unit-foo-0"},
  1705  		{Relation: rel.Tag().String(), Unit: "unit-wordpress-0"},
  1706  		{Relation: rel.Tag().String(), Unit: "unit-mysql-0"},
  1707  		{Relation: "relation-42", Unit: "unit-wordpress-0"},
  1708  		{Relation: "relation-foo", Unit: ""},
  1709  		{Relation: "service-wordpress", Unit: "unit-foo-0"},
  1710  		{Relation: "foo", Unit: "bar"},
  1711  		{Relation: rel.Tag().String(), Unit: "unit-mysql-0"},
  1712  		{Relation: rel.Tag().String(), Unit: "service-wordpress"},
  1713  		{Relation: rel.Tag().String(), Unit: "service-mysql"},
  1714  		{Relation: rel.Tag().String(), Unit: "user-foo"},
  1715  	}}
  1716  	result, err := facade.ReadSettings(args)
  1717  	c.Assert(err, jc.ErrorIsNil)
  1718  	c.Assert(result, gc.DeepEquals, params.SettingsResults{
  1719  		Results: []params.SettingsResult{
  1720  			{Error: apiservertesting.ErrUnauthorized},
  1721  			{Settings: params.Settings{
  1722  				"some": "settings",
  1723  			}},
  1724  			{Error: apiservertesting.ErrUnauthorized},
  1725  			{Error: apiservertesting.ErrUnauthorized},
  1726  			{Error: apiservertesting.ErrUnauthorized},
  1727  			{Error: apiservertesting.ErrUnauthorized},
  1728  			{Error: apiservertesting.ErrUnauthorized},
  1729  			{Error: apiservertesting.ErrUnauthorized},
  1730  			{Error: apiservertesting.ErrUnauthorized},
  1731  			{Error: apiservertesting.ErrUnauthorized},
  1732  			{Error: apiservertesting.ErrUnauthorized},
  1733  		},
  1734  	})
  1735  }
  1736  
  1737  func (s *uniterBaseSuite) testReadSettingsWithNonStringValuesFails(c *gc.C, facade readSettings) {
  1738  	rel := s.addRelation(c, "wordpress", "mysql")
  1739  	relUnit, err := rel.Unit(s.wordpressUnit)
  1740  	c.Assert(err, jc.ErrorIsNil)
  1741  	settings := map[string]interface{}{
  1742  		"other":        "things",
  1743  		"invalid-bool": false,
  1744  	}
  1745  	err = relUnit.EnterScope(settings)
  1746  	c.Assert(err, jc.ErrorIsNil)
  1747  	s.assertInScope(c, relUnit, true)
  1748  
  1749  	args := params.RelationUnits{RelationUnits: []params.RelationUnit{
  1750  		{Relation: rel.Tag().String(), Unit: "unit-wordpress-0"},
  1751  	}}
  1752  	expectErr := `unexpected relation setting "invalid-bool": expected string, got bool`
  1753  	result, err := facade.ReadSettings(args)
  1754  	c.Assert(err, jc.ErrorIsNil)
  1755  	c.Assert(result, gc.DeepEquals, params.SettingsResults{
  1756  		Results: []params.SettingsResult{
  1757  			{Error: &params.Error{Message: expectErr}},
  1758  		},
  1759  	})
  1760  }
  1761  
  1762  type readRemoteSettings interface {
  1763  	ReadRemoteSettings(args params.RelationUnitPairs) (params.SettingsResults, error)
  1764  }
  1765  
  1766  func (s *uniterBaseSuite) testReadRemoteSettings(c *gc.C, facade readRemoteSettings) {
  1767  	rel := s.addRelation(c, "wordpress", "mysql")
  1768  	relUnit, err := rel.Unit(s.wordpressUnit)
  1769  	c.Assert(err, jc.ErrorIsNil)
  1770  	settings := map[string]interface{}{
  1771  		"some": "settings",
  1772  	}
  1773  	err = relUnit.EnterScope(settings)
  1774  	c.Assert(err, jc.ErrorIsNil)
  1775  	s.assertInScope(c, relUnit, true)
  1776  
  1777  	// First test most of the invalid args tests and try to read the
  1778  	// (unset) remote unit settings.
  1779  	args := params.RelationUnitPairs{RelationUnitPairs: []params.RelationUnitPair{
  1780  		{Relation: "relation-42", LocalUnit: "unit-foo-0", RemoteUnit: "foo"},
  1781  		{Relation: rel.Tag().String(), LocalUnit: "unit-wordpress-0", RemoteUnit: "unit-wordpress-0"},
  1782  		{Relation: rel.Tag().String(), LocalUnit: "unit-wordpress-0", RemoteUnit: "unit-mysql-0"},
  1783  		{Relation: "relation-42", LocalUnit: "unit-wordpress-0", RemoteUnit: ""},
  1784  		{Relation: "relation-foo", LocalUnit: "", RemoteUnit: ""},
  1785  		{Relation: "service-wordpress", LocalUnit: "unit-foo-0", RemoteUnit: "user-foo"},
  1786  		{Relation: "foo", LocalUnit: "bar", RemoteUnit: "baz"},
  1787  		{Relation: rel.Tag().String(), LocalUnit: "unit-mysql-0", RemoteUnit: "unit-wordpress-0"},
  1788  		{Relation: rel.Tag().String(), LocalUnit: "service-wordpress", RemoteUnit: "service-mysql"},
  1789  		{Relation: rel.Tag().String(), LocalUnit: "service-mysql", RemoteUnit: "foo"},
  1790  		{Relation: rel.Tag().String(), LocalUnit: "user-foo", RemoteUnit: "unit-wordpress-0"},
  1791  	}}
  1792  	result, err := facade.ReadRemoteSettings(args)
  1793  
  1794  	// We don't set the remote unit settings on purpose to test the error.
  1795  	expectErr := `cannot read settings for unit "mysql/0" in relation "wordpress:db mysql:server": settings`
  1796  	c.Assert(err, jc.ErrorIsNil)
  1797  	c.Assert(result, jc.DeepEquals, params.SettingsResults{
  1798  		Results: []params.SettingsResult{
  1799  			{Error: apiservertesting.ErrUnauthorized},
  1800  			{Error: apiservertesting.ErrUnauthorized},
  1801  			{Error: apiservertesting.NotFoundError(expectErr)},
  1802  			{Error: apiservertesting.ErrUnauthorized},
  1803  			{Error: apiservertesting.ErrUnauthorized},
  1804  			{Error: apiservertesting.ErrUnauthorized},
  1805  			{Error: apiservertesting.ErrUnauthorized},
  1806  			{Error: apiservertesting.ErrUnauthorized},
  1807  			{Error: apiservertesting.ErrUnauthorized},
  1808  			{Error: apiservertesting.ErrUnauthorized},
  1809  			{Error: apiservertesting.ErrUnauthorized},
  1810  		},
  1811  	})
  1812  
  1813  	// Now leave the mysqlUnit and re-enter with new settings.
  1814  	relUnit, err = rel.Unit(s.mysqlUnit)
  1815  	c.Assert(err, jc.ErrorIsNil)
  1816  	settings = map[string]interface{}{
  1817  		"other": "things",
  1818  	}
  1819  	err = relUnit.LeaveScope()
  1820  	c.Assert(err, jc.ErrorIsNil)
  1821  	s.assertInScope(c, relUnit, false)
  1822  	err = relUnit.EnterScope(settings)
  1823  	c.Assert(err, jc.ErrorIsNil)
  1824  	s.assertInScope(c, relUnit, true)
  1825  
  1826  	// Test the remote unit settings can be read.
  1827  	args = params.RelationUnitPairs{RelationUnitPairs: []params.RelationUnitPair{{
  1828  		Relation:   rel.Tag().String(),
  1829  		LocalUnit:  "unit-wordpress-0",
  1830  		RemoteUnit: "unit-mysql-0",
  1831  	}}}
  1832  	expect := params.SettingsResults{
  1833  		Results: []params.SettingsResult{
  1834  			{Settings: params.Settings{
  1835  				"other": "things",
  1836  			}},
  1837  		},
  1838  	}
  1839  	result, err = facade.ReadRemoteSettings(args)
  1840  	c.Assert(err, jc.ErrorIsNil)
  1841  	c.Assert(result, gc.DeepEquals, expect)
  1842  
  1843  	// Now destroy the remote unit, and check its settings can still be read.
  1844  	err = s.mysqlUnit.Destroy()
  1845  	c.Assert(err, jc.ErrorIsNil)
  1846  	err = s.mysqlUnit.EnsureDead()
  1847  	c.Assert(err, jc.ErrorIsNil)
  1848  	err = s.mysqlUnit.Remove()
  1849  	c.Assert(err, jc.ErrorIsNil)
  1850  	result, err = facade.ReadRemoteSettings(args)
  1851  	c.Assert(err, jc.ErrorIsNil)
  1852  	c.Assert(result, gc.DeepEquals, expect)
  1853  }
  1854  
  1855  func (s *uniterBaseSuite) testReadRemoteSettingsWithNonStringValuesFails(c *gc.C, facade readRemoteSettings) {
  1856  	rel := s.addRelation(c, "wordpress", "mysql")
  1857  	relUnit, err := rel.Unit(s.mysqlUnit)
  1858  	c.Assert(err, jc.ErrorIsNil)
  1859  	settings := map[string]interface{}{
  1860  		"other":        "things",
  1861  		"invalid-bool": false,
  1862  	}
  1863  	err = relUnit.EnterScope(settings)
  1864  	c.Assert(err, jc.ErrorIsNil)
  1865  	s.assertInScope(c, relUnit, true)
  1866  
  1867  	args := params.RelationUnitPairs{RelationUnitPairs: []params.RelationUnitPair{{
  1868  		Relation:   rel.Tag().String(),
  1869  		LocalUnit:  "unit-wordpress-0",
  1870  		RemoteUnit: "unit-mysql-0",
  1871  	}}}
  1872  	expectErr := `unexpected relation setting "invalid-bool": expected string, got bool`
  1873  	result, err := facade.ReadRemoteSettings(args)
  1874  	c.Assert(err, jc.ErrorIsNil)
  1875  	c.Assert(result, gc.DeepEquals, params.SettingsResults{
  1876  		Results: []params.SettingsResult{
  1877  			{Error: &params.Error{Message: expectErr}},
  1878  		},
  1879  	})
  1880  }
  1881  
  1882  func (s *uniterBaseSuite) testUpdateSettings(
  1883  	c *gc.C,
  1884  	facade interface {
  1885  		UpdateSettings(args params.RelationUnitsSettings) (params.ErrorResults, error)
  1886  	},
  1887  ) {
  1888  	rel := s.addRelation(c, "wordpress", "mysql")
  1889  	relUnit, err := rel.Unit(s.wordpressUnit)
  1890  	c.Assert(err, jc.ErrorIsNil)
  1891  	settings := map[string]interface{}{
  1892  		"some":  "settings",
  1893  		"other": "stuff",
  1894  	}
  1895  	err = relUnit.EnterScope(settings)
  1896  	s.assertInScope(c, relUnit, true)
  1897  
  1898  	newSettings := params.Settings{
  1899  		"some":  "different",
  1900  		"other": "",
  1901  	}
  1902  
  1903  	args := params.RelationUnitsSettings{RelationUnits: []params.RelationUnitSettings{
  1904  		{Relation: "relation-42", Unit: "unit-foo-0", Settings: nil},
  1905  		{Relation: rel.Tag().String(), Unit: "unit-wordpress-0", Settings: newSettings},
  1906  		{Relation: "relation-42", Unit: "unit-wordpress-0", Settings: nil},
  1907  		{Relation: "relation-foo", Unit: "unit-wordpress-0", Settings: nil},
  1908  		{Relation: "service-wordpress", Unit: "unit-foo-0", Settings: nil},
  1909  		{Relation: "foo", Unit: "bar", Settings: nil},
  1910  		{Relation: rel.Tag().String(), Unit: "unit-mysql-0", Settings: nil},
  1911  		{Relation: rel.Tag().String(), Unit: "service-wordpress", Settings: nil},
  1912  		{Relation: rel.Tag().String(), Unit: "service-mysql", Settings: nil},
  1913  		{Relation: rel.Tag().String(), Unit: "user-foo", Settings: nil},
  1914  	}}
  1915  	result, err := facade.UpdateSettings(args)
  1916  	c.Assert(err, jc.ErrorIsNil)
  1917  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  1918  		Results: []params.ErrorResult{
  1919  			{apiservertesting.ErrUnauthorized},
  1920  			{nil},
  1921  			{apiservertesting.ErrUnauthorized},
  1922  			{apiservertesting.ErrUnauthorized},
  1923  			{apiservertesting.ErrUnauthorized},
  1924  			{apiservertesting.ErrUnauthorized},
  1925  			{apiservertesting.ErrUnauthorized},
  1926  			{apiservertesting.ErrUnauthorized},
  1927  			{apiservertesting.ErrUnauthorized},
  1928  			{apiservertesting.ErrUnauthorized},
  1929  		},
  1930  	})
  1931  
  1932  	// Verify the settings were saved.
  1933  	s.assertInScope(c, relUnit, true)
  1934  	readSettings, err := relUnit.ReadSettings(s.wordpressUnit.Name())
  1935  	c.Assert(err, jc.ErrorIsNil)
  1936  	c.Assert(readSettings, gc.DeepEquals, map[string]interface{}{
  1937  		"some": "different",
  1938  	})
  1939  }
  1940  
  1941  func (s *uniterBaseSuite) testWatchRelationUnits(
  1942  	c *gc.C,
  1943  	facade interface {
  1944  		WatchRelationUnits(args params.RelationUnits) (params.RelationUnitsWatchResults, error)
  1945  	},
  1946  ) {
  1947  	// Add a relation between wordpress and mysql and enter scope with
  1948  	// mysqlUnit.
  1949  	rel := s.addRelation(c, "wordpress", "mysql")
  1950  	myRelUnit, err := rel.Unit(s.mysqlUnit)
  1951  	c.Assert(err, jc.ErrorIsNil)
  1952  	err = myRelUnit.EnterScope(nil)
  1953  	c.Assert(err, jc.ErrorIsNil)
  1954  	s.assertInScope(c, myRelUnit, true)
  1955  
  1956  	c.Assert(s.resources.Count(), gc.Equals, 0)
  1957  
  1958  	args := params.RelationUnits{RelationUnits: []params.RelationUnit{
  1959  		{Relation: "relation-42", Unit: "unit-foo-0"},
  1960  		{Relation: rel.Tag().String(), Unit: "unit-wordpress-0"},
  1961  		{Relation: rel.Tag().String(), Unit: "unit-mysql-0"},
  1962  		{Relation: "relation-42", Unit: "unit-wordpress-0"},
  1963  		{Relation: "relation-foo", Unit: ""},
  1964  		{Relation: "service-wordpress", Unit: "unit-foo-0"},
  1965  		{Relation: "foo", Unit: "bar"},
  1966  		{Relation: rel.Tag().String(), Unit: "unit-mysql-0"},
  1967  		{Relation: rel.Tag().String(), Unit: "service-wordpress"},
  1968  		{Relation: rel.Tag().String(), Unit: "service-mysql"},
  1969  		{Relation: rel.Tag().String(), Unit: "user-foo"},
  1970  	}}
  1971  	result, err := facade.WatchRelationUnits(args)
  1972  	c.Assert(err, jc.ErrorIsNil)
  1973  	// UnitSettings versions are volatile, so we don't check them.
  1974  	// We just make sure the keys of the Changed field are as
  1975  	// expected.
  1976  	c.Assert(result.Results, gc.HasLen, len(args.RelationUnits))
  1977  	mysqlChanges := result.Results[1].Changes
  1978  	c.Assert(mysqlChanges, gc.NotNil)
  1979  	changed, ok := mysqlChanges.Changed["mysql/0"]
  1980  	c.Assert(ok, jc.IsTrue)
  1981  	expectChanges := multiwatcher.RelationUnitsChange{
  1982  		Changed: map[string]multiwatcher.UnitSettings{"mysql/0": changed},
  1983  	}
  1984  	c.Assert(result, gc.DeepEquals, params.RelationUnitsWatchResults{
  1985  		Results: []params.RelationUnitsWatchResult{
  1986  			{Error: apiservertesting.ErrUnauthorized},
  1987  			{
  1988  				RelationUnitsWatcherId: "1",
  1989  				Changes:                expectChanges,
  1990  			},
  1991  			{Error: apiservertesting.ErrUnauthorized},
  1992  			{Error: apiservertesting.ErrUnauthorized},
  1993  			{Error: apiservertesting.ErrUnauthorized},
  1994  			{Error: apiservertesting.ErrUnauthorized},
  1995  			{Error: apiservertesting.ErrUnauthorized},
  1996  			{Error: apiservertesting.ErrUnauthorized},
  1997  			{Error: apiservertesting.ErrUnauthorized},
  1998  			{Error: apiservertesting.ErrUnauthorized},
  1999  			{Error: apiservertesting.ErrUnauthorized},
  2000  		},
  2001  	})
  2002  
  2003  	// Verify the resource was registered and stop when done
  2004  	c.Assert(s.resources.Count(), gc.Equals, 1)
  2005  	resource := s.resources.Get("1")
  2006  	defer statetesting.AssertStop(c, resource)
  2007  
  2008  	// Check that the Watch has consumed the initial event ("returned" in
  2009  	// the Watch call)
  2010  	wc := statetesting.NewRelationUnitsWatcherC(c, s.State, resource.(state.RelationUnitsWatcher))
  2011  	wc.AssertNoChange()
  2012  
  2013  	// Leave scope with mysqlUnit and check it's detected.
  2014  	err = myRelUnit.LeaveScope()
  2015  	c.Assert(err, jc.ErrorIsNil)
  2016  	s.assertInScope(c, myRelUnit, false)
  2017  
  2018  	wc.AssertChange(nil, []string{"mysql/0"})
  2019  }
  2020  
  2021  func (s *uniterBaseSuite) testAPIAddresses(
  2022  	c *gc.C,
  2023  	facade interface {
  2024  		APIAddresses() (params.StringsResult, error)
  2025  	},
  2026  ) {
  2027  	hostPorts := [][]network.HostPort{{{
  2028  		Address: network.NewAddress("0.1.2.3", network.ScopeUnknown),
  2029  		Port:    1234,
  2030  	}}}
  2031  
  2032  	err := s.State.SetAPIHostPorts(hostPorts)
  2033  	c.Assert(err, jc.ErrorIsNil)
  2034  
  2035  	result, err := facade.APIAddresses()
  2036  	c.Assert(err, jc.ErrorIsNil)
  2037  	c.Assert(result, gc.DeepEquals, params.StringsResult{
  2038  		Result: []string{"0.1.2.3:1234"},
  2039  	})
  2040  }
  2041  
  2042  func (s *uniterBaseSuite) testWatchUnitAddresses(
  2043  	c *gc.C,
  2044  	facade interface {
  2045  		WatchUnitAddresses(args params.Entities) (params.NotifyWatchResults, error)
  2046  	},
  2047  ) {
  2048  	c.Assert(s.resources.Count(), gc.Equals, 0)
  2049  
  2050  	args := params.Entities{Entities: []params.Entity{
  2051  		{Tag: "unit-mysql-0"},
  2052  		{Tag: "unit-wordpress-0"},
  2053  		{Tag: "unit-foo-42"},
  2054  		{Tag: "machine-0"},
  2055  		{Tag: "service-wordpress"},
  2056  	}}
  2057  	result, err := facade.WatchUnitAddresses(args)
  2058  	c.Assert(err, jc.ErrorIsNil)
  2059  	c.Assert(result, gc.DeepEquals, params.NotifyWatchResults{
  2060  		Results: []params.NotifyWatchResult{
  2061  			{Error: apiservertesting.ErrUnauthorized},
  2062  			{NotifyWatcherId: "1"},
  2063  			{Error: apiservertesting.ErrUnauthorized},
  2064  			{Error: apiservertesting.ErrUnauthorized},
  2065  			{Error: apiservertesting.ErrUnauthorized},
  2066  		},
  2067  	})
  2068  
  2069  	// Verify the resource was registered and stop when done
  2070  	c.Assert(s.resources.Count(), gc.Equals, 1)
  2071  	resource := s.resources.Get("1")
  2072  	defer statetesting.AssertStop(c, resource)
  2073  
  2074  	// Check that the Watch has consumed the initial event ("returned" in
  2075  	// the Watch call)
  2076  	wc := statetesting.NewNotifyWatcherC(c, s.State, resource.(state.NotifyWatcher))
  2077  	wc.AssertNoChange()
  2078  }
  2079  
  2080  type addMetrics interface {
  2081  	AddMetrics(args params.MetricsParams) (params.ErrorResults, error)
  2082  }
  2083  
  2084  func (s *uniterBaseSuite) testAddMetrics(c *gc.C, facade addMetrics) {
  2085  	now := time.Now()
  2086  	sentMetrics := []params.Metric{{"pings", "5", now}, {"juju-unit-time", "0.71", now}}
  2087  	args := params.MetricsParams{
  2088  		Metrics: []params.MetricsParam{{
  2089  			Tag:     s.meteredUnit.Tag().String(),
  2090  			Metrics: sentMetrics,
  2091  		}},
  2092  	}
  2093  	result, err := facade.AddMetrics(args)
  2094  	c.Assert(err, jc.ErrorIsNil)
  2095  	c.Assert(result.Results, gc.HasLen, 1)
  2096  	c.Assert(result.Results[0].Error, gc.IsNil)
  2097  
  2098  	metrics, err := s.State.MetricBatches()
  2099  	c.Assert(err, jc.ErrorIsNil)
  2100  	c.Assert(metrics, gc.HasLen, 1)
  2101  
  2102  	unitMetrics := metrics[0].Metrics()
  2103  	c.Assert(unitMetrics, gc.HasLen, 2)
  2104  
  2105  	for i, unitMetric := range unitMetrics {
  2106  		c.Assert(unitMetric.Key, gc.Equals, sentMetrics[i].Key)
  2107  		c.Assert(unitMetric.Value, gc.Equals, sentMetrics[i].Value)
  2108  	}
  2109  }
  2110  
  2111  func (s *uniterBaseSuite) testAddMetricsIncorrectTag(c *gc.C, facade addMetrics) {
  2112  	now := time.Now()
  2113  
  2114  	tags := []string{"user-admin", "unit-nosuchunit", "thisisnotatag", "machine-0", "environment-blah"}
  2115  
  2116  	for _, tag := range tags {
  2117  
  2118  		args := params.MetricsParams{
  2119  			Metrics: []params.MetricsParam{{
  2120  				Tag:     tag,
  2121  				Metrics: []params.Metric{{"A", "5", now}, {"B", "0.71", now}},
  2122  			}},
  2123  		}
  2124  
  2125  		result, err := facade.AddMetrics(args)
  2126  		c.Assert(err, jc.ErrorIsNil)
  2127  		c.Assert(result.Results, gc.HasLen, 1)
  2128  		c.Assert(result.Results[0].Error, gc.ErrorMatches, "permission denied")
  2129  		metrics, err := s.State.MetricBatches()
  2130  		c.Assert(err, jc.ErrorIsNil)
  2131  		c.Assert(metrics, gc.HasLen, 0)
  2132  	}
  2133  }
  2134  
  2135  func (s *uniterBaseSuite) testAddMetricsUnauthenticated(c *gc.C, facade addMetrics) {
  2136  	now := time.Now()
  2137  	args := params.MetricsParams{
  2138  		Metrics: []params.MetricsParam{{
  2139  			Tag:     s.mysqlUnit.Tag().String(),
  2140  			Metrics: []params.Metric{{"A", "5", now}, {"B", "0.71", now}},
  2141  		}},
  2142  	}
  2143  	result, err := facade.AddMetrics(args)
  2144  	c.Assert(err, jc.ErrorIsNil)
  2145  	c.Assert(result.Results, gc.HasLen, 1)
  2146  	c.Assert(result.Results[0].Error, gc.ErrorMatches, "permission denied")
  2147  	metrics, err := s.State.MetricBatches()
  2148  	c.Assert(err, jc.ErrorIsNil)
  2149  	c.Assert(metrics, gc.HasLen, 0)
  2150  }
  2151  
  2152  type getMeterStatus interface {
  2153  	GetMeterStatus(args params.Entities) (params.MeterStatusResults, error)
  2154  }
  2155  
  2156  func (s *uniterBaseSuite) testGetMeterStatus(c *gc.C, facade getMeterStatus) {
  2157  	args := params.Entities{Entities: []params.Entity{{Tag: s.wordpressUnit.Tag().String()}}}
  2158  	result, err := facade.GetMeterStatus(args)
  2159  	c.Assert(err, jc.ErrorIsNil)
  2160  	c.Assert(result.Results, gc.HasLen, 1)
  2161  	c.Assert(result.Results[0].Error, gc.IsNil)
  2162  	c.Assert(result.Results[0].Code, gc.Equals, "NOT SET")
  2163  	c.Assert(result.Results[0].Info, gc.Equals, "")
  2164  
  2165  	newCode := "GREEN"
  2166  	newInfo := "All is ok."
  2167  
  2168  	err = s.wordpressUnit.SetMeterStatus(newCode, newInfo)
  2169  	c.Assert(err, jc.ErrorIsNil)
  2170  
  2171  	result, err = facade.GetMeterStatus(args)
  2172  	c.Assert(err, jc.ErrorIsNil)
  2173  	c.Assert(result.Results, gc.HasLen, 1)
  2174  	c.Assert(result.Results[0].Error, gc.IsNil)
  2175  	c.Assert(result.Results[0].Code, gc.DeepEquals, newCode)
  2176  	c.Assert(result.Results[0].Info, gc.DeepEquals, newInfo)
  2177  }
  2178  
  2179  func (s *uniterBaseSuite) testGetMeterStatusUnauthenticated(c *gc.C, facade getMeterStatus) {
  2180  	args := params.Entities{Entities: []params.Entity{{s.mysqlUnit.Tag().String()}}}
  2181  	result, err := facade.GetMeterStatus(args)
  2182  	c.Assert(err, jc.ErrorIsNil)
  2183  	c.Assert(result.Results, gc.HasLen, 1)
  2184  	c.Assert(result.Results[0].Error, gc.ErrorMatches, "permission denied")
  2185  	c.Assert(result.Results[0].Code, gc.Equals, "")
  2186  	c.Assert(result.Results[0].Info, gc.Equals, "")
  2187  }
  2188  
  2189  func (s *uniterBaseSuite) testGetMeterStatusBadTag(c *gc.C, facade getMeterStatus) {
  2190  	tags := []string{
  2191  		"user-admin",
  2192  		"unit-nosuchunit",
  2193  		"thisisnotatag",
  2194  		"machine-0",
  2195  		"environment-blah",
  2196  	}
  2197  	args := params.Entities{Entities: make([]params.Entity, len(tags))}
  2198  	for i, tag := range tags {
  2199  		args.Entities[i] = params.Entity{Tag: tag}
  2200  	}
  2201  	result, err := facade.GetMeterStatus(args)
  2202  	c.Assert(err, jc.ErrorIsNil)
  2203  	c.Assert(result.Results, gc.HasLen, len(tags))
  2204  	for i, result := range result.Results {
  2205  		c.Logf("checking result %d", i)
  2206  		c.Assert(result.Code, gc.Equals, "")
  2207  		c.Assert(result.Info, gc.Equals, "")
  2208  		c.Assert(result.Error, gc.ErrorMatches, "permission denied")
  2209  	}
  2210  }
  2211  
  2212  func (s *uniterBaseSuite) testWatchMeterStatus(
  2213  	c *gc.C,
  2214  	facade interface {
  2215  		WatchMeterStatus(args params.Entities) (params.NotifyWatchResults, error)
  2216  	},
  2217  ) {
  2218  	c.Assert(s.resources.Count(), gc.Equals, 0)
  2219  
  2220  	args := params.Entities{Entities: []params.Entity{
  2221  		{Tag: "unit-mysql-0"},
  2222  		{Tag: "unit-wordpress-0"},
  2223  		{Tag: "unit-foo-42"},
  2224  	}}
  2225  	result, err := facade.WatchMeterStatus(args)
  2226  	c.Assert(err, jc.ErrorIsNil)
  2227  	c.Assert(result, gc.DeepEquals, params.NotifyWatchResults{
  2228  		Results: []params.NotifyWatchResult{
  2229  			{Error: apiservertesting.ErrUnauthorized},
  2230  			{NotifyWatcherId: "1"},
  2231  			{Error: apiservertesting.ErrUnauthorized},
  2232  		},
  2233  	})
  2234  
  2235  	// Verify the resource was registered and stop when done
  2236  	c.Assert(s.resources.Count(), gc.Equals, 1)
  2237  	resource := s.resources.Get("1")
  2238  	defer statetesting.AssertStop(c, resource)
  2239  
  2240  	// Check that the Watch has consumed the initial event ("returned" in
  2241  	// the Watch call)
  2242  	wc := statetesting.NewNotifyWatcherC(c, s.State, resource.(state.NotifyWatcher))
  2243  	wc.AssertNoChange()
  2244  
  2245  	err = s.wordpressUnit.SetMeterStatus("GREEN", "No additional information.")
  2246  	wc.AssertOneChange()
  2247  }
  2248  
  2249  func (s *uniterBaseSuite) assertOneStringsWatcher(c *gc.C, result params.StringsWatchResults, err error) {
  2250  	c.Assert(err, jc.ErrorIsNil)
  2251  	c.Assert(result.Results, gc.HasLen, 3)
  2252  	c.Assert(result.Results[0].Error, gc.DeepEquals, apiservertesting.ErrUnauthorized)
  2253  	c.Assert(result.Results[1].StringsWatcherId, gc.Equals, "1")
  2254  	c.Assert(result.Results[1].Changes, gc.NotNil)
  2255  	c.Assert(result.Results[1].Error, gc.IsNil)
  2256  	c.Assert(result.Results[2].Error, gc.DeepEquals, apiservertesting.ErrUnauthorized)
  2257  
  2258  	// Verify the resource was registered and stop when done
  2259  	c.Assert(s.resources.Count(), gc.Equals, 1)
  2260  	resource := s.resources.Get("1")
  2261  	defer statetesting.AssertStop(c, resource)
  2262  
  2263  	// Check that the Watch has consumed the initial event ("returned" in
  2264  	// the Watch call)
  2265  	wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher))
  2266  	wc.AssertNoChange()
  2267  }
  2268  
  2269  func (s *uniterBaseSuite) assertInScope(c *gc.C, relUnit *state.RelationUnit, inScope bool) {
  2270  	ok, err := relUnit.InScope()
  2271  	c.Assert(err, jc.ErrorIsNil)
  2272  	c.Assert(ok, gc.Equals, inScope)
  2273  }
  2274  
  2275  func (s *uniterBaseSuite) addRelation(c *gc.C, first, second string) *state.Relation {
  2276  	eps, err := s.State.InferEndpoints(first, second)
  2277  	c.Assert(err, jc.ErrorIsNil)
  2278  	rel, err := s.State.AddRelation(eps...)
  2279  	c.Assert(err, jc.ErrorIsNil)
  2280  	return rel
  2281  }
  2282  
  2283  func (s *uniterBaseSuite) addRelatedService(c *gc.C, firstSvc, relatedSvc string, unit *state.Unit) (*state.Relation, *state.Service, *state.Unit) {
  2284  	relatedService := s.AddTestingService(c, relatedSvc, s.AddTestingCharm(c, relatedSvc))
  2285  	rel := s.addRelation(c, firstSvc, relatedSvc)
  2286  	relUnit, err := rel.Unit(unit)
  2287  	c.Assert(err, jc.ErrorIsNil)
  2288  	err = relUnit.EnterScope(nil)
  2289  	c.Assert(err, jc.ErrorIsNil)
  2290  	relatedUnit, err := s.State.Unit(relatedSvc + "/0")
  2291  	c.Assert(err, jc.ErrorIsNil)
  2292  	return rel, relatedService, relatedUnit
  2293  }
  2294  
  2295  func checkUnorderedActionIdsEqual(c *gc.C, ids []string, results params.StringsWatchResults) {
  2296  	c.Assert(results, gc.NotNil)
  2297  	content := results.Results
  2298  	c.Assert(len(content), gc.Equals, 1)
  2299  	result := content[0]
  2300  	c.Assert(result.StringsWatcherId, gc.Equals, "1")
  2301  	obtainedIds := map[string]int{}
  2302  	expectedIds := map[string]int{}
  2303  	for _, id := range ids {
  2304  		expectedIds[id]++
  2305  	}
  2306  	// The count of each ID that has been seen.
  2307  	for _, change := range result.Changes {
  2308  		obtainedIds[change]++
  2309  	}
  2310  	c.Check(obtainedIds, jc.DeepEquals, expectedIds)
  2311  }