github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/apiserver/uniter/uniter_test.go (about)

     1  // Copyright 2015 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  	jc "github.com/juju/testing/checkers"
    12  	"github.com/juju/utils"
    13  	gc "gopkg.in/check.v1"
    14  	"gopkg.in/juju/charm.v6-unstable"
    15  	"gopkg.in/juju/names.v2"
    16  
    17  	"github.com/juju/juju/apiserver/common"
    18  	commontesting "github.com/juju/juju/apiserver/common/testing"
    19  	"github.com/juju/juju/apiserver/params"
    20  	apiservertesting "github.com/juju/juju/apiserver/testing"
    21  	"github.com/juju/juju/apiserver/uniter"
    22  	"github.com/juju/juju/juju/testing"
    23  	"github.com/juju/juju/network"
    24  	"github.com/juju/juju/state"
    25  	"github.com/juju/juju/state/multiwatcher"
    26  	statetesting "github.com/juju/juju/state/testing"
    27  	"github.com/juju/juju/status"
    28  	"github.com/juju/juju/testing/factory"
    29  	jujuFactory "github.com/juju/juju/testing/factory"
    30  )
    31  
    32  // uniterSuite implements common testing suite for all API
    33  // versions. It's not intended to be used directly or registered as a
    34  // suite, but embedded.
    35  type uniterSuite struct {
    36  	testing.JujuConnSuite
    37  
    38  	authorizer apiservertesting.FakeAuthorizer
    39  	resources  *common.Resources
    40  	uniter     *uniter.UniterAPIV3
    41  
    42  	machine0      *state.Machine
    43  	machine1      *state.Machine
    44  	wordpress     *state.Application
    45  	wpCharm       *state.Charm
    46  	mysql         *state.Application
    47  	wordpressUnit *state.Unit
    48  	mysqlUnit     *state.Unit
    49  
    50  	meteredService *state.Application
    51  	meteredCharm   *state.Charm
    52  	meteredUnit    *state.Unit
    53  }
    54  
    55  var _ = gc.Suite(&uniterSuite{})
    56  
    57  func (s *uniterSuite) SetUpTest(c *gc.C) {
    58  	s.JujuConnSuite.SetUpTest(c)
    59  
    60  	factory := jujuFactory.NewFactory(s.State)
    61  	// Create two machines, two services and add a unit to each service.
    62  	s.machine0 = factory.MakeMachine(c, &jujuFactory.MachineParams{
    63  		Series: "quantal",
    64  		Jobs:   []state.MachineJob{state.JobHostUnits, state.JobManageModel},
    65  	})
    66  	s.machine1 = factory.MakeMachine(c, &jujuFactory.MachineParams{
    67  		Series: "quantal",
    68  		Jobs:   []state.MachineJob{state.JobHostUnits},
    69  	})
    70  	s.wpCharm = factory.MakeCharm(c, &jujuFactory.CharmParams{
    71  		Name: "wordpress",
    72  		URL:  "cs:quantal/wordpress-3",
    73  	})
    74  	s.wordpress = factory.MakeApplication(c, &jujuFactory.ApplicationParams{
    75  		Name:  "wordpress",
    76  		Charm: s.wpCharm,
    77  	})
    78  	mysqlCharm := factory.MakeCharm(c, &jujuFactory.CharmParams{
    79  		Name: "mysql",
    80  	})
    81  	s.mysql = factory.MakeApplication(c, &jujuFactory.ApplicationParams{
    82  		Name:  "mysql",
    83  		Charm: mysqlCharm,
    84  	})
    85  	s.wordpressUnit = factory.MakeUnit(c, &jujuFactory.UnitParams{
    86  		Application: s.wordpress,
    87  		Machine:     s.machine0,
    88  	})
    89  	s.mysqlUnit = factory.MakeUnit(c, &jujuFactory.UnitParams{
    90  		Application: s.mysql,
    91  		Machine:     s.machine1,
    92  	})
    93  
    94  	s.meteredCharm = s.Factory.MakeCharm(c, &jujuFactory.CharmParams{
    95  		Name: "metered",
    96  		URL:  "cs:quantal/metered",
    97  	})
    98  	s.meteredService = s.Factory.MakeApplication(c, &jujuFactory.ApplicationParams{
    99  		Charm: s.meteredCharm,
   100  	})
   101  	s.meteredUnit = s.Factory.MakeUnit(c, &jujuFactory.UnitParams{
   102  		Application: s.meteredService,
   103  		SetCharmURL: true,
   104  	})
   105  
   106  	// Create a FakeAuthorizer so we can check permissions,
   107  	// set up assuming unit 0 has logged in.
   108  	s.authorizer = apiservertesting.FakeAuthorizer{
   109  		Tag: s.wordpressUnit.Tag(),
   110  	}
   111  
   112  	// Create the resource registry separately to track invocations to
   113  	// Register.
   114  	s.resources = common.NewResources()
   115  	s.AddCleanup(func(_ *gc.C) { s.resources.StopAll() })
   116  
   117  	uniterAPIV3, err := uniter.NewUniterAPIV4(
   118  		s.State,
   119  		s.resources,
   120  		s.authorizer,
   121  	)
   122  	c.Assert(err, jc.ErrorIsNil)
   123  	s.uniter = uniterAPIV3
   124  }
   125  
   126  func (s *uniterSuite) TestUniterFailsWithNonUnitAgentUser(c *gc.C) {
   127  	anAuthorizer := s.authorizer
   128  	anAuthorizer.Tag = names.NewMachineTag("9")
   129  	_, err := uniter.NewUniterAPIV4(s.State, s.resources, anAuthorizer)
   130  	c.Assert(err, gc.NotNil)
   131  	c.Assert(err, gc.ErrorMatches, "permission denied")
   132  }
   133  
   134  func (s *uniterSuite) TestSetStatus(c *gc.C) {
   135  	now := time.Now()
   136  	sInfo := status.StatusInfo{
   137  		Status:  status.Executing,
   138  		Message: "blah",
   139  		Since:   &now,
   140  	}
   141  	err := s.wordpressUnit.SetAgentStatus(sInfo)
   142  	c.Assert(err, jc.ErrorIsNil)
   143  	sInfo = status.StatusInfo{
   144  		Status:  status.Executing,
   145  		Message: "foo",
   146  		Since:   &now,
   147  	}
   148  	err = s.mysqlUnit.SetAgentStatus(sInfo)
   149  	c.Assert(err, jc.ErrorIsNil)
   150  
   151  	args := params.SetStatus{
   152  		Entities: []params.EntityStatusArgs{
   153  			{Tag: "unit-mysql-0", Status: status.Error.String(), Info: "not really"},
   154  			{Tag: "unit-wordpress-0", Status: status.Rebooting.String(), Info: "foobar"},
   155  			{Tag: "unit-foo-42", Status: status.Active.String(), Info: "blah"},
   156  		}}
   157  	result, err := s.uniter.SetStatus(args)
   158  	c.Assert(err, jc.ErrorIsNil)
   159  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   160  		Results: []params.ErrorResult{
   161  			{apiservertesting.ErrUnauthorized},
   162  			{nil},
   163  			{apiservertesting.ErrUnauthorized},
   164  		},
   165  	})
   166  
   167  	// Verify mysqlUnit - no change.
   168  	statusInfo, err := s.mysqlUnit.AgentStatus()
   169  	c.Assert(err, jc.ErrorIsNil)
   170  	c.Assert(statusInfo.Status, gc.Equals, status.Executing)
   171  	c.Assert(statusInfo.Message, gc.Equals, "foo")
   172  	// ...wordpressUnit is fine though.
   173  	statusInfo, err = s.wordpressUnit.AgentStatus()
   174  	c.Assert(err, jc.ErrorIsNil)
   175  	c.Assert(statusInfo.Status, gc.Equals, status.Rebooting)
   176  	c.Assert(statusInfo.Message, gc.Equals, "foobar")
   177  }
   178  
   179  func (s *uniterSuite) TestSetAgentStatus(c *gc.C) {
   180  	now := time.Now()
   181  	sInfo := status.StatusInfo{
   182  		Status:  status.Executing,
   183  		Message: "blah",
   184  		Since:   &now,
   185  	}
   186  	err := s.wordpressUnit.SetAgentStatus(sInfo)
   187  	c.Assert(err, jc.ErrorIsNil)
   188  	sInfo = status.StatusInfo{
   189  		Status:  status.Executing,
   190  		Message: "foo",
   191  		Since:   &now,
   192  	}
   193  	err = s.mysqlUnit.SetAgentStatus(sInfo)
   194  	c.Assert(err, jc.ErrorIsNil)
   195  
   196  	args := params.SetStatus{
   197  		Entities: []params.EntityStatusArgs{
   198  			{Tag: "unit-mysql-0", Status: status.Error.String(), Info: "not really"},
   199  			{Tag: "unit-wordpress-0", Status: status.Executing.String(), Info: "foobar"},
   200  			{Tag: "unit-foo-42", Status: status.Rebooting.String(), Info: "blah"},
   201  		}}
   202  	result, err := s.uniter.SetAgentStatus(args)
   203  	c.Assert(err, jc.ErrorIsNil)
   204  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   205  		Results: []params.ErrorResult{
   206  			{apiservertesting.ErrUnauthorized},
   207  			{nil},
   208  			{apiservertesting.ErrUnauthorized},
   209  		},
   210  	})
   211  
   212  	// Verify mysqlUnit - no change.
   213  	statusInfo, err := s.mysqlUnit.AgentStatus()
   214  	c.Assert(err, jc.ErrorIsNil)
   215  	c.Assert(statusInfo.Status, gc.Equals, status.Executing)
   216  	c.Assert(statusInfo.Message, gc.Equals, "foo")
   217  	// ...wordpressUnit is fine though.
   218  	statusInfo, err = s.wordpressUnit.AgentStatus()
   219  	c.Assert(err, jc.ErrorIsNil)
   220  	c.Assert(statusInfo.Status, gc.Equals, status.Executing)
   221  	c.Assert(statusInfo.Message, gc.Equals, "foobar")
   222  }
   223  
   224  func (s *uniterSuite) TestSetUnitStatus(c *gc.C) {
   225  	now := time.Now()
   226  	sInfo := status.StatusInfo{
   227  		Status:  status.Active,
   228  		Message: "blah",
   229  		Since:   &now,
   230  	}
   231  	err := s.wordpressUnit.SetStatus(sInfo)
   232  	c.Assert(err, jc.ErrorIsNil)
   233  	sInfo = status.StatusInfo{
   234  		Status:  status.Terminated,
   235  		Message: "foo",
   236  		Since:   &now,
   237  	}
   238  	err = s.mysqlUnit.SetStatus(sInfo)
   239  	c.Assert(err, jc.ErrorIsNil)
   240  
   241  	args := params.SetStatus{
   242  		Entities: []params.EntityStatusArgs{
   243  			{Tag: "unit-mysql-0", Status: status.Error.String(), Info: "not really"},
   244  			{Tag: "unit-wordpress-0", Status: status.Terminated.String(), Info: "foobar"},
   245  			{Tag: "unit-foo-42", Status: status.Active.String(), Info: "blah"},
   246  		}}
   247  	result, err := s.uniter.SetUnitStatus(args)
   248  	c.Assert(err, jc.ErrorIsNil)
   249  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   250  		Results: []params.ErrorResult{
   251  			{apiservertesting.ErrUnauthorized},
   252  			{nil},
   253  			{apiservertesting.ErrUnauthorized},
   254  		},
   255  	})
   256  
   257  	// Verify mysqlUnit - no change.
   258  	statusInfo, err := s.mysqlUnit.Status()
   259  	c.Assert(err, jc.ErrorIsNil)
   260  	c.Assert(statusInfo.Status, gc.Equals, status.Terminated)
   261  	c.Assert(statusInfo.Message, gc.Equals, "foo")
   262  	// ...wordpressUnit is fine though.
   263  	statusInfo, err = s.wordpressUnit.Status()
   264  	c.Assert(err, jc.ErrorIsNil)
   265  	c.Assert(statusInfo.Status, gc.Equals, status.Terminated)
   266  	c.Assert(statusInfo.Message, gc.Equals, "foobar")
   267  }
   268  
   269  func (s *uniterSuite) TestLife(c *gc.C) {
   270  	// Add a relation wordpress-mysql.
   271  	rel := s.addRelation(c, "wordpress", "mysql")
   272  	relUnit, err := rel.Unit(s.wordpressUnit)
   273  	c.Assert(err, jc.ErrorIsNil)
   274  	err = relUnit.EnterScope(nil)
   275  	c.Assert(err, jc.ErrorIsNil)
   276  	c.Assert(rel.Life(), gc.Equals, state.Alive)
   277  
   278  	// Make the wordpressUnit dead.
   279  	err = s.wordpressUnit.EnsureDead()
   280  	c.Assert(err, jc.ErrorIsNil)
   281  	err = s.wordpressUnit.Refresh()
   282  	c.Assert(err, jc.ErrorIsNil)
   283  	c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Dead)
   284  
   285  	// Add another unit, so the service will stay dying when we
   286  	// destroy it later.
   287  	extraUnit, err := s.wordpress.AddUnit()
   288  	c.Assert(err, jc.ErrorIsNil)
   289  	c.Assert(extraUnit, gc.NotNil)
   290  
   291  	// Make the wordpress service dying.
   292  	err = s.wordpress.Destroy()
   293  	c.Assert(err, jc.ErrorIsNil)
   294  	err = s.wordpress.Refresh()
   295  	c.Assert(err, jc.ErrorIsNil)
   296  	c.Assert(s.wordpress.Life(), gc.Equals, state.Dying)
   297  
   298  	args := params.Entities{Entities: []params.Entity{
   299  		{Tag: "unit-mysql-0"},
   300  		{Tag: "unit-wordpress-0"},
   301  		{Tag: "unit-foo-42"},
   302  		{Tag: "application-mysql"},
   303  		{Tag: "application-wordpress"},
   304  		{Tag: "machine-0"},
   305  		{Tag: "machine-1"},
   306  		{Tag: "machine-42"},
   307  		{Tag: "application-foo"},
   308  		// TODO(dfc) these aren't valid tags any more
   309  		// but I hope to restore this test when params.Entity takes
   310  		// tags, not strings, which is coming soon.
   311  		// {Tag: "just-foo"},
   312  		{Tag: rel.Tag().String()},
   313  		{Tag: "relation-svc1.rel1#svc2.rel2"},
   314  		// {Tag: "relation-blah"},
   315  	}}
   316  	result, err := s.uniter.Life(args)
   317  	c.Assert(err, jc.ErrorIsNil)
   318  	c.Assert(result, gc.DeepEquals, params.LifeResults{
   319  		Results: []params.LifeResult{
   320  			{Error: apiservertesting.ErrUnauthorized},
   321  			{Life: "dead"},
   322  			{Error: apiservertesting.ErrUnauthorized},
   323  			{Error: apiservertesting.ErrUnauthorized},
   324  			{Life: "dying"},
   325  			{Error: apiservertesting.ErrUnauthorized},
   326  			{Error: apiservertesting.ErrUnauthorized},
   327  			{Error: apiservertesting.ErrUnauthorized},
   328  			{Error: apiservertesting.ErrUnauthorized},
   329  			// TODO(dfc) see above
   330  			// {Error: apiservertesting.ErrUnauthorized},
   331  			{Error: apiservertesting.ErrUnauthorized},
   332  			{Error: apiservertesting.ErrUnauthorized},
   333  			// {Error: apiservertesting.ErrUnauthorized},
   334  		},
   335  	})
   336  }
   337  
   338  func (s *uniterSuite) TestEnsureDead(c *gc.C) {
   339  	c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Alive)
   340  	c.Assert(s.mysqlUnit.Life(), gc.Equals, state.Alive)
   341  
   342  	args := params.Entities{Entities: []params.Entity{
   343  		{Tag: "unit-mysql-0"},
   344  		{Tag: "unit-wordpress-0"},
   345  		{Tag: "unit-foo-42"},
   346  	}}
   347  	result, err := s.uniter.EnsureDead(args)
   348  	c.Assert(err, jc.ErrorIsNil)
   349  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   350  		Results: []params.ErrorResult{
   351  			{apiservertesting.ErrUnauthorized},
   352  			{nil},
   353  			{apiservertesting.ErrUnauthorized},
   354  		},
   355  	})
   356  
   357  	err = s.wordpressUnit.Refresh()
   358  	c.Assert(err, jc.ErrorIsNil)
   359  	c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Dead)
   360  	err = s.mysqlUnit.Refresh()
   361  	c.Assert(err, jc.ErrorIsNil)
   362  	c.Assert(s.mysqlUnit.Life(), gc.Equals, state.Alive)
   363  
   364  	// Try it again on a Dead unit; should work.
   365  	args = params.Entities{
   366  		Entities: []params.Entity{{Tag: "unit-wordpress-0"}},
   367  	}
   368  	result, err = s.uniter.EnsureDead(args)
   369  	c.Assert(err, jc.ErrorIsNil)
   370  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   371  		Results: []params.ErrorResult{{nil}},
   372  	})
   373  
   374  	// Verify Life is unchanged.
   375  	err = s.wordpressUnit.Refresh()
   376  	c.Assert(err, jc.ErrorIsNil)
   377  	c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Dead)
   378  }
   379  
   380  func (s *uniterSuite) TestWatch(c *gc.C) {
   381  	c.Assert(s.resources.Count(), gc.Equals, 0)
   382  
   383  	args := params.Entities{Entities: []params.Entity{
   384  		{Tag: "unit-mysql-0"},
   385  		{Tag: "unit-wordpress-0"},
   386  		{Tag: "unit-foo-42"},
   387  		{Tag: "application-mysql"},
   388  		{Tag: "application-wordpress"},
   389  		{Tag: "application-foo"},
   390  		// TODO(dfc) these aren't valid tags any more
   391  		// but I hope to restore this test when params.Entity takes
   392  		// tags, not strings, which is coming soon.
   393  		// {Tag: "just-foo"},
   394  	}}
   395  	result, err := s.uniter.Watch(args)
   396  	c.Assert(err, jc.ErrorIsNil)
   397  	c.Assert(result, gc.DeepEquals, params.NotifyWatchResults{
   398  		Results: []params.NotifyWatchResult{
   399  			{Error: apiservertesting.ErrUnauthorized},
   400  			{NotifyWatcherId: "1"},
   401  			{Error: apiservertesting.ErrUnauthorized},
   402  			{Error: apiservertesting.ErrUnauthorized},
   403  			{NotifyWatcherId: "2"},
   404  			{Error: apiservertesting.ErrUnauthorized},
   405  			// see above
   406  			// {Error: apiservertesting.ErrUnauthorized},
   407  		},
   408  	})
   409  
   410  	// Verify the resource was registered and stop when done
   411  	c.Assert(s.resources.Count(), gc.Equals, 2)
   412  	resource1 := s.resources.Get("1")
   413  	defer statetesting.AssertStop(c, resource1)
   414  	resource2 := s.resources.Get("2")
   415  	defer statetesting.AssertStop(c, resource2)
   416  
   417  	// Check that the Watch has consumed the initial event ("returned" in
   418  	// the Watch call)
   419  	wc := statetesting.NewNotifyWatcherC(c, s.State, resource1.(state.NotifyWatcher))
   420  	wc.AssertNoChange()
   421  	wc = statetesting.NewNotifyWatcherC(c, s.State, resource2.(state.NotifyWatcher))
   422  	wc.AssertNoChange()
   423  }
   424  
   425  func (s *uniterSuite) TestPublicAddress(c *gc.C) {
   426  	// Try first without setting an address.
   427  	args := params.Entities{Entities: []params.Entity{
   428  		{Tag: "unit-mysql-0"},
   429  		{Tag: "unit-wordpress-0"},
   430  		{Tag: "unit-foo-42"},
   431  	}}
   432  	expectErr := &params.Error{
   433  		Code:    params.CodeNoAddressSet,
   434  		Message: `"unit-wordpress-0" has no public address set`,
   435  	}
   436  	result, err := s.uniter.PublicAddress(args)
   437  	c.Assert(err, jc.ErrorIsNil)
   438  	c.Assert(result, gc.DeepEquals, params.StringResults{
   439  		Results: []params.StringResult{
   440  			{Error: apiservertesting.ErrUnauthorized},
   441  			{Error: expectErr},
   442  			{Error: apiservertesting.ErrUnauthorized},
   443  		},
   444  	})
   445  
   446  	// Now set it an try again.
   447  	err = s.machine0.SetProviderAddresses(
   448  		network.NewScopedAddress("1.2.3.4", network.ScopePublic),
   449  	)
   450  	c.Assert(err, jc.ErrorIsNil)
   451  	address, err := s.wordpressUnit.PublicAddress()
   452  	c.Assert(address.Value, gc.Equals, "1.2.3.4")
   453  	c.Assert(err, jc.ErrorIsNil)
   454  
   455  	result, err = s.uniter.PublicAddress(args)
   456  	c.Assert(err, jc.ErrorIsNil)
   457  	c.Assert(result, gc.DeepEquals, params.StringResults{
   458  		Results: []params.StringResult{
   459  			{Error: apiservertesting.ErrUnauthorized},
   460  			{Result: "1.2.3.4"},
   461  			{Error: apiservertesting.ErrUnauthorized},
   462  		},
   463  	})
   464  }
   465  
   466  func (s *uniterSuite) TestPrivateAddress(c *gc.C) {
   467  	args := params.Entities{Entities: []params.Entity{
   468  		{Tag: "unit-mysql-0"},
   469  		{Tag: "unit-wordpress-0"},
   470  		{Tag: "unit-foo-42"},
   471  	}}
   472  	expectErr := &params.Error{
   473  		Code:    params.CodeNoAddressSet,
   474  		Message: `"unit-wordpress-0" has no private address set`,
   475  	}
   476  	result, err := s.uniter.PrivateAddress(args)
   477  	c.Assert(err, jc.ErrorIsNil)
   478  	c.Assert(result, gc.DeepEquals, params.StringResults{
   479  		Results: []params.StringResult{
   480  			{Error: apiservertesting.ErrUnauthorized},
   481  			{Error: expectErr},
   482  			{Error: apiservertesting.ErrUnauthorized},
   483  		},
   484  	})
   485  
   486  	// Now set it and try again.
   487  	err = s.machine0.SetProviderAddresses(
   488  		network.NewScopedAddress("1.2.3.4", network.ScopeCloudLocal),
   489  	)
   490  	c.Assert(err, jc.ErrorIsNil)
   491  	address, err := s.wordpressUnit.PrivateAddress()
   492  	c.Assert(address.Value, gc.Equals, "1.2.3.4")
   493  	c.Assert(err, jc.ErrorIsNil)
   494  
   495  	result, err = s.uniter.PrivateAddress(args)
   496  	c.Assert(err, jc.ErrorIsNil)
   497  	c.Assert(result, gc.DeepEquals, params.StringResults{
   498  		Results: []params.StringResult{
   499  			{Error: apiservertesting.ErrUnauthorized},
   500  			{Result: "1.2.3.4"},
   501  			{Error: apiservertesting.ErrUnauthorized},
   502  		},
   503  	})
   504  }
   505  
   506  func (s *uniterSuite) TestAvailabilityZone(c *gc.C) {
   507  	s.PatchValue(uniter.GetZone, func(st *state.State, tag names.Tag) (string, error) {
   508  		return "a_zone", nil
   509  	})
   510  
   511  	args := params.Entities{Entities: []params.Entity{
   512  		{Tag: "unit-wordpress-0"},
   513  	}}
   514  	result, err := s.uniter.AvailabilityZone(args)
   515  	c.Assert(err, jc.ErrorIsNil)
   516  
   517  	c.Check(result, gc.DeepEquals, params.StringResults{
   518  		Results: []params.StringResult{
   519  			{Result: "a_zone"},
   520  		},
   521  	})
   522  }
   523  
   524  func (s *uniterSuite) TestResolved(c *gc.C) {
   525  	err := s.wordpressUnit.SetResolved(state.ResolvedRetryHooks)
   526  	c.Assert(err, jc.ErrorIsNil)
   527  	mode := s.wordpressUnit.Resolved()
   528  	c.Assert(mode, gc.Equals, state.ResolvedRetryHooks)
   529  
   530  	args := params.Entities{Entities: []params.Entity{
   531  		{Tag: "unit-mysql-0"},
   532  		{Tag: "unit-wordpress-0"},
   533  		{Tag: "unit-foo-42"},
   534  	}}
   535  	result, err := s.uniter.Resolved(args)
   536  	c.Assert(err, jc.ErrorIsNil)
   537  	c.Assert(result, gc.DeepEquals, params.ResolvedModeResults{
   538  		Results: []params.ResolvedModeResult{
   539  			{Error: apiservertesting.ErrUnauthorized},
   540  			{Mode: params.ResolvedMode(mode)},
   541  			{Error: apiservertesting.ErrUnauthorized},
   542  		},
   543  	})
   544  }
   545  
   546  func (s *uniterSuite) TestClearResolved(c *gc.C) {
   547  	err := s.wordpressUnit.SetResolved(state.ResolvedRetryHooks)
   548  	c.Assert(err, jc.ErrorIsNil)
   549  	mode := s.wordpressUnit.Resolved()
   550  	c.Assert(mode, gc.Equals, state.ResolvedRetryHooks)
   551  
   552  	args := params.Entities{Entities: []params.Entity{
   553  		{Tag: "unit-mysql-0"},
   554  		{Tag: "unit-wordpress-0"},
   555  		{Tag: "unit-foo-42"},
   556  	}}
   557  	result, err := s.uniter.ClearResolved(args)
   558  	c.Assert(err, jc.ErrorIsNil)
   559  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   560  		Results: []params.ErrorResult{
   561  			{apiservertesting.ErrUnauthorized},
   562  			{nil},
   563  			{apiservertesting.ErrUnauthorized},
   564  		},
   565  	})
   566  
   567  	// Verify wordpressUnit's resolved mode has changed.
   568  	err = s.wordpressUnit.Refresh()
   569  	c.Assert(err, jc.ErrorIsNil)
   570  	mode = s.wordpressUnit.Resolved()
   571  	c.Assert(mode, gc.Equals, state.ResolvedNone)
   572  }
   573  
   574  func (s *uniterSuite) TestGetPrincipal(c *gc.C) {
   575  	// Add a subordinate to wordpressUnit.
   576  	_, _, subordinate := s.addRelatedService(c, "wordpress", "logging", s.wordpressUnit)
   577  
   578  	principal, ok := subordinate.PrincipalName()
   579  	c.Assert(principal, gc.Equals, s.wordpressUnit.Name())
   580  	c.Assert(ok, jc.IsTrue)
   581  
   582  	// First try it as wordpressUnit's agent.
   583  	args := params.Entities{Entities: []params.Entity{
   584  		{Tag: "unit-mysql-0"},
   585  		{Tag: "unit-wordpress-0"},
   586  		{Tag: subordinate.Tag().String()},
   587  		{Tag: "unit-foo-42"},
   588  	}}
   589  	result, err := s.uniter.GetPrincipal(args)
   590  	c.Assert(err, jc.ErrorIsNil)
   591  	c.Assert(result, gc.DeepEquals, params.StringBoolResults{
   592  		Results: []params.StringBoolResult{
   593  			{Error: apiservertesting.ErrUnauthorized},
   594  			{Result: "", Ok: false, Error: nil},
   595  			{Error: apiservertesting.ErrUnauthorized},
   596  			{Error: apiservertesting.ErrUnauthorized},
   597  		},
   598  	})
   599  
   600  	// Now try as subordinate's agent.
   601  	subAuthorizer := s.authorizer
   602  	subAuthorizer.Tag = subordinate.Tag()
   603  	subUniter, err := uniter.NewUniterAPIV4(s.State, s.resources, subAuthorizer)
   604  	c.Assert(err, jc.ErrorIsNil)
   605  
   606  	result, err = subUniter.GetPrincipal(args)
   607  	c.Assert(err, jc.ErrorIsNil)
   608  	c.Assert(result, gc.DeepEquals, params.StringBoolResults{
   609  		Results: []params.StringBoolResult{
   610  			{Error: apiservertesting.ErrUnauthorized},
   611  			{Error: apiservertesting.ErrUnauthorized},
   612  			{Result: "unit-wordpress-0", Ok: true, Error: nil},
   613  			{Error: apiservertesting.ErrUnauthorized},
   614  		},
   615  	})
   616  }
   617  
   618  func (s *uniterSuite) TestHasSubordinates(c *gc.C) {
   619  	// Try first without any subordinates for wordpressUnit.
   620  	args := params.Entities{Entities: []params.Entity{
   621  		{Tag: "unit-mysql-0"},
   622  		{Tag: "unit-wordpress-0"},
   623  		{Tag: "unit-logging-0"},
   624  		{Tag: "unit-foo-42"},
   625  	}}
   626  	result, err := s.uniter.HasSubordinates(args)
   627  	c.Assert(err, jc.ErrorIsNil)
   628  	c.Assert(result, gc.DeepEquals, params.BoolResults{
   629  		Results: []params.BoolResult{
   630  			{Error: apiservertesting.ErrUnauthorized},
   631  			{Result: false},
   632  			{Error: apiservertesting.ErrUnauthorized},
   633  			{Error: apiservertesting.ErrUnauthorized},
   634  		},
   635  	})
   636  
   637  	// Add two subordinates to wordpressUnit and try again.
   638  	s.addRelatedService(c, "wordpress", "logging", s.wordpressUnit)
   639  	s.addRelatedService(c, "wordpress", "monitoring", s.wordpressUnit)
   640  
   641  	result, err = s.uniter.HasSubordinates(args)
   642  	c.Assert(err, jc.ErrorIsNil)
   643  	c.Assert(result, gc.DeepEquals, params.BoolResults{
   644  		Results: []params.BoolResult{
   645  			{Error: apiservertesting.ErrUnauthorized},
   646  			{Result: true},
   647  			{Error: apiservertesting.ErrUnauthorized},
   648  			{Error: apiservertesting.ErrUnauthorized},
   649  		},
   650  	})
   651  }
   652  
   653  func (s *uniterSuite) TestDestroy(c *gc.C) {
   654  	c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Alive)
   655  
   656  	args := params.Entities{Entities: []params.Entity{
   657  		{Tag: "unit-mysql-0"},
   658  		{Tag: "unit-wordpress-0"},
   659  		{Tag: "unit-foo-42"},
   660  	}}
   661  	result, err := s.uniter.Destroy(args)
   662  	c.Assert(err, jc.ErrorIsNil)
   663  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   664  		Results: []params.ErrorResult{
   665  			{apiservertesting.ErrUnauthorized},
   666  			{nil},
   667  			{apiservertesting.ErrUnauthorized},
   668  		},
   669  	})
   670  
   671  	// Verify wordpressUnit is destroyed and removed.
   672  	err = s.wordpressUnit.Refresh()
   673  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   674  }
   675  
   676  func (s *uniterSuite) TestDestroyAllSubordinates(c *gc.C) {
   677  	// Add two subordinates to wordpressUnit.
   678  	_, _, loggingSub := s.addRelatedService(c, "wordpress", "logging", s.wordpressUnit)
   679  	_, _, monitoringSub := s.addRelatedService(c, "wordpress", "monitoring", s.wordpressUnit)
   680  	c.Assert(loggingSub.Life(), gc.Equals, state.Alive)
   681  	c.Assert(monitoringSub.Life(), gc.Equals, state.Alive)
   682  
   683  	err := s.wordpressUnit.Refresh()
   684  	c.Assert(err, jc.ErrorIsNil)
   685  	subordinates := s.wordpressUnit.SubordinateNames()
   686  	c.Assert(subordinates, gc.DeepEquals, []string{"logging/0", "monitoring/0"})
   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 := s.uniter.DestroyAllSubordinates(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's subordinates were destroyed.
   704  	err = loggingSub.Refresh()
   705  	c.Assert(err, jc.ErrorIsNil)
   706  	c.Assert(loggingSub.Life(), gc.Equals, state.Dying)
   707  	err = monitoringSub.Refresh()
   708  	c.Assert(err, jc.ErrorIsNil)
   709  	c.Assert(monitoringSub.Life(), gc.Equals, state.Dying)
   710  }
   711  
   712  func (s *uniterSuite) TestCharmURL(c *gc.C) {
   713  	// Set wordpressUnit's charm URL first.
   714  	err := s.wordpressUnit.SetCharmURL(s.wpCharm.URL())
   715  	c.Assert(err, jc.ErrorIsNil)
   716  	curl, ok := s.wordpressUnit.CharmURL()
   717  	c.Assert(curl, gc.DeepEquals, s.wpCharm.URL())
   718  	c.Assert(ok, jc.IsTrue)
   719  
   720  	// Make sure wordpress service's charm is what we expect.
   721  	curl, force := s.wordpress.CharmURL()
   722  	c.Assert(curl, gc.DeepEquals, s.wpCharm.URL())
   723  	c.Assert(force, jc.IsFalse)
   724  
   725  	args := params.Entities{Entities: []params.Entity{
   726  		{Tag: "unit-mysql-0"},
   727  		{Tag: "unit-wordpress-0"},
   728  		{Tag: "unit-foo-42"},
   729  		{Tag: "application-mysql"},
   730  		{Tag: "application-wordpress"},
   731  		{Tag: "application-foo"},
   732  		// TODO(dfc) these aren't valid tags any more
   733  		// but I hope to restore this test when params.Entity takes
   734  		// tags, not strings, which is coming soon.
   735  		// {Tag: "just-foo"},
   736  	}}
   737  	result, err := s.uniter.CharmURL(args)
   738  	c.Assert(err, jc.ErrorIsNil)
   739  	c.Assert(result, gc.DeepEquals, params.StringBoolResults{
   740  		Results: []params.StringBoolResult{
   741  			{Error: apiservertesting.ErrUnauthorized},
   742  			{Result: s.wpCharm.String(), Ok: ok},
   743  			{Error: apiservertesting.ErrUnauthorized},
   744  			{Error: apiservertesting.ErrUnauthorized},
   745  			{Result: s.wpCharm.String(), Ok: force},
   746  			{Error: apiservertesting.ErrUnauthorized},
   747  			// see above
   748  			// {Error: apiservertesting.ErrUnauthorized},
   749  		},
   750  	})
   751  }
   752  
   753  func (s *uniterSuite) TestSetCharmURL(c *gc.C) {
   754  	_, ok := s.wordpressUnit.CharmURL()
   755  	c.Assert(ok, jc.IsFalse)
   756  
   757  	args := params.EntitiesCharmURL{Entities: []params.EntityCharmURL{
   758  		{Tag: "unit-mysql-0", CharmURL: "cs:quantal/application-42"},
   759  		{Tag: "unit-wordpress-0", CharmURL: s.wpCharm.String()},
   760  		{Tag: "unit-foo-42", CharmURL: "cs:quantal/foo-321"},
   761  	}}
   762  	result, err := s.uniter.SetCharmURL(args)
   763  	c.Assert(err, jc.ErrorIsNil)
   764  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   765  		Results: []params.ErrorResult{
   766  			{apiservertesting.ErrUnauthorized},
   767  			{nil},
   768  			{apiservertesting.ErrUnauthorized},
   769  		},
   770  	})
   771  
   772  	// Verify the charm URL was set.
   773  	err = s.wordpressUnit.Refresh()
   774  	c.Assert(err, jc.ErrorIsNil)
   775  	charmURL, needsUpgrade := s.wordpressUnit.CharmURL()
   776  	c.Assert(charmURL, gc.NotNil)
   777  	c.Assert(charmURL.String(), gc.Equals, s.wpCharm.String())
   778  	c.Assert(needsUpgrade, jc.IsTrue)
   779  }
   780  
   781  func (s *uniterSuite) TestWorkloadVersion(c *gc.C) {
   782  	// Set wordpressUnit's workload version first.
   783  	err := s.wordpressUnit.SetWorkloadVersion("capulet")
   784  	c.Assert(err, jc.ErrorIsNil)
   785  	version, err := s.wordpressUnit.WorkloadVersion()
   786  	c.Assert(version, gc.Equals, "capulet")
   787  	c.Assert(err, jc.ErrorIsNil)
   788  
   789  	args := params.Entities{Entities: []params.Entity{
   790  		{Tag: "unit-mysql-0"},
   791  		{Tag: "unit-wordpress-0"},
   792  		{Tag: "unit-foo-42"},
   793  		{Tag: "application-wordpress"},
   794  		{Tag: "just-foo"},
   795  	}}
   796  
   797  	result, err := s.uniter.WorkloadVersion(args)
   798  	c.Assert(err, jc.ErrorIsNil)
   799  	c.Assert(result, gc.DeepEquals, params.StringResults{
   800  		Results: []params.StringResult{
   801  			{Error: apiservertesting.ErrUnauthorized},
   802  			{Result: "capulet"},
   803  			{Error: apiservertesting.ErrUnauthorized},
   804  			{Error: common.ServerError(errors.New(`"application-wordpress" is not a valid unit tag`))},
   805  			{Error: common.ServerError(errors.New(`"just-foo" is not a valid tag`))},
   806  		},
   807  	})
   808  }
   809  
   810  func (s *uniterSuite) TestSetWorkloadVersion(c *gc.C) {
   811  	currentVersion, err := s.wordpressUnit.WorkloadVersion()
   812  	c.Assert(err, jc.ErrorIsNil)
   813  	c.Assert(currentVersion, gc.Equals, "")
   814  
   815  	args := params.EntityWorkloadVersions{Entities: []params.EntityWorkloadVersion{
   816  		{Tag: "unit-mysql-0", WorkloadVersion: "allura"},
   817  		{Tag: "unit-wordpress-0", WorkloadVersion: "shiro"},
   818  		{Tag: "unit-foo-42", WorkloadVersion: "pidge"},
   819  	}}
   820  	result, err := s.uniter.SetWorkloadVersion(args)
   821  	c.Assert(err, jc.ErrorIsNil)
   822  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   823  		Results: []params.ErrorResult{
   824  			{apiservertesting.ErrUnauthorized},
   825  			{nil},
   826  			{apiservertesting.ErrUnauthorized},
   827  		},
   828  	})
   829  
   830  	// Verify the workload version was set.
   831  	err = s.wordpressUnit.Refresh()
   832  	c.Assert(err, jc.ErrorIsNil)
   833  	newVersion, err := s.wordpressUnit.WorkloadVersion()
   834  	c.Assert(err, jc.ErrorIsNil)
   835  	c.Assert(newVersion, gc.Equals, "shiro")
   836  }
   837  
   838  func (s *uniterSuite) TestCharmModifiedVersion(c *gc.C) {
   839  	args := params.Entities{Entities: []params.Entity{
   840  		{Tag: "application-mysql"},
   841  		{Tag: "application-wordpress"},
   842  		{Tag: "unit-wordpress-0"},
   843  		{Tag: "application-foo"},
   844  	}}
   845  	result, err := s.uniter.CharmModifiedVersion(args)
   846  	c.Assert(err, jc.ErrorIsNil)
   847  	c.Assert(result, gc.DeepEquals, params.IntResults{
   848  		Results: []params.IntResult{
   849  			{Error: apiservertesting.ErrUnauthorized},
   850  			{Result: s.wordpress.CharmModifiedVersion()},
   851  			{Result: s.wordpress.CharmModifiedVersion()},
   852  			{Error: apiservertesting.ErrUnauthorized},
   853  		},
   854  	})
   855  }
   856  
   857  func (s *uniterSuite) TestOpenPorts(c *gc.C) {
   858  	openedPorts, err := s.wordpressUnit.OpenedPorts()
   859  	c.Assert(err, jc.ErrorIsNil)
   860  	c.Assert(openedPorts, gc.HasLen, 0)
   861  
   862  	args := params.EntitiesPortRanges{Entities: []params.EntityPortRange{
   863  		{Tag: "unit-mysql-0", Protocol: "tcp", FromPort: 1234, ToPort: 1400},
   864  		{Tag: "unit-wordpress-0", Protocol: "udp", FromPort: 4321, ToPort: 5000},
   865  		{Tag: "unit-foo-42", Protocol: "tcp", FromPort: 42, ToPort: 42},
   866  	}}
   867  	result, err := s.uniter.OpenPorts(args)
   868  	c.Assert(err, jc.ErrorIsNil)
   869  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   870  		Results: []params.ErrorResult{
   871  			{apiservertesting.ErrUnauthorized},
   872  			{nil},
   873  			{apiservertesting.ErrUnauthorized},
   874  		},
   875  	})
   876  
   877  	// Verify the wordpressUnit's port is opened.
   878  	openedPorts, err = s.wordpressUnit.OpenedPorts()
   879  	c.Assert(err, jc.ErrorIsNil)
   880  	c.Assert(openedPorts, gc.DeepEquals, []network.PortRange{
   881  		{Protocol: "udp", FromPort: 4321, ToPort: 5000},
   882  	})
   883  }
   884  
   885  func (s *uniterSuite) TestClosePorts(c *gc.C) {
   886  	// Open port udp:4321 in advance on wordpressUnit.
   887  	err := s.wordpressUnit.OpenPorts("udp", 4321, 5000)
   888  	c.Assert(err, jc.ErrorIsNil)
   889  	openedPorts, err := s.wordpressUnit.OpenedPorts()
   890  	c.Assert(err, jc.ErrorIsNil)
   891  	c.Assert(openedPorts, gc.DeepEquals, []network.PortRange{
   892  		{Protocol: "udp", FromPort: 4321, ToPort: 5000},
   893  	})
   894  
   895  	args := params.EntitiesPortRanges{Entities: []params.EntityPortRange{
   896  		{Tag: "unit-mysql-0", Protocol: "tcp", FromPort: 1234, ToPort: 1400},
   897  		{Tag: "unit-wordpress-0", Protocol: "udp", FromPort: 4321, ToPort: 5000},
   898  		{Tag: "unit-foo-42", Protocol: "tcp", FromPort: 42, ToPort: 42},
   899  	}}
   900  	result, err := s.uniter.ClosePorts(args)
   901  	c.Assert(err, jc.ErrorIsNil)
   902  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   903  		Results: []params.ErrorResult{
   904  			{apiservertesting.ErrUnauthorized},
   905  			{nil},
   906  			{apiservertesting.ErrUnauthorized},
   907  		},
   908  	})
   909  
   910  	// Verify the wordpressUnit's port is closed.
   911  	openedPorts, err = s.wordpressUnit.OpenedPorts()
   912  	c.Assert(err, jc.ErrorIsNil)
   913  	c.Assert(openedPorts, gc.HasLen, 0)
   914  }
   915  
   916  func (s *uniterSuite) TestWatchConfigSettings(c *gc.C) {
   917  	err := s.wordpressUnit.SetCharmURL(s.wpCharm.URL())
   918  	c.Assert(err, jc.ErrorIsNil)
   919  
   920  	c.Assert(s.resources.Count(), gc.Equals, 0)
   921  
   922  	args := params.Entities{Entities: []params.Entity{
   923  		{Tag: "unit-mysql-0"},
   924  		{Tag: "unit-wordpress-0"},
   925  		{Tag: "unit-foo-42"},
   926  	}}
   927  	result, err := s.uniter.WatchConfigSettings(args)
   928  	c.Assert(err, jc.ErrorIsNil)
   929  	c.Assert(result, gc.DeepEquals, params.NotifyWatchResults{
   930  		Results: []params.NotifyWatchResult{
   931  			{Error: apiservertesting.ErrUnauthorized},
   932  			{NotifyWatcherId: "1"},
   933  			{Error: apiservertesting.ErrUnauthorized},
   934  		},
   935  	})
   936  
   937  	// Verify the resource was registered and stop when done
   938  	c.Assert(s.resources.Count(), gc.Equals, 1)
   939  	resource := s.resources.Get("1")
   940  	defer statetesting.AssertStop(c, resource)
   941  
   942  	// Check that the Watch has consumed the initial event ("returned" in
   943  	// the Watch call)
   944  	wc := statetesting.NewNotifyWatcherC(c, s.State, resource.(state.NotifyWatcher))
   945  	wc.AssertNoChange()
   946  }
   947  
   948  func (s *uniterSuite) TestWatchActionNotifications(c *gc.C) {
   949  	err := s.wordpressUnit.SetCharmURL(s.wpCharm.URL())
   950  	c.Assert(err, jc.ErrorIsNil)
   951  
   952  	c.Assert(s.resources.Count(), gc.Equals, 0)
   953  
   954  	args := params.Entities{Entities: []params.Entity{
   955  		{Tag: "unit-mysql-0"},
   956  		{Tag: "unit-wordpress-0"},
   957  		{Tag: "unit-foo-42"},
   958  	}}
   959  	result, err := s.uniter.WatchActionNotifications(args)
   960  	c.Assert(err, jc.ErrorIsNil)
   961  	c.Assert(result, gc.DeepEquals, params.StringsWatchResults{
   962  		Results: []params.StringsWatchResult{
   963  			{Error: apiservertesting.ErrUnauthorized},
   964  			{StringsWatcherId: "1"},
   965  			{Error: apiservertesting.ErrUnauthorized},
   966  		},
   967  	})
   968  
   969  	// Verify the resource was registered and stop when done
   970  	c.Assert(s.resources.Count(), gc.Equals, 1)
   971  	resource := s.resources.Get("1")
   972  	defer statetesting.AssertStop(c, resource)
   973  
   974  	// Check that the Watch has consumed the initial event ("returned" in
   975  	// the Watch call)
   976  	wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher))
   977  	wc.AssertNoChange()
   978  
   979  	addedAction, err := s.wordpressUnit.AddAction("fakeaction", nil)
   980  
   981  	wc.AssertChange(addedAction.Id())
   982  	wc.AssertNoChange()
   983  }
   984  
   985  func (s *uniterSuite) TestWatchPreexistingActions(c *gc.C) {
   986  	err := s.wordpressUnit.SetCharmURL(s.wpCharm.URL())
   987  	c.Assert(err, jc.ErrorIsNil)
   988  
   989  	c.Assert(s.resources.Count(), gc.Equals, 0)
   990  
   991  	action1, err := s.wordpressUnit.AddAction("fakeaction", nil)
   992  	c.Assert(err, jc.ErrorIsNil)
   993  	action2, err := s.wordpressUnit.AddAction("fakeaction", nil)
   994  	c.Assert(err, jc.ErrorIsNil)
   995  
   996  	args := params.Entities{Entities: []params.Entity{
   997  		{Tag: "unit-wordpress-0"},
   998  	}}
   999  
  1000  	s.State.StartSync()
  1001  	results, err := s.uniter.WatchActionNotifications(args)
  1002  	c.Assert(err, jc.ErrorIsNil)
  1003  
  1004  	checkUnorderedActionIdsEqual(c, []string{action1.Id(), action2.Id()}, results)
  1005  
  1006  	// Verify the resource was registered and stop when done
  1007  	c.Assert(s.resources.Count(), gc.Equals, 1)
  1008  	resource := s.resources.Get("1")
  1009  	defer statetesting.AssertStop(c, resource)
  1010  
  1011  	// Check that the Watch has consumed the initial event ("returned" in
  1012  	// the Watch call)
  1013  	wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher))
  1014  	wc.AssertNoChange()
  1015  
  1016  	addedAction, err := s.wordpressUnit.AddAction("fakeaction", nil)
  1017  	c.Assert(err, jc.ErrorIsNil)
  1018  	wc.AssertChange(addedAction.Id())
  1019  	wc.AssertNoChange()
  1020  }
  1021  
  1022  func (s *uniterSuite) TestWatchActionNotificationsMalformedTag(c *gc.C) {
  1023  	args := params.Entities{Entities: []params.Entity{
  1024  		{Tag: "ewenit-mysql-0"},
  1025  	}}
  1026  	results, err := s.uniter.WatchActionNotifications(args)
  1027  	c.Assert(err, jc.ErrorIsNil)
  1028  	c.Assert(results, gc.NotNil)
  1029  	c.Assert(len(results.Results), gc.Equals, 1)
  1030  	result := results.Results[0]
  1031  	c.Assert(result.Error, gc.NotNil)
  1032  	c.Assert(result.Error.Message, gc.Equals, `invalid actionreceiver tag "ewenit-mysql-0"`)
  1033  }
  1034  
  1035  func (s *uniterSuite) TestWatchActionNotificationsMalformedUnitName(c *gc.C) {
  1036  	args := params.Entities{Entities: []params.Entity{
  1037  		{Tag: "unit-mysql-01"},
  1038  	}}
  1039  	results, err := s.uniter.WatchActionNotifications(args)
  1040  	c.Assert(err, jc.ErrorIsNil)
  1041  	c.Assert(results, gc.NotNil)
  1042  	c.Assert(len(results.Results), gc.Equals, 1)
  1043  	result := results.Results[0]
  1044  	c.Assert(result.Error, gc.NotNil)
  1045  	c.Assert(result.Error.Message, gc.Equals, `invalid actionreceiver tag "unit-mysql-01"`)
  1046  }
  1047  
  1048  func (s *uniterSuite) TestWatchActionNotificationsNotUnit(c *gc.C) {
  1049  	action, err := s.mysqlUnit.AddAction("fakeaction", nil)
  1050  	c.Assert(err, jc.ErrorIsNil)
  1051  	args := params.Entities{Entities: []params.Entity{
  1052  		{Tag: action.Tag().String()},
  1053  	}}
  1054  	results, err := s.uniter.WatchActionNotifications(args)
  1055  	c.Assert(err, jc.ErrorIsNil)
  1056  	c.Assert(results, gc.NotNil)
  1057  	c.Assert(len(results.Results), gc.Equals, 1)
  1058  	result := results.Results[0]
  1059  	c.Assert(result.Error, gc.NotNil)
  1060  	c.Assert(result.Error.Message, gc.Equals, `invalid actionreceiver tag "action-`+action.Id()+`"`)
  1061  }
  1062  
  1063  func (s *uniterSuite) TestWatchActionNotificationsPermissionDenied(c *gc.C) {
  1064  	args := params.Entities{Entities: []params.Entity{
  1065  		{Tag: "unit-nonexistentgarbage-0"},
  1066  	}}
  1067  	results, err := s.uniter.WatchActionNotifications(args)
  1068  	c.Assert(err, jc.ErrorIsNil)
  1069  	c.Assert(results, gc.NotNil)
  1070  	c.Assert(len(results.Results), gc.Equals, 1)
  1071  	result := results.Results[0]
  1072  	c.Assert(result.Error, gc.NotNil)
  1073  	c.Assert(result.Error.Message, gc.Equals, "permission denied")
  1074  }
  1075  
  1076  func (s *uniterSuite) TestConfigSettings(c *gc.C) {
  1077  	err := s.wordpressUnit.SetCharmURL(s.wpCharm.URL())
  1078  	c.Assert(err, jc.ErrorIsNil)
  1079  	settings, err := s.wordpressUnit.ConfigSettings()
  1080  	c.Assert(err, jc.ErrorIsNil)
  1081  	c.Assert(settings, gc.DeepEquals, charm.Settings{"blog-title": "My Title"})
  1082  
  1083  	args := params.Entities{Entities: []params.Entity{
  1084  		{Tag: "unit-mysql-0"},
  1085  		{Tag: "unit-wordpress-0"},
  1086  		{Tag: "unit-foo-42"},
  1087  	}}
  1088  	result, err := s.uniter.ConfigSettings(args)
  1089  	c.Assert(err, jc.ErrorIsNil)
  1090  	c.Assert(result, gc.DeepEquals, params.ConfigSettingsResults{
  1091  		Results: []params.ConfigSettingsResult{
  1092  			{Error: apiservertesting.ErrUnauthorized},
  1093  			{Settings: params.ConfigSettings{"blog-title": "My Title"}},
  1094  			{Error: apiservertesting.ErrUnauthorized},
  1095  		},
  1096  	})
  1097  }
  1098  
  1099  func (s *uniterSuite) TestWatchApplicationRelations(c *gc.C) {
  1100  	c.Assert(s.resources.Count(), gc.Equals, 0)
  1101  
  1102  	args := params.Entities{Entities: []params.Entity{
  1103  		{Tag: "application-mysql"},
  1104  		{Tag: "application-wordpress"},
  1105  		{Tag: "application-foo"},
  1106  	}}
  1107  	result, err := s.uniter.WatchApplicationRelations(args)
  1108  	s.assertOneStringsWatcher(c, result, err)
  1109  }
  1110  
  1111  func (s *uniterSuite) TestCharmArchiveSha256(c *gc.C) {
  1112  	dummyCharm := s.AddTestingCharm(c, "dummy")
  1113  
  1114  	args := params.CharmURLs{URLs: []params.CharmURL{
  1115  		{URL: "something-invalid"},
  1116  		{URL: s.wpCharm.String()},
  1117  		{URL: dummyCharm.String()},
  1118  	}}
  1119  	result, err := s.uniter.CharmArchiveSha256(args)
  1120  	c.Assert(err, jc.ErrorIsNil)
  1121  	c.Assert(result, gc.DeepEquals, params.StringResults{
  1122  		Results: []params.StringResult{
  1123  			{Error: apiservertesting.ErrUnauthorized},
  1124  			{Result: s.wpCharm.BundleSha256()},
  1125  			{Result: dummyCharm.BundleSha256()},
  1126  		},
  1127  	})
  1128  }
  1129  
  1130  func (s *uniterSuite) TestCurrentModel(c *gc.C) {
  1131  	env, err := s.State.Model()
  1132  	c.Assert(err, jc.ErrorIsNil)
  1133  
  1134  	result, err := s.uniter.CurrentModel()
  1135  	c.Assert(err, jc.ErrorIsNil)
  1136  	expected := params.ModelResult{
  1137  		Name: env.Name(),
  1138  		UUID: env.UUID(),
  1139  	}
  1140  	c.Assert(result, gc.DeepEquals, expected)
  1141  }
  1142  
  1143  func (s *uniterSuite) TestActions(c *gc.C) {
  1144  	var actionTests = []struct {
  1145  		description string
  1146  		action      params.ActionResult
  1147  	}{{
  1148  		description: "A simple action.",
  1149  		action: params.ActionResult{
  1150  			Action: &params.Action{
  1151  				Name: "fakeaction",
  1152  				Parameters: map[string]interface{}{
  1153  					"outfile": "foo.txt",
  1154  				}},
  1155  		},
  1156  	}, {
  1157  		description: "An action with nested parameters.",
  1158  		action: params.ActionResult{
  1159  			Action: &params.Action{
  1160  				Name: "fakeaction",
  1161  				Parameters: map[string]interface{}{
  1162  					"outfile": "foo.bz2",
  1163  					"compression": map[string]interface{}{
  1164  						"kind":    "bzip",
  1165  						"quality": 5,
  1166  					},
  1167  				}},
  1168  		},
  1169  	}}
  1170  
  1171  	for i, actionTest := range actionTests {
  1172  		c.Logf("test %d: %s", i, actionTest.description)
  1173  
  1174  		a, err := s.wordpressUnit.AddAction(
  1175  			actionTest.action.Action.Name,
  1176  			actionTest.action.Action.Parameters)
  1177  		c.Assert(err, jc.ErrorIsNil)
  1178  		c.Assert(names.IsValidAction(a.Id()), gc.Equals, true)
  1179  		actionTag := names.NewActionTag(a.Id())
  1180  		c.Assert(a.ActionTag(), gc.Equals, actionTag)
  1181  
  1182  		args := params.Entities{
  1183  			Entities: []params.Entity{{
  1184  				Tag: actionTag.String(),
  1185  			}},
  1186  		}
  1187  		results, err := s.uniter.Actions(args)
  1188  		c.Assert(err, jc.ErrorIsNil)
  1189  		c.Assert(results.Results, gc.HasLen, 1)
  1190  
  1191  		actionsQueryResult := results.Results[0]
  1192  
  1193  		c.Assert(actionsQueryResult, jc.DeepEquals, actionTest.action)
  1194  	}
  1195  }
  1196  
  1197  func (s *uniterSuite) TestActionsNotPresent(c *gc.C) {
  1198  	uuid, err := utils.NewUUID()
  1199  	c.Assert(err, jc.ErrorIsNil)
  1200  	args := params.Entities{
  1201  		Entities: []params.Entity{{
  1202  			Tag: names.NewActionTag(uuid.String()).String(),
  1203  		}},
  1204  	}
  1205  	results, err := s.uniter.Actions(args)
  1206  	c.Assert(err, jc.ErrorIsNil)
  1207  
  1208  	c.Assert(results.Results, gc.HasLen, 1)
  1209  	actionsQueryResult := results.Results[0]
  1210  	c.Assert(actionsQueryResult.Error, gc.NotNil)
  1211  	c.Assert(actionsQueryResult.Error, gc.ErrorMatches, `action "[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}" not found`)
  1212  }
  1213  
  1214  func (s *uniterSuite) TestActionsWrongUnit(c *gc.C) {
  1215  	// Action doesn't match unit.
  1216  	mysqlUnitAuthorizer := apiservertesting.FakeAuthorizer{
  1217  		Tag: s.mysqlUnit.Tag(),
  1218  	}
  1219  	mysqlUnitFacade, err := uniter.NewUniterAPIV4(s.State, s.resources, mysqlUnitAuthorizer)
  1220  	c.Assert(err, jc.ErrorIsNil)
  1221  
  1222  	action, err := s.wordpressUnit.AddAction("fakeaction", nil)
  1223  	c.Assert(err, jc.ErrorIsNil)
  1224  	args := params.Entities{
  1225  		Entities: []params.Entity{{
  1226  			Tag: action.Tag().String(),
  1227  		}},
  1228  	}
  1229  	actions, err := mysqlUnitFacade.Actions(args)
  1230  	c.Assert(err, jc.ErrorIsNil)
  1231  	c.Assert(len(actions.Results), gc.Equals, 1)
  1232  	c.Assert(actions.Results[0].Error, jc.Satisfies, params.IsCodeUnauthorized)
  1233  }
  1234  
  1235  func (s *uniterSuite) TestActionsPermissionDenied(c *gc.C) {
  1236  	action, err := s.mysqlUnit.AddAction("fakeaction", nil)
  1237  	c.Assert(err, jc.ErrorIsNil)
  1238  	args := params.Entities{
  1239  		Entities: []params.Entity{{
  1240  			Tag: action.Tag().String(),
  1241  		}},
  1242  	}
  1243  	actions, err := s.uniter.Actions(args)
  1244  	c.Assert(err, jc.ErrorIsNil)
  1245  	c.Assert(len(actions.Results), gc.Equals, 1)
  1246  	c.Assert(actions.Results[0].Error, jc.Satisfies, params.IsCodeUnauthorized)
  1247  }
  1248  
  1249  func (s *uniterSuite) TestFinishActionsSuccess(c *gc.C) {
  1250  	testName := "fakeaction"
  1251  	testOutput := map[string]interface{}{"output": "completed fakeaction successfully"}
  1252  
  1253  	results, err := s.wordpressUnit.CompletedActions()
  1254  	c.Assert(err, jc.ErrorIsNil)
  1255  	c.Assert(results, gc.DeepEquals, ([]state.Action)(nil))
  1256  
  1257  	action, err := s.wordpressUnit.AddAction(testName, nil)
  1258  	c.Assert(err, jc.ErrorIsNil)
  1259  
  1260  	actionResults := params.ActionExecutionResults{
  1261  		Results: []params.ActionExecutionResult{{
  1262  			ActionTag: action.ActionTag().String(),
  1263  			Status:    params.ActionCompleted,
  1264  			Results:   testOutput,
  1265  		}},
  1266  	}
  1267  	res, err := s.uniter.FinishActions(actionResults)
  1268  	c.Assert(err, jc.ErrorIsNil)
  1269  	c.Assert(res, gc.DeepEquals, params.ErrorResults{Results: []params.ErrorResult{{Error: nil}}})
  1270  
  1271  	results, err = s.wordpressUnit.CompletedActions()
  1272  	c.Assert(err, jc.ErrorIsNil)
  1273  	c.Assert(len(results), gc.Equals, 1)
  1274  	c.Assert(results[0].Status(), gc.Equals, state.ActionCompleted)
  1275  	res2, errstr := results[0].Results()
  1276  	c.Assert(errstr, gc.Equals, "")
  1277  	c.Assert(res2, gc.DeepEquals, testOutput)
  1278  	c.Assert(results[0].Name(), gc.Equals, testName)
  1279  }
  1280  
  1281  func (s *uniterSuite) TestFinishActionsFailure(c *gc.C) {
  1282  	testName := "fakeaction"
  1283  	testError := "fakeaction was a dismal failure"
  1284  
  1285  	results, err := s.wordpressUnit.CompletedActions()
  1286  	c.Assert(err, jc.ErrorIsNil)
  1287  	c.Assert(results, gc.DeepEquals, ([]state.Action)(nil))
  1288  
  1289  	action, err := s.wordpressUnit.AddAction(testName, nil)
  1290  	c.Assert(err, jc.ErrorIsNil)
  1291  
  1292  	actionResults := params.ActionExecutionResults{
  1293  		Results: []params.ActionExecutionResult{{
  1294  			ActionTag: action.ActionTag().String(),
  1295  			Status:    params.ActionFailed,
  1296  			Results:   nil,
  1297  			Message:   testError,
  1298  		}},
  1299  	}
  1300  	res, err := s.uniter.FinishActions(actionResults)
  1301  	c.Assert(err, jc.ErrorIsNil)
  1302  	c.Assert(res, gc.DeepEquals, params.ErrorResults{Results: []params.ErrorResult{{Error: nil}}})
  1303  
  1304  	results, err = s.wordpressUnit.CompletedActions()
  1305  	c.Assert(err, jc.ErrorIsNil)
  1306  	c.Assert(len(results), gc.Equals, 1)
  1307  	c.Assert(results[0].Status(), gc.Equals, state.ActionFailed)
  1308  	res2, errstr := results[0].Results()
  1309  	c.Assert(errstr, gc.Equals, testError)
  1310  	c.Assert(res2, gc.DeepEquals, map[string]interface{}{})
  1311  	c.Assert(results[0].Name(), gc.Equals, testName)
  1312  }
  1313  
  1314  func (s *uniterSuite) TestFinishActionsAuthAccess(c *gc.C) {
  1315  	good, err := s.wordpressUnit.AddAction("fakeaction", nil)
  1316  	c.Assert(err, jc.ErrorIsNil)
  1317  
  1318  	bad, err := s.mysqlUnit.AddAction("fakeaction", nil)
  1319  	c.Assert(err, jc.ErrorIsNil)
  1320  
  1321  	var tests = []struct {
  1322  		actionTag names.ActionTag
  1323  		err       error
  1324  	}{
  1325  		{actionTag: good.ActionTag(), err: nil},
  1326  		{actionTag: bad.ActionTag(), err: common.ErrPerm},
  1327  	}
  1328  
  1329  	// Queue up actions from tests
  1330  	actionResults := params.ActionExecutionResults{Results: make([]params.ActionExecutionResult, len(tests))}
  1331  	for i, test := range tests {
  1332  		actionResults.Results[i] = params.ActionExecutionResult{
  1333  			ActionTag: test.actionTag.String(),
  1334  			Status:    params.ActionCompleted,
  1335  			Results:   map[string]interface{}{},
  1336  		}
  1337  	}
  1338  
  1339  	// Invoke FinishActions
  1340  	res, err := s.uniter.FinishActions(actionResults)
  1341  	c.Assert(err, jc.ErrorIsNil)
  1342  
  1343  	// Verify permissions errors for actions queued on different unit
  1344  	for i, result := range res.Results {
  1345  		expected := tests[i].err
  1346  		if expected != nil {
  1347  			c.Assert(result.Error, gc.NotNil)
  1348  			c.Assert(result.Error.Error(), gc.Equals, expected.Error())
  1349  		} else {
  1350  			c.Assert(result.Error, gc.IsNil)
  1351  		}
  1352  	}
  1353  }
  1354  
  1355  func (s *uniterSuite) TestBeginActions(c *gc.C) {
  1356  	ten_seconds_ago := time.Now().Add(-10 * time.Second)
  1357  	good, err := s.wordpressUnit.AddAction("fakeaction", nil)
  1358  	c.Assert(err, jc.ErrorIsNil)
  1359  
  1360  	running, err := s.wordpressUnit.RunningActions()
  1361  	c.Assert(err, jc.ErrorIsNil)
  1362  	c.Assert(len(running), gc.Equals, 0, gc.Commentf("expected no running actions, got %d", len(running)))
  1363  
  1364  	args := params.Entities{Entities: []params.Entity{{Tag: good.ActionTag().String()}}}
  1365  	res, err := s.uniter.BeginActions(args)
  1366  	c.Assert(err, jc.ErrorIsNil)
  1367  	c.Assert(len(res.Results), gc.Equals, 1)
  1368  	c.Assert(res.Results[0].Error, gc.IsNil)
  1369  
  1370  	running, err = s.wordpressUnit.RunningActions()
  1371  	c.Assert(err, jc.ErrorIsNil)
  1372  	c.Assert(len(running), gc.Equals, 1, gc.Commentf("expected one running action, got %d", len(running)))
  1373  	c.Assert(running[0].ActionTag(), gc.Equals, good.ActionTag())
  1374  	enqueued, started := running[0].Enqueued(), running[0].Started()
  1375  	c.Assert(ten_seconds_ago.Before(enqueued), jc.IsTrue, gc.Commentf("enqueued time should be after 10 seconds ago"))
  1376  	c.Assert(ten_seconds_ago.Before(started), jc.IsTrue, gc.Commentf("started time should be after 10 seconds ago"))
  1377  	c.Assert(started.After(enqueued) || started.Equal(enqueued), jc.IsTrue, gc.Commentf("started should be after or equal to enqueued time"))
  1378  }
  1379  
  1380  func (s *uniterSuite) TestRelation(c *gc.C) {
  1381  	rel := s.addRelation(c, "wordpress", "mysql")
  1382  	wpEp, err := rel.Endpoint("wordpress")
  1383  	c.Assert(err, jc.ErrorIsNil)
  1384  
  1385  	args := params.RelationUnits{RelationUnits: []params.RelationUnit{
  1386  		{Relation: "relation-42", Unit: "unit-foo-0"},
  1387  		{Relation: rel.Tag().String(), Unit: "unit-wordpress-0"},
  1388  		{Relation: rel.Tag().String(), Unit: "unit-mysql-0"},
  1389  		{Relation: rel.Tag().String(), Unit: "unit-foo-0"},
  1390  		{Relation: "relation-blah", Unit: "unit-wordpress-0"},
  1391  		{Relation: "application-foo", Unit: "user-foo"},
  1392  		{Relation: "foo", Unit: "bar"},
  1393  		{Relation: "unit-wordpress-0", Unit: rel.Tag().String()},
  1394  	}}
  1395  	result, err := s.uniter.Relation(args)
  1396  	c.Assert(err, jc.ErrorIsNil)
  1397  	c.Assert(result, gc.DeepEquals, params.RelationResults{
  1398  		Results: []params.RelationResult{
  1399  			{Error: apiservertesting.ErrUnauthorized},
  1400  			{
  1401  				Id:   rel.Id(),
  1402  				Key:  rel.String(),
  1403  				Life: params.Life(rel.Life().String()),
  1404  				Endpoint: multiwatcher.Endpoint{
  1405  					ApplicationName: wpEp.ApplicationName,
  1406  					Relation:        multiwatcher.NewCharmRelation(wpEp.Relation),
  1407  				},
  1408  			},
  1409  			{Error: apiservertesting.ErrUnauthorized},
  1410  			{Error: apiservertesting.ErrUnauthorized},
  1411  			{Error: apiservertesting.ErrUnauthorized},
  1412  			{Error: apiservertesting.ErrUnauthorized},
  1413  			{Error: apiservertesting.ErrUnauthorized},
  1414  			{Error: apiservertesting.ErrUnauthorized},
  1415  		},
  1416  	})
  1417  }
  1418  
  1419  func (s *uniterSuite) TestRelationById(c *gc.C) {
  1420  	rel := s.addRelation(c, "wordpress", "mysql")
  1421  	c.Assert(rel.Id(), gc.Equals, 0)
  1422  	wpEp, err := rel.Endpoint("wordpress")
  1423  	c.Assert(err, jc.ErrorIsNil)
  1424  
  1425  	// Add another relation to mysql service, so we can see we can't
  1426  	// get it.
  1427  	otherRel, _, _ := s.addRelatedService(c, "mysql", "logging", s.mysqlUnit)
  1428  
  1429  	args := params.RelationIds{
  1430  		RelationIds: []int{-1, rel.Id(), otherRel.Id(), 42, 234},
  1431  	}
  1432  	result, err := s.uniter.RelationById(args)
  1433  	c.Assert(err, jc.ErrorIsNil)
  1434  	c.Assert(result, gc.DeepEquals, params.RelationResults{
  1435  		Results: []params.RelationResult{
  1436  			{Error: apiservertesting.ErrUnauthorized},
  1437  			{
  1438  				Id:   rel.Id(),
  1439  				Key:  rel.String(),
  1440  				Life: params.Life(rel.Life().String()),
  1441  				Endpoint: multiwatcher.Endpoint{
  1442  					ApplicationName: wpEp.ApplicationName,
  1443  					Relation:        multiwatcher.NewCharmRelation(wpEp.Relation),
  1444  				},
  1445  			},
  1446  			{Error: apiservertesting.ErrUnauthorized},
  1447  			{Error: apiservertesting.ErrUnauthorized},
  1448  			{Error: apiservertesting.ErrUnauthorized},
  1449  		},
  1450  	})
  1451  }
  1452  
  1453  func (s *uniterSuite) TestProviderType(c *gc.C) {
  1454  	cfg, err := s.State.ModelConfig()
  1455  	c.Assert(err, jc.ErrorIsNil)
  1456  
  1457  	result, err := s.uniter.ProviderType()
  1458  	c.Assert(err, jc.ErrorIsNil)
  1459  	c.Assert(result, gc.DeepEquals, params.StringResult{Result: cfg.Type()})
  1460  }
  1461  
  1462  func (s *uniterSuite) TestEnterScope(c *gc.C) {
  1463  	// Set wordpressUnit's private address first.
  1464  	err := s.machine0.SetProviderAddresses(
  1465  		network.NewScopedAddress("1.2.3.4", network.ScopeCloudLocal),
  1466  	)
  1467  	c.Assert(err, jc.ErrorIsNil)
  1468  
  1469  	rel := s.addRelation(c, "wordpress", "mysql")
  1470  	relUnit, err := rel.Unit(s.wordpressUnit)
  1471  	c.Assert(err, jc.ErrorIsNil)
  1472  	s.assertInScope(c, relUnit, false)
  1473  
  1474  	args := params.RelationUnits{RelationUnits: []params.RelationUnit{
  1475  		{Relation: "relation-42", Unit: "unit-foo-0"},
  1476  		{Relation: rel.Tag().String(), Unit: "unit-wordpress-0"},
  1477  		{Relation: rel.Tag().String(), Unit: "unit-wordpress-0"},
  1478  		{Relation: "relation-42", Unit: "unit-wordpress-0"},
  1479  		{Relation: "relation-foo", Unit: "unit-wordpress-0"},
  1480  		{Relation: "application-wordpress", Unit: "unit-foo-0"},
  1481  		{Relation: "foo", Unit: "bar"},
  1482  		{Relation: rel.Tag().String(), Unit: "unit-mysql-0"},
  1483  		{Relation: rel.Tag().String(), Unit: "application-wordpress"},
  1484  		{Relation: rel.Tag().String(), Unit: "application-mysql"},
  1485  		{Relation: rel.Tag().String(), Unit: "user-foo"},
  1486  	}}
  1487  	result, err := s.uniter.EnterScope(args)
  1488  	c.Assert(err, jc.ErrorIsNil)
  1489  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  1490  		Results: []params.ErrorResult{
  1491  			{apiservertesting.ErrUnauthorized},
  1492  			{nil},
  1493  			{nil},
  1494  			{apiservertesting.ErrUnauthorized},
  1495  			{apiservertesting.ErrUnauthorized},
  1496  			{apiservertesting.ErrUnauthorized},
  1497  			{apiservertesting.ErrUnauthorized},
  1498  			{apiservertesting.ErrUnauthorized},
  1499  			{apiservertesting.ErrUnauthorized},
  1500  			{apiservertesting.ErrUnauthorized},
  1501  			{apiservertesting.ErrUnauthorized},
  1502  		},
  1503  	})
  1504  
  1505  	// Verify the scope changes and settings.
  1506  	s.assertInScope(c, relUnit, true)
  1507  	readSettings, err := relUnit.ReadSettings(s.wordpressUnit.Name())
  1508  	c.Assert(err, jc.ErrorIsNil)
  1509  	c.Assert(readSettings, gc.DeepEquals, map[string]interface{}{
  1510  		"private-address": "1.2.3.4",
  1511  	})
  1512  }
  1513  
  1514  func (s *uniterSuite) TestLeaveScope(c *gc.C) {
  1515  	rel := s.addRelation(c, "wordpress", "mysql")
  1516  	relUnit, err := rel.Unit(s.wordpressUnit)
  1517  	c.Assert(err, jc.ErrorIsNil)
  1518  	settings := map[string]interface{}{
  1519  		"some": "settings",
  1520  	}
  1521  	err = relUnit.EnterScope(settings)
  1522  	c.Assert(err, jc.ErrorIsNil)
  1523  	s.assertInScope(c, relUnit, true)
  1524  
  1525  	args := params.RelationUnits{RelationUnits: []params.RelationUnit{
  1526  		{Relation: "relation-42", Unit: "unit-foo-0"},
  1527  		{Relation: rel.Tag().String(), Unit: "unit-wordpress-0"},
  1528  		{Relation: rel.Tag().String(), Unit: "unit-wordpress-0"},
  1529  		{Relation: "relation-42", Unit: "unit-wordpress-0"},
  1530  		{Relation: "relation-foo", Unit: "unit-wordpress-0"},
  1531  		{Relation: "application-wordpress", Unit: "unit-foo-0"},
  1532  		{Relation: "foo", Unit: "bar"},
  1533  		{Relation: rel.Tag().String(), Unit: "unit-mysql-0"},
  1534  		{Relation: rel.Tag().String(), Unit: "application-wordpress"},
  1535  		{Relation: rel.Tag().String(), Unit: "application-mysql"},
  1536  		{Relation: rel.Tag().String(), Unit: "user-foo"},
  1537  	}}
  1538  	result, err := s.uniter.LeaveScope(args)
  1539  	c.Assert(err, jc.ErrorIsNil)
  1540  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  1541  		Results: []params.ErrorResult{
  1542  			{apiservertesting.ErrUnauthorized},
  1543  			{nil},
  1544  			{nil},
  1545  			{apiservertesting.ErrUnauthorized},
  1546  			{apiservertesting.ErrUnauthorized},
  1547  			{apiservertesting.ErrUnauthorized},
  1548  			{apiservertesting.ErrUnauthorized},
  1549  			{apiservertesting.ErrUnauthorized},
  1550  			{apiservertesting.ErrUnauthorized},
  1551  			{apiservertesting.ErrUnauthorized},
  1552  			{apiservertesting.ErrUnauthorized},
  1553  		},
  1554  	})
  1555  
  1556  	// Verify the scope changes.
  1557  	s.assertInScope(c, relUnit, false)
  1558  	readSettings, err := relUnit.ReadSettings(s.wordpressUnit.Name())
  1559  	c.Assert(err, jc.ErrorIsNil)
  1560  	c.Assert(readSettings, gc.DeepEquals, settings)
  1561  }
  1562  
  1563  func (s *uniterSuite) TestJoinedRelations(c *gc.C) {
  1564  	rel := s.addRelation(c, "wordpress", "mysql")
  1565  	relUnit, err := rel.Unit(s.wordpressUnit)
  1566  	c.Assert(err, jc.ErrorIsNil)
  1567  	err = relUnit.EnterScope(nil)
  1568  	c.Assert(err, jc.ErrorIsNil)
  1569  
  1570  	args := params.Entities{
  1571  		Entities: []params.Entity{
  1572  			{s.wordpressUnit.Tag().String()},
  1573  			{s.mysqlUnit.Tag().String()},
  1574  			{"unit-unknown-1"},
  1575  			{"application-wordpress"},
  1576  			{"machine-0"},
  1577  			{rel.Tag().String()},
  1578  		},
  1579  	}
  1580  	expect := params.StringsResults{
  1581  		Results: []params.StringsResult{
  1582  			{Result: []string{rel.Tag().String()}},
  1583  			{Error: apiservertesting.ErrUnauthorized},
  1584  			{Error: apiservertesting.ErrUnauthorized},
  1585  			{Error: apiservertesting.ErrUnauthorized},
  1586  			{Error: apiservertesting.ErrUnauthorized},
  1587  			{Error: apiservertesting.ErrUnauthorized},
  1588  		},
  1589  	}
  1590  	check := func() {
  1591  		result, err := s.uniter.JoinedRelations(args)
  1592  		c.Assert(err, jc.ErrorIsNil)
  1593  		c.Assert(result, gc.DeepEquals, expect)
  1594  	}
  1595  	check()
  1596  	err = relUnit.PrepareLeaveScope()
  1597  	c.Assert(err, jc.ErrorIsNil)
  1598  	check()
  1599  }
  1600  
  1601  func (s *uniterSuite) TestReadSettings(c *gc.C) {
  1602  	rel := s.addRelation(c, "wordpress", "mysql")
  1603  	relUnit, err := rel.Unit(s.wordpressUnit)
  1604  	c.Assert(err, jc.ErrorIsNil)
  1605  	settings := map[string]interface{}{
  1606  		"some": "settings",
  1607  	}
  1608  	err = relUnit.EnterScope(settings)
  1609  	c.Assert(err, jc.ErrorIsNil)
  1610  	s.assertInScope(c, relUnit, true)
  1611  
  1612  	args := params.RelationUnits{RelationUnits: []params.RelationUnit{
  1613  		{Relation: "relation-42", Unit: "unit-foo-0"},
  1614  		{Relation: rel.Tag().String(), Unit: "unit-wordpress-0"},
  1615  		{Relation: rel.Tag().String(), Unit: "unit-mysql-0"},
  1616  		{Relation: "relation-42", Unit: "unit-wordpress-0"},
  1617  		{Relation: "relation-foo", Unit: ""},
  1618  		{Relation: "application-wordpress", Unit: "unit-foo-0"},
  1619  		{Relation: "foo", Unit: "bar"},
  1620  		{Relation: rel.Tag().String(), Unit: "unit-mysql-0"},
  1621  		{Relation: rel.Tag().String(), Unit: "application-wordpress"},
  1622  		{Relation: rel.Tag().String(), Unit: "application-mysql"},
  1623  		{Relation: rel.Tag().String(), Unit: "user-foo"},
  1624  	}}
  1625  	result, err := s.uniter.ReadSettings(args)
  1626  	c.Assert(err, jc.ErrorIsNil)
  1627  	c.Assert(result, gc.DeepEquals, params.SettingsResults{
  1628  		Results: []params.SettingsResult{
  1629  			{Error: apiservertesting.ErrUnauthorized},
  1630  			{Settings: params.Settings{
  1631  				"some": "settings",
  1632  			}},
  1633  			{Error: apiservertesting.ErrUnauthorized},
  1634  			{Error: apiservertesting.ErrUnauthorized},
  1635  			{Error: apiservertesting.ErrUnauthorized},
  1636  			{Error: apiservertesting.ErrUnauthorized},
  1637  			{Error: apiservertesting.ErrUnauthorized},
  1638  			{Error: apiservertesting.ErrUnauthorized},
  1639  			{Error: apiservertesting.ErrUnauthorized},
  1640  			{Error: apiservertesting.ErrUnauthorized},
  1641  			{Error: apiservertesting.ErrUnauthorized},
  1642  		},
  1643  	})
  1644  }
  1645  
  1646  func (s *uniterSuite) TestReadSettingsWithNonStringValuesFails(c *gc.C) {
  1647  	rel := s.addRelation(c, "wordpress", "mysql")
  1648  	relUnit, err := rel.Unit(s.wordpressUnit)
  1649  	c.Assert(err, jc.ErrorIsNil)
  1650  	settings := map[string]interface{}{
  1651  		"other":        "things",
  1652  		"invalid-bool": false,
  1653  	}
  1654  	err = relUnit.EnterScope(settings)
  1655  	c.Assert(err, jc.ErrorIsNil)
  1656  	s.assertInScope(c, relUnit, true)
  1657  
  1658  	args := params.RelationUnits{RelationUnits: []params.RelationUnit{
  1659  		{Relation: rel.Tag().String(), Unit: "unit-wordpress-0"},
  1660  	}}
  1661  	expectErr := `unexpected relation setting "invalid-bool": expected string, got bool`
  1662  	result, err := s.uniter.ReadSettings(args)
  1663  	c.Assert(err, jc.ErrorIsNil)
  1664  	c.Assert(result, gc.DeepEquals, params.SettingsResults{
  1665  		Results: []params.SettingsResult{
  1666  			{Error: &params.Error{Message: expectErr}},
  1667  		},
  1668  	})
  1669  }
  1670  
  1671  func (s *uniterSuite) TestReadRemoteSettings(c *gc.C) {
  1672  	rel := s.addRelation(c, "wordpress", "mysql")
  1673  	relUnit, err := rel.Unit(s.wordpressUnit)
  1674  	c.Assert(err, jc.ErrorIsNil)
  1675  	settings := map[string]interface{}{
  1676  		"some": "settings",
  1677  	}
  1678  	err = relUnit.EnterScope(settings)
  1679  	c.Assert(err, jc.ErrorIsNil)
  1680  	s.assertInScope(c, relUnit, true)
  1681  
  1682  	// First test most of the invalid args tests and try to read the
  1683  	// (unset) remote unit settings.
  1684  	args := params.RelationUnitPairs{RelationUnitPairs: []params.RelationUnitPair{
  1685  		{Relation: "relation-42", LocalUnit: "unit-foo-0", RemoteUnit: "foo"},
  1686  		{Relation: rel.Tag().String(), LocalUnit: "unit-wordpress-0", RemoteUnit: "unit-wordpress-0"},
  1687  		{Relation: rel.Tag().String(), LocalUnit: "unit-wordpress-0", RemoteUnit: "unit-mysql-0"},
  1688  		{Relation: "relation-42", LocalUnit: "unit-wordpress-0", RemoteUnit: ""},
  1689  		{Relation: "relation-foo", LocalUnit: "", RemoteUnit: ""},
  1690  		{Relation: "application-wordpress", LocalUnit: "unit-foo-0", RemoteUnit: "user-foo"},
  1691  		{Relation: "foo", LocalUnit: "bar", RemoteUnit: "baz"},
  1692  		{Relation: rel.Tag().String(), LocalUnit: "unit-mysql-0", RemoteUnit: "unit-wordpress-0"},
  1693  		{Relation: rel.Tag().String(), LocalUnit: "application-wordpress", RemoteUnit: "application-mysql"},
  1694  		{Relation: rel.Tag().String(), LocalUnit: "application-mysql", RemoteUnit: "foo"},
  1695  		{Relation: rel.Tag().String(), LocalUnit: "user-foo", RemoteUnit: "unit-wordpress-0"},
  1696  	}}
  1697  	result, err := s.uniter.ReadRemoteSettings(args)
  1698  
  1699  	// We don't set the remote unit settings on purpose to test the error.
  1700  	expectErr := `cannot read settings for unit "mysql/0" in relation "wordpress:db mysql:server": settings`
  1701  	c.Assert(err, jc.ErrorIsNil)
  1702  	c.Assert(result, jc.DeepEquals, params.SettingsResults{
  1703  		Results: []params.SettingsResult{
  1704  			{Error: apiservertesting.ErrUnauthorized},
  1705  			{Error: apiservertesting.ErrUnauthorized},
  1706  			{Error: apiservertesting.NotFoundError(expectErr)},
  1707  			{Error: apiservertesting.ErrUnauthorized},
  1708  			{Error: apiservertesting.ErrUnauthorized},
  1709  			{Error: apiservertesting.ErrUnauthorized},
  1710  			{Error: apiservertesting.ErrUnauthorized},
  1711  			{Error: apiservertesting.ErrUnauthorized},
  1712  			{Error: apiservertesting.ErrUnauthorized},
  1713  			{Error: apiservertesting.ErrUnauthorized},
  1714  			{Error: apiservertesting.ErrUnauthorized},
  1715  		},
  1716  	})
  1717  
  1718  	// Now leave the mysqlUnit and re-enter with new settings.
  1719  	relUnit, err = rel.Unit(s.mysqlUnit)
  1720  	c.Assert(err, jc.ErrorIsNil)
  1721  	settings = map[string]interface{}{
  1722  		"other": "things",
  1723  	}
  1724  	err = relUnit.LeaveScope()
  1725  	c.Assert(err, jc.ErrorIsNil)
  1726  	s.assertInScope(c, relUnit, false)
  1727  	err = relUnit.EnterScope(settings)
  1728  	c.Assert(err, jc.ErrorIsNil)
  1729  	s.assertInScope(c, relUnit, true)
  1730  
  1731  	// Test the remote unit settings can be read.
  1732  	args = params.RelationUnitPairs{RelationUnitPairs: []params.RelationUnitPair{{
  1733  		Relation:   rel.Tag().String(),
  1734  		LocalUnit:  "unit-wordpress-0",
  1735  		RemoteUnit: "unit-mysql-0",
  1736  	}}}
  1737  	expect := params.SettingsResults{
  1738  		Results: []params.SettingsResult{
  1739  			{Settings: params.Settings{
  1740  				"other": "things",
  1741  			}},
  1742  		},
  1743  	}
  1744  	result, err = s.uniter.ReadRemoteSettings(args)
  1745  	c.Assert(err, jc.ErrorIsNil)
  1746  	c.Assert(result, gc.DeepEquals, expect)
  1747  
  1748  	// Now destroy the remote unit, and check its settings can still be read.
  1749  	err = s.mysqlUnit.Destroy()
  1750  	c.Assert(err, jc.ErrorIsNil)
  1751  	err = s.mysqlUnit.EnsureDead()
  1752  	c.Assert(err, jc.ErrorIsNil)
  1753  	err = s.mysqlUnit.Remove()
  1754  	c.Assert(err, jc.ErrorIsNil)
  1755  	result, err = s.uniter.ReadRemoteSettings(args)
  1756  	c.Assert(err, jc.ErrorIsNil)
  1757  	c.Assert(result, gc.DeepEquals, expect)
  1758  }
  1759  
  1760  func (s *uniterSuite) TestReadRemoteSettingsWithNonStringValuesFails(c *gc.C) {
  1761  	rel := s.addRelation(c, "wordpress", "mysql")
  1762  	relUnit, err := rel.Unit(s.mysqlUnit)
  1763  	c.Assert(err, jc.ErrorIsNil)
  1764  	settings := map[string]interface{}{
  1765  		"other":        "things",
  1766  		"invalid-bool": false,
  1767  	}
  1768  	err = relUnit.EnterScope(settings)
  1769  	c.Assert(err, jc.ErrorIsNil)
  1770  	s.assertInScope(c, relUnit, true)
  1771  
  1772  	args := params.RelationUnitPairs{RelationUnitPairs: []params.RelationUnitPair{{
  1773  		Relation:   rel.Tag().String(),
  1774  		LocalUnit:  "unit-wordpress-0",
  1775  		RemoteUnit: "unit-mysql-0",
  1776  	}}}
  1777  	expectErr := `unexpected relation setting "invalid-bool": expected string, got bool`
  1778  	result, err := s.uniter.ReadRemoteSettings(args)
  1779  	c.Assert(err, jc.ErrorIsNil)
  1780  	c.Assert(result, gc.DeepEquals, params.SettingsResults{
  1781  		Results: []params.SettingsResult{
  1782  			{Error: &params.Error{Message: expectErr}},
  1783  		},
  1784  	})
  1785  }
  1786  
  1787  func (s *uniterSuite) TestUpdateSettings(c *gc.C) {
  1788  	rel := s.addRelation(c, "wordpress", "mysql")
  1789  	relUnit, err := rel.Unit(s.wordpressUnit)
  1790  	c.Assert(err, jc.ErrorIsNil)
  1791  	settings := map[string]interface{}{
  1792  		"some":  "settings",
  1793  		"other": "stuff",
  1794  	}
  1795  	err = relUnit.EnterScope(settings)
  1796  	s.assertInScope(c, relUnit, true)
  1797  
  1798  	newSettings := params.Settings{
  1799  		"some":  "different",
  1800  		"other": "",
  1801  	}
  1802  
  1803  	args := params.RelationUnitsSettings{RelationUnits: []params.RelationUnitSettings{
  1804  		{Relation: "relation-42", Unit: "unit-foo-0", Settings: nil},
  1805  		{Relation: rel.Tag().String(), Unit: "unit-wordpress-0", Settings: newSettings},
  1806  		{Relation: "relation-42", Unit: "unit-wordpress-0", Settings: nil},
  1807  		{Relation: "relation-foo", Unit: "unit-wordpress-0", Settings: nil},
  1808  		{Relation: "application-wordpress", Unit: "unit-foo-0", Settings: nil},
  1809  		{Relation: "foo", Unit: "bar", Settings: nil},
  1810  		{Relation: rel.Tag().String(), Unit: "unit-mysql-0", Settings: nil},
  1811  		{Relation: rel.Tag().String(), Unit: "application-wordpress", Settings: nil},
  1812  		{Relation: rel.Tag().String(), Unit: "application-mysql", Settings: nil},
  1813  		{Relation: rel.Tag().String(), Unit: "user-foo", Settings: nil},
  1814  	}}
  1815  	result, err := s.uniter.UpdateSettings(args)
  1816  	c.Assert(err, jc.ErrorIsNil)
  1817  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  1818  		Results: []params.ErrorResult{
  1819  			{apiservertesting.ErrUnauthorized},
  1820  			{nil},
  1821  			{apiservertesting.ErrUnauthorized},
  1822  			{apiservertesting.ErrUnauthorized},
  1823  			{apiservertesting.ErrUnauthorized},
  1824  			{apiservertesting.ErrUnauthorized},
  1825  			{apiservertesting.ErrUnauthorized},
  1826  			{apiservertesting.ErrUnauthorized},
  1827  			{apiservertesting.ErrUnauthorized},
  1828  			{apiservertesting.ErrUnauthorized},
  1829  		},
  1830  	})
  1831  
  1832  	// Verify the settings were saved.
  1833  	s.assertInScope(c, relUnit, true)
  1834  	readSettings, err := relUnit.ReadSettings(s.wordpressUnit.Name())
  1835  	c.Assert(err, jc.ErrorIsNil)
  1836  	c.Assert(readSettings, gc.DeepEquals, map[string]interface{}{
  1837  		"some": "different",
  1838  	})
  1839  }
  1840  
  1841  func (s *uniterSuite) TestWatchRelationUnits(c *gc.C) {
  1842  	// Add a relation between wordpress and mysql and enter scope with
  1843  	// mysqlUnit.
  1844  	rel := s.addRelation(c, "wordpress", "mysql")
  1845  	myRelUnit, err := rel.Unit(s.mysqlUnit)
  1846  	c.Assert(err, jc.ErrorIsNil)
  1847  	err = myRelUnit.EnterScope(nil)
  1848  	c.Assert(err, jc.ErrorIsNil)
  1849  	s.assertInScope(c, myRelUnit, true)
  1850  
  1851  	c.Assert(s.resources.Count(), gc.Equals, 0)
  1852  
  1853  	args := params.RelationUnits{RelationUnits: []params.RelationUnit{
  1854  		{Relation: "relation-42", Unit: "unit-foo-0"},
  1855  		{Relation: rel.Tag().String(), Unit: "unit-wordpress-0"},
  1856  		{Relation: rel.Tag().String(), Unit: "unit-mysql-0"},
  1857  		{Relation: "relation-42", Unit: "unit-wordpress-0"},
  1858  		{Relation: "relation-foo", Unit: ""},
  1859  		{Relation: "application-wordpress", Unit: "unit-foo-0"},
  1860  		{Relation: "foo", Unit: "bar"},
  1861  		{Relation: rel.Tag().String(), Unit: "unit-mysql-0"},
  1862  		{Relation: rel.Tag().String(), Unit: "application-wordpress"},
  1863  		{Relation: rel.Tag().String(), Unit: "application-mysql"},
  1864  		{Relation: rel.Tag().String(), Unit: "user-foo"},
  1865  	}}
  1866  	result, err := s.uniter.WatchRelationUnits(args)
  1867  	c.Assert(err, jc.ErrorIsNil)
  1868  	// UnitSettings versions are volatile, so we don't check them.
  1869  	// We just make sure the keys of the Changed field are as
  1870  	// expected.
  1871  	c.Assert(result.Results, gc.HasLen, len(args.RelationUnits))
  1872  	mysqlChanges := result.Results[1].Changes
  1873  	c.Assert(mysqlChanges, gc.NotNil)
  1874  	changed, ok := mysqlChanges.Changed["mysql/0"]
  1875  	c.Assert(ok, jc.IsTrue)
  1876  	expectChanges := params.RelationUnitsChange{
  1877  		Changed: map[string]params.UnitSettings{
  1878  			"mysql/0": params.UnitSettings{changed.Version},
  1879  		},
  1880  	}
  1881  	c.Assert(result, gc.DeepEquals, params.RelationUnitsWatchResults{
  1882  		Results: []params.RelationUnitsWatchResult{
  1883  			{Error: apiservertesting.ErrUnauthorized},
  1884  			{
  1885  				RelationUnitsWatcherId: "1",
  1886  				Changes:                expectChanges,
  1887  			},
  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  			{Error: apiservertesting.ErrUnauthorized},
  1897  		},
  1898  	})
  1899  
  1900  	// Verify the resource was registered and stop when done
  1901  	c.Assert(s.resources.Count(), gc.Equals, 1)
  1902  	resource := s.resources.Get("1")
  1903  	defer statetesting.AssertStop(c, resource)
  1904  
  1905  	// Check that the Watch has consumed the initial event ("returned" in
  1906  	// the Watch call)
  1907  	wc := statetesting.NewRelationUnitsWatcherC(c, s.State, resource.(state.RelationUnitsWatcher))
  1908  	wc.AssertNoChange()
  1909  
  1910  	// Leave scope with mysqlUnit and check it's detected.
  1911  	err = myRelUnit.LeaveScope()
  1912  	c.Assert(err, jc.ErrorIsNil)
  1913  	s.assertInScope(c, myRelUnit, false)
  1914  
  1915  	wc.AssertChange(nil, []string{"mysql/0"})
  1916  }
  1917  
  1918  func (s *uniterSuite) TestAPIAddresses(c *gc.C) {
  1919  	hostPorts := [][]network.HostPort{
  1920  		network.NewHostPorts(1234, "0.1.2.3"),
  1921  	}
  1922  	err := s.State.SetAPIHostPorts(hostPorts)
  1923  	c.Assert(err, jc.ErrorIsNil)
  1924  
  1925  	result, err := s.uniter.APIAddresses()
  1926  	c.Assert(err, jc.ErrorIsNil)
  1927  	c.Assert(result, gc.DeepEquals, params.StringsResult{
  1928  		Result: []string{"0.1.2.3:1234"},
  1929  	})
  1930  }
  1931  
  1932  func (s *uniterSuite) TestWatchUnitAddresses(c *gc.C) {
  1933  	c.Assert(s.resources.Count(), gc.Equals, 0)
  1934  
  1935  	args := params.Entities{Entities: []params.Entity{
  1936  		{Tag: "unit-mysql-0"},
  1937  		{Tag: "unit-wordpress-0"},
  1938  		{Tag: "unit-foo-42"},
  1939  		{Tag: "machine-0"},
  1940  		{Tag: "application-wordpress"},
  1941  	}}
  1942  	result, err := s.uniter.WatchUnitAddresses(args)
  1943  	c.Assert(err, jc.ErrorIsNil)
  1944  	c.Assert(result, gc.DeepEquals, params.NotifyWatchResults{
  1945  		Results: []params.NotifyWatchResult{
  1946  			{Error: apiservertesting.ErrUnauthorized},
  1947  			{NotifyWatcherId: "1"},
  1948  			{Error: apiservertesting.ErrUnauthorized},
  1949  			{Error: apiservertesting.ErrUnauthorized},
  1950  			{Error: apiservertesting.ErrUnauthorized},
  1951  		},
  1952  	})
  1953  
  1954  	// Verify the resource was registered and stop when done
  1955  	c.Assert(s.resources.Count(), gc.Equals, 1)
  1956  	resource := s.resources.Get("1")
  1957  	defer statetesting.AssertStop(c, resource)
  1958  
  1959  	// Check that the Watch has consumed the initial event ("returned" in
  1960  	// the Watch call)
  1961  	wc := statetesting.NewNotifyWatcherC(c, s.State, resource.(state.NotifyWatcher))
  1962  	wc.AssertNoChange()
  1963  }
  1964  
  1965  func (s *uniterSuite) TestGetMeterStatusUnauthenticated(c *gc.C) {
  1966  	args := params.Entities{Entities: []params.Entity{{s.mysqlUnit.Tag().String()}}}
  1967  	result, err := s.uniter.GetMeterStatus(args)
  1968  	c.Assert(err, jc.ErrorIsNil)
  1969  	c.Assert(result.Results, gc.HasLen, 1)
  1970  	c.Assert(result.Results[0].Error, gc.ErrorMatches, "permission denied")
  1971  	c.Assert(result.Results[0].Code, gc.Equals, "")
  1972  	c.Assert(result.Results[0].Info, gc.Equals, "")
  1973  }
  1974  
  1975  func (s *uniterSuite) TestGetMeterStatusBadTag(c *gc.C) {
  1976  	tags := []string{
  1977  		"user-admin",
  1978  		"unit-nosuchunit",
  1979  		"thisisnotatag",
  1980  		"machine-0",
  1981  		"model-blah",
  1982  	}
  1983  	args := params.Entities{Entities: make([]params.Entity, len(tags))}
  1984  	for i, tag := range tags {
  1985  		args.Entities[i] = params.Entity{Tag: tag}
  1986  	}
  1987  	result, err := s.uniter.GetMeterStatus(args)
  1988  	c.Assert(err, jc.ErrorIsNil)
  1989  	c.Assert(result.Results, gc.HasLen, len(tags))
  1990  	for i, result := range result.Results {
  1991  		c.Logf("checking result %d", i)
  1992  		c.Assert(result.Code, gc.Equals, "")
  1993  		c.Assert(result.Info, gc.Equals, "")
  1994  		c.Assert(result.Error, gc.ErrorMatches, "permission denied")
  1995  	}
  1996  }
  1997  
  1998  func (s *uniterSuite) assertOneStringsWatcher(c *gc.C, result params.StringsWatchResults, err error) {
  1999  	c.Assert(err, jc.ErrorIsNil)
  2000  	c.Assert(result.Results, gc.HasLen, 3)
  2001  	c.Assert(result.Results[0].Error, gc.DeepEquals, apiservertesting.ErrUnauthorized)
  2002  	c.Assert(result.Results[1].StringsWatcherId, gc.Equals, "1")
  2003  	c.Assert(result.Results[1].Changes, gc.NotNil)
  2004  	c.Assert(result.Results[1].Error, gc.IsNil)
  2005  	c.Assert(result.Results[2].Error, gc.DeepEquals, apiservertesting.ErrUnauthorized)
  2006  
  2007  	// Verify the resource was registered and stop when done
  2008  	c.Assert(s.resources.Count(), gc.Equals, 1)
  2009  	resource := s.resources.Get("1")
  2010  	defer statetesting.AssertStop(c, resource)
  2011  
  2012  	// Check that the Watch has consumed the initial event ("returned" in
  2013  	// the Watch call)
  2014  	wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher))
  2015  	wc.AssertNoChange()
  2016  }
  2017  
  2018  func (s *uniterSuite) assertInScope(c *gc.C, relUnit *state.RelationUnit, inScope bool) {
  2019  	ok, err := relUnit.InScope()
  2020  	c.Assert(err, jc.ErrorIsNil)
  2021  	c.Assert(ok, gc.Equals, inScope)
  2022  }
  2023  
  2024  func (s *uniterSuite) addRelation(c *gc.C, first, second string) *state.Relation {
  2025  	eps, err := s.State.InferEndpoints(first, second)
  2026  	c.Assert(err, jc.ErrorIsNil)
  2027  	rel, err := s.State.AddRelation(eps...)
  2028  	c.Assert(err, jc.ErrorIsNil)
  2029  	return rel
  2030  }
  2031  
  2032  func (s *uniterSuite) addRelatedService(c *gc.C, firstSvc, relatedSvc string, unit *state.Unit) (*state.Relation, *state.Application, *state.Unit) {
  2033  	relatedService := s.AddTestingService(c, relatedSvc, s.AddTestingCharm(c, relatedSvc))
  2034  	rel := s.addRelation(c, firstSvc, relatedSvc)
  2035  	relUnit, err := rel.Unit(unit)
  2036  	c.Assert(err, jc.ErrorIsNil)
  2037  	err = relUnit.EnterScope(nil)
  2038  	c.Assert(err, jc.ErrorIsNil)
  2039  	relatedUnit, err := s.State.Unit(relatedSvc + "/0")
  2040  	c.Assert(err, jc.ErrorIsNil)
  2041  	return rel, relatedService, relatedUnit
  2042  }
  2043  
  2044  func (s *uniterSuite) TestRequestReboot(c *gc.C) {
  2045  	args := params.Entities{Entities: []params.Entity{
  2046  		{Tag: s.machine0.Tag().String()},
  2047  		{Tag: s.machine1.Tag().String()},
  2048  		{Tag: "bogus"},
  2049  		{Tag: "nasty-tag"},
  2050  	}}
  2051  	errResult, err := s.uniter.RequestReboot(args)
  2052  	c.Assert(err, jc.ErrorIsNil)
  2053  	c.Assert(errResult, gc.DeepEquals, params.ErrorResults{
  2054  		Results: []params.ErrorResult{
  2055  			{Error: nil},
  2056  			{Error: apiservertesting.ErrUnauthorized},
  2057  			{Error: apiservertesting.ErrUnauthorized},
  2058  			{Error: apiservertesting.ErrUnauthorized},
  2059  		}})
  2060  
  2061  	rFlag, err := s.machine0.GetRebootFlag()
  2062  	c.Assert(err, jc.ErrorIsNil)
  2063  	c.Assert(rFlag, jc.IsTrue)
  2064  
  2065  	rFlag, err = s.machine1.GetRebootFlag()
  2066  	c.Assert(err, jc.ErrorIsNil)
  2067  	c.Assert(rFlag, jc.IsFalse)
  2068  }
  2069  
  2070  func checkUnorderedActionIdsEqual(c *gc.C, ids []string, results params.StringsWatchResults) {
  2071  	c.Assert(results, gc.NotNil)
  2072  	content := results.Results
  2073  	c.Assert(len(content), gc.Equals, 1)
  2074  	result := content[0]
  2075  	c.Assert(result.StringsWatcherId, gc.Equals, "1")
  2076  	obtainedIds := map[string]int{}
  2077  	expectedIds := map[string]int{}
  2078  	for _, id := range ids {
  2079  		expectedIds[id]++
  2080  	}
  2081  	// The count of each ID that has been seen.
  2082  	for _, change := range result.Changes {
  2083  		obtainedIds[change]++
  2084  	}
  2085  	c.Check(obtainedIds, jc.DeepEquals, expectedIds)
  2086  }
  2087  
  2088  func (s *uniterSuite) TestStorageAttachments(c *gc.C) {
  2089  	// We need to set up a unit that has storage metadata defined.
  2090  	ch := s.AddTestingCharm(c, "storage-block")
  2091  	sCons := map[string]state.StorageConstraints{
  2092  		"data": {Pool: "", Size: 1024, Count: 1},
  2093  	}
  2094  	service := s.AddTestingServiceWithStorage(c, "storage-block", ch, sCons)
  2095  	unit, err := service.AddUnit()
  2096  	c.Assert(err, jc.ErrorIsNil)
  2097  	err = s.State.AssignUnit(unit, state.AssignCleanEmpty)
  2098  	c.Assert(err, jc.ErrorIsNil)
  2099  	assignedMachineId, err := unit.AssignedMachineId()
  2100  	c.Assert(err, jc.ErrorIsNil)
  2101  	machine, err := s.State.Machine(assignedMachineId)
  2102  	c.Assert(err, jc.ErrorIsNil)
  2103  
  2104  	volumeAttachments, err := machine.VolumeAttachments()
  2105  	c.Assert(err, jc.ErrorIsNil)
  2106  	c.Assert(volumeAttachments, gc.HasLen, 1)
  2107  
  2108  	err = machine.SetProvisioned("inst-id", "fake_nonce", nil)
  2109  	c.Assert(err, jc.ErrorIsNil)
  2110  
  2111  	err = s.State.SetVolumeInfo(
  2112  		volumeAttachments[0].Volume(),
  2113  		state.VolumeInfo{VolumeId: "vol-123", Size: 456},
  2114  	)
  2115  	c.Assert(err, jc.ErrorIsNil)
  2116  
  2117  	err = s.State.SetVolumeAttachmentInfo(
  2118  		machine.MachineTag(),
  2119  		volumeAttachments[0].Volume(),
  2120  		state.VolumeAttachmentInfo{DeviceName: "xvdf1"},
  2121  	)
  2122  	c.Assert(err, jc.ErrorIsNil)
  2123  
  2124  	password, err := utils.RandomPassword()
  2125  	err = unit.SetPassword(password)
  2126  	c.Assert(err, jc.ErrorIsNil)
  2127  	st := s.OpenAPIAs(c, unit.Tag(), password)
  2128  	uniter, err := st.Uniter()
  2129  	c.Assert(err, jc.ErrorIsNil)
  2130  
  2131  	attachments, err := uniter.UnitStorageAttachments(unit.UnitTag())
  2132  	c.Assert(err, jc.ErrorIsNil)
  2133  	c.Assert(attachments, gc.DeepEquals, []params.StorageAttachmentId{{
  2134  		StorageTag: "storage-data-0",
  2135  		UnitTag:    unit.Tag().String(),
  2136  	}})
  2137  }
  2138  
  2139  func (s *uniterSuite) TestUnitStatus(c *gc.C) {
  2140  	now := time.Now()
  2141  	sInfo := status.StatusInfo{
  2142  		Status:  status.Maintenance,
  2143  		Message: "blah",
  2144  		Since:   &now,
  2145  	}
  2146  	err := s.wordpressUnit.SetStatus(sInfo)
  2147  	c.Assert(err, jc.ErrorIsNil)
  2148  	sInfo = status.StatusInfo{
  2149  		Status:  status.Terminated,
  2150  		Message: "foo",
  2151  		Since:   &now,
  2152  	}
  2153  	err = s.mysqlUnit.SetStatus(sInfo)
  2154  	c.Assert(err, jc.ErrorIsNil)
  2155  
  2156  	args := params.Entities{
  2157  		Entities: []params.Entity{
  2158  			{Tag: "unit-mysql-0"},
  2159  			{Tag: "unit-wordpress-0"},
  2160  			{Tag: "unit-foo-42"},
  2161  			{Tag: "machine-1"},
  2162  			{Tag: "invalid"},
  2163  		}}
  2164  	result, err := s.uniter.UnitStatus(args)
  2165  	c.Assert(err, jc.ErrorIsNil)
  2166  	// Zero out the updated timestamps so we can easily check the results.
  2167  	for i, statusResult := range result.Results {
  2168  		r := statusResult
  2169  		if r.Status != "" {
  2170  			c.Assert(r.Since, gc.NotNil)
  2171  		}
  2172  		r.Since = nil
  2173  		result.Results[i] = r
  2174  	}
  2175  	c.Assert(result, gc.DeepEquals, params.StatusResults{
  2176  		Results: []params.StatusResult{
  2177  			{Error: apiservertesting.ErrUnauthorized},
  2178  			{Status: status.Maintenance.String(), Info: "blah", Data: map[string]interface{}{}},
  2179  			{Error: apiservertesting.ErrUnauthorized},
  2180  			{Error: apiservertesting.ErrUnauthorized},
  2181  			{Error: apiservertesting.ServerError(`"invalid" is not a valid tag`)},
  2182  		},
  2183  	})
  2184  }
  2185  
  2186  func (s *uniterSuite) TestAssignedMachine(c *gc.C) {
  2187  	args := params.Entities{Entities: []params.Entity{
  2188  		{Tag: "unit-mysql-0"},
  2189  		{Tag: "unit-wordpress-0"},
  2190  		{Tag: "unit-foo-42"},
  2191  		{Tag: "application-mysql"},
  2192  		{Tag: "application-wordpress"},
  2193  		{Tag: "machine-0"},
  2194  		{Tag: "machine-1"},
  2195  		{Tag: "machine-42"},
  2196  		{Tag: "application-foo"},
  2197  		{Tag: "relation-svc1.rel1#svc2.rel2"},
  2198  	}}
  2199  	result, err := s.uniter.AssignedMachine(args)
  2200  	c.Assert(err, jc.ErrorIsNil)
  2201  	c.Assert(result, jc.DeepEquals, params.StringResults{
  2202  		Results: []params.StringResult{
  2203  			{Error: apiservertesting.ErrUnauthorized},
  2204  			{Result: "machine-0"},
  2205  			{Error: apiservertesting.ErrUnauthorized},
  2206  			{Error: apiservertesting.ErrUnauthorized},
  2207  			{Error: apiservertesting.ErrUnauthorized},
  2208  			{Error: apiservertesting.ErrUnauthorized},
  2209  			{Error: apiservertesting.ErrUnauthorized},
  2210  			{Error: apiservertesting.ErrUnauthorized},
  2211  			{Error: apiservertesting.ErrUnauthorized},
  2212  			{Error: apiservertesting.ErrUnauthorized},
  2213  		},
  2214  	})
  2215  }
  2216  
  2217  func (s *uniterSuite) TestAllMachinePorts(c *gc.C) {
  2218  	// Verify no ports are opened yet on the machine or unit.
  2219  	machinePorts, err := s.machine0.AllPorts()
  2220  	c.Assert(err, jc.ErrorIsNil)
  2221  	c.Assert(machinePorts, gc.HasLen, 0)
  2222  	unitPorts, err := s.wordpressUnit.OpenedPorts()
  2223  	c.Assert(err, jc.ErrorIsNil)
  2224  	c.Assert(unitPorts, gc.HasLen, 0)
  2225  
  2226  	// Add another mysql unit on machine 0.
  2227  	mysqlUnit1, err := s.mysql.AddUnit()
  2228  	c.Assert(err, jc.ErrorIsNil)
  2229  	err = mysqlUnit1.AssignToMachine(s.machine0)
  2230  	c.Assert(err, jc.ErrorIsNil)
  2231  
  2232  	// Open some ports on both units.
  2233  	err = s.wordpressUnit.OpenPorts("tcp", 100, 200)
  2234  	c.Assert(err, jc.ErrorIsNil)
  2235  	err = s.wordpressUnit.OpenPorts("udp", 10, 20)
  2236  	c.Assert(err, jc.ErrorIsNil)
  2237  	err = mysqlUnit1.OpenPorts("tcp", 201, 250)
  2238  	c.Assert(err, jc.ErrorIsNil)
  2239  	err = mysqlUnit1.OpenPorts("udp", 1, 8)
  2240  	c.Assert(err, jc.ErrorIsNil)
  2241  
  2242  	args := params.Entities{Entities: []params.Entity{
  2243  		{Tag: "unit-mysql-0"},
  2244  		{Tag: "machine-0"},
  2245  		{Tag: "machine-1"},
  2246  		{Tag: "unit-foo-42"},
  2247  		{Tag: "machine-42"},
  2248  		{Tag: "application-wordpress"},
  2249  	}}
  2250  	expectPorts := []params.MachinePortRange{
  2251  		{UnitTag: "unit-wordpress-0", PortRange: params.PortRange{100, 200, "tcp"}},
  2252  		{UnitTag: "unit-mysql-1", PortRange: params.PortRange{201, 250, "tcp"}},
  2253  		{UnitTag: "unit-mysql-1", PortRange: params.PortRange{1, 8, "udp"}},
  2254  		{UnitTag: "unit-wordpress-0", PortRange: params.PortRange{10, 20, "udp"}},
  2255  	}
  2256  	result, err := s.uniter.AllMachinePorts(args)
  2257  	c.Assert(err, jc.ErrorIsNil)
  2258  	c.Assert(result, gc.DeepEquals, params.MachinePortsResults{
  2259  		Results: []params.MachinePortsResult{
  2260  			{Error: apiservertesting.ErrUnauthorized},
  2261  			{Ports: expectPorts},
  2262  			{Error: apiservertesting.ErrUnauthorized},
  2263  			{Error: apiservertesting.ErrUnauthorized},
  2264  			{Error: apiservertesting.ErrUnauthorized},
  2265  			{Error: apiservertesting.ErrUnauthorized},
  2266  		},
  2267  	})
  2268  }
  2269  
  2270  type unitMetricBatchesSuite struct {
  2271  	uniterSuite
  2272  	*commontesting.ModelWatcherTest
  2273  	uniter *uniter.UniterAPIV3
  2274  }
  2275  
  2276  var _ = gc.Suite(&unitMetricBatchesSuite{})
  2277  
  2278  func (s *unitMetricBatchesSuite) SetUpTest(c *gc.C) {
  2279  	s.uniterSuite.SetUpTest(c)
  2280  
  2281  	meteredAuthorizer := apiservertesting.FakeAuthorizer{
  2282  		Tag: s.meteredUnit.Tag(),
  2283  	}
  2284  	var err error
  2285  	s.uniter, err = uniter.NewUniterAPIV4(
  2286  		s.State,
  2287  		s.resources,
  2288  		meteredAuthorizer,
  2289  	)
  2290  	c.Assert(err, jc.ErrorIsNil)
  2291  
  2292  	s.ModelWatcherTest = commontesting.NewModelWatcherTest(
  2293  		s.uniter,
  2294  		s.State,
  2295  		s.resources,
  2296  	)
  2297  }
  2298  
  2299  func (s *unitMetricBatchesSuite) TestAddMetricsBatch(c *gc.C) {
  2300  	metrics := []params.Metric{{Key: "pings", Value: "5", Time: time.Now().UTC()}}
  2301  	uuid := utils.MustNewUUID().String()
  2302  
  2303  	result, err := s.uniter.AddMetricBatches(params.MetricBatchParams{
  2304  		Batches: []params.MetricBatchParam{{
  2305  			Tag: s.meteredUnit.Tag().String(),
  2306  			Batch: params.MetricBatch{
  2307  				UUID:     uuid,
  2308  				CharmURL: s.meteredCharm.URL().String(),
  2309  				Created:  time.Now(),
  2310  				Metrics:  metrics,
  2311  			}}}},
  2312  	)
  2313  
  2314  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  2315  		Results: []params.ErrorResult{{nil}},
  2316  	})
  2317  	c.Assert(err, jc.ErrorIsNil)
  2318  
  2319  	batch, err := s.State.MetricBatch(uuid)
  2320  	c.Assert(err, jc.ErrorIsNil)
  2321  	c.Assert(batch.UUID(), gc.Equals, uuid)
  2322  	c.Assert(batch.CharmURL(), gc.Equals, s.meteredCharm.URL().String())
  2323  	c.Assert(batch.Unit(), gc.Equals, s.meteredUnit.Name())
  2324  	storedMetrics := batch.Metrics()
  2325  	c.Assert(storedMetrics, gc.HasLen, 1)
  2326  	c.Assert(storedMetrics[0].Key, gc.Equals, metrics[0].Key)
  2327  	c.Assert(storedMetrics[0].Value, gc.Equals, metrics[0].Value)
  2328  }
  2329  
  2330  func (s *unitMetricBatchesSuite) TestAddMetricsBatchNoCharmURL(c *gc.C) {
  2331  	metrics := []params.Metric{{Key: "pings", Value: "5", Time: time.Now().UTC()}}
  2332  	uuid := utils.MustNewUUID().String()
  2333  
  2334  	result, err := s.uniter.AddMetricBatches(params.MetricBatchParams{
  2335  		Batches: []params.MetricBatchParam{{
  2336  			Tag: s.meteredUnit.Tag().String(),
  2337  			Batch: params.MetricBatch{
  2338  				UUID:     uuid,
  2339  				CharmURL: s.meteredCharm.URL().String(),
  2340  				Created:  time.Now(),
  2341  				Metrics:  metrics,
  2342  			}}}})
  2343  
  2344  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  2345  		Results: []params.ErrorResult{{nil}},
  2346  	})
  2347  	c.Assert(err, jc.ErrorIsNil)
  2348  
  2349  	batch, err := s.State.MetricBatch(uuid)
  2350  	c.Assert(err, jc.ErrorIsNil)
  2351  	c.Assert(batch.UUID(), gc.Equals, uuid)
  2352  	c.Assert(batch.CharmURL(), gc.Equals, s.meteredCharm.URL().String())
  2353  	c.Assert(batch.Unit(), gc.Equals, s.meteredUnit.Name())
  2354  	storedMetrics := batch.Metrics()
  2355  	c.Assert(storedMetrics, gc.HasLen, 1)
  2356  	c.Assert(storedMetrics[0].Key, gc.Equals, metrics[0].Key)
  2357  	c.Assert(storedMetrics[0].Value, gc.Equals, metrics[0].Value)
  2358  }
  2359  
  2360  func (s *unitMetricBatchesSuite) TestAddMetricsBatchDiffTag(c *gc.C) {
  2361  	unit2 := s.Factory.MakeUnit(c, &factory.UnitParams{Application: s.meteredService, SetCharmURL: true})
  2362  
  2363  	metrics := []params.Metric{{Key: "pings", Value: "5", Time: time.Now().UTC()}}
  2364  	uuid := utils.MustNewUUID().String()
  2365  
  2366  	tests := []struct {
  2367  		about  string
  2368  		tag    string
  2369  		expect string
  2370  	}{{
  2371  		about:  "different unit",
  2372  		tag:    unit2.Tag().String(),
  2373  		expect: "permission denied",
  2374  	}, {
  2375  		about:  "user tag",
  2376  		tag:    names.NewLocalUserTag("admin").String(),
  2377  		expect: `"user-admin" is not a valid unit tag`,
  2378  	}, {
  2379  		about:  "machine tag",
  2380  		tag:    names.NewMachineTag("0").String(),
  2381  		expect: `"machine-0" is not a valid unit tag`,
  2382  	}}
  2383  
  2384  	for i, test := range tests {
  2385  		c.Logf("test %d: %s", i, test.about)
  2386  		result, err := s.uniter.AddMetricBatches(params.MetricBatchParams{
  2387  			Batches: []params.MetricBatchParam{{
  2388  				Tag: test.tag,
  2389  				Batch: params.MetricBatch{
  2390  					UUID:     uuid,
  2391  					CharmURL: "",
  2392  					Created:  time.Now(),
  2393  					Metrics:  metrics,
  2394  				}}}})
  2395  
  2396  		if test.expect == "" {
  2397  			c.Assert(result.OneError(), jc.ErrorIsNil)
  2398  		} else {
  2399  			c.Assert(result.OneError(), gc.ErrorMatches, test.expect)
  2400  		}
  2401  		c.Assert(err, jc.ErrorIsNil)
  2402  
  2403  		_, err = s.State.MetricBatch(uuid)
  2404  		c.Assert(err, jc.Satisfies, errors.IsNotFound)
  2405  	}
  2406  }
  2407  
  2408  type uniterNetworkConfigSuite struct {
  2409  	base uniterSuite // not embedded so it doesn't run all tests.
  2410  }
  2411  
  2412  var _ = gc.Suite(&uniterNetworkConfigSuite{})
  2413  
  2414  func (s *uniterNetworkConfigSuite) SetUpSuite(c *gc.C) {
  2415  	s.base.SetUpSuite(c)
  2416  }
  2417  
  2418  func (s *uniterNetworkConfigSuite) TearDownSuite(c *gc.C) {
  2419  	s.base.TearDownSuite(c)
  2420  }
  2421  
  2422  func (s *uniterNetworkConfigSuite) SetUpTest(c *gc.C) {
  2423  	s.base.JujuConnSuite.SetUpTest(c)
  2424  
  2425  	// Add the spaces and subnets used by the test.
  2426  	subnetInfos := []state.SubnetInfo{{
  2427  		CIDR:      "8.8.0.0/16",
  2428  		SpaceName: "public",
  2429  	}, {
  2430  		CIDR:      "10.0.0.0/24",
  2431  		SpaceName: "internal",
  2432  	}}
  2433  	for _, info := range subnetInfos {
  2434  		_, err := s.base.State.AddSpace(info.SpaceName, "", nil, false)
  2435  		c.Assert(err, jc.ErrorIsNil)
  2436  		_, err = s.base.State.AddSubnet(info)
  2437  		c.Assert(err, jc.ErrorIsNil)
  2438  	}
  2439  
  2440  	s.base.machine0 = s.addProvisionedMachineWithDevicesAndAddresses(c, 10)
  2441  
  2442  	factory := jujuFactory.NewFactory(s.base.State)
  2443  	s.base.wpCharm = factory.MakeCharm(c, &jujuFactory.CharmParams{
  2444  		Name: "wordpress-extra-bindings",
  2445  		URL:  "cs:quantal/wordpress-extra-bindings-4",
  2446  	})
  2447  	var err error
  2448  	s.base.wordpress, err = s.base.State.AddApplication(state.AddApplicationArgs{
  2449  		Name:  "wordpress",
  2450  		Charm: s.base.wpCharm,
  2451  		EndpointBindings: map[string]string{
  2452  			"db":        "internal", // relation name
  2453  			"admin-api": "public",   // extra-binding name
  2454  		},
  2455  	})
  2456  	c.Assert(err, jc.ErrorIsNil)
  2457  	s.base.wordpressUnit = factory.MakeUnit(c, &jujuFactory.UnitParams{
  2458  		Application: s.base.wordpress,
  2459  		Machine:     s.base.machine0,
  2460  	})
  2461  
  2462  	s.base.machine1 = s.addProvisionedMachineWithDevicesAndAddresses(c, 20)
  2463  
  2464  	mysqlCharm := factory.MakeCharm(c, &jujuFactory.CharmParams{
  2465  		Name: "mysql",
  2466  	})
  2467  	s.base.mysql = factory.MakeApplication(c, &jujuFactory.ApplicationParams{
  2468  		Name:  "mysql",
  2469  		Charm: mysqlCharm,
  2470  	})
  2471  	s.base.wordpressUnit = factory.MakeUnit(c, &jujuFactory.UnitParams{
  2472  		Application: s.base.wordpress,
  2473  		Machine:     s.base.machine0,
  2474  	})
  2475  	s.base.mysqlUnit = factory.MakeUnit(c, &jujuFactory.UnitParams{
  2476  		Application: s.base.mysql,
  2477  		Machine:     s.base.machine1,
  2478  	})
  2479  
  2480  	// Create the resource registry separately to track invocations to
  2481  	// Register.
  2482  	s.base.resources = common.NewResources()
  2483  	s.base.AddCleanup(func(_ *gc.C) { s.base.resources.StopAll() })
  2484  
  2485  	s.setupUniterAPIForUnit(c, s.base.wordpressUnit)
  2486  }
  2487  
  2488  func (s *uniterNetworkConfigSuite) addProvisionedMachineWithDevicesAndAddresses(c *gc.C, addrSuffix int) *state.Machine {
  2489  	machine, err := s.base.State.AddMachine("quantal", state.JobHostUnits)
  2490  	c.Assert(err, jc.ErrorIsNil)
  2491  	devicesArgs, devicesAddrs := s.makeMachineDevicesAndAddressesArgs(addrSuffix)
  2492  	err = machine.SetInstanceInfo("i-am", "fake_nonce", nil, devicesArgs, devicesAddrs, nil, nil)
  2493  	c.Assert(err, jc.ErrorIsNil)
  2494  
  2495  	machineAddrs, err := machine.AllAddresses()
  2496  	c.Assert(err, jc.ErrorIsNil)
  2497  
  2498  	netAddrs := make([]network.Address, len(machineAddrs))
  2499  	for i, addr := range machineAddrs {
  2500  		netAddrs[i] = network.NewAddress(addr.Value())
  2501  	}
  2502  	err = machine.SetProviderAddresses(netAddrs...)
  2503  	c.Assert(err, jc.ErrorIsNil)
  2504  
  2505  	return machine
  2506  }
  2507  
  2508  func (s *uniterNetworkConfigSuite) makeMachineDevicesAndAddressesArgs(addrSuffix int) ([]state.LinkLayerDeviceArgs, []state.LinkLayerDeviceAddress) {
  2509  	return []state.LinkLayerDeviceArgs{{
  2510  			Name: "eth0",
  2511  			Type: state.EthernetDevice,
  2512  		}, {
  2513  			Name:       "eth0.100",
  2514  			Type:       state.VLAN_8021QDevice,
  2515  			ParentName: "eth0",
  2516  		}, {
  2517  			Name: "eth1",
  2518  			Type: state.EthernetDevice,
  2519  		}, {
  2520  			Name:       "eth1.100",
  2521  			Type:       state.VLAN_8021QDevice,
  2522  			ParentName: "eth1",
  2523  		}},
  2524  		[]state.LinkLayerDeviceAddress{{
  2525  			DeviceName:   "eth0",
  2526  			ConfigMethod: state.StaticAddress,
  2527  			CIDRAddress:  fmt.Sprintf("8.8.8.%d/16", addrSuffix),
  2528  		}, {
  2529  			DeviceName:   "eth0.100",
  2530  			ConfigMethod: state.StaticAddress,
  2531  			CIDRAddress:  fmt.Sprintf("10.0.0.%d/24", addrSuffix),
  2532  		}, {
  2533  			DeviceName:   "eth1",
  2534  			ConfigMethod: state.StaticAddress,
  2535  			CIDRAddress:  fmt.Sprintf("8.8.4.%d/16", addrSuffix),
  2536  		}, {
  2537  			DeviceName:   "eth1.100",
  2538  			ConfigMethod: state.StaticAddress,
  2539  			CIDRAddress:  fmt.Sprintf("10.0.0.%d/24", addrSuffix+1),
  2540  		}}
  2541  }
  2542  
  2543  func (s *uniterNetworkConfigSuite) TearDownTest(c *gc.C) {
  2544  	s.base.JujuConnSuite.TearDownTest(c)
  2545  }
  2546  
  2547  func (s *uniterNetworkConfigSuite) setupUniterAPIForUnit(c *gc.C, givenUnit *state.Unit) {
  2548  	// Create a FakeAuthorizer so we can check permissions, set up assuming the
  2549  	// given unit agent has logged in.
  2550  	s.base.authorizer = apiservertesting.FakeAuthorizer{
  2551  		Tag: givenUnit.Tag(),
  2552  	}
  2553  
  2554  	var err error
  2555  	s.base.uniter, err = uniter.NewUniterAPIV4(
  2556  		s.base.State,
  2557  		s.base.resources,
  2558  		s.base.authorizer,
  2559  	)
  2560  	c.Assert(err, jc.ErrorIsNil)
  2561  }
  2562  
  2563  func (s *uniterNetworkConfigSuite) TestNetworkConfigPermissions(c *gc.C) {
  2564  	s.addRelationAndAssertInScope(c)
  2565  
  2566  	args := params.UnitsNetworkConfig{Args: []params.UnitNetworkConfig{
  2567  		{BindingName: "foo", UnitTag: "unit-foo-0"},
  2568  		{BindingName: "db-client", UnitTag: "invalid"},
  2569  		{BindingName: "juju-info", UnitTag: "unit-mysql-0"},
  2570  		{BindingName: "", UnitTag: s.base.wordpressUnit.Tag().String()},
  2571  		{BindingName: "unknown", UnitTag: s.base.wordpressUnit.Tag().String()},
  2572  	}}
  2573  
  2574  	result, err := s.base.uniter.NetworkConfig(args)
  2575  	c.Assert(err, jc.ErrorIsNil)
  2576  	c.Assert(result, jc.DeepEquals, params.UnitNetworkConfigResults{
  2577  		Results: []params.UnitNetworkConfigResult{
  2578  			{Error: apiservertesting.ErrUnauthorized},
  2579  			{Error: apiservertesting.ServerError(`"invalid" is not a valid tag`)},
  2580  			{Error: apiservertesting.ErrUnauthorized},
  2581  			{Error: apiservertesting.ServerError(`binding name cannot be empty`)},
  2582  			{Error: apiservertesting.ServerError(`binding name "unknown" not defined by the unit's charm`)},
  2583  		},
  2584  	})
  2585  }
  2586  
  2587  func (s *uniterNetworkConfigSuite) addRelationAndAssertInScope(c *gc.C) {
  2588  	// Add a relation between wordpress and mysql and enter scope with
  2589  	// mysqlUnit.
  2590  	rel := s.base.addRelation(c, "wordpress", "mysql")
  2591  	wpRelUnit, err := rel.Unit(s.base.wordpressUnit)
  2592  	c.Assert(err, jc.ErrorIsNil)
  2593  	err = wpRelUnit.EnterScope(nil)
  2594  	c.Assert(err, jc.ErrorIsNil)
  2595  	s.base.assertInScope(c, wpRelUnit, true)
  2596  }
  2597  
  2598  func (s *uniterNetworkConfigSuite) TestNetworkConfigForExplicitlyBoundEndpoint(c *gc.C) {
  2599  	s.addRelationAndAssertInScope(c)
  2600  
  2601  	args := params.UnitsNetworkConfig{Args: []params.UnitNetworkConfig{
  2602  		{BindingName: "db", UnitTag: s.base.wordpressUnit.Tag().String()},
  2603  		{BindingName: "admin-api", UnitTag: s.base.wordpressUnit.Tag().String()},
  2604  	}}
  2605  
  2606  	// For the relation "wordpress:db mysql:server" we expect to see only
  2607  	// addresses bound to the "internal" space, where the "db" endpoint itself
  2608  	// is bound to.
  2609  	expectedConfigWithRelationName := []params.NetworkConfig{
  2610  		{Address: "10.0.0.10"},
  2611  		{Address: "10.0.0.11"},
  2612  	}
  2613  	// For the "admin-api" extra-binding we expect to see only addresses from
  2614  	// the "public" space.
  2615  	expectedConfigWithExtraBindingName := []params.NetworkConfig{
  2616  		{Address: "8.8.8.10"},
  2617  		{Address: "8.8.4.10"},
  2618  	}
  2619  
  2620  	result, err := s.base.uniter.NetworkConfig(args)
  2621  	c.Assert(err, jc.ErrorIsNil)
  2622  	c.Assert(result, jc.DeepEquals, params.UnitNetworkConfigResults{
  2623  		Results: []params.UnitNetworkConfigResult{
  2624  			{Config: expectedConfigWithRelationName},
  2625  			{Config: expectedConfigWithExtraBindingName},
  2626  		},
  2627  	})
  2628  }
  2629  
  2630  func (s *uniterNetworkConfigSuite) TestNetworkConfigForImplicitlyBoundEndpoint(c *gc.C) {
  2631  	// Since wordpressUnit has explicit binding for "db", switch the API to
  2632  	// mysqlUnit and check "mysql:server" uses the machine preferred private
  2633  	// address.
  2634  	s.setupUniterAPIForUnit(c, s.base.mysqlUnit)
  2635  	rel := s.base.addRelation(c, "mysql", "wordpress")
  2636  	mysqlRelUnit, err := rel.Unit(s.base.mysqlUnit)
  2637  	c.Assert(err, jc.ErrorIsNil)
  2638  	err = mysqlRelUnit.EnterScope(nil)
  2639  	c.Assert(err, jc.ErrorIsNil)
  2640  	s.base.assertInScope(c, mysqlRelUnit, true)
  2641  
  2642  	args := params.UnitsNetworkConfig{Args: []params.UnitNetworkConfig{
  2643  		{BindingName: "server", UnitTag: s.base.mysqlUnit.Tag().String()},
  2644  	}}
  2645  
  2646  	privateAddress, err := s.base.machine1.PrivateAddress()
  2647  	c.Assert(err, jc.ErrorIsNil)
  2648  
  2649  	expectedConfig := []params.NetworkConfig{{Address: privateAddress.Value}}
  2650  
  2651  	result, err := s.base.uniter.NetworkConfig(args)
  2652  	c.Assert(err, jc.ErrorIsNil)
  2653  	c.Assert(result, jc.DeepEquals, params.UnitNetworkConfigResults{
  2654  		Results: []params.UnitNetworkConfigResult{
  2655  			{Config: expectedConfig},
  2656  		},
  2657  	})
  2658  }