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