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