launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/state/apiserver/uniter/uniter_test.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package uniter_test
     5  
     6  import (
     7  	gc "launchpad.net/gocheck"
     8  	stdtesting "testing"
     9  
    10  	"launchpad.net/juju-core/charm"
    11  	envtesting "launchpad.net/juju-core/environs/testing"
    12  	"launchpad.net/juju-core/errors"
    13  	"launchpad.net/juju-core/instance"
    14  	"launchpad.net/juju-core/juju/testing"
    15  	"launchpad.net/juju-core/state"
    16  	"launchpad.net/juju-core/state/api/params"
    17  	"launchpad.net/juju-core/state/apiserver/common"
    18  	commontesting "launchpad.net/juju-core/state/apiserver/common/testing"
    19  	apiservertesting "launchpad.net/juju-core/state/apiserver/testing"
    20  	"launchpad.net/juju-core/state/apiserver/uniter"
    21  	statetesting "launchpad.net/juju-core/state/testing"
    22  	coretesting "launchpad.net/juju-core/testing"
    23  	jc "launchpad.net/juju-core/testing/checkers"
    24  )
    25  
    26  func Test(t *stdtesting.T) {
    27  	coretesting.MgoTestPackage(t)
    28  }
    29  
    30  type uniterSuite struct {
    31  	testing.JujuConnSuite
    32  	*commontesting.EnvironWatcherTest
    33  
    34  	authorizer apiservertesting.FakeAuthorizer
    35  	resources  *common.Resources
    36  
    37  	machine0      *state.Machine
    38  	machine1      *state.Machine
    39  	wordpress     *state.Service
    40  	wpCharm       *state.Charm
    41  	mysql         *state.Service
    42  	wordpressUnit *state.Unit
    43  	mysqlUnit     *state.Unit
    44  
    45  	uniter *uniter.UniterAPI
    46  }
    47  
    48  var _ = gc.Suite(&uniterSuite{})
    49  
    50  func (s *uniterSuite) SetUpTest(c *gc.C) {
    51  	s.JujuConnSuite.SetUpTest(c)
    52  
    53  	s.wpCharm = s.AddTestingCharm(c, "wordpress")
    54  	// Create two machines, two services and add a unit to each service.
    55  	var err error
    56  	s.machine0, err = s.State.AddMachine("quantal", state.JobHostUnits, state.JobManageState, state.JobManageEnviron)
    57  	c.Assert(err, gc.IsNil)
    58  	s.machine1, err = s.State.AddMachine("quantal", state.JobHostUnits)
    59  	c.Assert(err, gc.IsNil)
    60  	s.wordpress = s.AddTestingService(c, "wordpress", s.wpCharm)
    61  	s.mysql = s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql"))
    62  	s.wordpressUnit, err = s.wordpress.AddUnit()
    63  	c.Assert(err, gc.IsNil)
    64  	s.mysqlUnit, err = s.mysql.AddUnit()
    65  	c.Assert(err, gc.IsNil)
    66  	// Assign each unit to each machine.
    67  	err = s.wordpressUnit.AssignToMachine(s.machine0)
    68  	c.Assert(err, gc.IsNil)
    69  	err = s.mysqlUnit.AssignToMachine(s.machine1)
    70  	c.Assert(err, gc.IsNil)
    71  
    72  	// Create a FakeAuthorizer so we can check permissions,
    73  	// set up assuming unit 0 has logged in.
    74  	s.authorizer = apiservertesting.FakeAuthorizer{
    75  		Tag:       s.wordpressUnit.Tag(),
    76  		LoggedIn:  true,
    77  		UnitAgent: true,
    78  		Entity:    s.wordpressUnit,
    79  	}
    80  
    81  	// Create the resource registry separately to track invocations to
    82  	// Register.
    83  	s.resources = common.NewResources()
    84  
    85  	// Create a uniter API for unit 0.
    86  	s.uniter, err = uniter.NewUniterAPI(
    87  		s.State,
    88  		s.resources,
    89  		s.authorizer,
    90  	)
    91  	c.Assert(err, gc.IsNil)
    92  	s.EnvironWatcherTest = commontesting.NewEnvironWatcherTest(s.uniter, s.State, s.resources, commontesting.NoSecrets)
    93  }
    94  
    95  func (s *uniterSuite) TestUniterFailsWithNonUnitAgentUser(c *gc.C) {
    96  	anAuthorizer := s.authorizer
    97  	anAuthorizer.UnitAgent = false
    98  	anUniter, err := uniter.NewUniterAPI(s.State, s.resources, anAuthorizer)
    99  	c.Assert(err, gc.NotNil)
   100  	c.Assert(anUniter, gc.IsNil)
   101  	c.Assert(err, gc.ErrorMatches, "permission denied")
   102  }
   103  
   104  func (s *uniterSuite) TestSetStatus(c *gc.C) {
   105  	err := s.wordpressUnit.SetStatus(params.StatusStarted, "blah", nil)
   106  	c.Assert(err, gc.IsNil)
   107  	err = s.mysqlUnit.SetStatus(params.StatusStopped, "foo", nil)
   108  	c.Assert(err, gc.IsNil)
   109  
   110  	args := params.SetStatus{
   111  		Entities: []params.SetEntityStatus{
   112  			{Tag: "unit-mysql-0", Status: params.StatusError, Info: "not really"},
   113  			{Tag: "unit-wordpress-0", Status: params.StatusStopped, Info: "foobar"},
   114  			{Tag: "unit-foo-42", Status: params.StatusStarted, Info: "blah"},
   115  		}}
   116  	result, err := s.uniter.SetStatus(args)
   117  	c.Assert(err, gc.IsNil)
   118  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   119  		Results: []params.ErrorResult{
   120  			{apiservertesting.ErrUnauthorized},
   121  			{nil},
   122  			{apiservertesting.ErrUnauthorized},
   123  		},
   124  	})
   125  
   126  	// Verify mysqlUnit - no change.
   127  	status, info, _, err := s.mysqlUnit.Status()
   128  	c.Assert(err, gc.IsNil)
   129  	c.Assert(status, gc.Equals, params.StatusStopped)
   130  	c.Assert(info, gc.Equals, "foo")
   131  	// ...wordpressUnit is fine though.
   132  	status, info, _, err = s.wordpressUnit.Status()
   133  	c.Assert(err, gc.IsNil)
   134  	c.Assert(status, gc.Equals, params.StatusStopped)
   135  	c.Assert(info, gc.Equals, "foobar")
   136  }
   137  
   138  func (s *uniterSuite) TestLife(c *gc.C) {
   139  	// Add a relation wordpress-mysql.
   140  	rel := s.addRelation(c, "wordpress", "mysql")
   141  	relUnit, err := rel.Unit(s.wordpressUnit)
   142  	c.Assert(err, gc.IsNil)
   143  	err = relUnit.EnterScope(nil)
   144  	c.Assert(err, gc.IsNil)
   145  	c.Assert(rel.Life(), gc.Equals, state.Alive)
   146  
   147  	// Make the wordpressUnit dead.
   148  	err = s.wordpressUnit.EnsureDead()
   149  	c.Assert(err, gc.IsNil)
   150  	err = s.wordpressUnit.Refresh()
   151  	c.Assert(err, gc.IsNil)
   152  	c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Dead)
   153  
   154  	// Add another unit, so the service will stay dying when we
   155  	// destroy it later.
   156  	extraUnit, err := s.wordpress.AddUnit()
   157  	c.Assert(err, gc.IsNil)
   158  	c.Assert(extraUnit, gc.NotNil)
   159  
   160  	// Make the wordpress service dying.
   161  	err = s.wordpress.Destroy()
   162  	c.Assert(err, gc.IsNil)
   163  	err = s.wordpress.Refresh()
   164  	c.Assert(err, gc.IsNil)
   165  	c.Assert(s.wordpress.Life(), gc.Equals, state.Dying)
   166  
   167  	args := params.Entities{Entities: []params.Entity{
   168  		{Tag: "unit-mysql-0"},
   169  		{Tag: "unit-wordpress-0"},
   170  		{Tag: "unit-foo-42"},
   171  		{Tag: "service-mysql"},
   172  		{Tag: "service-wordpress"},
   173  		{Tag: "service-foo"},
   174  		{Tag: "just-foo"},
   175  		{Tag: rel.Tag()},
   176  		{Tag: "relation-svc1.rel1#svc2.rel2"},
   177  		{Tag: "relation-blah"},
   178  	}}
   179  	result, err := s.uniter.Life(args)
   180  	c.Assert(err, gc.IsNil)
   181  	c.Assert(result, gc.DeepEquals, params.LifeResults{
   182  		Results: []params.LifeResult{
   183  			{Error: apiservertesting.ErrUnauthorized},
   184  			{Life: "dead"},
   185  			{Error: apiservertesting.ErrUnauthorized},
   186  			{Error: apiservertesting.ErrUnauthorized},
   187  			{Life: "dying"},
   188  			{Error: apiservertesting.ErrUnauthorized},
   189  			{Error: apiservertesting.ErrUnauthorized},
   190  			{Error: apiservertesting.ErrUnauthorized},
   191  			{Error: apiservertesting.ErrUnauthorized},
   192  			{Error: apiservertesting.ErrUnauthorized},
   193  		},
   194  	})
   195  }
   196  
   197  func (s *uniterSuite) TestEnsureDead(c *gc.C) {
   198  	c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Alive)
   199  	c.Assert(s.mysqlUnit.Life(), gc.Equals, state.Alive)
   200  
   201  	args := params.Entities{Entities: []params.Entity{
   202  		{Tag: "unit-mysql-0"},
   203  		{Tag: "unit-wordpress-0"},
   204  		{Tag: "unit-foo-42"},
   205  	}}
   206  	result, err := s.uniter.EnsureDead(args)
   207  	c.Assert(err, gc.IsNil)
   208  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   209  		Results: []params.ErrorResult{
   210  			{apiservertesting.ErrUnauthorized},
   211  			{nil},
   212  			{apiservertesting.ErrUnauthorized},
   213  		},
   214  	})
   215  
   216  	err = s.wordpressUnit.Refresh()
   217  	c.Assert(err, gc.IsNil)
   218  	c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Dead)
   219  	err = s.mysqlUnit.Refresh()
   220  	c.Assert(err, gc.IsNil)
   221  	c.Assert(s.mysqlUnit.Life(), gc.Equals, state.Alive)
   222  
   223  	// Try it again on a Dead unit; should work.
   224  	args = params.Entities{
   225  		Entities: []params.Entity{{Tag: "unit-wordpress-0"}},
   226  	}
   227  	result, err = s.uniter.EnsureDead(args)
   228  	c.Assert(err, gc.IsNil)
   229  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   230  		Results: []params.ErrorResult{{nil}},
   231  	})
   232  
   233  	// Verify Life is unchanged.
   234  	err = s.wordpressUnit.Refresh()
   235  	c.Assert(err, gc.IsNil)
   236  	c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Dead)
   237  }
   238  
   239  func (s *uniterSuite) assertOneStringsWatcher(c *gc.C, result params.StringsWatchResults, err error) {
   240  	c.Assert(err, gc.IsNil)
   241  	c.Assert(result.Results, gc.HasLen, 3)
   242  	c.Assert(result.Results[0].Error, gc.DeepEquals, apiservertesting.ErrUnauthorized)
   243  	c.Assert(result.Results[1].StringsWatcherId, gc.Equals, "1")
   244  	c.Assert(result.Results[1].Changes, gc.NotNil)
   245  	c.Assert(result.Results[1].Error, gc.IsNil)
   246  	c.Assert(result.Results[2].Error, gc.DeepEquals, apiservertesting.ErrUnauthorized)
   247  
   248  	// Verify the resource was registered and stop when done
   249  	c.Assert(s.resources.Count(), gc.Equals, 1)
   250  	resource := s.resources.Get("1")
   251  	defer statetesting.AssertStop(c, resource)
   252  
   253  	// Check that the Watch has consumed the initial event ("returned" in
   254  	// the Watch call)
   255  	wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher))
   256  	wc.AssertNoChange()
   257  }
   258  
   259  func (s *uniterSuite) TestWatch(c *gc.C) {
   260  	c.Assert(s.resources.Count(), gc.Equals, 0)
   261  
   262  	args := params.Entities{Entities: []params.Entity{
   263  		{Tag: "unit-mysql-0"},
   264  		{Tag: "unit-wordpress-0"},
   265  		{Tag: "unit-foo-42"},
   266  		{Tag: "service-mysql"},
   267  		{Tag: "service-wordpress"},
   268  		{Tag: "service-foo"},
   269  		{Tag: "just-foo"},
   270  	}}
   271  	result, err := s.uniter.Watch(args)
   272  	c.Assert(err, gc.IsNil)
   273  	c.Assert(result, gc.DeepEquals, params.NotifyWatchResults{
   274  		Results: []params.NotifyWatchResult{
   275  			{Error: apiservertesting.ErrUnauthorized},
   276  			{NotifyWatcherId: "1"},
   277  			{Error: apiservertesting.ErrUnauthorized},
   278  			{Error: apiservertesting.ErrUnauthorized},
   279  			{NotifyWatcherId: "2"},
   280  			{Error: apiservertesting.ErrUnauthorized},
   281  			{Error: apiservertesting.ErrUnauthorized},
   282  		},
   283  	})
   284  
   285  	// Verify the resource was registered and stop when done
   286  	c.Assert(s.resources.Count(), gc.Equals, 2)
   287  	resource1 := s.resources.Get("1")
   288  	defer statetesting.AssertStop(c, resource1)
   289  	resource2 := s.resources.Get("2")
   290  	defer statetesting.AssertStop(c, resource2)
   291  
   292  	// Check that the Watch has consumed the initial event ("returned" in
   293  	// the Watch call)
   294  	wc := statetesting.NewNotifyWatcherC(c, s.State, resource1.(state.NotifyWatcher))
   295  	wc.AssertNoChange()
   296  	wc = statetesting.NewNotifyWatcherC(c, s.State, resource2.(state.NotifyWatcher))
   297  	wc.AssertNoChange()
   298  }
   299  
   300  func (s *uniterSuite) TestPublicAddress(c *gc.C) {
   301  	// Try first without setting an address.
   302  	args := params.Entities{Entities: []params.Entity{
   303  		{Tag: "unit-mysql-0"},
   304  		{Tag: "unit-wordpress-0"},
   305  		{Tag: "unit-foo-42"},
   306  	}}
   307  	expectErr := &params.Error{
   308  		Code:    params.CodeNoAddressSet,
   309  		Message: `"unit-wordpress-0" has no public address set`,
   310  	}
   311  	result, err := s.uniter.PublicAddress(args)
   312  	c.Assert(err, gc.IsNil)
   313  	c.Assert(result, gc.DeepEquals, params.StringResults{
   314  		Results: []params.StringResult{
   315  			{Error: apiservertesting.ErrUnauthorized},
   316  			{Error: expectErr},
   317  			{Error: apiservertesting.ErrUnauthorized},
   318  		},
   319  	})
   320  
   321  	// Now set it an try again.
   322  	err = s.wordpressUnit.SetPublicAddress("1.2.3.4")
   323  	c.Assert(err, gc.IsNil)
   324  	address, ok := s.wordpressUnit.PublicAddress()
   325  	c.Assert(address, gc.Equals, "1.2.3.4")
   326  	c.Assert(ok, jc.IsTrue)
   327  
   328  	result, err = s.uniter.PublicAddress(args)
   329  	c.Assert(err, gc.IsNil)
   330  	c.Assert(result, gc.DeepEquals, params.StringResults{
   331  		Results: []params.StringResult{
   332  			{Error: apiservertesting.ErrUnauthorized},
   333  			{Result: "1.2.3.4"},
   334  			{Error: apiservertesting.ErrUnauthorized},
   335  		},
   336  	})
   337  }
   338  
   339  func (s *uniterSuite) TestSetPublicAddress(c *gc.C) {
   340  	err := s.wordpressUnit.SetPublicAddress("1.2.3.4")
   341  	c.Assert(err, gc.IsNil)
   342  	address, ok := s.wordpressUnit.PublicAddress()
   343  	c.Assert(address, gc.Equals, "1.2.3.4")
   344  	c.Assert(ok, jc.IsTrue)
   345  
   346  	args := params.SetEntityAddresses{Entities: []params.SetEntityAddress{
   347  		{Tag: "unit-mysql-0", Address: "4.3.2.1"},
   348  		{Tag: "unit-wordpress-0", Address: "4.4.2.2"},
   349  		{Tag: "unit-foo-42", Address: "2.2.4.4"},
   350  	}}
   351  	result, err := s.uniter.SetPublicAddress(args)
   352  	c.Assert(err, gc.IsNil)
   353  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   354  		Results: []params.ErrorResult{
   355  			{apiservertesting.ErrUnauthorized},
   356  			{nil},
   357  			{apiservertesting.ErrUnauthorized},
   358  		},
   359  	})
   360  
   361  	// Verify wordpressUnit's address has changed.
   362  	err = s.wordpressUnit.Refresh()
   363  	c.Assert(err, gc.IsNil)
   364  	address, ok = s.wordpressUnit.PublicAddress()
   365  	c.Assert(address, gc.Equals, "4.4.2.2")
   366  	c.Assert(ok, jc.IsTrue)
   367  }
   368  
   369  func (s *uniterSuite) TestPrivateAddress(c *gc.C) {
   370  	args := params.Entities{Entities: []params.Entity{
   371  		{Tag: "unit-mysql-0"},
   372  		{Tag: "unit-wordpress-0"},
   373  		{Tag: "unit-foo-42"},
   374  	}}
   375  	expectErr := &params.Error{
   376  		Code:    params.CodeNoAddressSet,
   377  		Message: `"unit-wordpress-0" has no private address set`,
   378  	}
   379  	result, err := s.uniter.PrivateAddress(args)
   380  	c.Assert(err, gc.IsNil)
   381  	c.Assert(result, gc.DeepEquals, params.StringResults{
   382  		Results: []params.StringResult{
   383  			{Error: apiservertesting.ErrUnauthorized},
   384  			{Error: expectErr},
   385  			{Error: apiservertesting.ErrUnauthorized},
   386  		},
   387  	})
   388  
   389  	// Now set it and try again.
   390  	err = s.wordpressUnit.SetPrivateAddress("1.2.3.4")
   391  	c.Assert(err, gc.IsNil)
   392  	address, ok := s.wordpressUnit.PrivateAddress()
   393  	c.Assert(address, gc.Equals, "1.2.3.4")
   394  	c.Assert(ok, jc.IsTrue)
   395  
   396  	result, err = s.uniter.PrivateAddress(args)
   397  	c.Assert(err, gc.IsNil)
   398  	c.Assert(result, gc.DeepEquals, params.StringResults{
   399  		Results: []params.StringResult{
   400  			{Error: apiservertesting.ErrUnauthorized},
   401  			{Result: "1.2.3.4"},
   402  			{Error: apiservertesting.ErrUnauthorized},
   403  		},
   404  	})
   405  }
   406  
   407  func (s *uniterSuite) TestSetPrivateAddress(c *gc.C) {
   408  	err := s.wordpressUnit.SetPrivateAddress("1.2.3.4")
   409  	c.Assert(err, gc.IsNil)
   410  	address, ok := s.wordpressUnit.PrivateAddress()
   411  	c.Assert(address, gc.Equals, "1.2.3.4")
   412  	c.Assert(ok, jc.IsTrue)
   413  
   414  	args := params.SetEntityAddresses{Entities: []params.SetEntityAddress{
   415  		{Tag: "unit-mysql-0", Address: "4.3.2.1"},
   416  		{Tag: "unit-wordpress-0", Address: "4.4.2.2"},
   417  		{Tag: "unit-foo-42", Address: "2.2.4.4"},
   418  	}}
   419  	result, err := s.uniter.SetPrivateAddress(args)
   420  	c.Assert(err, gc.IsNil)
   421  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   422  		Results: []params.ErrorResult{
   423  			{apiservertesting.ErrUnauthorized},
   424  			{nil},
   425  			{apiservertesting.ErrUnauthorized},
   426  		},
   427  	})
   428  
   429  	// Verify wordpressUnit's address has changed.
   430  	err = s.wordpressUnit.Refresh()
   431  	c.Assert(err, gc.IsNil)
   432  	address, ok = s.wordpressUnit.PrivateAddress()
   433  	c.Assert(address, gc.Equals, "4.4.2.2")
   434  	c.Assert(ok, jc.IsTrue)
   435  }
   436  
   437  func (s *uniterSuite) TestResolved(c *gc.C) {
   438  	err := s.wordpressUnit.SetResolved(state.ResolvedRetryHooks)
   439  	c.Assert(err, gc.IsNil)
   440  	mode := s.wordpressUnit.Resolved()
   441  	c.Assert(mode, gc.Equals, state.ResolvedRetryHooks)
   442  
   443  	args := params.Entities{Entities: []params.Entity{
   444  		{Tag: "unit-mysql-0"},
   445  		{Tag: "unit-wordpress-0"},
   446  		{Tag: "unit-foo-42"},
   447  	}}
   448  	result, err := s.uniter.Resolved(args)
   449  	c.Assert(err, gc.IsNil)
   450  	c.Assert(result, gc.DeepEquals, params.ResolvedModeResults{
   451  		Results: []params.ResolvedModeResult{
   452  			{Error: apiservertesting.ErrUnauthorized},
   453  			{Mode: params.ResolvedMode(mode)},
   454  			{Error: apiservertesting.ErrUnauthorized},
   455  		},
   456  	})
   457  }
   458  
   459  func (s *uniterSuite) TestClearResolved(c *gc.C) {
   460  	err := s.wordpressUnit.SetResolved(state.ResolvedRetryHooks)
   461  	c.Assert(err, gc.IsNil)
   462  	mode := s.wordpressUnit.Resolved()
   463  	c.Assert(mode, gc.Equals, state.ResolvedRetryHooks)
   464  
   465  	args := params.Entities{Entities: []params.Entity{
   466  		{Tag: "unit-mysql-0"},
   467  		{Tag: "unit-wordpress-0"},
   468  		{Tag: "unit-foo-42"},
   469  	}}
   470  	result, err := s.uniter.ClearResolved(args)
   471  	c.Assert(err, gc.IsNil)
   472  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   473  		Results: []params.ErrorResult{
   474  			{apiservertesting.ErrUnauthorized},
   475  			{nil},
   476  			{apiservertesting.ErrUnauthorized},
   477  		},
   478  	})
   479  
   480  	// Verify wordpressUnit's resolved mode has changed.
   481  	err = s.wordpressUnit.Refresh()
   482  	c.Assert(err, gc.IsNil)
   483  	mode = s.wordpressUnit.Resolved()
   484  	c.Assert(mode, gc.Equals, state.ResolvedNone)
   485  }
   486  
   487  func (s *uniterSuite) TestGetPrincipal(c *gc.C) {
   488  	// Add a subordinate to wordpressUnit.
   489  	_, _, subordinate := s.addRelatedService(c, "wordpress", "logging", s.wordpressUnit)
   490  
   491  	principal, ok := subordinate.PrincipalName()
   492  	c.Assert(principal, gc.Equals, s.wordpressUnit.Name())
   493  	c.Assert(ok, jc.IsTrue)
   494  
   495  	// First try it as wordpressUnit's agent.
   496  	args := params.Entities{Entities: []params.Entity{
   497  		{Tag: "unit-mysql-0"},
   498  		{Tag: "unit-wordpress-0"},
   499  		{Tag: subordinate.Tag()},
   500  		{Tag: "unit-foo-42"},
   501  	}}
   502  	result, err := s.uniter.GetPrincipal(args)
   503  	c.Assert(err, gc.IsNil)
   504  	c.Assert(result, gc.DeepEquals, params.StringBoolResults{
   505  		Results: []params.StringBoolResult{
   506  			{Error: apiservertesting.ErrUnauthorized},
   507  			{Result: "", Ok: false, Error: nil},
   508  			{Error: apiservertesting.ErrUnauthorized},
   509  			{Error: apiservertesting.ErrUnauthorized},
   510  		},
   511  	})
   512  
   513  	// Now try as subordinate's agent.
   514  	subAuthorizer := s.authorizer
   515  	subAuthorizer.Tag = subordinate.Tag()
   516  	subUniter, err := uniter.NewUniterAPI(s.State, s.resources, subAuthorizer)
   517  	c.Assert(err, gc.IsNil)
   518  
   519  	result, err = subUniter.GetPrincipal(args)
   520  	c.Assert(err, gc.IsNil)
   521  	c.Assert(result, gc.DeepEquals, params.StringBoolResults{
   522  		Results: []params.StringBoolResult{
   523  			{Error: apiservertesting.ErrUnauthorized},
   524  			{Error: apiservertesting.ErrUnauthorized},
   525  			{Result: "unit-wordpress-0", Ok: true, Error: nil},
   526  			{Error: apiservertesting.ErrUnauthorized},
   527  		},
   528  	})
   529  }
   530  
   531  func (s *uniterSuite) addRelatedService(c *gc.C, firstSvc, relatedSvc string, unit *state.Unit) (*state.Relation, *state.Service, *state.Unit) {
   532  	relatedService := s.AddTestingService(c, relatedSvc, s.AddTestingCharm(c, relatedSvc))
   533  	rel := s.addRelation(c, firstSvc, relatedSvc)
   534  	relUnit, err := rel.Unit(unit)
   535  	c.Assert(err, gc.IsNil)
   536  	err = relUnit.EnterScope(nil)
   537  	c.Assert(err, gc.IsNil)
   538  	relatedUnit, err := relatedService.Unit(relatedSvc + "/0")
   539  	c.Assert(err, gc.IsNil)
   540  	return rel, relatedService, relatedUnit
   541  }
   542  
   543  func (s *uniterSuite) TestHasSubordinates(c *gc.C) {
   544  	// Try first without any subordinates for wordpressUnit.
   545  	args := params.Entities{Entities: []params.Entity{
   546  		{Tag: "unit-mysql-0"},
   547  		{Tag: "unit-wordpress-0"},
   548  		{Tag: "unit-logging-0"},
   549  		{Tag: "unit-foo-42"},
   550  	}}
   551  	result, err := s.uniter.HasSubordinates(args)
   552  	c.Assert(err, gc.IsNil)
   553  	c.Assert(result, gc.DeepEquals, params.BoolResults{
   554  		Results: []params.BoolResult{
   555  			{Error: apiservertesting.ErrUnauthorized},
   556  			{Result: false},
   557  			{Error: apiservertesting.ErrUnauthorized},
   558  			{Error: apiservertesting.ErrUnauthorized},
   559  		},
   560  	})
   561  
   562  	// Add two subordinates to wordpressUnit and try again.
   563  	s.addRelatedService(c, "wordpress", "logging", s.wordpressUnit)
   564  	s.addRelatedService(c, "wordpress", "monitoring", s.wordpressUnit)
   565  
   566  	result, err = s.uniter.HasSubordinates(args)
   567  	c.Assert(err, gc.IsNil)
   568  	c.Assert(result, gc.DeepEquals, params.BoolResults{
   569  		Results: []params.BoolResult{
   570  			{Error: apiservertesting.ErrUnauthorized},
   571  			{Result: true},
   572  			{Error: apiservertesting.ErrUnauthorized},
   573  			{Error: apiservertesting.ErrUnauthorized},
   574  		},
   575  	})
   576  }
   577  
   578  func (s *uniterSuite) TestDestroy(c *gc.C) {
   579  	c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Alive)
   580  
   581  	args := params.Entities{Entities: []params.Entity{
   582  		{Tag: "unit-mysql-0"},
   583  		{Tag: "unit-wordpress-0"},
   584  		{Tag: "unit-foo-42"},
   585  	}}
   586  	result, err := s.uniter.Destroy(args)
   587  	c.Assert(err, gc.IsNil)
   588  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   589  		Results: []params.ErrorResult{
   590  			{apiservertesting.ErrUnauthorized},
   591  			{nil},
   592  			{apiservertesting.ErrUnauthorized},
   593  		},
   594  	})
   595  
   596  	// Verify wordpressUnit is destroyed and removed.
   597  	err = s.wordpressUnit.Refresh()
   598  	c.Assert(err, jc.Satisfies, errors.IsNotFoundError)
   599  }
   600  
   601  func (s *uniterSuite) TestDestroyAllSubordinates(c *gc.C) {
   602  	// Add two subordinates to wordpressUnit.
   603  	_, _, loggingSub := s.addRelatedService(c, "wordpress", "logging", s.wordpressUnit)
   604  	_, _, monitoringSub := s.addRelatedService(c, "wordpress", "monitoring", s.wordpressUnit)
   605  	c.Assert(loggingSub.Life(), gc.Equals, state.Alive)
   606  	c.Assert(monitoringSub.Life(), gc.Equals, state.Alive)
   607  
   608  	err := s.wordpressUnit.Refresh()
   609  	c.Assert(err, gc.IsNil)
   610  	subordinates := s.wordpressUnit.SubordinateNames()
   611  	c.Assert(subordinates, gc.DeepEquals, []string{"logging/0", "monitoring/0"})
   612  
   613  	args := params.Entities{Entities: []params.Entity{
   614  		{Tag: "unit-mysql-0"},
   615  		{Tag: "unit-wordpress-0"},
   616  		{Tag: "unit-foo-42"},
   617  	}}
   618  	result, err := s.uniter.DestroyAllSubordinates(args)
   619  	c.Assert(err, gc.IsNil)
   620  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   621  		Results: []params.ErrorResult{
   622  			{apiservertesting.ErrUnauthorized},
   623  			{nil},
   624  			{apiservertesting.ErrUnauthorized},
   625  		},
   626  	})
   627  
   628  	// Verify wordpressUnit's subordinates were destroyed.
   629  	err = loggingSub.Refresh()
   630  	c.Assert(err, gc.IsNil)
   631  	c.Assert(loggingSub.Life(), gc.Equals, state.Dying)
   632  	err = monitoringSub.Refresh()
   633  	c.Assert(err, gc.IsNil)
   634  	c.Assert(monitoringSub.Life(), gc.Equals, state.Dying)
   635  }
   636  
   637  func (s *uniterSuite) TestCharmURL(c *gc.C) {
   638  	// Set wordpressUnit's charm URL first.
   639  	err := s.wordpressUnit.SetCharmURL(s.wpCharm.URL())
   640  	c.Assert(err, gc.IsNil)
   641  	curl, ok := s.wordpressUnit.CharmURL()
   642  	c.Assert(curl, gc.DeepEquals, s.wpCharm.URL())
   643  	c.Assert(ok, jc.IsTrue)
   644  
   645  	// Make sure wordpress service's charm is what we expect.
   646  	curl, force := s.wordpress.CharmURL()
   647  	c.Assert(curl, gc.DeepEquals, s.wpCharm.URL())
   648  	c.Assert(force, jc.IsFalse)
   649  
   650  	args := params.Entities{Entities: []params.Entity{
   651  		{Tag: "unit-mysql-0"},
   652  		{Tag: "unit-wordpress-0"},
   653  		{Tag: "unit-foo-42"},
   654  		{Tag: "service-mysql"},
   655  		{Tag: "service-wordpress"},
   656  		{Tag: "service-foo"},
   657  		{Tag: "just-foo"},
   658  	}}
   659  	result, err := s.uniter.CharmURL(args)
   660  	c.Assert(err, gc.IsNil)
   661  	c.Assert(result, gc.DeepEquals, params.StringBoolResults{
   662  		Results: []params.StringBoolResult{
   663  			{Error: apiservertesting.ErrUnauthorized},
   664  			{Result: s.wpCharm.String(), Ok: ok},
   665  			{Error: apiservertesting.ErrUnauthorized},
   666  			{Error: apiservertesting.ErrUnauthorized},
   667  			{Result: s.wpCharm.String(), Ok: force},
   668  			{Error: apiservertesting.ErrUnauthorized},
   669  			{Error: apiservertesting.ErrUnauthorized},
   670  		},
   671  	})
   672  }
   673  
   674  func (s *uniterSuite) TestSetCharmURL(c *gc.C) {
   675  	charmUrl, ok := s.wordpressUnit.CharmURL()
   676  	c.Assert(charmUrl, gc.IsNil)
   677  	c.Assert(ok, jc.IsFalse)
   678  
   679  	args := params.EntitiesCharmURL{Entities: []params.EntityCharmURL{
   680  		{Tag: "unit-mysql-0", CharmURL: "cs:quantal/service-42"},
   681  		{Tag: "unit-wordpress-0", CharmURL: s.wpCharm.String()},
   682  		{Tag: "unit-foo-42", CharmURL: "cs:quantal/foo-321"},
   683  	}}
   684  	result, err := s.uniter.SetCharmURL(args)
   685  	c.Assert(err, gc.IsNil)
   686  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   687  		Results: []params.ErrorResult{
   688  			{apiservertesting.ErrUnauthorized},
   689  			{nil},
   690  			{apiservertesting.ErrUnauthorized},
   691  		},
   692  	})
   693  
   694  	// Verify the charm URL was set.
   695  	err = s.wordpressUnit.Refresh()
   696  	c.Assert(err, gc.IsNil)
   697  	charmUrl, ok = s.wordpressUnit.CharmURL()
   698  	c.Assert(charmUrl, gc.NotNil)
   699  	c.Assert(charmUrl.String(), gc.Equals, s.wpCharm.String())
   700  	c.Assert(ok, jc.IsTrue)
   701  }
   702  
   703  func (s *uniterSuite) TestOpenPort(c *gc.C) {
   704  	openedPorts := s.wordpressUnit.OpenedPorts()
   705  	c.Assert(openedPorts, gc.HasLen, 0)
   706  
   707  	args := params.EntitiesPorts{Entities: []params.EntityPort{
   708  		{Tag: "unit-mysql-0", Protocol: "tcp", Port: 1234},
   709  		{Tag: "unit-wordpress-0", Protocol: "udp", Port: 4321},
   710  		{Tag: "unit-foo-42", Protocol: "tcp", Port: 42},
   711  	}}
   712  	result, err := s.uniter.OpenPort(args)
   713  	c.Assert(err, gc.IsNil)
   714  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   715  		Results: []params.ErrorResult{
   716  			{apiservertesting.ErrUnauthorized},
   717  			{nil},
   718  			{apiservertesting.ErrUnauthorized},
   719  		},
   720  	})
   721  
   722  	// Verify the wordpressUnit's port is opened.
   723  	err = s.wordpressUnit.Refresh()
   724  	c.Assert(err, gc.IsNil)
   725  	openedPorts = s.wordpressUnit.OpenedPorts()
   726  	c.Assert(openedPorts, gc.DeepEquals, []instance.Port{
   727  		{Protocol: "udp", Number: 4321},
   728  	})
   729  }
   730  
   731  func (s *uniterSuite) TestClosePort(c *gc.C) {
   732  	// Open port udp:4321 in advance on wordpressUnit.
   733  	err := s.wordpressUnit.OpenPort("udp", 4321)
   734  	c.Assert(err, gc.IsNil)
   735  	err = s.wordpressUnit.Refresh()
   736  	c.Assert(err, gc.IsNil)
   737  	openedPorts := s.wordpressUnit.OpenedPorts()
   738  	c.Assert(openedPorts, gc.DeepEquals, []instance.Port{
   739  		{Protocol: "udp", Number: 4321},
   740  	})
   741  
   742  	args := params.EntitiesPorts{Entities: []params.EntityPort{
   743  		{Tag: "unit-mysql-0", Protocol: "tcp", Port: 1234},
   744  		{Tag: "unit-wordpress-0", Protocol: "udp", Port: 4321},
   745  		{Tag: "unit-foo-42", Protocol: "tcp", Port: 42},
   746  	}}
   747  	result, err := s.uniter.ClosePort(args)
   748  	c.Assert(err, gc.IsNil)
   749  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   750  		Results: []params.ErrorResult{
   751  			{apiservertesting.ErrUnauthorized},
   752  			{nil},
   753  			{apiservertesting.ErrUnauthorized},
   754  		},
   755  	})
   756  
   757  	// Verify the wordpressUnit's port is closed.
   758  	err = s.wordpressUnit.Refresh()
   759  	c.Assert(err, gc.IsNil)
   760  	openedPorts = s.wordpressUnit.OpenedPorts()
   761  	c.Assert(openedPorts, gc.HasLen, 0)
   762  }
   763  
   764  func (s *uniterSuite) TestWatchConfigSettings(c *gc.C) {
   765  	err := s.wordpressUnit.SetCharmURL(s.wpCharm.URL())
   766  	c.Assert(err, gc.IsNil)
   767  
   768  	c.Assert(s.resources.Count(), gc.Equals, 0)
   769  
   770  	args := params.Entities{Entities: []params.Entity{
   771  		{Tag: "unit-mysql-0"},
   772  		{Tag: "unit-wordpress-0"},
   773  		{Tag: "unit-foo-42"},
   774  	}}
   775  	result, err := s.uniter.WatchConfigSettings(args)
   776  	c.Assert(err, gc.IsNil)
   777  	c.Assert(result, gc.DeepEquals, params.NotifyWatchResults{
   778  		Results: []params.NotifyWatchResult{
   779  			{Error: apiservertesting.ErrUnauthorized},
   780  			{NotifyWatcherId: "1"},
   781  			{Error: apiservertesting.ErrUnauthorized},
   782  		},
   783  	})
   784  
   785  	// Verify the resource was registered and stop when done
   786  	c.Assert(s.resources.Count(), gc.Equals, 1)
   787  	resource := s.resources.Get("1")
   788  	defer statetesting.AssertStop(c, resource)
   789  
   790  	// Check that the Watch has consumed the initial event ("returned" in
   791  	// the Watch call)
   792  	wc := statetesting.NewNotifyWatcherC(c, s.State, resource.(state.NotifyWatcher))
   793  	wc.AssertNoChange()
   794  }
   795  
   796  func (s *uniterSuite) TestConfigSettings(c *gc.C) {
   797  	err := s.wordpressUnit.SetCharmURL(s.wpCharm.URL())
   798  	c.Assert(err, gc.IsNil)
   799  	settings, err := s.wordpressUnit.ConfigSettings()
   800  	c.Assert(err, gc.IsNil)
   801  	c.Assert(settings, gc.DeepEquals, charm.Settings{"blog-title": "My Title"})
   802  
   803  	args := params.Entities{Entities: []params.Entity{
   804  		{Tag: "unit-mysql-0"},
   805  		{Tag: "unit-wordpress-0"},
   806  		{Tag: "unit-foo-42"},
   807  	}}
   808  	result, err := s.uniter.ConfigSettings(args)
   809  	c.Assert(err, gc.IsNil)
   810  	c.Assert(result, gc.DeepEquals, params.ConfigSettingsResults{
   811  		Results: []params.ConfigSettingsResult{
   812  			{Error: apiservertesting.ErrUnauthorized},
   813  			{Settings: params.ConfigSettings{"blog-title": "My Title"}},
   814  			{Error: apiservertesting.ErrUnauthorized},
   815  		},
   816  	})
   817  }
   818  
   819  func (s *uniterSuite) TestWatchServiceRelations(c *gc.C) {
   820  	c.Assert(s.resources.Count(), gc.Equals, 0)
   821  
   822  	args := params.Entities{Entities: []params.Entity{
   823  		{Tag: "service-mysql"},
   824  		{Tag: "service-wordpress"},
   825  		{Tag: "service-foo"},
   826  	}}
   827  	result, err := s.uniter.WatchServiceRelations(args)
   828  	s.assertOneStringsWatcher(c, result, err)
   829  }
   830  
   831  func (s *uniterSuite) TestCharmArchiveURL(c *gc.C) {
   832  	dummyCharm := s.AddTestingCharm(c, "dummy")
   833  
   834  	args := params.CharmURLs{URLs: []params.CharmURL{
   835  		{URL: "something-invalid"},
   836  		{URL: s.wpCharm.String()},
   837  		{URL: dummyCharm.String()},
   838  	}}
   839  	result, err := s.uniter.CharmArchiveURL(args)
   840  	c.Assert(err, gc.IsNil)
   841  	c.Assert(result, gc.DeepEquals, params.CharmArchiveURLResults{
   842  		Results: []params.CharmArchiveURLResult{
   843  			{Error: apiservertesting.ErrUnauthorized},
   844  			{
   845  				Result: s.wpCharm.BundleURL().String(),
   846  				DisableSSLHostnameVerification: false,
   847  			},
   848  			{
   849  				Result: dummyCharm.BundleURL().String(),
   850  				DisableSSLHostnameVerification: false,
   851  			},
   852  		},
   853  	})
   854  
   855  	envtesting.SetSSLHostnameVerification(c, s.State, false)
   856  
   857  	result, err = s.uniter.CharmArchiveURL(args)
   858  	c.Assert(err, gc.IsNil)
   859  	c.Assert(result, gc.DeepEquals, params.CharmArchiveURLResults{
   860  		Results: []params.CharmArchiveURLResult{
   861  			{Error: apiservertesting.ErrUnauthorized},
   862  			{
   863  				Result: s.wpCharm.BundleURL().String(),
   864  				DisableSSLHostnameVerification: true,
   865  			},
   866  			{
   867  				Result: dummyCharm.BundleURL().String(),
   868  				DisableSSLHostnameVerification: true,
   869  			},
   870  		},
   871  	})
   872  }
   873  
   874  func (s *uniterSuite) TestCharmArchiveSha256(c *gc.C) {
   875  	dummyCharm := s.AddTestingCharm(c, "dummy")
   876  
   877  	args := params.CharmURLs{URLs: []params.CharmURL{
   878  		{URL: "something-invalid"},
   879  		{URL: s.wpCharm.String()},
   880  		{URL: dummyCharm.String()},
   881  	}}
   882  	result, err := s.uniter.CharmArchiveSha256(args)
   883  	c.Assert(err, gc.IsNil)
   884  	c.Assert(result, gc.DeepEquals, params.StringResults{
   885  		Results: []params.StringResult{
   886  			{Error: apiservertesting.ErrUnauthorized},
   887  			{Result: s.wpCharm.BundleSha256()},
   888  			{Result: dummyCharm.BundleSha256()},
   889  		},
   890  	})
   891  }
   892  
   893  func (s *uniterSuite) TestCurrentEnvironUUID(c *gc.C) {
   894  	env, err := s.State.Environment()
   895  	c.Assert(err, gc.IsNil)
   896  
   897  	result, err := s.uniter.CurrentEnvironUUID()
   898  	c.Assert(err, gc.IsNil)
   899  	c.Assert(result, gc.DeepEquals, params.StringResult{Result: env.UUID()})
   900  }
   901  
   902  func (s *uniterSuite) TestCurrentEnvironment(c *gc.C) {
   903  	env, err := s.State.Environment()
   904  	c.Assert(err, gc.IsNil)
   905  
   906  	result, err := s.uniter.CurrentEnvironment()
   907  	c.Assert(err, gc.IsNil)
   908  	expected := params.EnvironmentResult{
   909  		Name: env.Name(),
   910  		UUID: env.UUID(),
   911  	}
   912  	c.Assert(result, gc.DeepEquals, expected)
   913  }
   914  
   915  func (s *uniterSuite) addRelation(c *gc.C, first, second string) *state.Relation {
   916  	eps, err := s.State.InferEndpoints([]string{first, second})
   917  	c.Assert(err, gc.IsNil)
   918  	rel, err := s.State.AddRelation(eps...)
   919  	c.Assert(err, gc.IsNil)
   920  	return rel
   921  }
   922  
   923  func (s *uniterSuite) TestRelation(c *gc.C) {
   924  	rel := s.addRelation(c, "wordpress", "mysql")
   925  	wpEp, err := rel.Endpoint("wordpress")
   926  	c.Assert(err, gc.IsNil)
   927  
   928  	args := params.RelationUnits{RelationUnits: []params.RelationUnit{
   929  		{Relation: "relation-42", Unit: "unit-foo-0"},
   930  		{Relation: rel.Tag(), Unit: "unit-wordpress-0"},
   931  		{Relation: rel.Tag(), Unit: "unit-mysql-0"},
   932  		{Relation: rel.Tag(), Unit: "unit-foo-0"},
   933  		{Relation: "relation-blah", Unit: "unit-wordpress-0"},
   934  		{Relation: "service-foo", Unit: "user-admin"},
   935  		{Relation: "foo", Unit: "bar"},
   936  		{Relation: "unit-wordpress-0", Unit: rel.Tag()},
   937  	}}
   938  	result, err := s.uniter.Relation(args)
   939  	c.Assert(err, gc.IsNil)
   940  	c.Assert(result, gc.DeepEquals, params.RelationResults{
   941  		Results: []params.RelationResult{
   942  			{Error: apiservertesting.ErrUnauthorized},
   943  			{
   944  				Id:   rel.Id(),
   945  				Key:  rel.String(),
   946  				Life: params.Life(rel.Life().String()),
   947  				Endpoint: params.Endpoint{
   948  					ServiceName: wpEp.ServiceName,
   949  					Relation:    wpEp.Relation,
   950  				},
   951  			},
   952  			{Error: apiservertesting.ErrUnauthorized},
   953  			{Error: apiservertesting.ErrUnauthorized},
   954  			{Error: apiservertesting.ErrUnauthorized},
   955  			{Error: apiservertesting.ErrUnauthorized},
   956  			{Error: apiservertesting.ErrUnauthorized},
   957  			{Error: apiservertesting.ErrUnauthorized},
   958  		},
   959  	})
   960  }
   961  
   962  func (s *uniterSuite) TestRelationById(c *gc.C) {
   963  	rel := s.addRelation(c, "wordpress", "mysql")
   964  	c.Assert(rel.Id(), gc.Equals, 0)
   965  	wpEp, err := rel.Endpoint("wordpress")
   966  	c.Assert(err, gc.IsNil)
   967  
   968  	// Add another relation to mysql service, so we can see we can't
   969  	// get it.
   970  	otherRel, _, _ := s.addRelatedService(c, "mysql", "logging", s.mysqlUnit)
   971  
   972  	args := params.RelationIds{
   973  		RelationIds: []int{-1, rel.Id(), otherRel.Id(), 42, 234},
   974  	}
   975  	result, err := s.uniter.RelationById(args)
   976  	c.Assert(err, gc.IsNil)
   977  	c.Assert(result, gc.DeepEquals, params.RelationResults{
   978  		Results: []params.RelationResult{
   979  			{Error: apiservertesting.ErrUnauthorized},
   980  			{
   981  				Id:   rel.Id(),
   982  				Key:  rel.String(),
   983  				Life: params.Life(rel.Life().String()),
   984  				Endpoint: params.Endpoint{
   985  					ServiceName: wpEp.ServiceName,
   986  					Relation:    wpEp.Relation,
   987  				},
   988  			},
   989  			{Error: apiservertesting.ErrUnauthorized},
   990  			{Error: apiservertesting.ErrUnauthorized},
   991  			{Error: apiservertesting.ErrUnauthorized},
   992  		},
   993  	})
   994  }
   995  
   996  func (s *uniterSuite) TestProviderType(c *gc.C) {
   997  	cfg, err := s.State.EnvironConfig()
   998  	c.Assert(err, gc.IsNil)
   999  
  1000  	result, err := s.uniter.ProviderType()
  1001  	c.Assert(err, gc.IsNil)
  1002  	c.Assert(result, gc.DeepEquals, params.StringResult{Result: cfg.Type()})
  1003  }
  1004  
  1005  func (s *uniterSuite) assertInScope(c *gc.C, relUnit *state.RelationUnit, inScope bool) {
  1006  	ok, err := relUnit.InScope()
  1007  	c.Assert(err, gc.IsNil)
  1008  	c.Assert(ok, gc.Equals, inScope)
  1009  }
  1010  
  1011  func (s *uniterSuite) TestEnterScope(c *gc.C) {
  1012  	// Set wordpressUnit's private address first.
  1013  	err := s.wordpressUnit.SetPrivateAddress("1.2.3.4")
  1014  	c.Assert(err, gc.IsNil)
  1015  
  1016  	rel := s.addRelation(c, "wordpress", "mysql")
  1017  	relUnit, err := rel.Unit(s.wordpressUnit)
  1018  	c.Assert(err, gc.IsNil)
  1019  	s.assertInScope(c, relUnit, false)
  1020  
  1021  	args := params.RelationUnits{RelationUnits: []params.RelationUnit{
  1022  		{Relation: "relation-42", Unit: "unit-foo-0"},
  1023  		{Relation: rel.Tag(), Unit: "unit-wordpress-0"},
  1024  		{Relation: rel.Tag(), Unit: "unit-wordpress-0"},
  1025  		{Relation: "relation-42", Unit: "unit-wordpress-0"},
  1026  		{Relation: "relation-foo", Unit: "unit-wordpress-0"},
  1027  		{Relation: "service-wordpress", Unit: "unit-foo-0"},
  1028  		{Relation: "foo", Unit: "bar"},
  1029  		{Relation: rel.Tag(), Unit: "unit-mysql-0"},
  1030  		{Relation: rel.Tag(), Unit: "service-wordpress"},
  1031  		{Relation: rel.Tag(), Unit: "service-mysql"},
  1032  		{Relation: rel.Tag(), Unit: "user-admin"},
  1033  	}}
  1034  	result, err := s.uniter.EnterScope(args)
  1035  	c.Assert(err, gc.IsNil)
  1036  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  1037  		Results: []params.ErrorResult{
  1038  			{apiservertesting.ErrUnauthorized},
  1039  			{nil},
  1040  			{nil},
  1041  			{apiservertesting.ErrUnauthorized},
  1042  			{apiservertesting.ErrUnauthorized},
  1043  			{apiservertesting.ErrUnauthorized},
  1044  			{apiservertesting.ErrUnauthorized},
  1045  			{apiservertesting.ErrUnauthorized},
  1046  			{apiservertesting.ErrUnauthorized},
  1047  			{apiservertesting.ErrUnauthorized},
  1048  			{apiservertesting.ErrUnauthorized},
  1049  		},
  1050  	})
  1051  
  1052  	// Verify the scope changes and settings.
  1053  	s.assertInScope(c, relUnit, true)
  1054  	readSettings, err := relUnit.ReadSettings(s.wordpressUnit.Name())
  1055  	c.Assert(err, gc.IsNil)
  1056  	c.Assert(readSettings, gc.DeepEquals, map[string]interface{}{
  1057  		"private-address": "1.2.3.4",
  1058  	})
  1059  }
  1060  
  1061  func (s *uniterSuite) TestLeaveScope(c *gc.C) {
  1062  	rel := s.addRelation(c, "wordpress", "mysql")
  1063  	relUnit, err := rel.Unit(s.wordpressUnit)
  1064  	c.Assert(err, gc.IsNil)
  1065  	settings := map[string]interface{}{
  1066  		"some": "settings",
  1067  	}
  1068  	err = relUnit.EnterScope(settings)
  1069  	c.Assert(err, gc.IsNil)
  1070  	s.assertInScope(c, relUnit, true)
  1071  
  1072  	args := params.RelationUnits{RelationUnits: []params.RelationUnit{
  1073  		{Relation: "relation-42", Unit: "unit-foo-0"},
  1074  		{Relation: rel.Tag(), Unit: "unit-wordpress-0"},
  1075  		{Relation: rel.Tag(), Unit: "unit-wordpress-0"},
  1076  		{Relation: "relation-42", Unit: "unit-wordpress-0"},
  1077  		{Relation: "relation-foo", Unit: "unit-wordpress-0"},
  1078  		{Relation: "service-wordpress", Unit: "unit-foo-0"},
  1079  		{Relation: "foo", Unit: "bar"},
  1080  		{Relation: rel.Tag(), Unit: "unit-mysql-0"},
  1081  		{Relation: rel.Tag(), Unit: "service-wordpress"},
  1082  		{Relation: rel.Tag(), Unit: "service-mysql"},
  1083  		{Relation: rel.Tag(), Unit: "user-admin"},
  1084  	}}
  1085  	result, err := s.uniter.LeaveScope(args)
  1086  	c.Assert(err, gc.IsNil)
  1087  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  1088  		Results: []params.ErrorResult{
  1089  			{apiservertesting.ErrUnauthorized},
  1090  			{nil},
  1091  			{nil},
  1092  			{apiservertesting.ErrUnauthorized},
  1093  			{apiservertesting.ErrUnauthorized},
  1094  			{apiservertesting.ErrUnauthorized},
  1095  			{apiservertesting.ErrUnauthorized},
  1096  			{apiservertesting.ErrUnauthorized},
  1097  			{apiservertesting.ErrUnauthorized},
  1098  			{apiservertesting.ErrUnauthorized},
  1099  			{apiservertesting.ErrUnauthorized},
  1100  		},
  1101  	})
  1102  
  1103  	// Verify the scope changes.
  1104  	s.assertInScope(c, relUnit, false)
  1105  	readSettings, err := relUnit.ReadSettings(s.wordpressUnit.Name())
  1106  	c.Assert(err, gc.IsNil)
  1107  	c.Assert(readSettings, gc.DeepEquals, settings)
  1108  }
  1109  
  1110  func (s *uniterSuite) TestReadSettings(c *gc.C) {
  1111  	rel := s.addRelation(c, "wordpress", "mysql")
  1112  	relUnit, err := rel.Unit(s.wordpressUnit)
  1113  	c.Assert(err, gc.IsNil)
  1114  	settings := map[string]interface{}{
  1115  		"some": "settings",
  1116  	}
  1117  	err = relUnit.EnterScope(settings)
  1118  	c.Assert(err, gc.IsNil)
  1119  	s.assertInScope(c, relUnit, true)
  1120  
  1121  	args := params.RelationUnits{RelationUnits: []params.RelationUnit{
  1122  		{Relation: "relation-42", Unit: "unit-foo-0"},
  1123  		{Relation: rel.Tag(), Unit: "unit-wordpress-0"},
  1124  		{Relation: rel.Tag(), Unit: "unit-mysql-0"},
  1125  		{Relation: "relation-42", Unit: "unit-wordpress-0"},
  1126  		{Relation: "relation-foo", Unit: ""},
  1127  		{Relation: "service-wordpress", Unit: "unit-foo-0"},
  1128  		{Relation: "foo", Unit: "bar"},
  1129  		{Relation: rel.Tag(), Unit: "unit-mysql-0"},
  1130  		{Relation: rel.Tag(), Unit: "service-wordpress"},
  1131  		{Relation: rel.Tag(), Unit: "service-mysql"},
  1132  		{Relation: rel.Tag(), Unit: "user-admin"},
  1133  	}}
  1134  	result, err := s.uniter.ReadSettings(args)
  1135  	c.Assert(err, gc.IsNil)
  1136  	c.Assert(result, gc.DeepEquals, params.RelationSettingsResults{
  1137  		Results: []params.RelationSettingsResult{
  1138  			{Error: apiservertesting.ErrUnauthorized},
  1139  			{Settings: params.RelationSettings{
  1140  				"some": "settings",
  1141  			}},
  1142  			{Error: apiservertesting.ErrUnauthorized},
  1143  			{Error: apiservertesting.ErrUnauthorized},
  1144  			{Error: apiservertesting.ErrUnauthorized},
  1145  			{Error: apiservertesting.ErrUnauthorized},
  1146  			{Error: apiservertesting.ErrUnauthorized},
  1147  			{Error: apiservertesting.ErrUnauthorized},
  1148  			{Error: apiservertesting.ErrUnauthorized},
  1149  			{Error: apiservertesting.ErrUnauthorized},
  1150  			{Error: apiservertesting.ErrUnauthorized},
  1151  		},
  1152  	})
  1153  }
  1154  
  1155  func (s *uniterSuite) TestReadSettingsWithNonStringValuesFails(c *gc.C) {
  1156  	rel := s.addRelation(c, "wordpress", "mysql")
  1157  	relUnit, err := rel.Unit(s.wordpressUnit)
  1158  	c.Assert(err, gc.IsNil)
  1159  	settings := map[string]interface{}{
  1160  		"other":        "things",
  1161  		"invalid-bool": false,
  1162  	}
  1163  	err = relUnit.EnterScope(settings)
  1164  	c.Assert(err, gc.IsNil)
  1165  	s.assertInScope(c, relUnit, true)
  1166  
  1167  	args := params.RelationUnits{RelationUnits: []params.RelationUnit{
  1168  		{Relation: rel.Tag(), Unit: "unit-wordpress-0"},
  1169  	}}
  1170  	expectErr := `unexpected relation setting "invalid-bool": expected string, got bool`
  1171  	result, err := s.uniter.ReadSettings(args)
  1172  	c.Assert(err, gc.IsNil)
  1173  	c.Assert(result, gc.DeepEquals, params.RelationSettingsResults{
  1174  		Results: []params.RelationSettingsResult{
  1175  			{Error: &params.Error{Message: expectErr}},
  1176  		},
  1177  	})
  1178  }
  1179  
  1180  func (s *uniterSuite) TestReadRemoteSettings(c *gc.C) {
  1181  	rel := s.addRelation(c, "wordpress", "mysql")
  1182  	relUnit, err := rel.Unit(s.wordpressUnit)
  1183  	c.Assert(err, gc.IsNil)
  1184  	settings := map[string]interface{}{
  1185  		"some": "settings",
  1186  	}
  1187  	err = relUnit.EnterScope(settings)
  1188  	c.Assert(err, gc.IsNil)
  1189  	s.assertInScope(c, relUnit, true)
  1190  
  1191  	// First test most of the invalid args tests and try to read the
  1192  	// (unset) remote unit settings.
  1193  	args := params.RelationUnitPairs{RelationUnitPairs: []params.RelationUnitPair{
  1194  		{Relation: "relation-42", LocalUnit: "unit-foo-0", RemoteUnit: "foo"},
  1195  		{Relation: rel.Tag(), LocalUnit: "unit-wordpress-0", RemoteUnit: "unit-wordpress-0"},
  1196  		{Relation: rel.Tag(), LocalUnit: "unit-wordpress-0", RemoteUnit: "unit-mysql-0"},
  1197  		{Relation: "relation-42", LocalUnit: "unit-wordpress-0", RemoteUnit: ""},
  1198  		{Relation: "relation-foo", LocalUnit: "", RemoteUnit: ""},
  1199  		{Relation: "service-wordpress", LocalUnit: "unit-foo-0", RemoteUnit: "user-admin"},
  1200  		{Relation: "foo", LocalUnit: "bar", RemoteUnit: "baz"},
  1201  		{Relation: rel.Tag(), LocalUnit: "unit-mysql-0", RemoteUnit: "unit-wordpress-0"},
  1202  		{Relation: rel.Tag(), LocalUnit: "service-wordpress", RemoteUnit: "service-mysql"},
  1203  		{Relation: rel.Tag(), LocalUnit: "service-mysql", RemoteUnit: "foo"},
  1204  		{Relation: rel.Tag(), LocalUnit: "user-admin", RemoteUnit: "unit-wordpress-0"},
  1205  	}}
  1206  	result, err := s.uniter.ReadRemoteSettings(args)
  1207  	// We don't set the remote unit settings on purpose to test the error.
  1208  	expectErr := `cannot read settings for unit "mysql/0" in relation "wordpress:db mysql:server": settings not found`
  1209  	c.Assert(err, gc.IsNil)
  1210  	c.Assert(result, gc.DeepEquals, params.RelationSettingsResults{
  1211  		Results: []params.RelationSettingsResult{
  1212  			{Error: apiservertesting.ErrUnauthorized},
  1213  			{Error: apiservertesting.ErrUnauthorized},
  1214  			{Error: &params.Error{Message: expectErr}},
  1215  			{Error: apiservertesting.ErrUnauthorized},
  1216  			{Error: apiservertesting.ErrUnauthorized},
  1217  			{Error: apiservertesting.ErrUnauthorized},
  1218  			{Error: apiservertesting.ErrUnauthorized},
  1219  			{Error: apiservertesting.ErrUnauthorized},
  1220  			{Error: apiservertesting.ErrUnauthorized},
  1221  			{Error: apiservertesting.ErrUnauthorized},
  1222  			{Error: apiservertesting.ErrUnauthorized},
  1223  		},
  1224  	})
  1225  	// Now leave the mysqlUnit and re-enter with new settings.
  1226  	relUnit, err = rel.Unit(s.mysqlUnit)
  1227  	c.Assert(err, gc.IsNil)
  1228  	settings = map[string]interface{}{
  1229  		"other": "things",
  1230  	}
  1231  	err = relUnit.LeaveScope()
  1232  	c.Assert(err, gc.IsNil)
  1233  	s.assertInScope(c, relUnit, false)
  1234  	err = relUnit.EnterScope(settings)
  1235  	c.Assert(err, gc.IsNil)
  1236  	s.assertInScope(c, relUnit, true)
  1237  
  1238  	// Test the remote unit settings can be read.
  1239  	args = params.RelationUnitPairs{RelationUnitPairs: []params.RelationUnitPair{{
  1240  		Relation:   rel.Tag(),
  1241  		LocalUnit:  "unit-wordpress-0",
  1242  		RemoteUnit: "unit-mysql-0",
  1243  	}}}
  1244  	result, err = s.uniter.ReadRemoteSettings(args)
  1245  	c.Assert(err, gc.IsNil)
  1246  	c.Assert(result, gc.DeepEquals, params.RelationSettingsResults{
  1247  		Results: []params.RelationSettingsResult{
  1248  			{Settings: params.RelationSettings{
  1249  				"other": "things",
  1250  			}},
  1251  		},
  1252  	})
  1253  }
  1254  
  1255  func (s *uniterSuite) TestReadRemoteSettingsWithNonStringValuesFails(c *gc.C) {
  1256  	rel := s.addRelation(c, "wordpress", "mysql")
  1257  	relUnit, err := rel.Unit(s.mysqlUnit)
  1258  	c.Assert(err, gc.IsNil)
  1259  	settings := map[string]interface{}{
  1260  		"other":        "things",
  1261  		"invalid-bool": false,
  1262  	}
  1263  	err = relUnit.EnterScope(settings)
  1264  	c.Assert(err, gc.IsNil)
  1265  	s.assertInScope(c, relUnit, true)
  1266  
  1267  	args := params.RelationUnitPairs{RelationUnitPairs: []params.RelationUnitPair{{
  1268  		Relation:   rel.Tag(),
  1269  		LocalUnit:  "unit-wordpress-0",
  1270  		RemoteUnit: "unit-mysql-0",
  1271  	}}}
  1272  	expectErr := `unexpected relation setting "invalid-bool": expected string, got bool`
  1273  	result, err := s.uniter.ReadRemoteSettings(args)
  1274  	c.Assert(err, gc.IsNil)
  1275  	c.Assert(result, gc.DeepEquals, params.RelationSettingsResults{
  1276  		Results: []params.RelationSettingsResult{
  1277  			{Error: &params.Error{Message: expectErr}},
  1278  		},
  1279  	})
  1280  }
  1281  
  1282  func (s *uniterSuite) TestUpdateSettings(c *gc.C) {
  1283  	rel := s.addRelation(c, "wordpress", "mysql")
  1284  	relUnit, err := rel.Unit(s.wordpressUnit)
  1285  	c.Assert(err, gc.IsNil)
  1286  	settings := map[string]interface{}{
  1287  		"some":  "settings",
  1288  		"other": "stuff",
  1289  	}
  1290  	err = relUnit.EnterScope(settings)
  1291  	s.assertInScope(c, relUnit, true)
  1292  
  1293  	newSettings := params.RelationSettings{
  1294  		"some":  "different",
  1295  		"other": "",
  1296  	}
  1297  
  1298  	args := params.RelationUnitsSettings{RelationUnits: []params.RelationUnitSettings{
  1299  		{Relation: "relation-42", Unit: "unit-foo-0", Settings: nil},
  1300  		{Relation: rel.Tag(), Unit: "unit-wordpress-0", Settings: newSettings},
  1301  		{Relation: "relation-42", Unit: "unit-wordpress-0", Settings: nil},
  1302  		{Relation: "relation-foo", Unit: "unit-wordpress-0", Settings: nil},
  1303  		{Relation: "service-wordpress", Unit: "unit-foo-0", Settings: nil},
  1304  		{Relation: "foo", Unit: "bar", Settings: nil},
  1305  		{Relation: rel.Tag(), Unit: "unit-mysql-0", Settings: nil},
  1306  		{Relation: rel.Tag(), Unit: "service-wordpress", Settings: nil},
  1307  		{Relation: rel.Tag(), Unit: "service-mysql", Settings: nil},
  1308  		{Relation: rel.Tag(), Unit: "user-admin", Settings: nil},
  1309  	}}
  1310  	result, err := s.uniter.UpdateSettings(args)
  1311  	c.Assert(err, gc.IsNil)
  1312  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  1313  		Results: []params.ErrorResult{
  1314  			{apiservertesting.ErrUnauthorized},
  1315  			{nil},
  1316  			{apiservertesting.ErrUnauthorized},
  1317  			{apiservertesting.ErrUnauthorized},
  1318  			{apiservertesting.ErrUnauthorized},
  1319  			{apiservertesting.ErrUnauthorized},
  1320  			{apiservertesting.ErrUnauthorized},
  1321  			{apiservertesting.ErrUnauthorized},
  1322  			{apiservertesting.ErrUnauthorized},
  1323  			{apiservertesting.ErrUnauthorized},
  1324  		},
  1325  	})
  1326  
  1327  	// Verify the settings were saved.
  1328  	s.assertInScope(c, relUnit, true)
  1329  	readSettings, err := relUnit.ReadSettings(s.wordpressUnit.Name())
  1330  	c.Assert(err, gc.IsNil)
  1331  	c.Assert(readSettings, gc.DeepEquals, map[string]interface{}{
  1332  		"some": "different",
  1333  	})
  1334  }
  1335  
  1336  func (s *uniterSuite) TestWatchRelationUnits(c *gc.C) {
  1337  	// Add a relation between wordpress and mysql and enter scope with
  1338  	// mysqlUnit.
  1339  	rel := s.addRelation(c, "wordpress", "mysql")
  1340  	myRelUnit, err := rel.Unit(s.mysqlUnit)
  1341  	c.Assert(err, gc.IsNil)
  1342  	err = myRelUnit.EnterScope(nil)
  1343  	c.Assert(err, gc.IsNil)
  1344  	s.assertInScope(c, myRelUnit, true)
  1345  
  1346  	c.Assert(s.resources.Count(), gc.Equals, 0)
  1347  
  1348  	args := params.RelationUnits{RelationUnits: []params.RelationUnit{
  1349  		{Relation: "relation-42", Unit: "unit-foo-0"},
  1350  		{Relation: rel.Tag(), Unit: "unit-wordpress-0"},
  1351  		{Relation: rel.Tag(), Unit: "unit-mysql-0"},
  1352  		{Relation: "relation-42", Unit: "unit-wordpress-0"},
  1353  		{Relation: "relation-foo", Unit: ""},
  1354  		{Relation: "service-wordpress", Unit: "unit-foo-0"},
  1355  		{Relation: "foo", Unit: "bar"},
  1356  		{Relation: rel.Tag(), Unit: "unit-mysql-0"},
  1357  		{Relation: rel.Tag(), Unit: "service-wordpress"},
  1358  		{Relation: rel.Tag(), Unit: "service-mysql"},
  1359  		{Relation: rel.Tag(), Unit: "user-admin"},
  1360  	}}
  1361  	result, err := s.uniter.WatchRelationUnits(args)
  1362  	c.Assert(err, gc.IsNil)
  1363  	// UnitSettings versions are volatile, so we don't check them.
  1364  	// We just make sure the keys of the Changed field are as
  1365  	// expected.
  1366  	c.Assert(result.Results, gc.HasLen, len(args.RelationUnits))
  1367  	mysqlChanges := result.Results[1].Changes
  1368  	c.Assert(mysqlChanges, gc.NotNil)
  1369  	changed, ok := mysqlChanges.Changed["mysql/0"]
  1370  	c.Assert(ok, jc.IsTrue)
  1371  	expectChanges := params.RelationUnitsChange{
  1372  		Changed: map[string]params.UnitSettings{"mysql/0": changed},
  1373  	}
  1374  	c.Assert(result, gc.DeepEquals, params.RelationUnitsWatchResults{
  1375  		Results: []params.RelationUnitsWatchResult{
  1376  			{Error: apiservertesting.ErrUnauthorized},
  1377  			{
  1378  				RelationUnitsWatcherId: "1",
  1379  				Changes:                expectChanges,
  1380  			},
  1381  			{Error: apiservertesting.ErrUnauthorized},
  1382  			{Error: apiservertesting.ErrUnauthorized},
  1383  			{Error: apiservertesting.ErrUnauthorized},
  1384  			{Error: apiservertesting.ErrUnauthorized},
  1385  			{Error: apiservertesting.ErrUnauthorized},
  1386  			{Error: apiservertesting.ErrUnauthorized},
  1387  			{Error: apiservertesting.ErrUnauthorized},
  1388  			{Error: apiservertesting.ErrUnauthorized},
  1389  			{Error: apiservertesting.ErrUnauthorized},
  1390  		},
  1391  	})
  1392  
  1393  	// Verify the resource was registered and stop when done
  1394  	c.Assert(s.resources.Count(), gc.Equals, 1)
  1395  	resource := s.resources.Get("1")
  1396  	defer statetesting.AssertStop(c, resource)
  1397  
  1398  	// Check that the Watch has consumed the initial event ("returned" in
  1399  	// the Watch call)
  1400  	wc := statetesting.NewRelationUnitsWatcherC(c, s.State, resource.(state.RelationUnitsWatcher))
  1401  	wc.AssertNoChange()
  1402  
  1403  	// Leave scope with mysqlUnit and check it's detected.
  1404  	err = myRelUnit.LeaveScope()
  1405  	c.Assert(err, gc.IsNil)
  1406  	s.assertInScope(c, myRelUnit, false)
  1407  
  1408  	wc.AssertChange(nil, []string{"mysql/0"})
  1409  }
  1410  
  1411  func (s *uniterSuite) TestAPIAddresses(c *gc.C) {
  1412  	err := s.machine0.SetAddresses([]instance.Address{
  1413  		instance.NewAddress("0.1.2.3"),
  1414  	})
  1415  	c.Assert(err, gc.IsNil)
  1416  	apiAddresses, err := s.State.APIAddresses()
  1417  	c.Assert(err, gc.IsNil)
  1418  
  1419  	result, err := s.uniter.APIAddresses()
  1420  	c.Assert(err, gc.IsNil)
  1421  	c.Assert(result, gc.DeepEquals, params.StringsResult{
  1422  		Result: apiAddresses,
  1423  	})
  1424  }
  1425  
  1426  func (s *uniterSuite) TestGetOwnerTag(c *gc.C) {
  1427  	tag := s.mysql.Tag()
  1428  	args := params.Entities{Entities: []params.Entity{
  1429  		{Tag: tag},
  1430  	}}
  1431  	result, err := s.uniter.GetOwnerTag(args)
  1432  	c.Assert(err, gc.IsNil)
  1433  	c.Assert(result, gc.DeepEquals, params.StringResult{
  1434  		Result: "user-admin",
  1435  	})
  1436  }