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