github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/agent/uniter/uniter_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package uniter_test
     5  
     6  import (
     7  	"fmt"
     8  	"time"
     9  
    10  	"github.com/juju/errors"
    11  	jc "github.com/juju/testing/checkers"
    12  	"github.com/juju/utils"
    13  	gc "gopkg.in/check.v1"
    14  	"gopkg.in/juju/charm.v6"
    15  	"gopkg.in/juju/environschema.v1"
    16  	"gopkg.in/juju/names.v2"
    17  	"gopkg.in/mgo.v2/bson"
    18  
    19  	"github.com/juju/juju/api"
    20  	apiuniter "github.com/juju/juju/api/uniter"
    21  	"github.com/juju/juju/apiserver/common"
    22  	commontesting "github.com/juju/juju/apiserver/common/testing"
    23  	"github.com/juju/juju/apiserver/facade/facadetest"
    24  	"github.com/juju/juju/apiserver/facades/agent/uniter"
    25  	"github.com/juju/juju/apiserver/facades/client/application"
    26  	"github.com/juju/juju/apiserver/params"
    27  	apiservertesting "github.com/juju/juju/apiserver/testing"
    28  	coreapplication "github.com/juju/juju/core/application"
    29  	corenetwork "github.com/juju/juju/core/network"
    30  	"github.com/juju/juju/core/status"
    31  	"github.com/juju/juju/environs"
    32  	"github.com/juju/juju/environs/config"
    33  	"github.com/juju/juju/environs/context"
    34  	"github.com/juju/juju/juju/testing"
    35  	"github.com/juju/juju/network"
    36  	"github.com/juju/juju/state"
    37  	"github.com/juju/juju/state/multiwatcher"
    38  	statetesting "github.com/juju/juju/state/testing"
    39  	coretesting "github.com/juju/juju/testing"
    40  	"github.com/juju/juju/testing/factory"
    41  )
    42  
    43  // uniterSuiteBase implements common testing suite for all API versions.
    44  // It is not intended to be used directly or registered as a suite,
    45  // but embedded.
    46  type uniterSuiteBase struct {
    47  	testing.JujuConnSuite
    48  
    49  	authorizer apiservertesting.FakeAuthorizer
    50  	resources  *common.Resources
    51  	uniter     *uniter.UniterAPI
    52  
    53  	machine0      *state.Machine
    54  	machine1      *state.Machine
    55  	wordpress     *state.Application
    56  	wpCharm       *state.Charm
    57  	mysql         *state.Application
    58  	wordpressUnit *state.Unit
    59  	mysqlUnit     *state.Unit
    60  
    61  	meteredApplication *state.Application
    62  	meteredCharm       *state.Charm
    63  	meteredUnit        *state.Unit
    64  }
    65  
    66  func (s *uniterSuiteBase) SetUpTest(c *gc.C) {
    67  	s.JujuConnSuite.SetUpTest(c)
    68  
    69  	// Create two machines, two services and add a unit to each service.
    70  	s.machine0 = s.Factory.MakeMachine(c, &factory.MachineParams{
    71  		Series: "quantal",
    72  		Jobs:   []state.MachineJob{state.JobHostUnits, state.JobManageModel},
    73  	})
    74  	s.machine1 = s.Factory.MakeMachine(c, &factory.MachineParams{
    75  		Series: "quantal",
    76  		Jobs:   []state.MachineJob{state.JobHostUnits},
    77  	})
    78  	s.wpCharm = s.Factory.MakeCharm(c, &factory.CharmParams{
    79  		Name: "wordpress",
    80  		URL:  "cs:quantal/wordpress-3",
    81  	})
    82  	s.wordpress = s.Factory.MakeApplication(c, &factory.ApplicationParams{
    83  		Name:  "wordpress",
    84  		Charm: s.wpCharm,
    85  	})
    86  	mysqlCharm := s.Factory.MakeCharm(c, &factory.CharmParams{
    87  		Name: "mysql",
    88  	})
    89  	s.mysql = s.Factory.MakeApplication(c, &factory.ApplicationParams{
    90  		Name:  "mysql",
    91  		Charm: mysqlCharm,
    92  	})
    93  	s.wordpressUnit = s.Factory.MakeUnit(c, &factory.UnitParams{
    94  		Application: s.wordpress,
    95  		Machine:     s.machine0,
    96  	})
    97  	s.mysqlUnit = s.Factory.MakeUnit(c, &factory.UnitParams{
    98  		Application: s.mysql,
    99  		Machine:     s.machine1,
   100  	})
   101  
   102  	s.meteredCharm = s.Factory.MakeCharm(c, &factory.CharmParams{
   103  		Name: "metered",
   104  		URL:  "cs:quantal/metered",
   105  	})
   106  	s.meteredApplication = s.Factory.MakeApplication(c, &factory.ApplicationParams{
   107  		Charm: s.meteredCharm,
   108  	})
   109  	s.meteredUnit = s.Factory.MakeUnit(c, &factory.UnitParams{
   110  		Application: s.meteredApplication,
   111  		SetCharmURL: true,
   112  	})
   113  
   114  	// Create a FakeAuthorizer so we can check permissions,
   115  	// set up assuming unit 0 has logged in.
   116  	s.authorizer = apiservertesting.FakeAuthorizer{
   117  		Tag: s.wordpressUnit.Tag(),
   118  	}
   119  
   120  	// Create the resource registry separately to track invocations to
   121  	// Register.
   122  	s.resources = common.NewResources()
   123  	s.AddCleanup(func(_ *gc.C) { s.resources.StopAll() })
   124  
   125  	uniterAPI, err := uniter.NewUniterAPI(facadetest.Context{
   126  		State_:             s.State,
   127  		Resources_:         s.resources,
   128  		Auth_:              s.authorizer,
   129  		LeadershipChecker_: s.State.LeadershipChecker(),
   130  	})
   131  	c.Assert(err, jc.ErrorIsNil)
   132  	s.uniter = uniterAPI
   133  }
   134  
   135  func (s *uniterSuiteBase) addRelation(c *gc.C, first, second string) *state.Relation {
   136  	eps, err := s.State.InferEndpoints(first, second)
   137  	c.Assert(err, jc.ErrorIsNil)
   138  	rel, err := s.State.AddRelation(eps...)
   139  	c.Assert(err, jc.ErrorIsNil)
   140  	return rel
   141  }
   142  
   143  func (s *uniterSuiteBase) assertInScope(c *gc.C, relUnit *state.RelationUnit, inScope bool) {
   144  	ok, err := relUnit.InScope()
   145  	c.Assert(err, jc.ErrorIsNil)
   146  	c.Assert(ok, gc.Equals, inScope)
   147  }
   148  
   149  func (s *uniterSuiteBase) setupCAASModel(c *gc.C) (*apiuniter.State, *state.CAASModel, *state.Application, *state.Unit) {
   150  	st := s.Factory.MakeCAASModel(c, nil)
   151  	m, err := st.Model()
   152  	c.Assert(err, jc.ErrorIsNil)
   153  	s.CleanupSuite.AddCleanup(func(*gc.C) { st.Close() })
   154  	cm, err := m.CAASModel()
   155  	c.Assert(err, jc.ErrorIsNil)
   156  
   157  	f := factory.NewFactory(st, s.StatePool)
   158  	ch := f.MakeCharm(c, &factory.CharmParams{Name: "gitlab", Series: "kubernetes"})
   159  	app := f.MakeApplication(c, &factory.ApplicationParams{Name: "gitlab", Charm: ch})
   160  	unit := f.MakeUnit(c, &factory.UnitParams{
   161  		Application: app,
   162  		SetCharmURL: true,
   163  	})
   164  
   165  	password, err := utils.RandomPassword()
   166  	c.Assert(err, jc.ErrorIsNil)
   167  	err = unit.SetPassword(password)
   168  	c.Assert(err, jc.ErrorIsNil)
   169  
   170  	apiInfo, err := environs.APIInfo(context.NewCloudCallContext(), s.ControllerConfig.ControllerUUID(), st.ModelUUID(), coretesting.CACert, s.ControllerConfig.APIPort(), s.Environ)
   171  	c.Assert(err, jc.ErrorIsNil)
   172  	apiInfo.Tag = unit.Tag()
   173  	apiInfo.Password = password
   174  	apiState, err := api.Open(apiInfo, api.DialOpts{})
   175  	c.Assert(err, jc.ErrorIsNil)
   176  	s.CleanupSuite.AddCleanup(func(*gc.C) { apiState.Close() })
   177  
   178  	s.authorizer = apiservertesting.FakeAuthorizer{
   179  		Tag: unit.Tag(),
   180  	}
   181  	u, err := apiState.Uniter()
   182  	c.Assert(err, jc.ErrorIsNil)
   183  	return u, cm, app, unit
   184  }
   185  
   186  type uniterSuite struct {
   187  	uniterSuiteBase
   188  }
   189  
   190  var _ = gc.Suite(&uniterSuite{})
   191  
   192  func (s *uniterSuite) TestUniterFailsWithNonUnitAgentUser(c *gc.C) {
   193  	anAuthorizer := s.authorizer
   194  	anAuthorizer.Tag = names.NewMachineTag("9")
   195  	_, err := uniter.NewUniterAPI(facadetest.Context{
   196  		State_:     s.State,
   197  		Resources_: s.resources,
   198  		Auth_:      anAuthorizer,
   199  	})
   200  	c.Assert(err, gc.NotNil)
   201  	c.Assert(err, gc.ErrorMatches, "permission denied")
   202  }
   203  
   204  func (s *uniterSuite) TestSetStatus(c *gc.C) {
   205  	now := time.Now()
   206  	sInfo := status.StatusInfo{
   207  		Status:  status.Executing,
   208  		Message: "blah",
   209  		Since:   &now,
   210  	}
   211  	err := s.wordpressUnit.SetAgentStatus(sInfo)
   212  	c.Assert(err, jc.ErrorIsNil)
   213  	sInfo = status.StatusInfo{
   214  		Status:  status.Executing,
   215  		Message: "foo",
   216  		Since:   &now,
   217  	}
   218  	err = s.mysqlUnit.SetAgentStatus(sInfo)
   219  	c.Assert(err, jc.ErrorIsNil)
   220  
   221  	args := params.SetStatus{
   222  		Entities: []params.EntityStatusArgs{
   223  			{Tag: "unit-mysql-0", Status: status.Error.String(), Info: "not really"},
   224  			{Tag: "unit-wordpress-0", Status: status.Rebooting.String(), Info: "foobar"},
   225  			{Tag: "unit-foo-42", Status: status.Active.String(), Info: "blah"},
   226  		}}
   227  	result, err := s.uniter.SetStatus(args)
   228  	c.Assert(err, jc.ErrorIsNil)
   229  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   230  		Results: []params.ErrorResult{
   231  			{apiservertesting.ErrUnauthorized},
   232  			{nil},
   233  			{apiservertesting.ErrUnauthorized},
   234  		},
   235  	})
   236  
   237  	// Verify mysqlUnit - no change.
   238  	statusInfo, err := s.mysqlUnit.AgentStatus()
   239  	c.Assert(err, jc.ErrorIsNil)
   240  	c.Assert(statusInfo.Status, gc.Equals, status.Executing)
   241  	c.Assert(statusInfo.Message, gc.Equals, "foo")
   242  	// ...wordpressUnit is fine though.
   243  	statusInfo, err = s.wordpressUnit.AgentStatus()
   244  	c.Assert(err, jc.ErrorIsNil)
   245  	c.Assert(statusInfo.Status, gc.Equals, status.Rebooting)
   246  	c.Assert(statusInfo.Message, gc.Equals, "foobar")
   247  }
   248  
   249  func (s *uniterSuite) TestSetAgentStatus(c *gc.C) {
   250  	now := time.Now()
   251  	sInfo := status.StatusInfo{
   252  		Status:  status.Executing,
   253  		Message: "blah",
   254  		Since:   &now,
   255  	}
   256  	err := s.wordpressUnit.SetAgentStatus(sInfo)
   257  	c.Assert(err, jc.ErrorIsNil)
   258  	sInfo = status.StatusInfo{
   259  		Status:  status.Executing,
   260  		Message: "foo",
   261  		Since:   &now,
   262  	}
   263  	err = s.mysqlUnit.SetAgentStatus(sInfo)
   264  	c.Assert(err, jc.ErrorIsNil)
   265  
   266  	args := params.SetStatus{
   267  		Entities: []params.EntityStatusArgs{
   268  			{Tag: "unit-mysql-0", Status: status.Error.String(), Info: "not really"},
   269  			{Tag: "unit-wordpress-0", Status: status.Executing.String(), Info: "foobar"},
   270  			{Tag: "unit-foo-42", Status: status.Rebooting.String(), Info: "blah"},
   271  		}}
   272  	result, err := s.uniter.SetAgentStatus(args)
   273  	c.Assert(err, jc.ErrorIsNil)
   274  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   275  		Results: []params.ErrorResult{
   276  			{apiservertesting.ErrUnauthorized},
   277  			{nil},
   278  			{apiservertesting.ErrUnauthorized},
   279  		},
   280  	})
   281  
   282  	// Verify mysqlUnit - no change.
   283  	statusInfo, err := s.mysqlUnit.AgentStatus()
   284  	c.Assert(err, jc.ErrorIsNil)
   285  	c.Assert(statusInfo.Status, gc.Equals, status.Executing)
   286  	c.Assert(statusInfo.Message, gc.Equals, "foo")
   287  	// ...wordpressUnit is fine though.
   288  	statusInfo, err = s.wordpressUnit.AgentStatus()
   289  	c.Assert(err, jc.ErrorIsNil)
   290  	c.Assert(statusInfo.Status, gc.Equals, status.Executing)
   291  	c.Assert(statusInfo.Message, gc.Equals, "foobar")
   292  }
   293  
   294  func (s *uniterSuite) TestSetUnitStatus(c *gc.C) {
   295  	now := time.Now()
   296  	sInfo := status.StatusInfo{
   297  		Status:  status.Active,
   298  		Message: "blah",
   299  		Since:   &now,
   300  	}
   301  	err := s.wordpressUnit.SetStatus(sInfo)
   302  	c.Assert(err, jc.ErrorIsNil)
   303  	sInfo = status.StatusInfo{
   304  		Status:  status.Terminated,
   305  		Message: "foo",
   306  		Since:   &now,
   307  	}
   308  	err = s.mysqlUnit.SetStatus(sInfo)
   309  	c.Assert(err, jc.ErrorIsNil)
   310  
   311  	args := params.SetStatus{
   312  		Entities: []params.EntityStatusArgs{
   313  			{Tag: "unit-mysql-0", Status: status.Error.String(), Info: "not really"},
   314  			{Tag: "unit-wordpress-0", Status: status.Terminated.String(), Info: "foobar"},
   315  			{Tag: "unit-foo-42", Status: status.Active.String(), Info: "blah"},
   316  		}}
   317  	result, err := s.uniter.SetUnitStatus(args)
   318  	c.Assert(err, jc.ErrorIsNil)
   319  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   320  		Results: []params.ErrorResult{
   321  			{apiservertesting.ErrUnauthorized},
   322  			{nil},
   323  			{apiservertesting.ErrUnauthorized},
   324  		},
   325  	})
   326  
   327  	// Verify mysqlUnit - no change.
   328  	statusInfo, err := s.mysqlUnit.Status()
   329  	c.Assert(err, jc.ErrorIsNil)
   330  	c.Assert(statusInfo.Status, gc.Equals, status.Terminated)
   331  	c.Assert(statusInfo.Message, gc.Equals, "foo")
   332  	// ...wordpressUnit is fine though.
   333  	statusInfo, err = s.wordpressUnit.Status()
   334  	c.Assert(err, jc.ErrorIsNil)
   335  	c.Assert(statusInfo.Status, gc.Equals, status.Terminated)
   336  	c.Assert(statusInfo.Message, gc.Equals, "foobar")
   337  }
   338  
   339  func (s *uniterSuite) TestLife(c *gc.C) {
   340  	// Add a relation wordpress-mysql.
   341  	rel := s.addRelation(c, "wordpress", "mysql")
   342  	relUnit, err := rel.Unit(s.wordpressUnit)
   343  	c.Assert(err, jc.ErrorIsNil)
   344  	err = relUnit.EnterScope(nil)
   345  	c.Assert(err, jc.ErrorIsNil)
   346  	c.Assert(rel.Life(), gc.Equals, state.Alive)
   347  	relStatus, err := rel.Status()
   348  	c.Assert(err, jc.ErrorIsNil)
   349  	c.Assert(relStatus.Status, gc.Equals, status.Joining)
   350  
   351  	// Make the wordpressUnit dead.
   352  	err = s.wordpressUnit.EnsureDead()
   353  	c.Assert(err, jc.ErrorIsNil)
   354  	err = s.wordpressUnit.Refresh()
   355  	c.Assert(err, jc.ErrorIsNil)
   356  	c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Dead)
   357  
   358  	// Add another unit, so the service will stay dying when we
   359  	// destroy it later.
   360  	extraUnit, err := s.wordpress.AddUnit(state.AddUnitParams{})
   361  	c.Assert(err, jc.ErrorIsNil)
   362  	c.Assert(extraUnit, gc.NotNil)
   363  
   364  	// Make the wordpress service dying.
   365  	err = s.wordpress.Destroy()
   366  	c.Assert(err, jc.ErrorIsNil)
   367  	err = s.wordpress.Refresh()
   368  	c.Assert(err, jc.ErrorIsNil)
   369  	c.Assert(s.wordpress.Life(), gc.Equals, state.Dying)
   370  
   371  	args := params.Entities{Entities: []params.Entity{
   372  		{Tag: "unit-mysql-0"},
   373  		{Tag: "unit-wordpress-0"},
   374  		{Tag: "unit-foo-42"},
   375  		{Tag: "application-mysql"},
   376  		{Tag: "application-wordpress"},
   377  		{Tag: "machine-0"},
   378  		{Tag: "machine-1"},
   379  		{Tag: "machine-42"},
   380  		{Tag: "application-foo"},
   381  		// TODO(dfc) these aren't valid tags any more
   382  		// but I hope to restore this test when params.Entity takes
   383  		// tags, not strings, which is coming soon.
   384  		// {Tag: "just-foo"},
   385  		{Tag: rel.Tag().String()},
   386  		{Tag: "relation-svc1.rel1#svc2.rel2"},
   387  		// {Tag: "relation-blah"},
   388  	}}
   389  	result, err := s.uniter.Life(args)
   390  	c.Assert(err, jc.ErrorIsNil)
   391  	c.Assert(result, gc.DeepEquals, params.LifeResults{
   392  		Results: []params.LifeResult{
   393  			{Error: apiservertesting.ErrUnauthorized},
   394  			{Life: "dead"},
   395  			{Error: apiservertesting.ErrUnauthorized},
   396  			{Error: apiservertesting.ErrUnauthorized},
   397  			{Life: "dying"},
   398  			{Error: apiservertesting.ErrUnauthorized},
   399  			{Error: apiservertesting.ErrUnauthorized},
   400  			{Error: apiservertesting.ErrUnauthorized},
   401  			{Error: apiservertesting.ErrUnauthorized},
   402  			// TODO(dfc) see above
   403  			// {Error: apiservertesting.ErrUnauthorized},
   404  			{Error: apiservertesting.ErrUnauthorized},
   405  			{Error: apiservertesting.ErrUnauthorized},
   406  			// {Error: apiservertesting.ErrUnauthorized},
   407  		},
   408  	})
   409  }
   410  
   411  func (s *uniterSuite) TestEnsureDead(c *gc.C) {
   412  	c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Alive)
   413  	c.Assert(s.mysqlUnit.Life(), gc.Equals, state.Alive)
   414  
   415  	args := params.Entities{Entities: []params.Entity{
   416  		{Tag: "unit-mysql-0"},
   417  		{Tag: "unit-wordpress-0"},
   418  		{Tag: "unit-foo-42"},
   419  	}}
   420  	result, err := s.uniter.EnsureDead(args)
   421  	c.Assert(err, jc.ErrorIsNil)
   422  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   423  		Results: []params.ErrorResult{
   424  			{apiservertesting.ErrUnauthorized},
   425  			{nil},
   426  			{apiservertesting.ErrUnauthorized},
   427  		},
   428  	})
   429  
   430  	err = s.wordpressUnit.Refresh()
   431  	c.Assert(err, jc.ErrorIsNil)
   432  	c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Dead)
   433  	err = s.mysqlUnit.Refresh()
   434  	c.Assert(err, jc.ErrorIsNil)
   435  	c.Assert(s.mysqlUnit.Life(), gc.Equals, state.Alive)
   436  
   437  	// Try it again on a Dead unit; should work.
   438  	args = params.Entities{
   439  		Entities: []params.Entity{{Tag: "unit-wordpress-0"}},
   440  	}
   441  	result, err = s.uniter.EnsureDead(args)
   442  	c.Assert(err, jc.ErrorIsNil)
   443  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   444  		Results: []params.ErrorResult{{nil}},
   445  	})
   446  
   447  	// Verify Life is unchanged.
   448  	err = s.wordpressUnit.Refresh()
   449  	c.Assert(err, jc.ErrorIsNil)
   450  	c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Dead)
   451  }
   452  
   453  func (s *uniterSuite) TestWatch(c *gc.C) {
   454  	c.Assert(s.resources.Count(), gc.Equals, 0)
   455  
   456  	args := params.Entities{Entities: []params.Entity{
   457  		{Tag: "unit-mysql-0"},
   458  		{Tag: "unit-wordpress-0"},
   459  		{Tag: "unit-foo-42"},
   460  		{Tag: "application-mysql"},
   461  		{Tag: "application-wordpress"},
   462  		{Tag: "application-foo"},
   463  		// TODO(dfc) these aren't valid tags any more
   464  		// but I hope to restore this test when params.Entity takes
   465  		// tags, not strings, which is coming soon.
   466  		// {Tag: "just-foo"},
   467  	}}
   468  	result, err := s.uniter.Watch(args)
   469  	c.Assert(err, jc.ErrorIsNil)
   470  	c.Assert(result, gc.DeepEquals, params.NotifyWatchResults{
   471  		Results: []params.NotifyWatchResult{
   472  			{Error: apiservertesting.ErrUnauthorized},
   473  			{NotifyWatcherId: "1"},
   474  			{Error: apiservertesting.ErrUnauthorized},
   475  			{Error: apiservertesting.ErrUnauthorized},
   476  			{NotifyWatcherId: "2"},
   477  			{Error: apiservertesting.ErrUnauthorized},
   478  			// see above
   479  			// {Error: apiservertesting.ErrUnauthorized},
   480  		},
   481  	})
   482  
   483  	// Verify the resource was registered and stop when done
   484  	c.Assert(s.resources.Count(), gc.Equals, 2)
   485  	resource1 := s.resources.Get("1")
   486  	defer statetesting.AssertStop(c, resource1)
   487  	resource2 := s.resources.Get("2")
   488  	defer statetesting.AssertStop(c, resource2)
   489  
   490  	// Check that the Watch has consumed the initial event ("returned" in
   491  	// the Watch call)
   492  	wc := statetesting.NewNotifyWatcherC(c, s.State, resource1.(state.NotifyWatcher))
   493  	wc.AssertNoChange()
   494  	wc = statetesting.NewNotifyWatcherC(c, s.State, resource2.(state.NotifyWatcher))
   495  	wc.AssertNoChange()
   496  }
   497  
   498  func (s *uniterSuite) TestPublicAddress(c *gc.C) {
   499  	// Try first without setting an address.
   500  	args := params.Entities{Entities: []params.Entity{
   501  		{Tag: "unit-mysql-0"},
   502  		{Tag: "unit-wordpress-0"},
   503  		{Tag: "unit-foo-42"},
   504  	}}
   505  	expectErr := &params.Error{
   506  		Code:    params.CodeNoAddressSet,
   507  		Message: `"unit-wordpress-0" has no public address set`,
   508  	}
   509  	result, err := s.uniter.PublicAddress(args)
   510  	c.Assert(err, jc.ErrorIsNil)
   511  	c.Assert(result, gc.DeepEquals, params.StringResults{
   512  		Results: []params.StringResult{
   513  			{Error: apiservertesting.ErrUnauthorized},
   514  			{Error: expectErr},
   515  			{Error: apiservertesting.ErrUnauthorized},
   516  		},
   517  	})
   518  
   519  	// Now set it an try again.
   520  	err = s.machine0.SetProviderAddresses(
   521  		network.NewScopedAddress("1.2.3.4", network.ScopePublic),
   522  	)
   523  	c.Assert(err, jc.ErrorIsNil)
   524  	address, err := s.wordpressUnit.PublicAddress()
   525  	c.Assert(address.Value, gc.Equals, "1.2.3.4")
   526  	c.Assert(err, jc.ErrorIsNil)
   527  
   528  	result, err = s.uniter.PublicAddress(args)
   529  	c.Assert(err, jc.ErrorIsNil)
   530  	c.Assert(result, gc.DeepEquals, params.StringResults{
   531  		Results: []params.StringResult{
   532  			{Error: apiservertesting.ErrUnauthorized},
   533  			{Result: "1.2.3.4"},
   534  			{Error: apiservertesting.ErrUnauthorized},
   535  		},
   536  	})
   537  }
   538  
   539  func (s *uniterSuite) TestPrivateAddress(c *gc.C) {
   540  	args := params.Entities{Entities: []params.Entity{
   541  		{Tag: "unit-mysql-0"},
   542  		{Tag: "unit-wordpress-0"},
   543  		{Tag: "unit-foo-42"},
   544  	}}
   545  	expectErr := &params.Error{
   546  		Code:    params.CodeNoAddressSet,
   547  		Message: `"unit-wordpress-0" has no private address set`,
   548  	}
   549  	result, err := s.uniter.PrivateAddress(args)
   550  	c.Assert(err, jc.ErrorIsNil)
   551  	c.Assert(result, gc.DeepEquals, params.StringResults{
   552  		Results: []params.StringResult{
   553  			{Error: apiservertesting.ErrUnauthorized},
   554  			{Error: expectErr},
   555  			{Error: apiservertesting.ErrUnauthorized},
   556  		},
   557  	})
   558  
   559  	// Now set it and try again.
   560  	err = s.machine0.SetProviderAddresses(
   561  		network.NewScopedAddress("1.2.3.4", network.ScopeCloudLocal),
   562  	)
   563  	c.Assert(err, jc.ErrorIsNil)
   564  	address, err := s.wordpressUnit.PrivateAddress()
   565  	c.Assert(address.Value, gc.Equals, "1.2.3.4")
   566  	c.Assert(err, jc.ErrorIsNil)
   567  
   568  	result, err = s.uniter.PrivateAddress(args)
   569  	c.Assert(err, jc.ErrorIsNil)
   570  	c.Assert(result, gc.DeepEquals, params.StringResults{
   571  		Results: []params.StringResult{
   572  			{Error: apiservertesting.ErrUnauthorized},
   573  			{Result: "1.2.3.4"},
   574  			{Error: apiservertesting.ErrUnauthorized},
   575  		},
   576  	})
   577  }
   578  
   579  // TestNetworkInfoSpaceless is in uniterSuite and not uniterNetworkInfoSuite since we don't want
   580  // all the spaces set up.
   581  func (s *uniterSuite) TestNetworkInfoSpaceless(c *gc.C) {
   582  	err := s.machine0.SetProviderAddresses(
   583  		network.NewScopedAddress("1.2.3.4", network.ScopeCloudLocal),
   584  	)
   585  	err = s.Model.UpdateModelConfig(map[string]interface{}{config.EgressSubnets: "10.0.0.0/8"}, nil)
   586  	c.Assert(err, jc.ErrorIsNil)
   587  
   588  	args := params.NetworkInfoParams{
   589  		Unit:     s.wordpressUnit.Tag().String(),
   590  		Bindings: []string{"db"},
   591  	}
   592  
   593  	privateAddress, err := s.machine0.PrivateAddress()
   594  	c.Assert(err, jc.ErrorIsNil)
   595  
   596  	expectedInfo := params.NetworkInfoResult{
   597  		Info: []params.NetworkInfo{
   598  			{
   599  				Addresses: []params.InterfaceAddress{
   600  					{Address: privateAddress.Value},
   601  				},
   602  			},
   603  		},
   604  		EgressSubnets:    []string{"10.0.0.0/8"},
   605  		IngressAddresses: []string{privateAddress.Value},
   606  	}
   607  
   608  	result, err := s.uniter.NetworkInfo(args)
   609  	c.Assert(err, jc.ErrorIsNil)
   610  	c.Check(result, jc.DeepEquals, params.NetworkInfoResults{
   611  		Results: map[string]params.NetworkInfoResult{
   612  			"db": expectedInfo,
   613  		},
   614  	})
   615  }
   616  
   617  func (s *uniterSuite) TestAvailabilityZone(c *gc.C) {
   618  	s.PatchValue(uniter.GetZone, func(st *state.State, tag names.Tag) (string, error) {
   619  		return "a_zone", nil
   620  	})
   621  
   622  	args := params.Entities{Entities: []params.Entity{
   623  		{Tag: "unit-wordpress-0"},
   624  	}}
   625  	result, err := s.uniter.AvailabilityZone(args)
   626  	c.Assert(err, jc.ErrorIsNil)
   627  
   628  	c.Check(result, gc.DeepEquals, params.StringResults{
   629  		Results: []params.StringResult{
   630  			{Result: "a_zone"},
   631  		},
   632  	})
   633  }
   634  
   635  func (s *uniterSuite) TestResolvedAPIV6(c *gc.C) {
   636  	err := s.wordpressUnit.SetResolved(state.ResolvedRetryHooks)
   637  	c.Assert(err, jc.ErrorIsNil)
   638  	mode := s.wordpressUnit.Resolved()
   639  	c.Assert(mode, gc.Equals, state.ResolvedRetryHooks)
   640  
   641  	args := params.Entities{Entities: []params.Entity{
   642  		{Tag: "unit-mysql-0"},
   643  		{Tag: "unit-wordpress-0"},
   644  		{Tag: "unit-foo-42"},
   645  	}}
   646  	result, err := s.uniter.Resolved(args)
   647  	c.Assert(err, jc.ErrorIsNil)
   648  	c.Assert(result, gc.DeepEquals, params.ResolvedModeResults{
   649  		Results: []params.ResolvedModeResult{
   650  			{Error: apiservertesting.ErrUnauthorized},
   651  			{Mode: params.ResolvedMode(mode)},
   652  			{Error: apiservertesting.ErrUnauthorized},
   653  		},
   654  	})
   655  }
   656  
   657  func (s *uniterSuite) TestClearResolved(c *gc.C) {
   658  	err := s.wordpressUnit.SetResolved(state.ResolvedRetryHooks)
   659  	c.Assert(err, jc.ErrorIsNil)
   660  	mode := s.wordpressUnit.Resolved()
   661  	c.Assert(mode, gc.Equals, state.ResolvedRetryHooks)
   662  
   663  	args := params.Entities{Entities: []params.Entity{
   664  		{Tag: "unit-mysql-0"},
   665  		{Tag: "unit-wordpress-0"},
   666  		{Tag: "unit-foo-42"},
   667  	}}
   668  	result, err := s.uniter.ClearResolved(args)
   669  	c.Assert(err, jc.ErrorIsNil)
   670  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   671  		Results: []params.ErrorResult{
   672  			{apiservertesting.ErrUnauthorized},
   673  			{nil},
   674  			{apiservertesting.ErrUnauthorized},
   675  		},
   676  	})
   677  
   678  	// Verify wordpressUnit's resolved mode has changed.
   679  	err = s.wordpressUnit.Refresh()
   680  	c.Assert(err, jc.ErrorIsNil)
   681  	mode = s.wordpressUnit.Resolved()
   682  	c.Assert(mode, gc.Equals, state.ResolvedNone)
   683  }
   684  
   685  func (s *uniterSuite) TestGetPrincipal(c *gc.C) {
   686  	// Add a subordinate to wordpressUnit.
   687  	_, _, subordinate := s.addRelatedApplication(c, "wordpress", "logging", s.wordpressUnit)
   688  
   689  	principal, ok := subordinate.PrincipalName()
   690  	c.Assert(principal, gc.Equals, s.wordpressUnit.Name())
   691  	c.Assert(ok, jc.IsTrue)
   692  
   693  	// First try it as wordpressUnit's agent.
   694  	args := params.Entities{Entities: []params.Entity{
   695  		{Tag: "unit-mysql-0"},
   696  		{Tag: "unit-wordpress-0"},
   697  		{Tag: subordinate.Tag().String()},
   698  		{Tag: "unit-foo-42"},
   699  	}}
   700  	result, err := s.uniter.GetPrincipal(args)
   701  	c.Assert(err, jc.ErrorIsNil)
   702  	c.Assert(result, gc.DeepEquals, params.StringBoolResults{
   703  		Results: []params.StringBoolResult{
   704  			{Error: apiservertesting.ErrUnauthorized},
   705  			{Result: "", Ok: false, Error: nil},
   706  			{Error: apiservertesting.ErrUnauthorized},
   707  			{Error: apiservertesting.ErrUnauthorized},
   708  		},
   709  	})
   710  
   711  	// Now try as subordinate's agent.
   712  	subAuthorizer := s.authorizer
   713  	subAuthorizer.Tag = subordinate.Tag()
   714  	subUniter, err := uniter.NewUniterAPI(facadetest.Context{
   715  		State_:             s.State,
   716  		Resources_:         s.resources,
   717  		Auth_:              subAuthorizer,
   718  		LeadershipChecker_: s.State.LeadershipChecker(),
   719  	})
   720  	c.Assert(err, jc.ErrorIsNil)
   721  
   722  	result, err = subUniter.GetPrincipal(args)
   723  	c.Assert(err, jc.ErrorIsNil)
   724  	c.Assert(result, gc.DeepEquals, params.StringBoolResults{
   725  		Results: []params.StringBoolResult{
   726  			{Error: apiservertesting.ErrUnauthorized},
   727  			{Error: apiservertesting.ErrUnauthorized},
   728  			{Result: "unit-wordpress-0", Ok: true, Error: nil},
   729  			{Error: apiservertesting.ErrUnauthorized},
   730  		},
   731  	})
   732  }
   733  
   734  func (s *uniterSuite) TestHasSubordinates(c *gc.C) {
   735  	// Try first without any subordinates for wordpressUnit.
   736  	args := params.Entities{Entities: []params.Entity{
   737  		{Tag: "unit-mysql-0"},
   738  		{Tag: "unit-wordpress-0"},
   739  		{Tag: "unit-logging-0"},
   740  		{Tag: "unit-foo-42"},
   741  	}}
   742  	result, err := s.uniter.HasSubordinates(args)
   743  	c.Assert(err, jc.ErrorIsNil)
   744  	c.Assert(result, gc.DeepEquals, params.BoolResults{
   745  		Results: []params.BoolResult{
   746  			{Error: apiservertesting.ErrUnauthorized},
   747  			{Result: false},
   748  			{Error: apiservertesting.ErrUnauthorized},
   749  			{Error: apiservertesting.ErrUnauthorized},
   750  		},
   751  	})
   752  
   753  	// Add two subordinates to wordpressUnit and try again.
   754  	s.addRelatedApplication(c, "wordpress", "logging", s.wordpressUnit)
   755  	s.addRelatedApplication(c, "wordpress", "monitoring", s.wordpressUnit)
   756  
   757  	result, err = s.uniter.HasSubordinates(args)
   758  	c.Assert(err, jc.ErrorIsNil)
   759  	c.Assert(result, gc.DeepEquals, params.BoolResults{
   760  		Results: []params.BoolResult{
   761  			{Error: apiservertesting.ErrUnauthorized},
   762  			{Result: true},
   763  			{Error: apiservertesting.ErrUnauthorized},
   764  			{Error: apiservertesting.ErrUnauthorized},
   765  		},
   766  	})
   767  }
   768  
   769  func (s *uniterSuite) TestDestroy(c *gc.C) {
   770  	c.Assert(s.wordpressUnit.Life(), gc.Equals, state.Alive)
   771  
   772  	args := params.Entities{Entities: []params.Entity{
   773  		{Tag: "unit-mysql-0"},
   774  		{Tag: "unit-wordpress-0"},
   775  		{Tag: "unit-foo-42"},
   776  	}}
   777  	result, err := s.uniter.Destroy(args)
   778  	c.Assert(err, jc.ErrorIsNil)
   779  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   780  		Results: []params.ErrorResult{
   781  			{apiservertesting.ErrUnauthorized},
   782  			{nil},
   783  			{apiservertesting.ErrUnauthorized},
   784  		},
   785  	})
   786  
   787  	// Verify wordpressUnit is destroyed and removed.
   788  	err = s.wordpressUnit.Refresh()
   789  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   790  }
   791  
   792  func (s *uniterSuite) TestDestroyAllSubordinates(c *gc.C) {
   793  	// Add two subordinates to wordpressUnit.
   794  	_, _, loggingSub := s.addRelatedApplication(c, "wordpress", "logging", s.wordpressUnit)
   795  	_, _, monitoringSub := s.addRelatedApplication(c, "wordpress", "monitoring", s.wordpressUnit)
   796  	c.Assert(loggingSub.Life(), gc.Equals, state.Alive)
   797  	c.Assert(monitoringSub.Life(), gc.Equals, state.Alive)
   798  
   799  	err := s.wordpressUnit.Refresh()
   800  	c.Assert(err, jc.ErrorIsNil)
   801  	subordinates := s.wordpressUnit.SubordinateNames()
   802  	c.Assert(subordinates, gc.DeepEquals, []string{"logging/0", "monitoring/0"})
   803  
   804  	args := params.Entities{Entities: []params.Entity{
   805  		{Tag: "unit-mysql-0"},
   806  		{Tag: "unit-wordpress-0"},
   807  		{Tag: "unit-foo-42"},
   808  	}}
   809  	result, err := s.uniter.DestroyAllSubordinates(args)
   810  	c.Assert(err, jc.ErrorIsNil)
   811  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   812  		Results: []params.ErrorResult{
   813  			{apiservertesting.ErrUnauthorized},
   814  			{nil},
   815  			{apiservertesting.ErrUnauthorized},
   816  		},
   817  	})
   818  
   819  	// Verify wordpressUnit's subordinates were destroyed.
   820  	err = loggingSub.Refresh()
   821  	c.Assert(err, jc.ErrorIsNil)
   822  	c.Assert(loggingSub.Life(), gc.Equals, state.Dying)
   823  	err = monitoringSub.Refresh()
   824  	c.Assert(err, jc.ErrorIsNil)
   825  	c.Assert(monitoringSub.Life(), gc.Equals, state.Dying)
   826  }
   827  
   828  func (s *uniterSuite) TestCharmURL(c *gc.C) {
   829  	// Set wordpressUnit's charm URL first.
   830  	err := s.wordpressUnit.SetCharmURL(s.wpCharm.URL())
   831  	c.Assert(err, jc.ErrorIsNil)
   832  	curl, ok := s.wordpressUnit.CharmURL()
   833  	c.Assert(curl, gc.DeepEquals, s.wpCharm.URL())
   834  	c.Assert(ok, jc.IsTrue)
   835  
   836  	// Make sure wordpress application's charm is what we expect.
   837  	curl, force := s.wordpress.CharmURL()
   838  	c.Assert(curl, gc.DeepEquals, s.wpCharm.URL())
   839  	c.Assert(force, jc.IsFalse)
   840  
   841  	args := params.Entities{Entities: []params.Entity{
   842  		{Tag: "unit-mysql-0"},
   843  		{Tag: "unit-wordpress-0"},
   844  		{Tag: "unit-foo-42"},
   845  		{Tag: "application-mysql"},
   846  		{Tag: "application-wordpress"},
   847  		{Tag: "application-foo"},
   848  		// TODO(dfc) these aren't valid tags any more
   849  		// but I hope to restore this test when params.Entity takes
   850  		// tags, not strings, which is coming soon.
   851  		// {Tag: "just-foo"},
   852  	}}
   853  	result, err := s.uniter.CharmURL(args)
   854  	c.Assert(err, jc.ErrorIsNil)
   855  	c.Assert(result, gc.DeepEquals, params.StringBoolResults{
   856  		Results: []params.StringBoolResult{
   857  			{Error: apiservertesting.ErrUnauthorized},
   858  			{Result: s.wpCharm.String(), Ok: ok},
   859  			{Error: apiservertesting.ErrUnauthorized},
   860  			{Error: apiservertesting.ErrUnauthorized},
   861  			{Result: s.wpCharm.String(), Ok: force},
   862  			{Error: apiservertesting.ErrUnauthorized},
   863  			// see above
   864  			// {Error: apiservertesting.ErrUnauthorized},
   865  		},
   866  	})
   867  }
   868  
   869  func (s *uniterSuite) TestSetCharmURL(c *gc.C) {
   870  	_, ok := s.wordpressUnit.CharmURL()
   871  	c.Assert(ok, jc.IsFalse)
   872  
   873  	args := params.EntitiesCharmURL{Entities: []params.EntityCharmURL{
   874  		{Tag: "unit-mysql-0", CharmURL: "cs:quantal/application-42"},
   875  		{Tag: "unit-wordpress-0", CharmURL: s.wpCharm.String()},
   876  		{Tag: "unit-foo-42", CharmURL: "cs:quantal/foo-321"},
   877  	}}
   878  	result, err := s.uniter.SetCharmURL(args)
   879  	c.Assert(err, jc.ErrorIsNil)
   880  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   881  		Results: []params.ErrorResult{
   882  			{apiservertesting.ErrUnauthorized},
   883  			{nil},
   884  			{apiservertesting.ErrUnauthorized},
   885  		},
   886  	})
   887  
   888  	// Verify the charm URL was set.
   889  	err = s.wordpressUnit.Refresh()
   890  	c.Assert(err, jc.ErrorIsNil)
   891  	charmURL, needsUpgrade := s.wordpressUnit.CharmURL()
   892  	c.Assert(charmURL, gc.NotNil)
   893  	c.Assert(charmURL.String(), gc.Equals, s.wpCharm.String())
   894  	c.Assert(needsUpgrade, jc.IsTrue)
   895  }
   896  
   897  func (s *uniterSuite) TestWorkloadVersion(c *gc.C) {
   898  	// Set wordpressUnit's workload version first.
   899  	err := s.wordpressUnit.SetWorkloadVersion("capulet")
   900  	c.Assert(err, jc.ErrorIsNil)
   901  	version, err := s.wordpressUnit.WorkloadVersion()
   902  	c.Assert(version, gc.Equals, "capulet")
   903  	c.Assert(err, jc.ErrorIsNil)
   904  
   905  	args := params.Entities{Entities: []params.Entity{
   906  		{Tag: "unit-mysql-0"},
   907  		{Tag: "unit-wordpress-0"},
   908  		{Tag: "unit-foo-42"},
   909  		{Tag: "application-wordpress"},
   910  		{Tag: "just-foo"},
   911  	}}
   912  
   913  	result, err := s.uniter.WorkloadVersion(args)
   914  	c.Assert(err, jc.ErrorIsNil)
   915  	c.Assert(result, gc.DeepEquals, params.StringResults{
   916  		Results: []params.StringResult{
   917  			{Error: apiservertesting.ErrUnauthorized},
   918  			{Result: "capulet"},
   919  			{Error: apiservertesting.ErrUnauthorized},
   920  			{Error: common.ServerError(errors.New(`"application-wordpress" is not a valid unit tag`))},
   921  			{Error: common.ServerError(errors.New(`"just-foo" is not a valid tag`))},
   922  		},
   923  	})
   924  }
   925  
   926  func (s *uniterSuite) TestSetWorkloadVersion(c *gc.C) {
   927  	currentVersion, err := s.wordpressUnit.WorkloadVersion()
   928  	c.Assert(err, jc.ErrorIsNil)
   929  	c.Assert(currentVersion, gc.Equals, "")
   930  
   931  	args := params.EntityWorkloadVersions{Entities: []params.EntityWorkloadVersion{
   932  		{Tag: "unit-mysql-0", WorkloadVersion: "allura"},
   933  		{Tag: "unit-wordpress-0", WorkloadVersion: "shiro"},
   934  		{Tag: "unit-foo-42", WorkloadVersion: "pidge"},
   935  	}}
   936  	result, err := s.uniter.SetWorkloadVersion(args)
   937  	c.Assert(err, jc.ErrorIsNil)
   938  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   939  		Results: []params.ErrorResult{
   940  			{apiservertesting.ErrUnauthorized},
   941  			{nil},
   942  			{apiservertesting.ErrUnauthorized},
   943  		},
   944  	})
   945  
   946  	// Verify the workload version was set.
   947  	err = s.wordpressUnit.Refresh()
   948  	c.Assert(err, jc.ErrorIsNil)
   949  	newVersion, err := s.wordpressUnit.WorkloadVersion()
   950  	c.Assert(err, jc.ErrorIsNil)
   951  	c.Assert(newVersion, gc.Equals, "shiro")
   952  }
   953  
   954  func (s *uniterSuite) TestCharmModifiedVersion(c *gc.C) {
   955  	args := params.Entities{Entities: []params.Entity{
   956  		{Tag: "application-mysql"},
   957  		{Tag: "application-wordpress"},
   958  		{Tag: "unit-wordpress-0"},
   959  		{Tag: "application-foo"},
   960  	}}
   961  	result, err := s.uniter.CharmModifiedVersion(args)
   962  	c.Assert(err, jc.ErrorIsNil)
   963  	c.Assert(result, gc.DeepEquals, params.IntResults{
   964  		Results: []params.IntResult{
   965  			{Error: apiservertesting.ErrUnauthorized},
   966  			{Result: s.wordpress.CharmModifiedVersion()},
   967  			{Result: s.wordpress.CharmModifiedVersion()},
   968  			{Error: apiservertesting.ErrUnauthorized},
   969  		},
   970  	})
   971  }
   972  
   973  func (s *uniterSuite) TestOpenPorts(c *gc.C) {
   974  	openedPorts, err := s.wordpressUnit.OpenedPorts()
   975  	c.Assert(err, jc.ErrorIsNil)
   976  	c.Assert(openedPorts, gc.HasLen, 0)
   977  
   978  	args := params.EntitiesPortRanges{Entities: []params.EntityPortRange{
   979  		{Tag: "unit-mysql-0", Protocol: "tcp", FromPort: 1234, ToPort: 1400},
   980  		{Tag: "unit-wordpress-0", Protocol: "udp", FromPort: 4321, ToPort: 5000},
   981  		{Tag: "unit-foo-42", Protocol: "tcp", FromPort: 42, ToPort: 42},
   982  	}}
   983  	result, err := s.uniter.OpenPorts(args)
   984  	c.Assert(err, jc.ErrorIsNil)
   985  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   986  		Results: []params.ErrorResult{
   987  			{apiservertesting.ErrUnauthorized},
   988  			{nil},
   989  			{apiservertesting.ErrUnauthorized},
   990  		},
   991  	})
   992  
   993  	// Verify the wordpressUnit's port is opened.
   994  	openedPorts, err = s.wordpressUnit.OpenedPorts()
   995  	c.Assert(err, jc.ErrorIsNil)
   996  	c.Assert(openedPorts, gc.DeepEquals, []corenetwork.PortRange{
   997  		{Protocol: "udp", FromPort: 4321, ToPort: 5000},
   998  	})
   999  }
  1000  
  1001  func (s *uniterSuite) TestClosePorts(c *gc.C) {
  1002  	// Open port udp:4321 in advance on wordpressUnit.
  1003  	err := s.wordpressUnit.OpenPorts("udp", 4321, 5000)
  1004  	c.Assert(err, jc.ErrorIsNil)
  1005  	openedPorts, err := s.wordpressUnit.OpenedPorts()
  1006  	c.Assert(err, jc.ErrorIsNil)
  1007  	c.Assert(openedPorts, gc.DeepEquals, []corenetwork.PortRange{
  1008  		{Protocol: "udp", FromPort: 4321, ToPort: 5000},
  1009  	})
  1010  
  1011  	args := params.EntitiesPortRanges{Entities: []params.EntityPortRange{
  1012  		{Tag: "unit-mysql-0", Protocol: "tcp", FromPort: 1234, ToPort: 1400},
  1013  		{Tag: "unit-wordpress-0", Protocol: "udp", FromPort: 4321, ToPort: 5000},
  1014  		{Tag: "unit-foo-42", Protocol: "tcp", FromPort: 42, ToPort: 42},
  1015  	}}
  1016  	result, err := s.uniter.ClosePorts(args)
  1017  	c.Assert(err, jc.ErrorIsNil)
  1018  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  1019  		Results: []params.ErrorResult{
  1020  			{apiservertesting.ErrUnauthorized},
  1021  			{nil},
  1022  			{apiservertesting.ErrUnauthorized},
  1023  		},
  1024  	})
  1025  
  1026  	// Verify the wordpressUnit's port is closed.
  1027  	openedPorts, err = s.wordpressUnit.OpenedPorts()
  1028  	c.Assert(err, jc.ErrorIsNil)
  1029  	c.Assert(openedPorts, gc.HasLen, 0)
  1030  }
  1031  
  1032  func (s *uniterSuite) TestWatchConfigSettingsHash(c *gc.C) {
  1033  	err := s.wordpressUnit.SetCharmURL(s.wpCharm.URL())
  1034  	c.Assert(err, jc.ErrorIsNil)
  1035  	err = s.wordpress.UpdateCharmConfig(charm.Settings{
  1036  		"blog-title": "sauceror central",
  1037  	})
  1038  	c.Assert(err, jc.ErrorIsNil)
  1039  
  1040  	c.Assert(s.resources.Count(), gc.Equals, 0)
  1041  
  1042  	args := params.Entities{Entities: []params.Entity{
  1043  		{Tag: "unit-mysql-0"},
  1044  		{Tag: "unit-wordpress-0"},
  1045  		{Tag: "unit-foo-42"},
  1046  	}}
  1047  	result, err := s.uniter.WatchConfigSettingsHash(args)
  1048  	c.Assert(err, jc.ErrorIsNil)
  1049  	c.Assert(result, gc.DeepEquals, params.StringsWatchResults{
  1050  		Results: []params.StringsWatchResult{
  1051  			{Error: apiservertesting.ErrUnauthorized},
  1052  			{
  1053  				StringsWatcherId: "1",
  1054  				Changes:          []string{"af35e298300150f2c357b4a1c40c1109bde305841c6343113b634b9dada22d00"},
  1055  			},
  1056  			{Error: apiservertesting.ErrUnauthorized},
  1057  		},
  1058  	})
  1059  
  1060  	// Verify the resource was registered and stop when done
  1061  	c.Assert(s.resources.Count(), gc.Equals, 1)
  1062  	resource := s.resources.Get("1")
  1063  	defer statetesting.AssertStop(c, resource)
  1064  
  1065  	// Check that the Watch has consumed the initial event ("returned" in
  1066  	// the Watch call)
  1067  	wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher))
  1068  	wc.AssertNoChange()
  1069  }
  1070  
  1071  func (s *uniterSuite) TestWatchTrustConfigSettingsHash(c *gc.C) {
  1072  	schema := environschema.Fields{
  1073  		"trust": environschema.Attr{Type: environschema.Tbool},
  1074  	}
  1075  	err := s.wordpress.UpdateApplicationConfig(coreapplication.ConfigAttributes{
  1076  		"trust": true,
  1077  	}, nil, schema, nil)
  1078  	c.Assert(err, jc.ErrorIsNil)
  1079  
  1080  	c.Assert(s.resources.Count(), gc.Equals, 0)
  1081  
  1082  	args := params.Entities{Entities: []params.Entity{
  1083  		{Tag: "unit-mysql-0"},
  1084  		{Tag: "unit-wordpress-0"},
  1085  		{Tag: "unit-foo-42"},
  1086  	}}
  1087  	result, err := s.uniter.WatchTrustConfigSettingsHash(args)
  1088  	c.Assert(err, jc.ErrorIsNil)
  1089  	c.Assert(result, gc.DeepEquals, params.StringsWatchResults{
  1090  		Results: []params.StringsWatchResult{
  1091  			{Error: apiservertesting.ErrUnauthorized},
  1092  			{
  1093  				StringsWatcherId: "1",
  1094  				Changes:          []string{"2f1368bde39be8106dcdca15e35cc3b5f7db5b8e429806369f621a47fb938519"},
  1095  			},
  1096  			{Error: apiservertesting.ErrUnauthorized},
  1097  		},
  1098  	})
  1099  
  1100  	// Verify the resource was registered and stop when done
  1101  	c.Assert(s.resources.Count(), gc.Equals, 1)
  1102  	resource := s.resources.Get("1")
  1103  	defer statetesting.AssertStop(c, resource)
  1104  
  1105  	// Check that the Watch has consumed the initial event ("returned" in
  1106  	// the Watch call)
  1107  	wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher))
  1108  	wc.AssertNoChange()
  1109  }
  1110  
  1111  func (s *uniterSuite) TestWatchActionNotifications(c *gc.C) {
  1112  	err := s.wordpressUnit.SetCharmURL(s.wpCharm.URL())
  1113  	c.Assert(err, jc.ErrorIsNil)
  1114  
  1115  	c.Assert(s.resources.Count(), gc.Equals, 0)
  1116  
  1117  	args := params.Entities{Entities: []params.Entity{
  1118  		{Tag: "unit-mysql-0"},
  1119  		{Tag: "unit-wordpress-0"},
  1120  		{Tag: "unit-foo-42"},
  1121  	}}
  1122  	result, err := s.uniter.WatchActionNotifications(args)
  1123  	c.Assert(err, jc.ErrorIsNil)
  1124  	c.Assert(result, gc.DeepEquals, params.StringsWatchResults{
  1125  		Results: []params.StringsWatchResult{
  1126  			{Error: apiservertesting.ErrUnauthorized},
  1127  			{StringsWatcherId: "1"},
  1128  			{Error: apiservertesting.ErrUnauthorized},
  1129  		},
  1130  	})
  1131  
  1132  	// Verify the resource was registered and stop when done
  1133  	c.Assert(s.resources.Count(), gc.Equals, 1)
  1134  	resource := s.resources.Get("1")
  1135  	defer statetesting.AssertStop(c, resource)
  1136  
  1137  	// Check that the Watch has consumed the initial event ("returned" in
  1138  	// the Watch call)
  1139  	wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher))
  1140  	wc.AssertNoChange()
  1141  
  1142  	addedAction, err := s.wordpressUnit.AddAction("fakeaction", nil)
  1143  
  1144  	wc.AssertChange(addedAction.Id())
  1145  	wc.AssertNoChange()
  1146  }
  1147  
  1148  func (s *uniterSuite) TestWatchPreexistingActions(c *gc.C) {
  1149  	err := s.wordpressUnit.SetCharmURL(s.wpCharm.URL())
  1150  	c.Assert(err, jc.ErrorIsNil)
  1151  
  1152  	c.Assert(s.resources.Count(), gc.Equals, 0)
  1153  
  1154  	action1, err := s.wordpressUnit.AddAction("fakeaction", nil)
  1155  	c.Assert(err, jc.ErrorIsNil)
  1156  	action2, err := s.wordpressUnit.AddAction("fakeaction", nil)
  1157  	c.Assert(err, jc.ErrorIsNil)
  1158  
  1159  	args := params.Entities{Entities: []params.Entity{
  1160  		{Tag: "unit-wordpress-0"},
  1161  	}}
  1162  
  1163  	s.WaitForModelWatchersIdle(c, s.State.ModelUUID())
  1164  	results, err := s.uniter.WatchActionNotifications(args)
  1165  	c.Assert(err, jc.ErrorIsNil)
  1166  
  1167  	checkUnorderedActionIdsEqual(c, []string{action1.Id(), action2.Id()}, results)
  1168  
  1169  	// Verify the resource was registered and stop when done
  1170  	c.Assert(s.resources.Count(), gc.Equals, 1)
  1171  	resource := s.resources.Get("1")
  1172  	defer statetesting.AssertStop(c, resource)
  1173  
  1174  	// Check that the Watch has consumed the initial event ("returned" in
  1175  	// the Watch call)
  1176  	wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher))
  1177  	wc.AssertNoChange()
  1178  
  1179  	addedAction, err := s.wordpressUnit.AddAction("fakeaction", nil)
  1180  	c.Assert(err, jc.ErrorIsNil)
  1181  	wc.AssertChange(addedAction.Id())
  1182  	wc.AssertNoChange()
  1183  }
  1184  
  1185  func (s *uniterSuite) TestWatchActionNotificationsMalformedTag(c *gc.C) {
  1186  	args := params.Entities{Entities: []params.Entity{
  1187  		{Tag: "ewenit-mysql-0"},
  1188  	}}
  1189  	results, err := s.uniter.WatchActionNotifications(args)
  1190  	c.Assert(err, jc.ErrorIsNil)
  1191  	c.Assert(results, gc.NotNil)
  1192  	c.Assert(len(results.Results), gc.Equals, 1)
  1193  	result := results.Results[0]
  1194  	c.Assert(result.Error, gc.NotNil)
  1195  	c.Assert(result.Error.Message, gc.Equals, `invalid actionreceiver tag "ewenit-mysql-0"`)
  1196  }
  1197  
  1198  func (s *uniterSuite) TestWatchActionNotificationsMalformedUnitName(c *gc.C) {
  1199  	args := params.Entities{Entities: []params.Entity{
  1200  		{Tag: "unit-mysql-01"},
  1201  	}}
  1202  	results, err := s.uniter.WatchActionNotifications(args)
  1203  	c.Assert(err, jc.ErrorIsNil)
  1204  	c.Assert(results, gc.NotNil)
  1205  	c.Assert(len(results.Results), gc.Equals, 1)
  1206  	result := results.Results[0]
  1207  	c.Assert(result.Error, gc.NotNil)
  1208  	c.Assert(result.Error.Message, gc.Equals, `invalid actionreceiver tag "unit-mysql-01"`)
  1209  }
  1210  
  1211  func (s *uniterSuite) TestWatchActionNotificationsNotUnit(c *gc.C) {
  1212  	action, err := s.mysqlUnit.AddAction("fakeaction", nil)
  1213  	c.Assert(err, jc.ErrorIsNil)
  1214  	args := params.Entities{Entities: []params.Entity{
  1215  		{Tag: action.Tag().String()},
  1216  	}}
  1217  	results, err := s.uniter.WatchActionNotifications(args)
  1218  	c.Assert(err, jc.ErrorIsNil)
  1219  	c.Assert(results, gc.NotNil)
  1220  	c.Assert(len(results.Results), gc.Equals, 1)
  1221  	result := results.Results[0]
  1222  	c.Assert(result.Error, gc.NotNil)
  1223  	c.Assert(result.Error.Message, gc.Equals, `invalid actionreceiver tag "action-`+action.Id()+`"`)
  1224  }
  1225  
  1226  func (s *uniterSuite) TestWatchActionNotificationsPermissionDenied(c *gc.C) {
  1227  	args := params.Entities{Entities: []params.Entity{
  1228  		{Tag: "unit-nonexistentgarbage-0"},
  1229  	}}
  1230  	results, err := s.uniter.WatchActionNotifications(args)
  1231  	c.Assert(err, jc.ErrorIsNil)
  1232  	c.Assert(results, gc.NotNil)
  1233  	c.Assert(len(results.Results), gc.Equals, 1)
  1234  	result := results.Results[0]
  1235  	c.Assert(result.Error, gc.NotNil)
  1236  	c.Assert(result.Error.Message, gc.Equals, "permission denied")
  1237  }
  1238  
  1239  func (s *uniterSuite) TestConfigSettings(c *gc.C) {
  1240  	err := s.wordpressUnit.SetCharmURL(s.wpCharm.URL())
  1241  	c.Assert(err, jc.ErrorIsNil)
  1242  	settings, err := s.wordpressUnit.ConfigSettings()
  1243  	c.Assert(err, jc.ErrorIsNil)
  1244  	c.Assert(settings, gc.DeepEquals, charm.Settings{"blog-title": "My Title"})
  1245  
  1246  	args := params.Entities{Entities: []params.Entity{
  1247  		{Tag: "unit-mysql-0"},
  1248  		{Tag: "unit-wordpress-0"},
  1249  		{Tag: "unit-foo-42"},
  1250  	}}
  1251  	result, err := s.uniter.ConfigSettings(args)
  1252  	c.Assert(err, jc.ErrorIsNil)
  1253  	c.Assert(result, gc.DeepEquals, params.ConfigSettingsResults{
  1254  		Results: []params.ConfigSettingsResult{
  1255  			{Error: apiservertesting.ErrUnauthorized},
  1256  			{Settings: params.ConfigSettings{"blog-title": "My Title"}},
  1257  			{Error: apiservertesting.ErrUnauthorized},
  1258  		},
  1259  	})
  1260  }
  1261  
  1262  func (s *uniterSuite) TestWatchUnitRelations(c *gc.C) {
  1263  	c.Assert(s.resources.Count(), gc.Equals, 0)
  1264  
  1265  	args := params.Entities{Entities: []params.Entity{
  1266  		{Tag: "unit-mysql-0"},
  1267  		{Tag: "unit-wordpress-0"},
  1268  		{Tag: "unit-foo-0"},
  1269  	}}
  1270  	result, err := s.uniter.WatchUnitRelations(args)
  1271  	c.Assert(err, jc.ErrorIsNil)
  1272  	c.Assert(result.Results, gc.HasLen, 3)
  1273  	c.Assert(result.Results[0].Error, gc.DeepEquals, apiservertesting.ErrUnauthorized)
  1274  	c.Assert(result.Results[1].StringsWatcherId, gc.Equals, "1")
  1275  	c.Assert(result.Results[1].Changes, gc.NotNil)
  1276  	c.Assert(result.Results[1].Error, gc.IsNil)
  1277  	c.Assert(result.Results[2].Error, gc.DeepEquals, apiservertesting.ErrUnauthorized)
  1278  
  1279  	// Verify the resource was registered and stop when done
  1280  	c.Assert(s.resources.Count(), gc.Equals, 1)
  1281  	resource := s.resources.Get("1")
  1282  	defer statetesting.AssertStop(c, resource)
  1283  
  1284  	// Check that the Watch has consumed the initial event ("returned" in
  1285  	// the Watch call)
  1286  	wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher))
  1287  	wc.AssertNoChange()
  1288  }
  1289  
  1290  func (s *uniterSuite) TestWatchSubordinateUnitRelations(c *gc.C) {
  1291  	// The logging charm is subordinate (and the info endpoint is scope=container).
  1292  	loggingCharm := s.Factory.MakeCharm(c, &factory.CharmParams{
  1293  		Name: "logging",
  1294  		URL:  "cs:quantal/logging-1",
  1295  	})
  1296  	loggingApp := s.Factory.MakeApplication(c, &factory.ApplicationParams{
  1297  		Name:  "logging",
  1298  		Charm: loggingCharm,
  1299  	})
  1300  
  1301  	mysqlRel := s.makeSubordinateRelation(c, loggingApp, s.mysql, s.mysqlUnit)
  1302  	wpRel := s.makeSubordinateRelation(c, loggingApp, s.wordpress, s.wordpressUnit)
  1303  	mysqlLogUnit := findSubordinateUnit(c, loggingApp, s.mysqlUnit)
  1304  
  1305  	subAuthorizer := s.authorizer
  1306  	subAuthorizer.Tag = mysqlLogUnit.Tag()
  1307  	api, err := uniter.NewUniterAPI(facadetest.Context{
  1308  		State_:             s.State,
  1309  		Resources_:         s.resources,
  1310  		Auth_:              subAuthorizer,
  1311  		LeadershipChecker_: s.State.LeadershipChecker(),
  1312  	})
  1313  	c.Assert(err, jc.ErrorIsNil)
  1314  
  1315  	result, err := api.WatchUnitRelations(params.Entities{
  1316  		Entities: []params.Entity{{Tag: mysqlLogUnit.Tag().String()}},
  1317  	})
  1318  	c.Assert(err, jc.ErrorIsNil)
  1319  	c.Assert(result.Results, gc.HasLen, 1)
  1320  	c.Assert(result.Results[0].Error, gc.IsNil)
  1321  	c.Assert(result.Results[0].StringsWatcherId, gc.Equals, "1")
  1322  	c.Assert(result.Results[0].Changes, gc.NotNil)
  1323  
  1324  	c.Assert(s.resources.Count(), gc.Equals, 1)
  1325  	resource := s.resources.Get("1")
  1326  	defer statetesting.AssertStop(c, resource)
  1327  
  1328  	// Check that the Watch has consumed the initial event ("returned" in
  1329  	// the Watch call)
  1330  	wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher))
  1331  	wc.AssertNoChange()
  1332  
  1333  	// We get notified about the mysql relation going away but not the
  1334  	// wordpress one.
  1335  	err = mysqlRel.Destroy()
  1336  	c.Assert(err, jc.ErrorIsNil)
  1337  
  1338  	wc.AssertChange(mysqlRel.Tag().Id())
  1339  	wc.AssertNoChange()
  1340  
  1341  	err = wpRel.Destroy()
  1342  	c.Assert(err, jc.ErrorIsNil)
  1343  	wc.AssertNoChange()
  1344  }
  1345  
  1346  func (s *uniterSuite) TestWatchUnitRelationsSubordinateWithGlobalEndpoint(c *gc.C) {
  1347  	// A subordinate unit should still be notified about changes to
  1348  	// relations with applications that aren't the one this unit is
  1349  	// attached to if they have global scope.
  1350  	// The logging charm is subordinate (and the info endpoint is scope=container).
  1351  	loggingCharm := s.Factory.MakeCharm(c, &factory.CharmParams{
  1352  		Name: "logging",
  1353  		URL:  "cs:quantal/logging-1",
  1354  	})
  1355  	loggingApp := s.Factory.MakeApplication(c, &factory.ApplicationParams{
  1356  		Name:  "logging",
  1357  		Charm: loggingCharm,
  1358  	})
  1359  
  1360  	uiCharm := s.Factory.MakeCharm(c, &factory.CharmParams{
  1361  		Name: "logging-frontend",
  1362  		URL:  "cs:quantal/logging-frontend-1",
  1363  	})
  1364  	uiApp := s.Factory.MakeApplication(c, &factory.ApplicationParams{
  1365  		Name:  "logging-frontend",
  1366  		Charm: uiCharm,
  1367  	})
  1368  
  1369  	_ = s.makeSubordinateRelation(c, loggingApp, s.mysql, s.mysqlUnit)
  1370  	mysqlLogUnit := findSubordinateUnit(c, loggingApp, s.mysqlUnit)
  1371  
  1372  	subAuthorizer := s.authorizer
  1373  	subAuthorizer.Tag = mysqlLogUnit.Tag()
  1374  	api, err := uniter.NewUniterAPI(facadetest.Context{
  1375  		State_:             s.State,
  1376  		Resources_:         s.resources,
  1377  		Auth_:              subAuthorizer,
  1378  		LeadershipChecker_: s.State.LeadershipChecker(),
  1379  	})
  1380  	c.Assert(err, jc.ErrorIsNil)
  1381  
  1382  	result, err := api.WatchUnitRelations(params.Entities{
  1383  		Entities: []params.Entity{{Tag: mysqlLogUnit.Tag().String()}},
  1384  	})
  1385  	c.Assert(err, jc.ErrorIsNil)
  1386  	c.Assert(result.Results, gc.HasLen, 1)
  1387  	c.Assert(result.Results[0].Error, gc.IsNil)
  1388  	c.Assert(result.Results[0].StringsWatcherId, gc.Equals, "1")
  1389  	c.Assert(result.Results[0].Changes, gc.NotNil)
  1390  
  1391  	c.Assert(s.resources.Count(), gc.Equals, 1)
  1392  	resource := s.resources.Get("1")
  1393  	defer statetesting.AssertStop(c, resource)
  1394  
  1395  	wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher))
  1396  	wc.AssertNoChange()
  1397  
  1398  	// Should be notified about the relation to logging frontend, since it's global scope.
  1399  	subEndpoint, err := loggingApp.Endpoint("logging-client")
  1400  	c.Assert(err, jc.ErrorIsNil)
  1401  	uiEndpoint, err := uiApp.Endpoint("logging-client")
  1402  	c.Assert(err, jc.ErrorIsNil)
  1403  	rel := s.Factory.MakeRelation(c, &factory.RelationParams{
  1404  		Endpoints: []state.Endpoint{subEndpoint, uiEndpoint},
  1405  	})
  1406  
  1407  	wc.AssertChange(rel.Tag().Id())
  1408  	wc.AssertNoChange()
  1409  }
  1410  
  1411  func (s *uniterSuite) TestWatchUnitRelationsWithSubSubRelation(c *gc.C) {
  1412  	// We should be notified about relations to other subordinates
  1413  	// (since it's possible that they'll be colocated in the same
  1414  	// container).
  1415  	loggingCharm := s.Factory.MakeCharm(c, &factory.CharmParams{
  1416  		Name: "logging",
  1417  		URL:  "cs:quantal/logging-1",
  1418  	})
  1419  	loggingApp := s.Factory.MakeApplication(c, &factory.ApplicationParams{
  1420  		Name:  "logging",
  1421  		Charm: loggingCharm,
  1422  	})
  1423  	monitoringCharm := s.Factory.MakeCharm(c, &factory.CharmParams{
  1424  		Name: "monitoring",
  1425  		URL:  "cs:quantal/monitoring-1",
  1426  	})
  1427  	monitoringApp := s.Factory.MakeApplication(c, &factory.ApplicationParams{
  1428  		Name:  "monitoring",
  1429  		Charm: monitoringCharm,
  1430  	})
  1431  
  1432  	s.makeSubordinateRelation(c, loggingApp, s.mysql, s.mysqlUnit)
  1433  	mysqlMonitoring := s.makeSubordinateRelation(c, monitoringApp, s.mysql, s.mysqlUnit)
  1434  
  1435  	monUnit := findSubordinateUnit(c, monitoringApp, s.mysqlUnit)
  1436  
  1437  	subAuthorizer := s.authorizer
  1438  	subAuthorizer.Tag = monUnit.Tag()
  1439  	api, err := uniter.NewUniterAPI(facadetest.Context{
  1440  		State_:             s.State,
  1441  		Resources_:         s.resources,
  1442  		Auth_:              subAuthorizer,
  1443  		LeadershipChecker_: s.State.LeadershipChecker(),
  1444  	})
  1445  	c.Assert(err, jc.ErrorIsNil)
  1446  
  1447  	result, err := api.WatchUnitRelations(params.Entities{
  1448  		Entities: []params.Entity{{Tag: monUnit.Tag().String()}},
  1449  	})
  1450  	c.Assert(err, jc.ErrorIsNil)
  1451  	c.Assert(result.Results, gc.HasLen, 1)
  1452  	c.Assert(result.Results[0].Error, gc.IsNil)
  1453  	c.Assert(result.Results[0].StringsWatcherId, gc.Equals, "1")
  1454  	c.Assert(result.Results[0].Changes, gc.DeepEquals, []string{mysqlMonitoring.Tag().Id()})
  1455  
  1456  	c.Assert(s.resources.Count(), gc.Equals, 1)
  1457  	resource := s.resources.Get("1")
  1458  	defer statetesting.AssertStop(c, resource)
  1459  
  1460  	// Check that the Watch has consumed the initial event ("returned" in
  1461  	// the Watch call)
  1462  	wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher))
  1463  	wc.AssertNoChange()
  1464  
  1465  	// Now we relate logging and monitoring together.
  1466  	monEp, err := monitoringApp.Endpoint("info")
  1467  	c.Assert(err, jc.ErrorIsNil)
  1468  
  1469  	logEp, err := loggingApp.Endpoint("juju-info")
  1470  	c.Assert(err, jc.ErrorIsNil)
  1471  	rel := s.Factory.MakeRelation(c, &factory.RelationParams{
  1472  		Endpoints: []state.Endpoint{monEp, logEp},
  1473  	})
  1474  	c.Assert(err, jc.ErrorIsNil)
  1475  
  1476  	// We should be told about the new logging-monitoring relation.
  1477  	wc.AssertChange(rel.Tag().Id())
  1478  	wc.AssertNoChange()
  1479  
  1480  	err = rel.Destroy()
  1481  	c.Assert(err, jc.ErrorIsNil)
  1482  
  1483  	wc.AssertChange(rel.Tag().Id())
  1484  	wc.AssertNoChange()
  1485  }
  1486  
  1487  func (s *uniterSuite) makeSubordinateRelation(c *gc.C, subApp, principalApp *state.Application, principalUnit *state.Unit) *state.Relation {
  1488  	subEndpoint, err := subApp.Endpoint("info")
  1489  	c.Assert(err, jc.ErrorIsNil)
  1490  
  1491  	principalEndpoint, err := principalApp.Endpoint("juju-info")
  1492  	c.Assert(err, jc.ErrorIsNil)
  1493  	rel := s.Factory.MakeRelation(c, &factory.RelationParams{
  1494  		Endpoints: []state.Endpoint{subEndpoint, principalEndpoint},
  1495  	})
  1496  	c.Assert(err, jc.ErrorIsNil)
  1497  	// Trigger the creation of the subordinate unit by entering scope
  1498  	// on the principal unit.
  1499  	ru, err := rel.Unit(principalUnit)
  1500  	c.Assert(err, jc.ErrorIsNil)
  1501  	err = ru.EnterScope(nil)
  1502  	c.Assert(err, jc.ErrorIsNil)
  1503  
  1504  	return rel
  1505  }
  1506  
  1507  func findSubordinateUnit(c *gc.C, subApp *state.Application, principalUnit *state.Unit) *state.Unit {
  1508  	subUnits, err := subApp.AllUnits()
  1509  	c.Assert(err, jc.ErrorIsNil)
  1510  	for _, subUnit := range subUnits {
  1511  		principal, ok := subUnit.PrincipalName()
  1512  		c.Assert(ok, jc.IsTrue)
  1513  		if principal == principalUnit.Name() {
  1514  			return subUnit
  1515  		}
  1516  	}
  1517  	c.Fatalf("couldn't find subordinate unit for %q", principalUnit.Name())
  1518  	return nil
  1519  }
  1520  
  1521  func (s *uniterSuite) TestCharmArchiveSha256(c *gc.C) {
  1522  	dummyCharm := s.AddTestingCharm(c, "dummy")
  1523  
  1524  	args := params.CharmURLs{URLs: []params.CharmURL{
  1525  		{URL: "something-invalid"},
  1526  		{URL: s.wpCharm.String()},
  1527  		{URL: dummyCharm.String()},
  1528  	}}
  1529  	result, err := s.uniter.CharmArchiveSha256(args)
  1530  	c.Assert(err, jc.ErrorIsNil)
  1531  	c.Assert(result, gc.DeepEquals, params.StringResults{
  1532  		Results: []params.StringResult{
  1533  			{Error: apiservertesting.ErrUnauthorized},
  1534  			{Result: s.wpCharm.BundleSha256()},
  1535  			{Result: dummyCharm.BundleSha256()},
  1536  		},
  1537  	})
  1538  }
  1539  
  1540  func (s *uniterSuite) TestCurrentModel(c *gc.C) {
  1541  	model, err := s.State.Model()
  1542  	c.Assert(err, jc.ErrorIsNil)
  1543  
  1544  	result, err := s.uniter.CurrentModel()
  1545  	c.Assert(err, jc.ErrorIsNil)
  1546  	expected := params.ModelResult{
  1547  		Name: model.Name(),
  1548  		UUID: model.UUID(),
  1549  		Type: "iaas",
  1550  	}
  1551  	c.Assert(result, gc.DeepEquals, expected)
  1552  }
  1553  
  1554  func (s *uniterSuite) TestActions(c *gc.C) {
  1555  	var actionTests = []struct {
  1556  		description string
  1557  		action      params.ActionResult
  1558  	}{{
  1559  		description: "A simple action.",
  1560  		action: params.ActionResult{
  1561  			Action: &params.Action{
  1562  				Name: "fakeaction",
  1563  				Parameters: map[string]interface{}{
  1564  					"outfile": "foo.txt",
  1565  				}},
  1566  		},
  1567  	}, {
  1568  		description: "An action with nested parameters.",
  1569  		action: params.ActionResult{
  1570  			Action: &params.Action{
  1571  				Name: "fakeaction",
  1572  				Parameters: map[string]interface{}{
  1573  					"outfile": "foo.bz2",
  1574  					"compression": map[string]interface{}{
  1575  						"kind":    "bzip",
  1576  						"quality": 5,
  1577  					},
  1578  				}},
  1579  		},
  1580  	}}
  1581  
  1582  	for i, actionTest := range actionTests {
  1583  		c.Logf("test %d: %s", i, actionTest.description)
  1584  
  1585  		a, err := s.wordpressUnit.AddAction(
  1586  			actionTest.action.Action.Name,
  1587  			actionTest.action.Action.Parameters)
  1588  		c.Assert(err, jc.ErrorIsNil)
  1589  		c.Assert(names.IsValidAction(a.Id()), gc.Equals, true)
  1590  		actionTag := names.NewActionTag(a.Id())
  1591  		c.Assert(a.ActionTag(), gc.Equals, actionTag)
  1592  
  1593  		args := params.Entities{
  1594  			Entities: []params.Entity{{
  1595  				Tag: actionTag.String(),
  1596  			}},
  1597  		}
  1598  		results, err := s.uniter.Actions(args)
  1599  		c.Assert(err, jc.ErrorIsNil)
  1600  		c.Assert(results.Results, gc.HasLen, 1)
  1601  
  1602  		actionsQueryResult := results.Results[0]
  1603  
  1604  		c.Assert(actionsQueryResult, jc.DeepEquals, actionTest.action)
  1605  	}
  1606  }
  1607  
  1608  func (s *uniterSuite) TestActionsNotPresent(c *gc.C) {
  1609  	uuid, err := utils.NewUUID()
  1610  	c.Assert(err, jc.ErrorIsNil)
  1611  	args := params.Entities{
  1612  		Entities: []params.Entity{{
  1613  			Tag: names.NewActionTag(uuid.String()).String(),
  1614  		}},
  1615  	}
  1616  	results, err := s.uniter.Actions(args)
  1617  	c.Assert(err, jc.ErrorIsNil)
  1618  
  1619  	c.Assert(results.Results, gc.HasLen, 1)
  1620  	actionsQueryResult := results.Results[0]
  1621  	c.Assert(actionsQueryResult.Error, gc.NotNil)
  1622  	c.Assert(actionsQueryResult.Error, gc.ErrorMatches, `action "[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}" not found`)
  1623  }
  1624  
  1625  func (s *uniterSuite) TestActionsWrongUnit(c *gc.C) {
  1626  	// Action doesn't match unit.
  1627  	mysqlUnitAuthorizer := apiservertesting.FakeAuthorizer{
  1628  		Tag: s.mysqlUnit.Tag(),
  1629  	}
  1630  	mysqlUnitFacade, err := uniter.NewUniterAPI(facadetest.Context{
  1631  		State_:             s.State,
  1632  		Resources_:         s.resources,
  1633  		Auth_:              mysqlUnitAuthorizer,
  1634  		LeadershipChecker_: s.State.LeadershipChecker(),
  1635  	})
  1636  	c.Assert(err, jc.ErrorIsNil)
  1637  
  1638  	action, err := s.wordpressUnit.AddAction("fakeaction", nil)
  1639  	c.Assert(err, jc.ErrorIsNil)
  1640  	args := params.Entities{
  1641  		Entities: []params.Entity{{
  1642  			Tag: action.Tag().String(),
  1643  		}},
  1644  	}
  1645  	actions, err := mysqlUnitFacade.Actions(args)
  1646  	c.Assert(err, jc.ErrorIsNil)
  1647  	c.Assert(len(actions.Results), gc.Equals, 1)
  1648  	c.Assert(actions.Results[0].Error, jc.Satisfies, params.IsCodeUnauthorized)
  1649  }
  1650  
  1651  func (s *uniterSuite) TestActionsPermissionDenied(c *gc.C) {
  1652  	action, err := s.mysqlUnit.AddAction("fakeaction", nil)
  1653  	c.Assert(err, jc.ErrorIsNil)
  1654  	args := params.Entities{
  1655  		Entities: []params.Entity{{
  1656  			Tag: action.Tag().String(),
  1657  		}},
  1658  	}
  1659  	actions, err := s.uniter.Actions(args)
  1660  	c.Assert(err, jc.ErrorIsNil)
  1661  	c.Assert(len(actions.Results), gc.Equals, 1)
  1662  	c.Assert(actions.Results[0].Error, jc.Satisfies, params.IsCodeUnauthorized)
  1663  }
  1664  
  1665  func (s *uniterSuite) TestFinishActionsSuccess(c *gc.C) {
  1666  	testName := "fakeaction"
  1667  	testOutput := map[string]interface{}{"output": "completed fakeaction successfully"}
  1668  
  1669  	results, err := s.wordpressUnit.CompletedActions()
  1670  	c.Assert(err, jc.ErrorIsNil)
  1671  	c.Assert(results, gc.DeepEquals, ([]state.Action)(nil))
  1672  
  1673  	action, err := s.wordpressUnit.AddAction(testName, nil)
  1674  	c.Assert(err, jc.ErrorIsNil)
  1675  
  1676  	actionResults := params.ActionExecutionResults{
  1677  		Results: []params.ActionExecutionResult{{
  1678  			ActionTag: action.ActionTag().String(),
  1679  			Status:    params.ActionCompleted,
  1680  			Results:   testOutput,
  1681  		}},
  1682  	}
  1683  	res, err := s.uniter.FinishActions(actionResults)
  1684  	c.Assert(err, jc.ErrorIsNil)
  1685  	c.Assert(res, gc.DeepEquals, params.ErrorResults{Results: []params.ErrorResult{{Error: nil}}})
  1686  
  1687  	results, err = s.wordpressUnit.CompletedActions()
  1688  	c.Assert(err, jc.ErrorIsNil)
  1689  	c.Assert(len(results), gc.Equals, 1)
  1690  	c.Assert(results[0].Status(), gc.Equals, state.ActionCompleted)
  1691  	res2, errstr := results[0].Results()
  1692  	c.Assert(errstr, gc.Equals, "")
  1693  	c.Assert(res2, gc.DeepEquals, testOutput)
  1694  	c.Assert(results[0].Name(), gc.Equals, testName)
  1695  }
  1696  
  1697  func (s *uniterSuite) TestFinishActionsFailure(c *gc.C) {
  1698  	testName := "fakeaction"
  1699  	testError := "fakeaction was a dismal failure"
  1700  
  1701  	results, err := s.wordpressUnit.CompletedActions()
  1702  	c.Assert(err, jc.ErrorIsNil)
  1703  	c.Assert(results, gc.DeepEquals, ([]state.Action)(nil))
  1704  
  1705  	action, err := s.wordpressUnit.AddAction(testName, nil)
  1706  	c.Assert(err, jc.ErrorIsNil)
  1707  
  1708  	actionResults := params.ActionExecutionResults{
  1709  		Results: []params.ActionExecutionResult{{
  1710  			ActionTag: action.ActionTag().String(),
  1711  			Status:    params.ActionFailed,
  1712  			Results:   nil,
  1713  			Message:   testError,
  1714  		}},
  1715  	}
  1716  	res, err := s.uniter.FinishActions(actionResults)
  1717  	c.Assert(err, jc.ErrorIsNil)
  1718  	c.Assert(res, gc.DeepEquals, params.ErrorResults{Results: []params.ErrorResult{{Error: nil}}})
  1719  
  1720  	results, err = s.wordpressUnit.CompletedActions()
  1721  	c.Assert(err, jc.ErrorIsNil)
  1722  	c.Assert(len(results), gc.Equals, 1)
  1723  	c.Assert(results[0].Status(), gc.Equals, state.ActionFailed)
  1724  	res2, errstr := results[0].Results()
  1725  	c.Assert(errstr, gc.Equals, testError)
  1726  	c.Assert(res2, gc.DeepEquals, map[string]interface{}{})
  1727  	c.Assert(results[0].Name(), gc.Equals, testName)
  1728  }
  1729  
  1730  func (s *uniterSuite) TestFinishActionsAuthAccess(c *gc.C) {
  1731  	good, err := s.wordpressUnit.AddAction("fakeaction", nil)
  1732  	c.Assert(err, jc.ErrorIsNil)
  1733  
  1734  	bad, err := s.mysqlUnit.AddAction("fakeaction", nil)
  1735  	c.Assert(err, jc.ErrorIsNil)
  1736  
  1737  	var tests = []struct {
  1738  		actionTag names.ActionTag
  1739  		err       error
  1740  	}{
  1741  		{actionTag: good.ActionTag(), err: nil},
  1742  		{actionTag: bad.ActionTag(), err: common.ErrPerm},
  1743  	}
  1744  
  1745  	// Queue up actions from tests
  1746  	actionResults := params.ActionExecutionResults{Results: make([]params.ActionExecutionResult, len(tests))}
  1747  	for i, test := range tests {
  1748  		actionResults.Results[i] = params.ActionExecutionResult{
  1749  			ActionTag: test.actionTag.String(),
  1750  			Status:    params.ActionCompleted,
  1751  			Results:   map[string]interface{}{},
  1752  		}
  1753  	}
  1754  
  1755  	// Invoke FinishActions
  1756  	res, err := s.uniter.FinishActions(actionResults)
  1757  	c.Assert(err, jc.ErrorIsNil)
  1758  
  1759  	// Verify permissions errors for actions queued on different unit
  1760  	for i, result := range res.Results {
  1761  		expected := tests[i].err
  1762  		if expected != nil {
  1763  			c.Assert(result.Error, gc.NotNil)
  1764  			c.Assert(result.Error.Error(), gc.Equals, expected.Error())
  1765  		} else {
  1766  			c.Assert(result.Error, gc.IsNil)
  1767  		}
  1768  	}
  1769  }
  1770  
  1771  func (s *uniterSuite) TestBeginActions(c *gc.C) {
  1772  	ten_seconds_ago := time.Now().Add(-10 * time.Second)
  1773  	good, err := s.wordpressUnit.AddAction("fakeaction", nil)
  1774  	c.Assert(err, jc.ErrorIsNil)
  1775  
  1776  	running, err := s.wordpressUnit.RunningActions()
  1777  	c.Assert(err, jc.ErrorIsNil)
  1778  	c.Assert(len(running), gc.Equals, 0, gc.Commentf("expected no running actions, got %d", len(running)))
  1779  
  1780  	args := params.Entities{Entities: []params.Entity{{Tag: good.ActionTag().String()}}}
  1781  	res, err := s.uniter.BeginActions(args)
  1782  	c.Assert(err, jc.ErrorIsNil)
  1783  	c.Assert(len(res.Results), gc.Equals, 1)
  1784  	c.Assert(res.Results[0].Error, gc.IsNil)
  1785  
  1786  	running, err = s.wordpressUnit.RunningActions()
  1787  	c.Assert(err, jc.ErrorIsNil)
  1788  	c.Assert(len(running), gc.Equals, 1, gc.Commentf("expected one running action, got %d", len(running)))
  1789  	c.Assert(running[0].ActionTag(), gc.Equals, good.ActionTag())
  1790  	enqueued, started := running[0].Enqueued(), running[0].Started()
  1791  	c.Assert(ten_seconds_ago.Before(enqueued), jc.IsTrue, gc.Commentf("enqueued time should be after 10 seconds ago"))
  1792  	c.Assert(ten_seconds_ago.Before(started), jc.IsTrue, gc.Commentf("started time should be after 10 seconds ago"))
  1793  	c.Assert(started.After(enqueued) || started.Equal(enqueued), jc.IsTrue, gc.Commentf("started should be after or equal to enqueued time"))
  1794  }
  1795  
  1796  func (s *uniterSuite) TestRelation(c *gc.C) {
  1797  	rel := s.addRelation(c, "wordpress", "mysql")
  1798  	wpEp, err := rel.Endpoint("wordpress")
  1799  	c.Assert(err, jc.ErrorIsNil)
  1800  
  1801  	args := params.RelationUnits{RelationUnits: []params.RelationUnit{
  1802  		{Relation: "relation-42", Unit: "unit-foo-0"},
  1803  		{Relation: rel.Tag().String(), Unit: "unit-wordpress-0"},
  1804  		{Relation: rel.Tag().String(), Unit: "unit-mysql-0"},
  1805  		{Relation: rel.Tag().String(), Unit: "unit-foo-0"},
  1806  		{Relation: "relation-blah", Unit: "unit-wordpress-0"},
  1807  		{Relation: "application-foo", Unit: "user-foo"},
  1808  		{Relation: "foo", Unit: "bar"},
  1809  		{Relation: "unit-wordpress-0", Unit: rel.Tag().String()},
  1810  	}}
  1811  	result, err := s.uniter.Relation(args)
  1812  	c.Assert(err, jc.ErrorIsNil)
  1813  	c.Assert(result, gc.DeepEquals, params.RelationResults{
  1814  		Results: []params.RelationResult{
  1815  			{Error: apiservertesting.ErrUnauthorized},
  1816  			{
  1817  				Id:        rel.Id(),
  1818  				Key:       rel.String(),
  1819  				Life:      params.Life(rel.Life().String()),
  1820  				Suspended: rel.Suspended(),
  1821  				Endpoint: multiwatcher.Endpoint{
  1822  					ApplicationName: wpEp.ApplicationName,
  1823  					Relation:        multiwatcher.NewCharmRelation(wpEp.Relation),
  1824  				},
  1825  				OtherApplication: s.mysql.Name(),
  1826  			},
  1827  			{Error: apiservertesting.ErrUnauthorized},
  1828  			{Error: apiservertesting.ErrUnauthorized},
  1829  			{Error: apiservertesting.ErrUnauthorized},
  1830  			{Error: apiservertesting.ErrUnauthorized},
  1831  			{Error: apiservertesting.ErrUnauthorized},
  1832  			{Error: apiservertesting.ErrUnauthorized},
  1833  		},
  1834  	})
  1835  }
  1836  
  1837  func (s *uniterSuite) TestRelationById(c *gc.C) {
  1838  	rel := s.addRelation(c, "wordpress", "mysql")
  1839  	c.Assert(rel.Id(), gc.Equals, 0)
  1840  	wpEp, err := rel.Endpoint("wordpress")
  1841  	c.Assert(err, jc.ErrorIsNil)
  1842  
  1843  	// Add another relation to mysql application, so we can see we can't
  1844  	// get it.
  1845  	otherRel, _, _ := s.addRelatedApplication(c, "mysql", "logging", s.mysqlUnit)
  1846  
  1847  	args := params.RelationIds{
  1848  		RelationIds: []int{-1, rel.Id(), otherRel.Id(), 42, 234},
  1849  	}
  1850  	result, err := s.uniter.RelationById(args)
  1851  	c.Assert(err, jc.ErrorIsNil)
  1852  	c.Assert(result, gc.DeepEquals, params.RelationResults{
  1853  		Results: []params.RelationResult{
  1854  			{Error: apiservertesting.ErrUnauthorized},
  1855  			{
  1856  				Id:        rel.Id(),
  1857  				Key:       rel.String(),
  1858  				Life:      params.Life(rel.Life().String()),
  1859  				Suspended: rel.Suspended(),
  1860  				Endpoint: multiwatcher.Endpoint{
  1861  					ApplicationName: wpEp.ApplicationName,
  1862  					Relation:        multiwatcher.NewCharmRelation(wpEp.Relation),
  1863  				},
  1864  				OtherApplication: s.mysql.Name(),
  1865  			},
  1866  			{Error: apiservertesting.ErrUnauthorized},
  1867  			{Error: apiservertesting.ErrUnauthorized},
  1868  			{Error: apiservertesting.ErrUnauthorized},
  1869  		},
  1870  	})
  1871  }
  1872  
  1873  func (s *uniterSuite) TestProviderType(c *gc.C) {
  1874  	cfg, err := s.Model.ModelConfig()
  1875  	c.Assert(err, jc.ErrorIsNil)
  1876  
  1877  	result, err := s.uniter.ProviderType()
  1878  	c.Assert(err, jc.ErrorIsNil)
  1879  	c.Assert(result, gc.DeepEquals, params.StringResult{Result: cfg.Type()})
  1880  }
  1881  
  1882  func (s *uniterSuite) TestEnterScope(c *gc.C) {
  1883  	// Set wordpressUnit's private address first.
  1884  	err := s.machine0.SetProviderAddresses(
  1885  		network.NewScopedAddress("1.2.3.4", network.ScopeCloudLocal),
  1886  	)
  1887  	c.Assert(err, jc.ErrorIsNil)
  1888  
  1889  	rel := s.addRelation(c, "wordpress", "mysql")
  1890  	relUnit, err := rel.Unit(s.wordpressUnit)
  1891  	c.Assert(err, jc.ErrorIsNil)
  1892  	s.assertInScope(c, relUnit, false)
  1893  
  1894  	args := params.RelationUnits{RelationUnits: []params.RelationUnit{
  1895  		{Relation: "relation-42", Unit: "unit-foo-0"},
  1896  		{Relation: rel.Tag().String(), Unit: "unit-wordpress-0"},
  1897  		{Relation: rel.Tag().String(), Unit: "unit-wordpress-0"},
  1898  		{Relation: "relation-42", Unit: "unit-wordpress-0"},
  1899  		{Relation: "relation-foo", Unit: "unit-wordpress-0"},
  1900  		{Relation: "application-wordpress", Unit: "unit-foo-0"},
  1901  		{Relation: "foo", Unit: "bar"},
  1902  		{Relation: rel.Tag().String(), Unit: "unit-mysql-0"},
  1903  		{Relation: rel.Tag().String(), Unit: "application-wordpress"},
  1904  		{Relation: rel.Tag().String(), Unit: "application-mysql"},
  1905  		{Relation: rel.Tag().String(), Unit: "user-foo"},
  1906  	}}
  1907  	result, err := s.uniter.EnterScope(args)
  1908  	c.Assert(err, jc.ErrorIsNil)
  1909  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  1910  		Results: []params.ErrorResult{
  1911  			{apiservertesting.ErrUnauthorized},
  1912  			{nil},
  1913  			{nil},
  1914  			{apiservertesting.ErrUnauthorized},
  1915  			{apiservertesting.ErrUnauthorized},
  1916  			{apiservertesting.ErrUnauthorized},
  1917  			{apiservertesting.ErrUnauthorized},
  1918  			{apiservertesting.ErrUnauthorized},
  1919  			{apiservertesting.ErrUnauthorized},
  1920  			{apiservertesting.ErrUnauthorized},
  1921  			{apiservertesting.ErrUnauthorized},
  1922  		},
  1923  	})
  1924  
  1925  	// Verify the scope changes and settings.
  1926  	s.assertInScope(c, relUnit, true)
  1927  	readSettings, err := relUnit.ReadSettings(s.wordpressUnit.Name())
  1928  	c.Assert(err, jc.ErrorIsNil)
  1929  	c.Assert(readSettings, gc.DeepEquals, map[string]interface{}{
  1930  		"private-address": "1.2.3.4",
  1931  		"ingress-address": "1.2.3.4",
  1932  		"egress-subnets":  "1.2.3.4/32",
  1933  	})
  1934  }
  1935  
  1936  func (s *uniterSuite) TestEnterScopeIgnoredForInvalidPrincipals(c *gc.C) {
  1937  	loggingCharm := s.Factory.MakeCharm(c, &factory.CharmParams{
  1938  		Name: "logging",
  1939  		URL:  "cs:quantal/logging-1",
  1940  	})
  1941  	logging := s.Factory.MakeApplication(c, &factory.ApplicationParams{
  1942  		Name:  "logging",
  1943  		Charm: loggingCharm,
  1944  	})
  1945  	mysqlRel := s.addRelation(c, "logging", "mysql")
  1946  	wpRel := s.addRelation(c, "logging", "wordpress")
  1947  
  1948  	// Create logging units for each of the mysql and wp units.
  1949  	mysqlRU, err := mysqlRel.Unit(s.mysqlUnit)
  1950  	c.Assert(err, jc.ErrorIsNil)
  1951  	err = mysqlRU.EnterScope(nil)
  1952  	c.Assert(err, jc.ErrorIsNil)
  1953  	mysqlLoggingU := findSubordinateUnit(c, logging, s.mysqlUnit)
  1954  	mysqlLoggingRU, err := mysqlRel.Unit(mysqlLoggingU)
  1955  	c.Assert(err, jc.ErrorIsNil)
  1956  	err = mysqlLoggingRU.EnterScope(nil)
  1957  	c.Assert(err, jc.ErrorIsNil)
  1958  
  1959  	wpRU, err := wpRel.Unit(s.wordpressUnit)
  1960  	c.Assert(err, jc.ErrorIsNil)
  1961  	err = wpRU.EnterScope(nil)
  1962  	c.Assert(err, jc.ErrorIsNil)
  1963  	wpLoggingU := findSubordinateUnit(c, logging, s.wordpressUnit)
  1964  	_, err = wpRel.Unit(wpLoggingU)
  1965  	c.Assert(err, jc.ErrorIsNil)
  1966  
  1967  	// Sanity check - a mysqlRel RU for wpLoggingU is invalid.
  1968  	ru, err := mysqlRel.Unit(wpLoggingU)
  1969  	c.Assert(err, jc.ErrorIsNil)
  1970  	valid, err := ru.Valid()
  1971  	c.Assert(err, jc.ErrorIsNil)
  1972  	c.Assert(valid, jc.IsFalse)
  1973  
  1974  	subAuthorizer := s.authorizer
  1975  	subAuthorizer.Tag = wpLoggingU.Tag()
  1976  	api, err := uniter.NewUniterAPI(facadetest.Context{
  1977  		State_:             s.State,
  1978  		Resources_:         s.resources,
  1979  		Auth_:              subAuthorizer,
  1980  		LeadershipChecker_: s.State.LeadershipChecker(),
  1981  	})
  1982  	c.Assert(err, jc.ErrorIsNil)
  1983  
  1984  	// Count how many relationscopes records there are beforehand.
  1985  	scopesBefore := countRelationScopes(c, s.State, mysqlRel)
  1986  	// One for each unit of mysql and the logging subordinate.
  1987  	c.Assert(scopesBefore, gc.Equals, 2)
  1988  
  1989  	// Asking the API to add wpLoggingU to mysqlRel silently
  1990  	// fails. This means that we'll drop incorrect requests from
  1991  	// uniters to re-enter the relation scope after the upgrade step
  1992  	// has cleaned them up.
  1993  	// See https://bugs.launchpad.net/juju/+bug/1699050
  1994  	args := params.RelationUnits{RelationUnits: []params.RelationUnit{{
  1995  		Relation: mysqlRel.Tag().String(),
  1996  		Unit:     wpLoggingU.Tag().String(),
  1997  	}}}
  1998  	result, err := api.EnterScope(args)
  1999  	c.Assert(err, jc.ErrorIsNil)
  2000  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  2001  		Results: []params.ErrorResult{{Error: nil}},
  2002  	})
  2003  
  2004  	scopesAfter := countRelationScopes(c, s.State, mysqlRel)
  2005  	c.Assert(scopesAfter, gc.Equals, scopesBefore)
  2006  }
  2007  
  2008  func countRelationScopes(c *gc.C, st *state.State, rel *state.Relation) int {
  2009  	coll := st.MongoSession().DB("juju").C("relationscopes")
  2010  	count, err := coll.Find(bson.M{"key": bson.M{
  2011  		"$regex": fmt.Sprintf(`^r#%d#`, rel.Id()),
  2012  	}}).Count()
  2013  	c.Assert(err, jc.ErrorIsNil)
  2014  	return count
  2015  }
  2016  
  2017  func (s *uniterSuite) TestLeaveScope(c *gc.C) {
  2018  	rel := s.addRelation(c, "wordpress", "mysql")
  2019  	relUnit, err := rel.Unit(s.wordpressUnit)
  2020  	c.Assert(err, jc.ErrorIsNil)
  2021  	settings := map[string]interface{}{
  2022  		"some": "settings",
  2023  	}
  2024  	err = relUnit.EnterScope(settings)
  2025  	c.Assert(err, jc.ErrorIsNil)
  2026  	s.assertInScope(c, relUnit, true)
  2027  
  2028  	args := params.RelationUnits{RelationUnits: []params.RelationUnit{
  2029  		{Relation: "relation-42", Unit: "unit-foo-0"},
  2030  		{Relation: rel.Tag().String(), Unit: "unit-wordpress-0"},
  2031  		{Relation: rel.Tag().String(), Unit: "unit-wordpress-0"},
  2032  		{Relation: "relation-42", Unit: "unit-wordpress-0"},
  2033  		{Relation: "relation-foo", Unit: "unit-wordpress-0"},
  2034  		{Relation: "application-wordpress", Unit: "unit-foo-0"},
  2035  		{Relation: "foo", Unit: "bar"},
  2036  		{Relation: rel.Tag().String(), Unit: "unit-mysql-0"},
  2037  		{Relation: rel.Tag().String(), Unit: "application-wordpress"},
  2038  		{Relation: rel.Tag().String(), Unit: "application-mysql"},
  2039  		{Relation: rel.Tag().String(), Unit: "user-foo"},
  2040  	}}
  2041  	result, err := s.uniter.LeaveScope(args)
  2042  	c.Assert(err, jc.ErrorIsNil)
  2043  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  2044  		Results: []params.ErrorResult{
  2045  			{apiservertesting.ErrUnauthorized},
  2046  			{nil},
  2047  			{nil},
  2048  			{apiservertesting.ErrUnauthorized},
  2049  			{apiservertesting.ErrUnauthorized},
  2050  			{apiservertesting.ErrUnauthorized},
  2051  			{apiservertesting.ErrUnauthorized},
  2052  			{apiservertesting.ErrUnauthorized},
  2053  			{apiservertesting.ErrUnauthorized},
  2054  			{apiservertesting.ErrUnauthorized},
  2055  			{apiservertesting.ErrUnauthorized},
  2056  		},
  2057  	})
  2058  
  2059  	// Verify the scope changes.
  2060  	s.assertInScope(c, relUnit, false)
  2061  	readSettings, err := relUnit.ReadSettings(s.wordpressUnit.Name())
  2062  	c.Assert(err, jc.ErrorIsNil)
  2063  	c.Assert(readSettings, gc.DeepEquals, settings)
  2064  }
  2065  
  2066  func (s *uniterSuite) TestRelationsSuspended(c *gc.C) {
  2067  	rel := s.addRelation(c, "wordpress", "mysql")
  2068  	relUnit, err := rel.Unit(s.wordpressUnit)
  2069  	c.Assert(err, jc.ErrorIsNil)
  2070  	err = relUnit.EnterScope(nil)
  2071  	c.Assert(err, jc.ErrorIsNil)
  2072  
  2073  	s.AddTestingApplication(c, "logging", s.AddTestingCharm(c, "logging"))
  2074  	rel2 := s.addRelation(c, "wordpress", "logging")
  2075  	err = rel2.SetSuspended(true, "")
  2076  	c.Assert(err, jc.ErrorIsNil)
  2077  
  2078  	args := params.Entities{
  2079  		Entities: []params.Entity{
  2080  			{s.wordpressUnit.Tag().String()},
  2081  			{s.mysqlUnit.Tag().String()},
  2082  			{"unit-unknown-1"},
  2083  			{"application-wordpress"},
  2084  			{"machine-0"},
  2085  			{rel.Tag().String()},
  2086  		},
  2087  	}
  2088  	expect := params.RelationUnitStatusResults{
  2089  		Results: []params.RelationUnitStatusResult{
  2090  			{RelationResults: []params.RelationUnitStatus{{
  2091  				RelationTag: rel.Tag().String(),
  2092  				InScope:     true,
  2093  				Suspended:   false,
  2094  			}, {
  2095  				RelationTag: rel2.Tag().String(),
  2096  				InScope:     false,
  2097  				Suspended:   true,
  2098  			}},
  2099  			},
  2100  			{Error: apiservertesting.ErrUnauthorized},
  2101  			{Error: apiservertesting.ErrUnauthorized},
  2102  			{Error: apiservertesting.ErrUnauthorized},
  2103  			{Error: apiservertesting.ErrUnauthorized},
  2104  			{Error: apiservertesting.ErrUnauthorized},
  2105  		},
  2106  	}
  2107  	check := func() {
  2108  		result, err := s.uniter.RelationsStatus(args)
  2109  		c.Assert(err, jc.ErrorIsNil)
  2110  		c.Assert(result, gc.DeepEquals, expect)
  2111  	}
  2112  	check()
  2113  	err = relUnit.PrepareLeaveScope()
  2114  	c.Assert(err, jc.ErrorIsNil)
  2115  	check()
  2116  }
  2117  
  2118  func (s *uniterSuite) TestSetRelationsStatusNotLeader(c *gc.C) {
  2119  	rel := s.addRelation(c, "wordpress", "mysql")
  2120  	relUnit, err := rel.Unit(s.wordpressUnit)
  2121  	c.Assert(err, jc.ErrorIsNil)
  2122  	err = relUnit.EnterScope(nil)
  2123  	c.Assert(err, jc.ErrorIsNil)
  2124  
  2125  	args := params.RelationStatusArgs{
  2126  		Args: []params.RelationStatusArg{
  2127  			{s.wordpressUnit.Tag().String(), rel.Id(), params.Suspended, "message"},
  2128  		},
  2129  	}
  2130  	result, err := s.uniter.SetRelationStatus(args)
  2131  	c.Assert(err, jc.ErrorIsNil)
  2132  	c.Assert(result.OneError(), gc.ErrorMatches, `"wordpress/0" is not leader of "wordpress"`)
  2133  }
  2134  
  2135  func (s *uniterSuite) TestSetRelationsStatusLeader(c *gc.C) {
  2136  	rel := s.addRelation(c, "wordpress", "mysql")
  2137  	err := rel.SetStatus(status.StatusInfo{Status: status.Suspending, Message: "going, going"})
  2138  	c.Assert(err, jc.ErrorIsNil)
  2139  	relUnit, err := rel.Unit(s.wordpressUnit)
  2140  	c.Assert(err, jc.ErrorIsNil)
  2141  	err = relUnit.EnterScope(nil)
  2142  	c.Assert(err, jc.ErrorIsNil)
  2143  
  2144  	s.AddTestingApplication(c, "logging", s.AddTestingCharm(c, "logging"))
  2145  	rel2 := s.addRelation(c, "wordpress", "logging")
  2146  	err = rel2.SetSuspended(true, "")
  2147  	c.Assert(err, jc.ErrorIsNil)
  2148  	err = rel.SetStatus(status.StatusInfo{Status: status.Suspending, Message: ""})
  2149  	c.Assert(err, jc.ErrorIsNil)
  2150  
  2151  	s.AddTestingApplication(c, "wp2", s.wpCharm)
  2152  	rel3 := s.addRelation(c, "wp2", "logging")
  2153  	c.Assert(err, jc.ErrorIsNil)
  2154  
  2155  	args := params.RelationStatusArgs{
  2156  		Args: []params.RelationStatusArg{
  2157  			{s.wordpressUnit.Tag().String(), rel.Id(), params.Suspended, "message"},
  2158  			// This arg omits the explicit unit tag to test older servers.
  2159  			{RelationId: rel2.Id(), Status: params.Suspended, Message: "gone"},
  2160  			{s.wordpressUnit.Tag().String(), rel3.Id(), params.Broken, ""},
  2161  			{RelationId: 4},
  2162  		},
  2163  	}
  2164  	expect := params.ErrorResults{
  2165  		Results: []params.ErrorResult{
  2166  			{},
  2167  			{},
  2168  			{Error: apiservertesting.ErrUnauthorized},
  2169  			{Error: apiservertesting.ErrUnauthorized},
  2170  		},
  2171  	}
  2172  	check := func(rel *state.Relation, expectedStatus status.Status, expectedMessage string) {
  2173  		err = rel.Refresh()
  2174  		c.Assert(err, jc.ErrorIsNil)
  2175  		relStatus, err := rel.Status()
  2176  		c.Assert(err, jc.ErrorIsNil)
  2177  		c.Assert(relStatus.Status, gc.Equals, expectedStatus)
  2178  		c.Assert(relStatus.Message, gc.Equals, expectedMessage)
  2179  	}
  2180  
  2181  	err = s.State.LeadershipClaimer().ClaimLeadership("wordpress", "wordpress/0", time.Minute)
  2182  	c.Assert(err, jc.ErrorIsNil)
  2183  
  2184  	result, err := s.uniter.SetRelationStatus(args)
  2185  	c.Assert(err, jc.ErrorIsNil)
  2186  	c.Assert(result, gc.DeepEquals, expect)
  2187  	check(rel, status.Suspended, "message")
  2188  	check(rel2, status.Suspended, "gone")
  2189  }
  2190  
  2191  func (s *uniterSuite) TestReadSettings(c *gc.C) {
  2192  	rel := s.addRelation(c, "wordpress", "mysql")
  2193  	relUnit, err := rel.Unit(s.wordpressUnit)
  2194  	c.Assert(err, jc.ErrorIsNil)
  2195  	settings := map[string]interface{}{
  2196  		"some": "settings",
  2197  	}
  2198  	err = relUnit.EnterScope(settings)
  2199  	c.Assert(err, jc.ErrorIsNil)
  2200  	s.assertInScope(c, relUnit, true)
  2201  
  2202  	args := params.RelationUnits{RelationUnits: []params.RelationUnit{
  2203  		{Relation: "relation-42", Unit: "unit-foo-0"},
  2204  		{Relation: rel.Tag().String(), Unit: "unit-wordpress-0"},
  2205  		{Relation: rel.Tag().String(), Unit: "unit-mysql-0"},
  2206  		{Relation: "relation-42", Unit: "unit-wordpress-0"},
  2207  		{Relation: "relation-foo", Unit: ""},
  2208  		{Relation: "application-wordpress", Unit: "unit-foo-0"},
  2209  		{Relation: "foo", Unit: "bar"},
  2210  		{Relation: rel.Tag().String(), Unit: "unit-mysql-0"},
  2211  		{Relation: rel.Tag().String(), Unit: "application-wordpress"},
  2212  		{Relation: rel.Tag().String(), Unit: "application-mysql"},
  2213  		{Relation: rel.Tag().String(), Unit: "user-foo"},
  2214  	}}
  2215  	result, err := s.uniter.ReadSettings(args)
  2216  	c.Assert(err, jc.ErrorIsNil)
  2217  	c.Assert(result, gc.DeepEquals, params.SettingsResults{
  2218  		Results: []params.SettingsResult{
  2219  			{Error: apiservertesting.ErrUnauthorized},
  2220  			{Settings: params.Settings{
  2221  				"some": "settings",
  2222  			}},
  2223  			{Error: apiservertesting.ErrUnauthorized},
  2224  			{Error: apiservertesting.ErrUnauthorized},
  2225  			{Error: apiservertesting.ErrUnauthorized},
  2226  			{Error: apiservertesting.ErrUnauthorized},
  2227  			{Error: apiservertesting.ErrUnauthorized},
  2228  			{Error: apiservertesting.ErrUnauthorized},
  2229  			{Error: apiservertesting.ErrUnauthorized},
  2230  			{Error: apiservertesting.ErrUnauthorized},
  2231  			{Error: apiservertesting.ErrUnauthorized},
  2232  		},
  2233  	})
  2234  }
  2235  
  2236  func (s *uniterSuite) TestReadSettingsWithNonStringValuesFails(c *gc.C) {
  2237  	rel := s.addRelation(c, "wordpress", "mysql")
  2238  	relUnit, err := rel.Unit(s.wordpressUnit)
  2239  	c.Assert(err, jc.ErrorIsNil)
  2240  	settings := map[string]interface{}{
  2241  		"other":        "things",
  2242  		"invalid-bool": false,
  2243  	}
  2244  	err = relUnit.EnterScope(settings)
  2245  	c.Assert(err, jc.ErrorIsNil)
  2246  	s.assertInScope(c, relUnit, true)
  2247  
  2248  	args := params.RelationUnits{RelationUnits: []params.RelationUnit{
  2249  		{Relation: rel.Tag().String(), Unit: "unit-wordpress-0"},
  2250  	}}
  2251  	expectErr := `unexpected relation setting "invalid-bool": expected string, got bool`
  2252  	result, err := s.uniter.ReadSettings(args)
  2253  	c.Assert(err, jc.ErrorIsNil)
  2254  	c.Assert(result, gc.DeepEquals, params.SettingsResults{
  2255  		Results: []params.SettingsResult{
  2256  			{Error: &params.Error{Message: expectErr}},
  2257  		},
  2258  	})
  2259  }
  2260  
  2261  func (s *uniterSuite) TestReadRemoteSettings(c *gc.C) {
  2262  	rel := s.addRelation(c, "wordpress", "mysql")
  2263  	relUnit, err := rel.Unit(s.wordpressUnit)
  2264  	c.Assert(err, jc.ErrorIsNil)
  2265  	settings := map[string]interface{}{
  2266  		"some": "settings",
  2267  	}
  2268  	err = relUnit.EnterScope(settings)
  2269  	c.Assert(err, jc.ErrorIsNil)
  2270  	s.assertInScope(c, relUnit, true)
  2271  
  2272  	// First test most of the invalid args tests and try to read the
  2273  	// (unset) remote unit settings.
  2274  	args := params.RelationUnitPairs{RelationUnitPairs: []params.RelationUnitPair{
  2275  		{Relation: "relation-42", LocalUnit: "unit-foo-0", RemoteUnit: "foo"},
  2276  		{Relation: rel.Tag().String(), LocalUnit: "unit-wordpress-0", RemoteUnit: "unit-wordpress-0"},
  2277  		{Relation: rel.Tag().String(), LocalUnit: "unit-wordpress-0", RemoteUnit: "unit-mysql-0"},
  2278  		{Relation: "relation-42", LocalUnit: "unit-wordpress-0", RemoteUnit: ""},
  2279  		{Relation: "relation-foo", LocalUnit: "", RemoteUnit: ""},
  2280  		{Relation: "application-wordpress", LocalUnit: "unit-foo-0", RemoteUnit: "user-foo"},
  2281  		{Relation: "foo", LocalUnit: "bar", RemoteUnit: "baz"},
  2282  		{Relation: rel.Tag().String(), LocalUnit: "unit-mysql-0", RemoteUnit: "unit-wordpress-0"},
  2283  		{Relation: rel.Tag().String(), LocalUnit: "application-wordpress", RemoteUnit: "application-mysql"},
  2284  		{Relation: rel.Tag().String(), LocalUnit: "application-mysql", RemoteUnit: "foo"},
  2285  		{Relation: rel.Tag().String(), LocalUnit: "user-foo", RemoteUnit: "unit-wordpress-0"},
  2286  	}}
  2287  	result, err := s.uniter.ReadRemoteSettings(args)
  2288  
  2289  	// We don't set the remote unit settings on purpose to test the error.
  2290  	expectErr := `cannot read settings for unit "mysql/0" in relation "wordpress:db mysql:server": unit "mysql/0": settings`
  2291  	c.Assert(err, jc.ErrorIsNil)
  2292  	c.Assert(result, jc.DeepEquals, params.SettingsResults{
  2293  		Results: []params.SettingsResult{
  2294  			{Error: apiservertesting.ErrUnauthorized},
  2295  			{Error: apiservertesting.ErrUnauthorized},
  2296  			{Error: apiservertesting.NotFoundError(expectErr)},
  2297  			{Error: apiservertesting.ErrUnauthorized},
  2298  			{Error: apiservertesting.ErrUnauthorized},
  2299  			{Error: apiservertesting.ErrUnauthorized},
  2300  			{Error: apiservertesting.ErrUnauthorized},
  2301  			{Error: apiservertesting.ErrUnauthorized},
  2302  			{Error: apiservertesting.ErrUnauthorized},
  2303  			{Error: apiservertesting.ErrUnauthorized},
  2304  			{Error: apiservertesting.ErrUnauthorized},
  2305  		},
  2306  	})
  2307  
  2308  	// Now leave the mysqlUnit and re-enter with new settings.
  2309  	relUnit, err = rel.Unit(s.mysqlUnit)
  2310  	c.Assert(err, jc.ErrorIsNil)
  2311  	settings = map[string]interface{}{
  2312  		"other": "things",
  2313  	}
  2314  	err = relUnit.LeaveScope()
  2315  	c.Assert(err, jc.ErrorIsNil)
  2316  	s.assertInScope(c, relUnit, false)
  2317  	err = relUnit.EnterScope(settings)
  2318  	c.Assert(err, jc.ErrorIsNil)
  2319  	s.assertInScope(c, relUnit, true)
  2320  
  2321  	// Test the remote unit settings can be read.
  2322  	args = params.RelationUnitPairs{RelationUnitPairs: []params.RelationUnitPair{{
  2323  		Relation:   rel.Tag().String(),
  2324  		LocalUnit:  "unit-wordpress-0",
  2325  		RemoteUnit: "unit-mysql-0",
  2326  	}}}
  2327  	expect := params.SettingsResults{
  2328  		Results: []params.SettingsResult{
  2329  			{Settings: params.Settings{
  2330  				"other": "things",
  2331  			}},
  2332  		},
  2333  	}
  2334  	result, err = s.uniter.ReadRemoteSettings(args)
  2335  	c.Assert(err, jc.ErrorIsNil)
  2336  	c.Assert(result, gc.DeepEquals, expect)
  2337  
  2338  	// Now destroy the remote unit, and check its settings can still be read.
  2339  	err = s.mysqlUnit.Destroy()
  2340  	c.Assert(err, jc.ErrorIsNil)
  2341  	err = s.mysqlUnit.EnsureDead()
  2342  	c.Assert(err, jc.ErrorIsNil)
  2343  	err = s.mysqlUnit.Remove()
  2344  	c.Assert(err, jc.ErrorIsNil)
  2345  	result, err = s.uniter.ReadRemoteSettings(args)
  2346  	c.Assert(err, jc.ErrorIsNil)
  2347  	c.Assert(result, gc.DeepEquals, expect)
  2348  }
  2349  
  2350  func (s *uniterSuite) TestReadRemoteSettingsWithNonStringValuesFails(c *gc.C) {
  2351  	rel := s.addRelation(c, "wordpress", "mysql")
  2352  	relUnit, err := rel.Unit(s.mysqlUnit)
  2353  	c.Assert(err, jc.ErrorIsNil)
  2354  	settings := map[string]interface{}{
  2355  		"other":        "things",
  2356  		"invalid-bool": false,
  2357  	}
  2358  	err = relUnit.EnterScope(settings)
  2359  	c.Assert(err, jc.ErrorIsNil)
  2360  	s.assertInScope(c, relUnit, true)
  2361  
  2362  	args := params.RelationUnitPairs{RelationUnitPairs: []params.RelationUnitPair{{
  2363  		Relation:   rel.Tag().String(),
  2364  		LocalUnit:  "unit-wordpress-0",
  2365  		RemoteUnit: "unit-mysql-0",
  2366  	}}}
  2367  	expectErr := `unexpected relation setting "invalid-bool": expected string, got bool`
  2368  	result, err := s.uniter.ReadRemoteSettings(args)
  2369  	c.Assert(err, jc.ErrorIsNil)
  2370  	c.Assert(result, gc.DeepEquals, params.SettingsResults{
  2371  		Results: []params.SettingsResult{
  2372  			{Error: &params.Error{Message: expectErr}},
  2373  		},
  2374  	})
  2375  }
  2376  
  2377  func (s *uniterSuite) TestUpdateSettings(c *gc.C) {
  2378  	rel := s.addRelation(c, "wordpress", "mysql")
  2379  	relUnit, err := rel.Unit(s.wordpressUnit)
  2380  	c.Assert(err, jc.ErrorIsNil)
  2381  	settings := map[string]interface{}{
  2382  		"some":  "settings",
  2383  		"other": "stuff",
  2384  	}
  2385  	err = relUnit.EnterScope(settings)
  2386  	s.assertInScope(c, relUnit, true)
  2387  
  2388  	newSettings := params.Settings{
  2389  		"some":  "different",
  2390  		"other": "",
  2391  	}
  2392  
  2393  	args := params.RelationUnitsSettings{RelationUnits: []params.RelationUnitSettings{
  2394  		{Relation: "relation-42", Unit: "unit-foo-0", Settings: nil},
  2395  		{Relation: rel.Tag().String(), Unit: "unit-wordpress-0", Settings: newSettings},
  2396  		{Relation: "relation-42", Unit: "unit-wordpress-0", Settings: nil},
  2397  		{Relation: "relation-foo", Unit: "unit-wordpress-0", Settings: nil},
  2398  		{Relation: "application-wordpress", Unit: "unit-foo-0", Settings: nil},
  2399  		{Relation: "foo", Unit: "bar", Settings: nil},
  2400  		{Relation: rel.Tag().String(), Unit: "unit-mysql-0", Settings: nil},
  2401  		{Relation: rel.Tag().String(), Unit: "application-wordpress", Settings: nil},
  2402  		{Relation: rel.Tag().String(), Unit: "application-mysql", Settings: nil},
  2403  		{Relation: rel.Tag().String(), Unit: "user-foo", Settings: nil},
  2404  	}}
  2405  	result, err := s.uniter.UpdateSettings(args)
  2406  	c.Assert(err, jc.ErrorIsNil)
  2407  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  2408  		Results: []params.ErrorResult{
  2409  			{apiservertesting.ErrUnauthorized},
  2410  			{nil},
  2411  			{apiservertesting.ErrUnauthorized},
  2412  			{apiservertesting.ErrUnauthorized},
  2413  			{apiservertesting.ErrUnauthorized},
  2414  			{apiservertesting.ErrUnauthorized},
  2415  			{apiservertesting.ErrUnauthorized},
  2416  			{apiservertesting.ErrUnauthorized},
  2417  			{apiservertesting.ErrUnauthorized},
  2418  			{apiservertesting.ErrUnauthorized},
  2419  		},
  2420  	})
  2421  
  2422  	// Verify the settings were saved.
  2423  	s.assertInScope(c, relUnit, true)
  2424  	readSettings, err := relUnit.ReadSettings(s.wordpressUnit.Name())
  2425  	c.Assert(err, jc.ErrorIsNil)
  2426  	c.Assert(readSettings, gc.DeepEquals, map[string]interface{}{
  2427  		"some": "different",
  2428  	})
  2429  }
  2430  
  2431  func (s *uniterSuite) TestWatchRelationUnits(c *gc.C) {
  2432  	// Add a relation between wordpress and mysql and enter scope with
  2433  	// mysqlUnit.
  2434  	rel := s.addRelation(c, "wordpress", "mysql")
  2435  	myRelUnit, err := rel.Unit(s.mysqlUnit)
  2436  	c.Assert(err, jc.ErrorIsNil)
  2437  	err = myRelUnit.EnterScope(nil)
  2438  	c.Assert(err, jc.ErrorIsNil)
  2439  	s.assertInScope(c, myRelUnit, true)
  2440  
  2441  	c.Assert(s.resources.Count(), gc.Equals, 0)
  2442  
  2443  	s.WaitForModelWatchersIdle(c, s.Model.UUID())
  2444  	args := params.RelationUnits{RelationUnits: []params.RelationUnit{
  2445  		{Relation: "relation-42", Unit: "unit-foo-0"},
  2446  		{Relation: rel.Tag().String(), Unit: "unit-wordpress-0"},
  2447  		{Relation: rel.Tag().String(), Unit: "unit-mysql-0"},
  2448  		{Relation: "relation-42", Unit: "unit-wordpress-0"},
  2449  		{Relation: "relation-foo", Unit: ""},
  2450  		{Relation: "application-wordpress", Unit: "unit-foo-0"},
  2451  		{Relation: "foo", Unit: "bar"},
  2452  		{Relation: rel.Tag().String(), Unit: "unit-mysql-0"},
  2453  		{Relation: rel.Tag().String(), Unit: "application-wordpress"},
  2454  		{Relation: rel.Tag().String(), Unit: "application-mysql"},
  2455  		{Relation: rel.Tag().String(), Unit: "user-foo"},
  2456  	}}
  2457  	result, err := s.uniter.WatchRelationUnits(args)
  2458  	c.Assert(err, jc.ErrorIsNil)
  2459  	// UnitSettings versions are volatile, so we don't check them.
  2460  	// We just make sure the keys of the Changed field are as
  2461  	// expected.
  2462  	c.Assert(result.Results, gc.HasLen, len(args.RelationUnits))
  2463  	mysqlChanges := result.Results[1].Changes
  2464  	c.Assert(mysqlChanges, gc.NotNil)
  2465  	changed, ok := mysqlChanges.Changed["mysql/0"]
  2466  	c.Assert(ok, jc.IsTrue)
  2467  	expectChanges := params.RelationUnitsChange{
  2468  		Changed: map[string]params.UnitSettings{
  2469  			"mysql/0": {changed.Version},
  2470  		},
  2471  	}
  2472  	c.Assert(result, gc.DeepEquals, params.RelationUnitsWatchResults{
  2473  		Results: []params.RelationUnitsWatchResult{
  2474  			{Error: apiservertesting.ErrUnauthorized},
  2475  			{
  2476  				RelationUnitsWatcherId: "1",
  2477  				Changes:                expectChanges,
  2478  			},
  2479  			{Error: apiservertesting.ErrUnauthorized},
  2480  			{Error: apiservertesting.ErrUnauthorized},
  2481  			{Error: apiservertesting.ErrUnauthorized},
  2482  			{Error: apiservertesting.ErrUnauthorized},
  2483  			{Error: apiservertesting.ErrUnauthorized},
  2484  			{Error: apiservertesting.ErrUnauthorized},
  2485  			{Error: apiservertesting.ErrUnauthorized},
  2486  			{Error: apiservertesting.ErrUnauthorized},
  2487  			{Error: apiservertesting.ErrUnauthorized},
  2488  		},
  2489  	})
  2490  
  2491  	// Verify the resource was registered and stop when done
  2492  	c.Assert(s.resources.Count(), gc.Equals, 1)
  2493  	resource := s.resources.Get("1")
  2494  	defer statetesting.AssertStop(c, resource)
  2495  
  2496  	// Check that the Watch has consumed the initial event ("returned" in
  2497  	// the Watch call)
  2498  	wc := statetesting.NewRelationUnitsWatcherC(c, s.State, resource.(state.RelationUnitsWatcher))
  2499  	wc.AssertNoChange()
  2500  
  2501  	// Leave scope with mysqlUnit and check it's detected.
  2502  	err = myRelUnit.LeaveScope()
  2503  	c.Assert(err, jc.ErrorIsNil)
  2504  	s.assertInScope(c, myRelUnit, false)
  2505  
  2506  	wc.AssertChange(nil, []string{"mysql/0"})
  2507  }
  2508  
  2509  func (s *uniterSuite) TestAPIAddresses(c *gc.C) {
  2510  	hostPorts := [][]network.HostPort{
  2511  		network.NewHostPorts(1234, "0.1.2.3"),
  2512  	}
  2513  	err := s.State.SetAPIHostPorts(hostPorts)
  2514  	c.Assert(err, jc.ErrorIsNil)
  2515  
  2516  	result, err := s.uniter.APIAddresses()
  2517  	c.Assert(err, jc.ErrorIsNil)
  2518  	c.Assert(result, gc.DeepEquals, params.StringsResult{
  2519  		Result: []string{"0.1.2.3:1234"},
  2520  	})
  2521  }
  2522  
  2523  func (s *uniterSuite) TestWatchUnitAddressesHash(c *gc.C) {
  2524  	c.Assert(s.resources.Count(), gc.Equals, 0)
  2525  
  2526  	args := params.Entities{Entities: []params.Entity{
  2527  		{Tag: "unit-mysql-0"},
  2528  		{Tag: "unit-wordpress-0"},
  2529  		{Tag: "unit-foo-42"},
  2530  		{Tag: "machine-0"},
  2531  		{Tag: "application-wordpress"},
  2532  	}}
  2533  	result, err := s.uniter.WatchUnitAddressesHash(args)
  2534  	c.Assert(err, jc.ErrorIsNil)
  2535  	c.Assert(result, gc.DeepEquals, params.StringsWatchResults{
  2536  		Results: []params.StringsWatchResult{
  2537  			{Error: apiservertesting.ErrUnauthorized},
  2538  			{
  2539  				StringsWatcherId: "1",
  2540  				// This is an empty sha256 hash.
  2541  				Changes: []string{"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"},
  2542  			},
  2543  			{Error: apiservertesting.ErrUnauthorized},
  2544  			{Error: apiservertesting.ErrUnauthorized},
  2545  			{Error: apiservertesting.ErrUnauthorized},
  2546  		},
  2547  	})
  2548  
  2549  	// Verify the resource was registered and stop when done
  2550  	c.Assert(s.resources.Count(), gc.Equals, 1)
  2551  	resource := s.resources.Get("1")
  2552  	defer statetesting.AssertStop(c, resource)
  2553  
  2554  	// Check that the Watch has consumed the initial event ("returned" in
  2555  	// the Watch call)
  2556  	wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher))
  2557  	wc.AssertNoChange()
  2558  }
  2559  
  2560  func (s *uniterSuite) TestWatchCAASUnitAddressesHash(c *gc.C) {
  2561  	_, cm, _, _ := s.setupCAASModel(c)
  2562  	c.Assert(s.resources.Count(), gc.Equals, 0)
  2563  
  2564  	args := params.Entities{Entities: []params.Entity{
  2565  		{Tag: "unit-mysql-0"},
  2566  		{Tag: "unit-gitlab-0"},
  2567  		{Tag: "unit-foo-42"},
  2568  		{Tag: "machine-0"},
  2569  		{Tag: "application-gitlab"},
  2570  	}}
  2571  
  2572  	uniterAPI, err := uniter.NewUniterAPI(facadetest.Context{
  2573  		State_:             cm.State(),
  2574  		Resources_:         s.resources,
  2575  		Auth_:              s.authorizer,
  2576  		LeadershipChecker_: s.State.LeadershipChecker(),
  2577  	})
  2578  	c.Assert(err, jc.ErrorIsNil)
  2579  
  2580  	result, err := uniterAPI.WatchUnitAddressesHash(args)
  2581  	c.Assert(err, jc.ErrorIsNil)
  2582  	c.Assert(result, gc.DeepEquals, params.StringsWatchResults{
  2583  		Results: []params.StringsWatchResult{
  2584  			{Error: apiservertesting.ErrUnauthorized},
  2585  			{
  2586  				StringsWatcherId: "1",
  2587  				// The container doesn't have an address.
  2588  				Changes: []string{""},
  2589  			},
  2590  			{Error: apiservertesting.ErrUnauthorized},
  2591  			{Error: apiservertesting.ErrUnauthorized},
  2592  			{Error: apiservertesting.ErrUnauthorized},
  2593  		},
  2594  	})
  2595  
  2596  	// Verify the resource was registered and stop when done
  2597  	c.Assert(s.resources.Count(), gc.Equals, 1)
  2598  	resource := s.resources.Get("1")
  2599  	defer statetesting.AssertStop(c, resource)
  2600  
  2601  	// Check that the Watch has consumed the initial event ("returned" in
  2602  	// the Watch call)
  2603  	wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher))
  2604  	wc.AssertNoChange()
  2605  }
  2606  
  2607  func (s *uniterSuite) TestGetMeterStatusUnauthenticated(c *gc.C) {
  2608  	args := params.Entities{Entities: []params.Entity{{s.mysqlUnit.Tag().String()}}}
  2609  	result, err := s.uniter.GetMeterStatus(args)
  2610  	c.Assert(err, jc.ErrorIsNil)
  2611  	c.Assert(result.Results, gc.HasLen, 1)
  2612  	c.Assert(result.Results[0].Error, gc.ErrorMatches, "permission denied")
  2613  	c.Assert(result.Results[0].Code, gc.Equals, "")
  2614  	c.Assert(result.Results[0].Info, gc.Equals, "")
  2615  }
  2616  
  2617  func (s *uniterSuite) TestGetMeterStatusBadTag(c *gc.C) {
  2618  	tags := []string{
  2619  		"user-admin",
  2620  		"unit-nosuchunit",
  2621  		"thisisnotatag",
  2622  		"machine-0",
  2623  		"model-blah",
  2624  	}
  2625  	args := params.Entities{Entities: make([]params.Entity, len(tags))}
  2626  	for i, tag := range tags {
  2627  		args.Entities[i] = params.Entity{Tag: tag}
  2628  	}
  2629  	result, err := s.uniter.GetMeterStatus(args)
  2630  	c.Assert(err, jc.ErrorIsNil)
  2631  	c.Assert(result.Results, gc.HasLen, len(tags))
  2632  	for i, result := range result.Results {
  2633  		c.Logf("checking result %d", i)
  2634  		c.Assert(result.Code, gc.Equals, "")
  2635  		c.Assert(result.Info, gc.Equals, "")
  2636  		c.Assert(result.Error, gc.ErrorMatches, "permission denied")
  2637  	}
  2638  }
  2639  
  2640  func (s *uniterSuite) addRelatedApplication(c *gc.C, firstSvc, relatedSvc string, unit *state.Unit) (*state.Relation, *state.Application, *state.Unit) {
  2641  	relatedApplication := s.AddTestingApplication(c, relatedSvc, s.AddTestingCharm(c, relatedSvc))
  2642  	rel := s.addRelation(c, firstSvc, relatedSvc)
  2643  	relUnit, err := rel.Unit(unit)
  2644  	c.Assert(err, jc.ErrorIsNil)
  2645  	err = relUnit.EnterScope(nil)
  2646  	c.Assert(err, jc.ErrorIsNil)
  2647  	relatedUnit, err := s.State.Unit(relatedSvc + "/0")
  2648  	c.Assert(err, jc.ErrorIsNil)
  2649  	return rel, relatedApplication, relatedUnit
  2650  }
  2651  
  2652  func (s *uniterSuite) TestRequestReboot(c *gc.C) {
  2653  	args := params.Entities{Entities: []params.Entity{
  2654  		{Tag: s.machine0.Tag().String()},
  2655  		{Tag: s.machine1.Tag().String()},
  2656  		{Tag: "bogus"},
  2657  		{Tag: "nasty-tag"},
  2658  	}}
  2659  	errResult, err := s.uniter.RequestReboot(args)
  2660  	c.Assert(err, jc.ErrorIsNil)
  2661  	c.Assert(errResult, gc.DeepEquals, params.ErrorResults{
  2662  		Results: []params.ErrorResult{
  2663  			{Error: nil},
  2664  			{Error: apiservertesting.ErrUnauthorized},
  2665  			{Error: apiservertesting.ErrUnauthorized},
  2666  			{Error: apiservertesting.ErrUnauthorized},
  2667  		}})
  2668  
  2669  	rFlag, err := s.machine0.GetRebootFlag()
  2670  	c.Assert(err, jc.ErrorIsNil)
  2671  	c.Assert(rFlag, jc.IsTrue)
  2672  
  2673  	rFlag, err = s.machine1.GetRebootFlag()
  2674  	c.Assert(err, jc.ErrorIsNil)
  2675  	c.Assert(rFlag, jc.IsFalse)
  2676  }
  2677  
  2678  func checkUnorderedActionIdsEqual(c *gc.C, ids []string, results params.StringsWatchResults) {
  2679  	c.Assert(results, gc.NotNil)
  2680  	content := results.Results
  2681  	c.Assert(len(content), gc.Equals, 1)
  2682  	result := content[0]
  2683  	c.Assert(result.StringsWatcherId, gc.Equals, "1")
  2684  	obtainedIds := map[string]int{}
  2685  	expectedIds := map[string]int{}
  2686  	for _, id := range ids {
  2687  		expectedIds[id]++
  2688  	}
  2689  	// The count of each ID that has been seen.
  2690  	for _, change := range result.Changes {
  2691  		obtainedIds[change]++
  2692  	}
  2693  	c.Check(obtainedIds, jc.DeepEquals, expectedIds)
  2694  }
  2695  
  2696  func (s *uniterSuite) TestStorageAttachments(c *gc.C) {
  2697  	// We need to set up a unit that has storage metadata defined.
  2698  	ch := s.AddTestingCharm(c, "storage-block")
  2699  	sCons := map[string]state.StorageConstraints{
  2700  		"data": {Pool: "", Size: 1024, Count: 1},
  2701  	}
  2702  	application := s.AddTestingApplicationWithStorage(c, "storage-block", ch, sCons)
  2703  	unit, err := application.AddUnit(state.AddUnitParams{})
  2704  	c.Assert(err, jc.ErrorIsNil)
  2705  	err = s.State.AssignUnit(unit, state.AssignCleanEmpty)
  2706  	c.Assert(err, jc.ErrorIsNil)
  2707  	assignedMachineId, err := unit.AssignedMachineId()
  2708  	c.Assert(err, jc.ErrorIsNil)
  2709  	machine, err := s.State.Machine(assignedMachineId)
  2710  	c.Assert(err, jc.ErrorIsNil)
  2711  
  2712  	volumeAttachments, err := machine.VolumeAttachments()
  2713  	c.Assert(err, jc.ErrorIsNil)
  2714  	c.Assert(volumeAttachments, gc.HasLen, 1)
  2715  
  2716  	err = machine.SetProvisioned("inst-id", "", "fake_nonce", nil)
  2717  	c.Assert(err, jc.ErrorIsNil)
  2718  
  2719  	sb, err := state.NewStorageBackend(s.State)
  2720  	c.Assert(err, jc.ErrorIsNil)
  2721  	err = sb.SetVolumeInfo(
  2722  		volumeAttachments[0].Volume(),
  2723  		state.VolumeInfo{VolumeId: "vol-123", Size: 456},
  2724  	)
  2725  	c.Assert(err, jc.ErrorIsNil)
  2726  
  2727  	err = sb.SetVolumeAttachmentInfo(
  2728  		machine.MachineTag(),
  2729  		volumeAttachments[0].Volume(),
  2730  		state.VolumeAttachmentInfo{DeviceName: "xvdf1"},
  2731  	)
  2732  	c.Assert(err, jc.ErrorIsNil)
  2733  
  2734  	password, err := utils.RandomPassword()
  2735  	err = unit.SetPassword(password)
  2736  	c.Assert(err, jc.ErrorIsNil)
  2737  	st := s.OpenAPIAs(c, unit.Tag(), password)
  2738  	uniter, err := st.Uniter()
  2739  	c.Assert(err, jc.ErrorIsNil)
  2740  
  2741  	attachments, err := uniter.UnitStorageAttachments(unit.UnitTag())
  2742  	c.Assert(err, jc.ErrorIsNil)
  2743  	c.Assert(attachments, gc.DeepEquals, []params.StorageAttachmentId{{
  2744  		StorageTag: "storage-data-0",
  2745  		UnitTag:    unit.Tag().String(),
  2746  	}})
  2747  }
  2748  
  2749  func (s *uniterSuite) TestUnitStatus(c *gc.C) {
  2750  	now := time.Now()
  2751  	sInfo := status.StatusInfo{
  2752  		Status:  status.Maintenance,
  2753  		Message: "blah",
  2754  		Since:   &now,
  2755  	}
  2756  	err := s.wordpressUnit.SetStatus(sInfo)
  2757  	c.Assert(err, jc.ErrorIsNil)
  2758  	sInfo = status.StatusInfo{
  2759  		Status:  status.Terminated,
  2760  		Message: "foo",
  2761  		Since:   &now,
  2762  	}
  2763  	err = s.mysqlUnit.SetStatus(sInfo)
  2764  	c.Assert(err, jc.ErrorIsNil)
  2765  
  2766  	args := params.Entities{
  2767  		Entities: []params.Entity{
  2768  			{Tag: "unit-mysql-0"},
  2769  			{Tag: "unit-wordpress-0"},
  2770  			{Tag: "unit-foo-42"},
  2771  			{Tag: "machine-1"},
  2772  			{Tag: "invalid"},
  2773  		}}
  2774  	result, err := s.uniter.UnitStatus(args)
  2775  	c.Assert(err, jc.ErrorIsNil)
  2776  	// Zero out the updated timestamps so we can easily check the results.
  2777  	for i, statusResult := range result.Results {
  2778  		r := statusResult
  2779  		if r.Status != "" {
  2780  			c.Assert(r.Since, gc.NotNil)
  2781  		}
  2782  		r.Since = nil
  2783  		result.Results[i] = r
  2784  	}
  2785  	c.Assert(result, gc.DeepEquals, params.StatusResults{
  2786  		Results: []params.StatusResult{
  2787  			{Error: apiservertesting.ErrUnauthorized},
  2788  			{Status: status.Maintenance.String(), Info: "blah", Data: map[string]interface{}{}},
  2789  			{Error: apiservertesting.ErrUnauthorized},
  2790  			{Error: apiservertesting.ErrUnauthorized},
  2791  			{Error: apiservertesting.ServerError(`"invalid" is not a valid tag`)},
  2792  		},
  2793  	})
  2794  }
  2795  
  2796  func (s *uniterSuite) TestAssignedMachine(c *gc.C) {
  2797  	args := params.Entities{Entities: []params.Entity{
  2798  		{Tag: "unit-mysql-0"},
  2799  		{Tag: "unit-wordpress-0"},
  2800  		{Tag: "unit-foo-42"},
  2801  		{Tag: "application-mysql"},
  2802  		{Tag: "application-wordpress"},
  2803  		{Tag: "machine-0"},
  2804  		{Tag: "machine-1"},
  2805  		{Tag: "machine-42"},
  2806  		{Tag: "application-foo"},
  2807  		{Tag: "relation-svc1.rel1#svc2.rel2"},
  2808  	}}
  2809  	result, err := s.uniter.AssignedMachine(args)
  2810  	c.Assert(err, jc.ErrorIsNil)
  2811  	c.Assert(result, jc.DeepEquals, params.StringResults{
  2812  		Results: []params.StringResult{
  2813  			{Error: apiservertesting.ErrUnauthorized},
  2814  			{Result: "machine-0"},
  2815  			{Error: apiservertesting.ErrUnauthorized},
  2816  			{Error: apiservertesting.ErrUnauthorized},
  2817  			{Error: apiservertesting.ErrUnauthorized},
  2818  			{Error: apiservertesting.ErrUnauthorized},
  2819  			{Error: apiservertesting.ErrUnauthorized},
  2820  			{Error: apiservertesting.ErrUnauthorized},
  2821  			{Error: apiservertesting.ErrUnauthorized},
  2822  			{Error: apiservertesting.ErrUnauthorized},
  2823  		},
  2824  	})
  2825  }
  2826  
  2827  func (s *uniterSuite) TestAllMachinePorts(c *gc.C) {
  2828  	// Verify no ports are opened yet on the machine or unit.
  2829  	machinePorts, err := s.machine0.AllPorts()
  2830  	c.Assert(err, jc.ErrorIsNil)
  2831  	c.Assert(machinePorts, gc.HasLen, 0)
  2832  	unitPorts, err := s.wordpressUnit.OpenedPorts()
  2833  	c.Assert(err, jc.ErrorIsNil)
  2834  	c.Assert(unitPorts, gc.HasLen, 0)
  2835  
  2836  	// Add another mysql unit on machine 0.
  2837  	mysqlUnit1, err := s.mysql.AddUnit(state.AddUnitParams{})
  2838  	c.Assert(err, jc.ErrorIsNil)
  2839  	err = mysqlUnit1.AssignToMachine(s.machine0)
  2840  	c.Assert(err, jc.ErrorIsNil)
  2841  
  2842  	// Open some ports on both units.
  2843  	err = s.wordpressUnit.OpenPorts("tcp", 100, 200)
  2844  	c.Assert(err, jc.ErrorIsNil)
  2845  	err = s.wordpressUnit.OpenPorts("udp", 10, 20)
  2846  	c.Assert(err, jc.ErrorIsNil)
  2847  	err = mysqlUnit1.OpenPorts("tcp", 201, 250)
  2848  	c.Assert(err, jc.ErrorIsNil)
  2849  	err = mysqlUnit1.OpenPorts("udp", 1, 8)
  2850  	c.Assert(err, jc.ErrorIsNil)
  2851  
  2852  	args := params.Entities{Entities: []params.Entity{
  2853  		{Tag: "unit-mysql-0"},
  2854  		{Tag: "machine-0"},
  2855  		{Tag: "machine-1"},
  2856  		{Tag: "unit-foo-42"},
  2857  		{Tag: "machine-42"},
  2858  		{Tag: "application-wordpress"},
  2859  	}}
  2860  	expectPorts := []params.MachinePortRange{
  2861  		{UnitTag: "unit-wordpress-0", PortRange: params.PortRange{100, 200, "tcp"}},
  2862  		{UnitTag: "unit-mysql-1", PortRange: params.PortRange{201, 250, "tcp"}},
  2863  		{UnitTag: "unit-mysql-1", PortRange: params.PortRange{1, 8, "udp"}},
  2864  		{UnitTag: "unit-wordpress-0", PortRange: params.PortRange{10, 20, "udp"}},
  2865  	}
  2866  	result, err := s.uniter.AllMachinePorts(args)
  2867  	c.Assert(err, jc.ErrorIsNil)
  2868  	c.Assert(result, gc.DeepEquals, params.MachinePortsResults{
  2869  		Results: []params.MachinePortsResult{
  2870  			{Error: apiservertesting.ErrUnauthorized},
  2871  			{Ports: expectPorts},
  2872  			{Error: apiservertesting.ErrUnauthorized},
  2873  			{Error: apiservertesting.ErrUnauthorized},
  2874  			{Error: apiservertesting.ErrUnauthorized},
  2875  			{Error: apiservertesting.ErrUnauthorized},
  2876  		},
  2877  	})
  2878  }
  2879  
  2880  func (s *uniterSuite) TestSLALevel(c *gc.C) {
  2881  	err := s.State.SetSLA("essential", "bob", []byte("creds"))
  2882  	c.Assert(err, jc.ErrorIsNil)
  2883  
  2884  	result, err := s.uniter.SLALevel()
  2885  	c.Assert(err, jc.ErrorIsNil)
  2886  	c.Assert(result, jc.DeepEquals, params.StringResult{Result: "essential"})
  2887  }
  2888  
  2889  func (s *uniterSuite) setupRemoteRelationScenario(c *gc.C) (names.Tag, *state.RelationUnit) {
  2890  	s.makeRemoteWordpress(c)
  2891  
  2892  	// Set mysql's addresses first.
  2893  	err := s.machine1.SetProviderAddresses(
  2894  		network.NewScopedAddress("1.2.3.4", network.ScopeCloudLocal),
  2895  		network.NewScopedAddress("4.3.2.1", network.ScopePublic),
  2896  	)
  2897  	c.Assert(err, jc.ErrorIsNil)
  2898  
  2899  	eps, err := s.State.InferEndpoints("mysql", "remote-wordpress")
  2900  	c.Assert(err, jc.ErrorIsNil)
  2901  	rel, err := s.State.AddRelation(eps...)
  2902  	c.Assert(err, jc.ErrorIsNil)
  2903  
  2904  	relUnit, err := rel.Unit(s.mysqlUnit)
  2905  	c.Assert(err, jc.ErrorIsNil)
  2906  	s.assertInScope(c, relUnit, false)
  2907  	return rel.Tag(), relUnit
  2908  }
  2909  
  2910  func (s *uniterSuite) TestPrivateAddressWithRemoteRelation(c *gc.C) {
  2911  	relTag, relUnit := s.setupRemoteRelationScenario(c)
  2912  
  2913  	thisUniter := s.makeMysqlUniter(c)
  2914  	args := params.RelationUnits{RelationUnits: []params.RelationUnit{
  2915  		{Relation: relTag.String(), Unit: "unit-mysql-0"},
  2916  	}}
  2917  	result, err := thisUniter.EnterScope(args)
  2918  	c.Assert(err, jc.ErrorIsNil)
  2919  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  2920  		Results: []params.ErrorResult{{Error: nil}},
  2921  	})
  2922  
  2923  	// Verify the scope changes and settings.
  2924  	s.assertInScope(c, relUnit, true)
  2925  	readSettings, err := relUnit.ReadSettings(s.mysqlUnit.Name())
  2926  	c.Assert(err, jc.ErrorIsNil)
  2927  	c.Assert(readSettings, gc.DeepEquals, map[string]interface{}{
  2928  		"private-address": "4.3.2.1",
  2929  		"ingress-address": "4.3.2.1",
  2930  		"egress-subnets":  "4.3.2.1/32",
  2931  	})
  2932  }
  2933  
  2934  func (s *uniterSuite) TestPrivateAddressWithRemoteRelationNoPublic(c *gc.C) {
  2935  	relTag, relUnit := s.setupRemoteRelationScenario(c)
  2936  
  2937  	thisUniter := s.makeMysqlUniter(c)
  2938  	// Set mysql's addresses - no public address.
  2939  	err := s.machine1.SetProviderAddresses(
  2940  		network.NewScopedAddress("1.2.3.4", network.ScopeCloudLocal),
  2941  	)
  2942  	c.Assert(err, jc.ErrorIsNil)
  2943  
  2944  	args := params.RelationUnits{RelationUnits: []params.RelationUnit{
  2945  		{Relation: relTag.String(), Unit: "unit-mysql-0"},
  2946  	}}
  2947  	result, err := thisUniter.EnterScope(args)
  2948  	c.Assert(err, jc.ErrorIsNil)
  2949  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  2950  		Results: []params.ErrorResult{{Error: nil}},
  2951  	})
  2952  
  2953  	// Verify that we fell back to the private address.
  2954  	s.assertInScope(c, relUnit, true)
  2955  	readSettings, err := relUnit.ReadSettings(s.mysqlUnit.Name())
  2956  	c.Assert(err, jc.ErrorIsNil)
  2957  	c.Assert(readSettings, gc.DeepEquals, map[string]interface{}{
  2958  		"private-address": "1.2.3.4",
  2959  		"ingress-address": "1.2.3.4",
  2960  		"egress-subnets":  "1.2.3.4/32",
  2961  	})
  2962  }
  2963  
  2964  func (s *uniterSuite) TestRelationEgressSubnets(c *gc.C) {
  2965  	relTag, relUnit := s.setupRemoteRelationScenario(c)
  2966  
  2967  	// Check model attributes are overridden by setting up a value.
  2968  	err := s.Model.UpdateModelConfig(map[string]interface{}{"egress-subnets": "192.168.0.0/16"}, nil)
  2969  	c.Assert(err, jc.ErrorIsNil)
  2970  	egress := state.NewRelationEgressNetworks(s.State)
  2971  	_, err = egress.Save(relTag.Id(), false, []string{"10.0.0.0/16", "10.1.2.0/8"})
  2972  	c.Assert(err, jc.ErrorIsNil)
  2973  
  2974  	thisUniter := s.makeMysqlUniter(c)
  2975  	args := params.RelationUnits{RelationUnits: []params.RelationUnit{
  2976  		{Relation: relTag.String(), Unit: "unit-mysql-0"},
  2977  	}}
  2978  	result, err := thisUniter.EnterScope(args)
  2979  	c.Assert(err, jc.ErrorIsNil)
  2980  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  2981  		Results: []params.ErrorResult{{Error: nil}},
  2982  	})
  2983  
  2984  	// Verify the scope changes and settings.
  2985  	s.assertInScope(c, relUnit, true)
  2986  	readSettings, err := relUnit.ReadSettings(s.mysqlUnit.Name())
  2987  	c.Assert(err, jc.ErrorIsNil)
  2988  	c.Assert(readSettings, gc.DeepEquals, map[string]interface{}{
  2989  		"private-address": "4.3.2.1",
  2990  		"ingress-address": "4.3.2.1",
  2991  		"egress-subnets":  "10.0.0.0/16,10.1.2.0/8",
  2992  	})
  2993  }
  2994  
  2995  func (s *uniterSuite) TestModelEgressSubnets(c *gc.C) {
  2996  	relTag, relUnit := s.setupRemoteRelationScenario(c)
  2997  
  2998  	err := s.Model.UpdateModelConfig(map[string]interface{}{"egress-subnets": "192.168.0.0/16"}, nil)
  2999  	c.Assert(err, jc.ErrorIsNil)
  3000  
  3001  	thisUniter := s.makeMysqlUniter(c)
  3002  	args := params.RelationUnits{RelationUnits: []params.RelationUnit{
  3003  		{Relation: relTag.String(), Unit: "unit-mysql-0"},
  3004  	}}
  3005  	result, err := thisUniter.EnterScope(args)
  3006  	c.Assert(err, jc.ErrorIsNil)
  3007  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  3008  		Results: []params.ErrorResult{{Error: nil}},
  3009  	})
  3010  
  3011  	// Verify the scope changes and settings.
  3012  	s.assertInScope(c, relUnit, true)
  3013  	readSettings, err := relUnit.ReadSettings(s.mysqlUnit.Name())
  3014  	c.Assert(err, jc.ErrorIsNil)
  3015  	c.Assert(readSettings, gc.DeepEquals, map[string]interface{}{
  3016  		"private-address": "4.3.2.1",
  3017  		"ingress-address": "4.3.2.1",
  3018  		"egress-subnets":  "192.168.0.0/16",
  3019  	})
  3020  }
  3021  
  3022  func (s *uniterSuite) makeMysqlUniter(c *gc.C) *uniter.UniterAPI {
  3023  	authorizer := s.authorizer
  3024  	authorizer.Tag = s.mysqlUnit.Tag()
  3025  	result, err := uniter.NewUniterAPI(facadetest.Context{
  3026  		State_:             s.State,
  3027  		Resources_:         s.resources,
  3028  		Auth_:              authorizer,
  3029  		LeadershipChecker_: s.State.LeadershipChecker(),
  3030  	})
  3031  	c.Assert(err, jc.ErrorIsNil)
  3032  	return result
  3033  }
  3034  
  3035  func (s *uniterSuite) makeRemoteWordpress(c *gc.C) {
  3036  	_, err := s.State.AddRemoteApplication(state.AddRemoteApplicationParams{
  3037  		Name:            "remote-wordpress",
  3038  		SourceModel:     names.NewModelTag("source-model"),
  3039  		IsConsumerProxy: true,
  3040  		OfferUUID:       "offer-uuid",
  3041  		Endpoints: []charm.Relation{{
  3042  			Interface: "mysql",
  3043  			Limit:     1,
  3044  			Name:      "db",
  3045  			Role:      charm.RoleRequirer,
  3046  			Scope:     charm.ScopeGlobal,
  3047  		}},
  3048  	})
  3049  	c.Assert(err, jc.ErrorIsNil)
  3050  }
  3051  
  3052  func (s *uniterSuite) TestV4WatchApplicationRelations(c *gc.C) {
  3053  	c.Assert(s.resources.Count(), gc.Equals, 0)
  3054  
  3055  	args := params.Entities{Entities: []params.Entity{
  3056  		{Tag: "application-mysql"},
  3057  		{Tag: "application-wordpress"},
  3058  		{Tag: "application-foo"},
  3059  	}}
  3060  	apiV4, err := uniter.NewUniterAPIV4(facadetest.Context{
  3061  		State_:             s.State,
  3062  		Resources_:         s.resources,
  3063  		Auth_:              s.authorizer,
  3064  		LeadershipChecker_: s.State.LeadershipChecker(),
  3065  	})
  3066  	c.Assert(err, jc.ErrorIsNil)
  3067  	result, err := apiV4.WatchApplicationRelations(args)
  3068  	c.Assert(err, jc.ErrorIsNil)
  3069  	c.Assert(result.Results, gc.HasLen, 3)
  3070  	c.Assert(result.Results[0].Error, gc.DeepEquals, apiservertesting.ErrUnauthorized)
  3071  	c.Assert(result.Results[1].StringsWatcherId, gc.Equals, "1")
  3072  	c.Assert(result.Results[1].Changes, gc.NotNil)
  3073  	c.Assert(result.Results[1].Error, gc.IsNil)
  3074  	c.Assert(result.Results[2].Error, gc.DeepEquals, apiservertesting.ErrUnauthorized)
  3075  
  3076  	// Verify the resource was registered and stop when done
  3077  	c.Assert(s.resources.Count(), gc.Equals, 1)
  3078  	resource := s.resources.Get("1")
  3079  	defer statetesting.AssertStop(c, resource)
  3080  
  3081  	// Check that the Watch has consumed the initial event ("returned" in
  3082  	// the Watch call)
  3083  	wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher))
  3084  	wc.AssertNoChange()
  3085  }
  3086  
  3087  func (s *uniterSuite) TestV5Relation(c *gc.C) {
  3088  	rel := s.addRelation(c, "wordpress", "mysql")
  3089  	wpEp, err := rel.Endpoint("wordpress")
  3090  	c.Assert(err, jc.ErrorIsNil)
  3091  
  3092  	args := params.RelationUnits{RelationUnits: []params.RelationUnit{
  3093  		{Relation: rel.Tag().String(), Unit: "unit-wordpress-0"},
  3094  	}}
  3095  	apiV5, err := uniter.NewUniterAPIV5(facadetest.Context{
  3096  		State_:             s.State,
  3097  		Resources_:         s.resources,
  3098  		Auth_:              s.authorizer,
  3099  		LeadershipChecker_: s.State.LeadershipChecker(),
  3100  	})
  3101  	c.Assert(err, jc.ErrorIsNil)
  3102  	result, err := apiV5.Relation(args)
  3103  	c.Assert(err, jc.ErrorIsNil)
  3104  	c.Assert(result, gc.DeepEquals, params.RelationResultsV5{
  3105  		Results: []params.RelationResultV5{
  3106  			{
  3107  				Id:   rel.Id(),
  3108  				Key:  rel.String(),
  3109  				Life: params.Life(rel.Life().String()),
  3110  				Endpoint: multiwatcher.Endpoint{
  3111  					ApplicationName: wpEp.ApplicationName,
  3112  					Relation:        multiwatcher.NewCharmRelation(wpEp.Relation),
  3113  				},
  3114  			},
  3115  		},
  3116  	})
  3117  }
  3118  
  3119  func (s *uniterSuite) TestV5RelationById(c *gc.C) {
  3120  	rel := s.addRelation(c, "wordpress", "mysql")
  3121  	wpEp, err := rel.Endpoint("wordpress")
  3122  	c.Assert(err, jc.ErrorIsNil)
  3123  
  3124  	args := params.RelationIds{RelationIds: []int{rel.Id()}}
  3125  	apiV5, err := uniter.NewUniterAPIV5(facadetest.Context{
  3126  		State_:             s.State,
  3127  		Resources_:         s.resources,
  3128  		Auth_:              s.authorizer,
  3129  		LeadershipChecker_: s.State.LeadershipChecker(),
  3130  	})
  3131  	c.Assert(err, jc.ErrorIsNil)
  3132  	result, err := apiV5.RelationById(args)
  3133  	c.Assert(err, jc.ErrorIsNil)
  3134  	c.Assert(result, gc.DeepEquals, params.RelationResultsV5{
  3135  		Results: []params.RelationResultV5{
  3136  			{
  3137  				Id:   rel.Id(),
  3138  				Key:  rel.String(),
  3139  				Life: params.Life(rel.Life().String()),
  3140  				Endpoint: multiwatcher.Endpoint{
  3141  					ApplicationName: wpEp.ApplicationName,
  3142  					Relation:        multiwatcher.NewCharmRelation(wpEp.Relation),
  3143  				},
  3144  			},
  3145  		},
  3146  	})
  3147  }
  3148  
  3149  func (s *uniterSuite) TestRefresh(c *gc.C) {
  3150  	args := params.Entities{
  3151  		Entities: []params.Entity{
  3152  			{s.wordpressUnit.Tag().String()},
  3153  			{s.mysqlUnit.Tag().String()},
  3154  			{s.mysql.Tag().String()},
  3155  			{s.machine0.Tag().String()},
  3156  			{"some-word"},
  3157  		},
  3158  	}
  3159  	expect := params.UnitRefreshResults{
  3160  		Results: []params.UnitRefreshResult{
  3161  			{Life: params.Alive, Resolved: params.ResolvedNone},
  3162  			{Error: apiservertesting.ErrUnauthorized},
  3163  			{Error: apiservertesting.ErrUnauthorized},
  3164  			{Error: apiservertesting.ErrUnauthorized},
  3165  			{Error: apiservertesting.ErrUnauthorized},
  3166  		},
  3167  	}
  3168  	results, err := s.uniter.Refresh(args)
  3169  	c.Assert(err, jc.ErrorIsNil)
  3170  	c.Assert(results, gc.DeepEquals, expect)
  3171  }
  3172  
  3173  func (s *uniterSuite) TestRefreshNoArgs(c *gc.C) {
  3174  	results, err := s.uniter.Refresh(params.Entities{Entities: []params.Entity{}})
  3175  	c.Assert(err, jc.ErrorIsNil)
  3176  	c.Assert(results, gc.DeepEquals, params.UnitRefreshResults{Results: []params.UnitRefreshResult{}})
  3177  }
  3178  
  3179  var podSpec = `
  3180  containers:
  3181    - name: gitlab
  3182      image: gitlab/latest
  3183      ports:
  3184      - containerPort: 80
  3185        protocol: TCP
  3186      - containerPort: 443
  3187      config:
  3188        attr: foo=bar; fred=blogs
  3189        foo: bar
  3190  `[1:]
  3191  
  3192  func (s *uniterSuite) TestSetPodSpec(c *gc.C) {
  3193  	u, cm, app, _ := s.setupCAASModel(c)
  3194  	err := u.SetPodSpec(app.Name(), podSpec)
  3195  	c.Assert(err, jc.ErrorIsNil)
  3196  	spec, err := cm.PodSpec(app.ApplicationTag())
  3197  	c.Assert(err, jc.ErrorIsNil)
  3198  	c.Assert(spec, gc.Equals, podSpec)
  3199  }
  3200  
  3201  type unitMetricBatchesSuite struct {
  3202  	uniterSuiteBase
  3203  	*commontesting.ModelWatcherTest
  3204  	uniter *uniter.UniterAPI
  3205  }
  3206  
  3207  var _ = gc.Suite(&unitMetricBatchesSuite{})
  3208  
  3209  func (s *unitMetricBatchesSuite) SetUpTest(c *gc.C) {
  3210  	s.uniterSuiteBase.SetUpTest(c)
  3211  
  3212  	meteredAuthorizer := apiservertesting.FakeAuthorizer{
  3213  		Tag: s.meteredUnit.Tag(),
  3214  	}
  3215  	var err error
  3216  	s.uniter, err = uniter.NewUniterAPI(facadetest.Context{
  3217  		State_:             s.State,
  3218  		Resources_:         s.resources,
  3219  		Auth_:              meteredAuthorizer,
  3220  		LeadershipChecker_: s.State.LeadershipChecker(),
  3221  	})
  3222  	c.Assert(err, jc.ErrorIsNil)
  3223  
  3224  	s.ModelWatcherTest = commontesting.NewModelWatcherTest(
  3225  		s.uniter,
  3226  		s.State,
  3227  		s.resources,
  3228  	)
  3229  }
  3230  
  3231  func (s *unitMetricBatchesSuite) TestAddMetricsBatch(c *gc.C) {
  3232  	metrics := []params.Metric{{Key: "pings", Value: "5", Time: time.Now().UTC()}}
  3233  	uuid := utils.MustNewUUID().String()
  3234  
  3235  	result, err := s.uniter.AddMetricBatches(params.MetricBatchParams{
  3236  		Batches: []params.MetricBatchParam{{
  3237  			Tag: s.meteredUnit.Tag().String(),
  3238  			Batch: params.MetricBatch{
  3239  				UUID:     uuid,
  3240  				CharmURL: s.meteredCharm.URL().String(),
  3241  				Created:  time.Now(),
  3242  				Metrics:  metrics,
  3243  			}}}},
  3244  	)
  3245  
  3246  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  3247  		Results: []params.ErrorResult{{nil}},
  3248  	})
  3249  	c.Assert(err, jc.ErrorIsNil)
  3250  
  3251  	batch, err := s.State.MetricBatch(uuid)
  3252  	c.Assert(err, jc.ErrorIsNil)
  3253  	c.Assert(batch.UUID(), gc.Equals, uuid)
  3254  	c.Assert(batch.CharmURL(), gc.Equals, s.meteredCharm.URL().String())
  3255  	c.Assert(batch.Unit(), gc.Equals, s.meteredUnit.Name())
  3256  	storedMetrics := batch.Metrics()
  3257  	c.Assert(storedMetrics, gc.HasLen, 1)
  3258  	c.Assert(storedMetrics[0].Key, gc.Equals, metrics[0].Key)
  3259  	c.Assert(storedMetrics[0].Value, gc.Equals, metrics[0].Value)
  3260  }
  3261  
  3262  func (s *unitMetricBatchesSuite) TestAddMetricsBatchNoCharmURL(c *gc.C) {
  3263  	metrics := []params.Metric{{Key: "pings", Value: "5", Time: time.Now().UTC()}}
  3264  	uuid := utils.MustNewUUID().String()
  3265  
  3266  	result, err := s.uniter.AddMetricBatches(params.MetricBatchParams{
  3267  		Batches: []params.MetricBatchParam{{
  3268  			Tag: s.meteredUnit.Tag().String(),
  3269  			Batch: params.MetricBatch{
  3270  				UUID:     uuid,
  3271  				CharmURL: s.meteredCharm.URL().String(),
  3272  				Created:  time.Now(),
  3273  				Metrics:  metrics,
  3274  			}}}})
  3275  
  3276  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
  3277  		Results: []params.ErrorResult{{nil}},
  3278  	})
  3279  	c.Assert(err, jc.ErrorIsNil)
  3280  
  3281  	batch, err := s.State.MetricBatch(uuid)
  3282  	c.Assert(err, jc.ErrorIsNil)
  3283  	c.Assert(batch.UUID(), gc.Equals, uuid)
  3284  	c.Assert(batch.CharmURL(), gc.Equals, s.meteredCharm.URL().String())
  3285  	c.Assert(batch.Unit(), gc.Equals, s.meteredUnit.Name())
  3286  	storedMetrics := batch.Metrics()
  3287  	c.Assert(storedMetrics, gc.HasLen, 1)
  3288  	c.Assert(storedMetrics[0].Key, gc.Equals, metrics[0].Key)
  3289  	c.Assert(storedMetrics[0].Value, gc.Equals, metrics[0].Value)
  3290  }
  3291  
  3292  func (s *unitMetricBatchesSuite) TestAddMetricsBatchDiffTag(c *gc.C) {
  3293  	unit2 := s.Factory.MakeUnit(c, &factory.UnitParams{Application: s.meteredApplication, SetCharmURL: true})
  3294  
  3295  	metrics := []params.Metric{{Key: "pings", Value: "5", Time: time.Now().UTC()}}
  3296  	uuid := utils.MustNewUUID().String()
  3297  
  3298  	tests := []struct {
  3299  		about  string
  3300  		tag    string
  3301  		expect string
  3302  	}{{
  3303  		about:  "different unit",
  3304  		tag:    unit2.Tag().String(),
  3305  		expect: "permission denied",
  3306  	}, {
  3307  		about:  "user tag",
  3308  		tag:    names.NewLocalUserTag("admin").String(),
  3309  		expect: `"user-admin" is not a valid unit tag`,
  3310  	}, {
  3311  		about:  "machine tag",
  3312  		tag:    names.NewMachineTag("0").String(),
  3313  		expect: `"machine-0" is not a valid unit tag`,
  3314  	}}
  3315  
  3316  	for i, test := range tests {
  3317  		c.Logf("test %d: %s", i, test.about)
  3318  		result, err := s.uniter.AddMetricBatches(params.MetricBatchParams{
  3319  			Batches: []params.MetricBatchParam{{
  3320  				Tag: test.tag,
  3321  				Batch: params.MetricBatch{
  3322  					UUID:     uuid,
  3323  					CharmURL: "",
  3324  					Created:  time.Now(),
  3325  					Metrics:  metrics,
  3326  				}}}})
  3327  
  3328  		if test.expect == "" {
  3329  			c.Assert(result.OneError(), jc.ErrorIsNil)
  3330  		} else {
  3331  			c.Assert(result.OneError(), gc.ErrorMatches, test.expect)
  3332  		}
  3333  		c.Assert(err, jc.ErrorIsNil)
  3334  
  3335  		_, err = s.State.MetricBatch(uuid)
  3336  		c.Assert(err, jc.Satisfies, errors.IsNotFound)
  3337  	}
  3338  }
  3339  
  3340  type uniterNetworkConfigSuite struct {
  3341  	uniterSuiteBase
  3342  	uniterv4 *uniter.UniterAPIV4
  3343  }
  3344  
  3345  var _ = gc.Suite(&uniterNetworkConfigSuite{})
  3346  
  3347  func (s *uniterNetworkConfigSuite) SetUpTest(c *gc.C) {
  3348  	s.uniterSuiteBase.JujuConnSuite.SetUpTest(c)
  3349  
  3350  	// Add the spaces and subnets used by the test.
  3351  	subnetInfos := []state.SubnetInfo{{
  3352  		CIDR:      "8.8.0.0/16",
  3353  		SpaceName: "public",
  3354  	}, {
  3355  		CIDR:      "10.0.0.0/24",
  3356  		SpaceName: "internal",
  3357  	}}
  3358  	for _, info := range subnetInfos {
  3359  		_, err := s.State.AddSpace(info.SpaceName, "", nil, false)
  3360  		c.Assert(err, jc.ErrorIsNil)
  3361  		_, err = s.State.AddSubnet(info)
  3362  		c.Assert(err, jc.ErrorIsNil)
  3363  	}
  3364  
  3365  	s.machine0 = s.addProvisionedMachineWithDevicesAndAddresses(c, 10)
  3366  
  3367  	s.wpCharm = s.Factory.MakeCharm(c, &factory.CharmParams{
  3368  		Name: "wordpress-extra-bindings",
  3369  		URL:  "cs:quantal/wordpress-extra-bindings-4",
  3370  	})
  3371  	var err error
  3372  	s.wordpress, err = s.State.AddApplication(state.AddApplicationArgs{
  3373  		Name:  "wordpress",
  3374  		Charm: s.wpCharm,
  3375  		EndpointBindings: map[string]string{
  3376  			"db":        "internal", // relation name
  3377  			"admin-api": "public",   // extra-binding name
  3378  		},
  3379  	})
  3380  	c.Assert(err, jc.ErrorIsNil)
  3381  	s.wordpressUnit = s.Factory.MakeUnit(c, &factory.UnitParams{
  3382  		Application: s.wordpress,
  3383  		Machine:     s.machine0,
  3384  	})
  3385  
  3386  	s.machine1 = s.addProvisionedMachineWithDevicesAndAddresses(c, 20)
  3387  
  3388  	mysqlCharm := s.Factory.MakeCharm(c, &factory.CharmParams{
  3389  		Name: "mysql",
  3390  	})
  3391  	s.mysql = s.Factory.MakeApplication(c, &factory.ApplicationParams{
  3392  		Name:  "mysql",
  3393  		Charm: mysqlCharm,
  3394  	})
  3395  	s.wordpressUnit = s.Factory.MakeUnit(c, &factory.UnitParams{
  3396  		Application: s.wordpress,
  3397  		Machine:     s.machine0,
  3398  	})
  3399  	s.mysqlUnit = s.Factory.MakeUnit(c, &factory.UnitParams{
  3400  		Application: s.mysql,
  3401  		Machine:     s.machine1,
  3402  	})
  3403  
  3404  	// Create the resource registry separately to track invocations to register.
  3405  	s.resources = common.NewResources()
  3406  	s.AddCleanup(func(_ *gc.C) { s.resources.StopAll() })
  3407  
  3408  	s.setupUniterAPIForUnit(c, s.wordpressUnit)
  3409  }
  3410  
  3411  func (s *uniterNetworkConfigSuite) addProvisionedMachineWithDevicesAndAddresses(c *gc.C, addrSuffix int) *state.Machine {
  3412  	machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
  3413  	c.Assert(err, jc.ErrorIsNil)
  3414  	devicesArgs, devicesAddrs := s.makeMachineDevicesAndAddressesArgs(addrSuffix)
  3415  	err = machine.SetInstanceInfo("i-am", "", "fake_nonce", nil, devicesArgs, devicesAddrs, nil, nil, nil)
  3416  	c.Assert(err, jc.ErrorIsNil)
  3417  
  3418  	machineAddrs, err := machine.AllAddresses()
  3419  	c.Assert(err, jc.ErrorIsNil)
  3420  
  3421  	netAddrs := make([]network.Address, len(machineAddrs))
  3422  	for i, addr := range machineAddrs {
  3423  		netAddrs[i] = network.NewAddress(addr.Value())
  3424  	}
  3425  	err = machine.SetProviderAddresses(netAddrs...)
  3426  	c.Assert(err, jc.ErrorIsNil)
  3427  
  3428  	return machine
  3429  }
  3430  
  3431  func (s *uniterNetworkConfigSuite) makeMachineDevicesAndAddressesArgs(addrSuffix int) ([]state.LinkLayerDeviceArgs, []state.LinkLayerDeviceAddress) {
  3432  	return []state.LinkLayerDeviceArgs{{
  3433  			Name: "eth0",
  3434  			Type: state.EthernetDevice,
  3435  		}, {
  3436  			Name:       "eth0.100",
  3437  			Type:       state.VLAN_8021QDevice,
  3438  			ParentName: "eth0",
  3439  		}, {
  3440  			Name: "eth1",
  3441  			Type: state.EthernetDevice,
  3442  		}, {
  3443  			Name:       "eth1.100",
  3444  			Type:       state.VLAN_8021QDevice,
  3445  			ParentName: "eth1",
  3446  		}},
  3447  		[]state.LinkLayerDeviceAddress{{
  3448  			DeviceName:   "eth0",
  3449  			ConfigMethod: state.StaticAddress,
  3450  			CIDRAddress:  fmt.Sprintf("8.8.8.%d/16", addrSuffix),
  3451  		}, {
  3452  			DeviceName:   "eth0.100",
  3453  			ConfigMethod: state.StaticAddress,
  3454  			CIDRAddress:  fmt.Sprintf("10.0.0.%d/24", addrSuffix),
  3455  		}, {
  3456  			DeviceName:   "eth1",
  3457  			ConfigMethod: state.StaticAddress,
  3458  			CIDRAddress:  fmt.Sprintf("8.8.4.%d/16", addrSuffix),
  3459  		}, {
  3460  			DeviceName:   "eth1.100",
  3461  			ConfigMethod: state.StaticAddress,
  3462  			CIDRAddress:  fmt.Sprintf("10.0.0.%d/24", addrSuffix+1),
  3463  		}}
  3464  }
  3465  
  3466  func (s *uniterNetworkConfigSuite) setupUniterAPIForUnit(c *gc.C, givenUnit *state.Unit) {
  3467  	// Create a FakeAuthorizer so we can check permissions, set up assuming the
  3468  	// given unit agent has logged in.
  3469  	s.authorizer = apiservertesting.FakeAuthorizer{
  3470  		Tag: givenUnit.Tag(),
  3471  	}
  3472  
  3473  	var err error
  3474  	s.uniterv4, err = uniter.NewUniterAPIV4(facadetest.Context{
  3475  		State_:             s.State,
  3476  		Resources_:         s.resources,
  3477  		Auth_:              s.authorizer,
  3478  		LeadershipChecker_: s.State.LeadershipChecker(),
  3479  	})
  3480  	c.Assert(err, jc.ErrorIsNil)
  3481  }
  3482  
  3483  func (s *uniterNetworkConfigSuite) TestNetworkConfigPermissions(c *gc.C) {
  3484  	s.addRelationAndAssertInScope(c)
  3485  
  3486  	args := params.UnitsNetworkConfig{Args: []params.UnitNetworkConfig{
  3487  		{BindingName: "foo", UnitTag: "unit-foo-0"},
  3488  		{BindingName: "db-client", UnitTag: "invalid"},
  3489  		{BindingName: "juju-info", UnitTag: "unit-mysql-0"},
  3490  		{BindingName: "", UnitTag: s.wordpressUnit.Tag().String()},
  3491  		{BindingName: "unknown", UnitTag: s.wordpressUnit.Tag().String()},
  3492  	}}
  3493  
  3494  	result, err := s.uniterv4.NetworkConfig(args)
  3495  	c.Assert(err, jc.ErrorIsNil)
  3496  	c.Assert(result, jc.DeepEquals, params.UnitNetworkConfigResults{
  3497  		Results: []params.UnitNetworkConfigResult{
  3498  			{Error: apiservertesting.ErrUnauthorized},
  3499  			{Error: apiservertesting.ServerError(`"invalid" is not a valid tag`)},
  3500  			{Error: apiservertesting.ErrUnauthorized},
  3501  			{Error: apiservertesting.ServerError(`binding name cannot be empty`)},
  3502  			{Error: apiservertesting.ServerError(`binding name "unknown" not defined by the unit's charm`)},
  3503  		},
  3504  	})
  3505  }
  3506  
  3507  func (s *uniterNetworkConfigSuite) addRelationAndAssertInScope(c *gc.C) {
  3508  	// Add a relation between wordpress and mysql and enter scope with
  3509  	// mysqlUnit.
  3510  	rel := s.addRelation(c, "wordpress", "mysql")
  3511  	wpRelUnit, err := rel.Unit(s.wordpressUnit)
  3512  	c.Assert(err, jc.ErrorIsNil)
  3513  	err = wpRelUnit.EnterScope(nil)
  3514  	c.Assert(err, jc.ErrorIsNil)
  3515  	s.assertInScope(c, wpRelUnit, true)
  3516  }
  3517  
  3518  func (s *uniterNetworkConfigSuite) TestNetworkConfigForExplicitlyBoundEndpoint(c *gc.C) {
  3519  	s.addRelationAndAssertInScope(c)
  3520  
  3521  	args := params.UnitsNetworkConfig{Args: []params.UnitNetworkConfig{
  3522  		{BindingName: "db", UnitTag: s.wordpressUnit.Tag().String()},
  3523  		{BindingName: "admin-api", UnitTag: s.wordpressUnit.Tag().String()},
  3524  	}}
  3525  
  3526  	// For the relation "wordpress:db mysql:server" we expect to see only
  3527  	// addresses bound to the "internal" space, where the "db" endpoint itself
  3528  	// is bound to.
  3529  	expectedConfigWithRelationName := []params.NetworkConfig{
  3530  		{Address: "10.0.0.10"},
  3531  		{Address: "10.0.0.11"},
  3532  	}
  3533  	// For the "admin-api" extra-binding we expect to see only addresses from
  3534  	// the "public" space.
  3535  	expectedConfigWithExtraBindingName := []params.NetworkConfig{
  3536  		{Address: "8.8.8.10"},
  3537  		{Address: "8.8.4.10"},
  3538  	}
  3539  
  3540  	result, err := s.uniterv4.NetworkConfig(args)
  3541  	c.Assert(err, jc.ErrorIsNil)
  3542  	c.Assert(result, jc.DeepEquals, params.UnitNetworkConfigResults{
  3543  		Results: []params.UnitNetworkConfigResult{
  3544  			{Config: expectedConfigWithRelationName},
  3545  			{Config: expectedConfigWithExtraBindingName},
  3546  		},
  3547  	})
  3548  }
  3549  
  3550  func (s *uniterNetworkConfigSuite) TestNetworkConfigForImplicitlyBoundEndpoint(c *gc.C) {
  3551  	// Since wordpressUnit has explicit binding for "db", switch the API to
  3552  	// mysqlUnit and check "mysql:server" uses the machine preferred private
  3553  	// address.
  3554  	s.setupUniterAPIForUnit(c, s.mysqlUnit)
  3555  	rel := s.addRelation(c, "mysql", "wordpress")
  3556  	mysqlRelUnit, err := rel.Unit(s.mysqlUnit)
  3557  	c.Assert(err, jc.ErrorIsNil)
  3558  	err = mysqlRelUnit.EnterScope(nil)
  3559  	c.Assert(err, jc.ErrorIsNil)
  3560  	s.assertInScope(c, mysqlRelUnit, true)
  3561  
  3562  	args := params.UnitsNetworkConfig{Args: []params.UnitNetworkConfig{
  3563  		{BindingName: "server", UnitTag: s.mysqlUnit.Tag().String()},
  3564  	}}
  3565  
  3566  	privateAddress, err := s.machine1.PrivateAddress()
  3567  	c.Assert(err, jc.ErrorIsNil)
  3568  
  3569  	expectedConfig := []params.NetworkConfig{{Address: privateAddress.Value}}
  3570  
  3571  	result, err := s.uniterv4.NetworkConfig(args)
  3572  	c.Assert(err, jc.ErrorIsNil)
  3573  	c.Assert(result, jc.DeepEquals, params.UnitNetworkConfigResults{
  3574  		Results: []params.UnitNetworkConfigResult{
  3575  			{Config: expectedConfig},
  3576  		},
  3577  	})
  3578  }
  3579  
  3580  type uniterNetworkInfoSuite struct {
  3581  	uniterSuiteBase
  3582  	mysqlCharm *state.Charm
  3583  }
  3584  
  3585  var _ = gc.Suite(&uniterNetworkInfoSuite{})
  3586  
  3587  func (s *uniterNetworkInfoSuite) SetUpTest(c *gc.C) {
  3588  	s.uniterSuiteBase.JujuConnSuite.SetUpTest(c)
  3589  
  3590  	// Add the spaces and subnets used by the test.
  3591  	subnetInfos := []state.SubnetInfo{{
  3592  		CIDR:      "8.8.0.0/16",
  3593  		SpaceName: "public",
  3594  	}, {
  3595  		CIDR:      "10.0.0.0/24",
  3596  		SpaceName: "internal",
  3597  	}, {
  3598  		CIDR:      "100.64.0.0/16",
  3599  		SpaceName: "wp-default",
  3600  	}, {
  3601  		CIDR:      "192.168.1.0/24",
  3602  		SpaceName: "database",
  3603  	}, {
  3604  		SpaceName: "layertwo",
  3605  	}}
  3606  	for _, info := range subnetInfos {
  3607  		_, err := s.State.AddSpace(info.SpaceName, "", nil, false)
  3608  		c.Assert(err, jc.ErrorIsNil)
  3609  		if info.CIDR != "" {
  3610  			_, err = s.State.AddSubnet(info)
  3611  			c.Assert(err, jc.ErrorIsNil)
  3612  		}
  3613  	}
  3614  
  3615  	s.machine0 = s.addProvisionedMachineWithDevicesAndAddresses(c, 10)
  3616  
  3617  	s.wpCharm = s.Factory.MakeCharm(c, &factory.CharmParams{
  3618  		Name: "wordpress-extra-bindings",
  3619  		URL:  "cs:quantal/wordpress-extra-bindings-4",
  3620  	})
  3621  	var err error
  3622  	s.wordpress, err = s.State.AddApplication(state.AddApplicationArgs{
  3623  		Name:  "wordpress",
  3624  		Charm: s.wpCharm,
  3625  		EndpointBindings: map[string]string{
  3626  			"db":        "internal",   // relation name
  3627  			"admin-api": "public",     // extra-binding name
  3628  			"foo-bar":   "layertwo",   // extra-binding to L2
  3629  			"":          "wp-default", // explicitly specified default space
  3630  		},
  3631  	})
  3632  	c.Assert(err, jc.ErrorIsNil)
  3633  	s.wordpressUnit = s.Factory.MakeUnit(c, &factory.UnitParams{
  3634  		Application: s.wordpress,
  3635  		Machine:     s.machine0,
  3636  	})
  3637  
  3638  	s.machine1 = s.addProvisionedMachineWithDevicesAndAddresses(c, 20)
  3639  
  3640  	s.mysqlCharm = s.Factory.MakeCharm(c, &factory.CharmParams{
  3641  		Name: "mysql",
  3642  	})
  3643  	s.mysql = s.Factory.MakeApplication(c, &factory.ApplicationParams{
  3644  		Name:  "mysql",
  3645  		Charm: s.mysqlCharm,
  3646  		EndpointBindings: map[string]string{
  3647  			"server": "database",
  3648  		},
  3649  	})
  3650  	s.wordpressUnit = s.Factory.MakeUnit(c, &factory.UnitParams{
  3651  		Application: s.wordpress,
  3652  		Machine:     s.machine0,
  3653  	})
  3654  	s.mysqlUnit = s.Factory.MakeUnit(c, &factory.UnitParams{
  3655  		Application: s.mysql,
  3656  		Machine:     s.machine1,
  3657  	})
  3658  
  3659  	// Create the resource registry separately to track invocations to
  3660  	// Register.
  3661  	s.resources = common.NewResources()
  3662  	s.AddCleanup(func(_ *gc.C) { s.resources.StopAll() })
  3663  
  3664  	s.setupUniterAPIForUnit(c, s.wordpressUnit)
  3665  }
  3666  
  3667  func (s *uniterNetworkInfoSuite) addProvisionedMachineWithDevicesAndAddresses(c *gc.C, addrSuffix int) *state.Machine {
  3668  	machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
  3669  	c.Assert(err, jc.ErrorIsNil)
  3670  	devicesArgs, devicesAddrs := s.makeMachineDevicesAndAddressesArgs(addrSuffix)
  3671  	err = machine.SetInstanceInfo("i-am", "", "fake_nonce", nil, devicesArgs, devicesAddrs, nil, nil, nil)
  3672  	c.Assert(err, jc.ErrorIsNil)
  3673  
  3674  	machineAddrs, err := machine.AllAddresses()
  3675  	c.Assert(err, jc.ErrorIsNil)
  3676  
  3677  	netAddrs := make([]network.Address, len(machineAddrs))
  3678  	for i, addr := range machineAddrs {
  3679  		netAddrs[i] = network.NewAddress(addr.Value())
  3680  	}
  3681  	err = machine.SetProviderAddresses(netAddrs...)
  3682  	c.Assert(err, jc.ErrorIsNil)
  3683  
  3684  	return machine
  3685  }
  3686  
  3687  func (s *uniterNetworkInfoSuite) makeMachineDevicesAndAddressesArgs(addrSuffix int) ([]state.LinkLayerDeviceArgs, []state.LinkLayerDeviceAddress) {
  3688  	return []state.LinkLayerDeviceArgs{{
  3689  			Name:       "eth0",
  3690  			Type:       state.EthernetDevice,
  3691  			MACAddress: fmt.Sprintf("00:11:22:33:%0.2d:50", addrSuffix),
  3692  		}, {
  3693  			Name:       "eth0.100",
  3694  			Type:       state.VLAN_8021QDevice,
  3695  			ParentName: "eth0",
  3696  			MACAddress: fmt.Sprintf("00:11:22:33:%0.2d:50", addrSuffix),
  3697  		}, {
  3698  			Name:       "eth1",
  3699  			Type:       state.EthernetDevice,
  3700  			MACAddress: fmt.Sprintf("00:11:22:33:%0.2d:51", addrSuffix),
  3701  		}, {
  3702  			Name:       "eth1.100",
  3703  			Type:       state.VLAN_8021QDevice,
  3704  			ParentName: "eth1",
  3705  			MACAddress: fmt.Sprintf("00:11:22:33:%0.2d:51", addrSuffix),
  3706  		}, {
  3707  			Name:       "eth2",
  3708  			Type:       state.EthernetDevice,
  3709  			MACAddress: fmt.Sprintf("00:11:22:33:%0.2d:52", addrSuffix),
  3710  		}, {
  3711  			Name:       "eth3",
  3712  			Type:       state.EthernetDevice,
  3713  			MACAddress: fmt.Sprintf("00:11:22:33:%0.2d:53", addrSuffix),
  3714  		}, {
  3715  			Name:       "eth4",
  3716  			Type:       state.EthernetDevice,
  3717  			MACAddress: fmt.Sprintf("00:11:22:33:%0.2d:54", addrSuffix),
  3718  		}},
  3719  		[]state.LinkLayerDeviceAddress{{
  3720  			DeviceName:   "eth0",
  3721  			ConfigMethod: state.StaticAddress,
  3722  			CIDRAddress:  fmt.Sprintf("8.8.8.%d/16", addrSuffix),
  3723  		}, {
  3724  			DeviceName:   "eth0.100",
  3725  			ConfigMethod: state.StaticAddress,
  3726  			CIDRAddress:  fmt.Sprintf("10.0.0.%d/24", addrSuffix),
  3727  		}, {
  3728  			DeviceName:   "eth1",
  3729  			ConfigMethod: state.StaticAddress,
  3730  			CIDRAddress:  fmt.Sprintf("8.8.4.%d/16", addrSuffix),
  3731  		}, {
  3732  			DeviceName:   "eth1",
  3733  			ConfigMethod: state.StaticAddress,
  3734  			CIDRAddress:  fmt.Sprintf("8.8.4.%d/16", addrSuffix+1),
  3735  		}, {
  3736  			DeviceName:   "eth1.100",
  3737  			ConfigMethod: state.StaticAddress,
  3738  			CIDRAddress:  fmt.Sprintf("10.0.0.%d/24", addrSuffix+1),
  3739  		}, {
  3740  			DeviceName:   "eth2",
  3741  			ConfigMethod: state.StaticAddress,
  3742  			CIDRAddress:  fmt.Sprintf("100.64.0.%d/16", addrSuffix),
  3743  		}, {
  3744  			DeviceName:   "eth4",
  3745  			ConfigMethod: state.StaticAddress,
  3746  			CIDRAddress:  fmt.Sprintf("192.168.1.%d/24", addrSuffix),
  3747  		}}
  3748  }
  3749  
  3750  func (s *uniterNetworkInfoSuite) setupUniterAPIForUnit(c *gc.C, givenUnit *state.Unit) {
  3751  	// Create a FakeAuthorizer so we can check permissions, set up assuming the
  3752  	// given unit agent has logged in.
  3753  	s.authorizer = apiservertesting.FakeAuthorizer{
  3754  		Tag: givenUnit.Tag(),
  3755  	}
  3756  
  3757  	var err error
  3758  	s.uniter, err = uniter.NewUniterAPI(facadetest.Context{
  3759  		State_:             s.State,
  3760  		Resources_:         s.resources,
  3761  		Auth_:              s.authorizer,
  3762  		LeadershipChecker_: s.State.LeadershipChecker(),
  3763  	})
  3764  	c.Assert(err, jc.ErrorIsNil)
  3765  }
  3766  
  3767  func (s *uniterNetworkInfoSuite) addRelationAndAssertInScope(c *gc.C) {
  3768  	// Add a relation between wordpress and mysql and enter scope with
  3769  	// mysqlUnit.
  3770  	rel := s.addRelation(c, "wordpress", "mysql")
  3771  	wpRelUnit, err := rel.Unit(s.wordpressUnit)
  3772  	c.Assert(err, jc.ErrorIsNil)
  3773  	err = wpRelUnit.EnterScope(nil)
  3774  	c.Assert(err, jc.ErrorIsNil)
  3775  	s.assertInScope(c, wpRelUnit, true)
  3776  }
  3777  
  3778  func (s *uniterNetworkInfoSuite) TestNetworkInfoPermissions(c *gc.C) {
  3779  	s.addRelationAndAssertInScope(c)
  3780  	var tests = []struct {
  3781  		Name   string
  3782  		Arg    params.NetworkInfoParams
  3783  		Result params.NetworkInfoResults
  3784  		Error  string
  3785  	}{
  3786  		{
  3787  			"Wrong unit name",
  3788  			params.NetworkInfoParams{Unit: "unit-foo-0", Bindings: []string{"foo"}},
  3789  			params.NetworkInfoResults{},
  3790  			"permission denied",
  3791  		},
  3792  		{
  3793  			"Invalid tag",
  3794  			params.NetworkInfoParams{Unit: "invalid", Bindings: []string{"db-client"}},
  3795  			params.NetworkInfoResults{},
  3796  			`"invalid" is not a valid tag`,
  3797  		},
  3798  		{
  3799  			"No access to unit",
  3800  			params.NetworkInfoParams{Unit: "unit-mysql-0", Bindings: []string{"juju-info"}},
  3801  			params.NetworkInfoResults{},
  3802  			"permission denied",
  3803  		},
  3804  		{
  3805  			"Unknown binding name",
  3806  			params.NetworkInfoParams{Unit: s.wordpressUnit.Tag().String(), Bindings: []string{"unknown"}},
  3807  			params.NetworkInfoResults{
  3808  				Results: map[string]params.NetworkInfoResult{
  3809  					"unknown": {
  3810  						Error: &params.Error{
  3811  							Message: `binding name "unknown" not defined by the unit's charm`,
  3812  						},
  3813  					},
  3814  				},
  3815  			},
  3816  			"",
  3817  		},
  3818  	}
  3819  
  3820  	for _, test := range tests {
  3821  		c.Logf("Testing %s", test.Name)
  3822  		result, err := s.uniter.NetworkInfo(test.Arg)
  3823  		if test.Error != "" {
  3824  			c.Check(err, gc.ErrorMatches, test.Error)
  3825  		} else {
  3826  			c.Assert(err, jc.ErrorIsNil)
  3827  			c.Check(result, jc.DeepEquals, test.Result)
  3828  		}
  3829  	}
  3830  }
  3831  
  3832  func (s *uniterNetworkInfoSuite) TestNetworkInfoForExplicitlyBoundEndpointAndDefaultSpace(c *gc.C) {
  3833  	s.addRelationAndAssertInScope(c)
  3834  
  3835  	args := params.NetworkInfoParams{
  3836  		Unit:     s.wordpressUnit.Tag().String(),
  3837  		Bindings: []string{"db", "admin-api", "db-client"},
  3838  	}
  3839  	// For the relation "wordpress:db mysql:server" we expect to see only
  3840  	// ifaces in the "internal" space, where the "db" endpoint itself
  3841  	// is bound to.
  3842  	expectedConfigWithRelationName := params.NetworkInfoResult{
  3843  		Info: []params.NetworkInfo{
  3844  			{
  3845  				MACAddress:    "00:11:22:33:10:50",
  3846  				InterfaceName: "eth0.100",
  3847  				Addresses: []params.InterfaceAddress{
  3848  					{Address: "10.0.0.10", CIDR: "10.0.0.0/24"},
  3849  				},
  3850  			},
  3851  			{
  3852  				MACAddress:    "00:11:22:33:10:51",
  3853  				InterfaceName: "eth1.100",
  3854  				Addresses: []params.InterfaceAddress{
  3855  					{Address: "10.0.0.11", CIDR: "10.0.0.0/24"},
  3856  				},
  3857  			},
  3858  		},
  3859  		EgressSubnets:    []string{"10.0.0.10/32"},
  3860  		IngressAddresses: []string{"10.0.0.10", "10.0.0.11"},
  3861  	}
  3862  	// For the "admin-api" extra-binding we expect to see only interfaces from
  3863  	// the "public" space.
  3864  	expectedConfigWithExtraBindingName := params.NetworkInfoResult{
  3865  		Info: []params.NetworkInfo{
  3866  			{
  3867  				MACAddress:    "00:11:22:33:10:50",
  3868  				InterfaceName: "eth0",
  3869  				Addresses: []params.InterfaceAddress{
  3870  					{Address: "8.8.8.10", CIDR: "8.8.0.0/16"},
  3871  				},
  3872  			},
  3873  			{
  3874  				MACAddress:    "00:11:22:33:10:51",
  3875  				InterfaceName: "eth1",
  3876  				Addresses: []params.InterfaceAddress{
  3877  					{Address: "8.8.4.10", CIDR: "8.8.0.0/16"},
  3878  					{Address: "8.8.4.11", CIDR: "8.8.0.0/16"},
  3879  				},
  3880  			},
  3881  		},
  3882  		EgressSubnets:    []string{"8.8.8.10/32"},
  3883  		IngressAddresses: []string{"8.8.8.10", "8.8.4.10", "8.8.4.11"},
  3884  	}
  3885  
  3886  	// For the "db-client" extra-binding we expect to see interfaces from default
  3887  	// "wp-default" space
  3888  	expectedConfigWithDefaultSpace := params.NetworkInfoResult{
  3889  		Info: []params.NetworkInfo{
  3890  			{
  3891  				MACAddress:    "00:11:22:33:10:52",
  3892  				InterfaceName: "eth2",
  3893  				Addresses: []params.InterfaceAddress{
  3894  					{Address: "100.64.0.10", CIDR: "100.64.0.0/16"},
  3895  				},
  3896  			},
  3897  		},
  3898  		EgressSubnets:    []string{"100.64.0.10/32"},
  3899  		IngressAddresses: []string{"100.64.0.10"},
  3900  	}
  3901  
  3902  	result, err := s.uniter.NetworkInfo(args)
  3903  	c.Assert(err, jc.ErrorIsNil)
  3904  	c.Check(result, jc.DeepEquals, params.NetworkInfoResults{
  3905  		Results: map[string]params.NetworkInfoResult{
  3906  			"db":        expectedConfigWithRelationName,
  3907  			"admin-api": expectedConfigWithExtraBindingName,
  3908  			"db-client": expectedConfigWithDefaultSpace,
  3909  		},
  3910  	})
  3911  }
  3912  
  3913  func (s *uniterNetworkInfoSuite) TestNetworkInfoL2Binding(c *gc.C) {
  3914  	c.Skip("L2 not supported yet")
  3915  	s.addRelationAndAssertInScope(c)
  3916  
  3917  	args := params.NetworkInfoParams{
  3918  		Unit:     s.wordpressUnit.Tag().String(),
  3919  		Bindings: []string{"foo-bar"},
  3920  	}
  3921  
  3922  	expectedInfo := params.NetworkInfoResult{
  3923  		Info: []params.NetworkInfo{
  3924  			{
  3925  				MACAddress:    "00:11:22:33:10:50",
  3926  				InterfaceName: "eth2",
  3927  			},
  3928  		},
  3929  	}
  3930  
  3931  	result, err := s.uniter.NetworkInfo(args)
  3932  	c.Assert(err, jc.ErrorIsNil)
  3933  	c.Check(result, jc.DeepEquals, params.NetworkInfoResults{
  3934  		Results: map[string]params.NetworkInfoResult{
  3935  			"foo-bar": expectedInfo,
  3936  		},
  3937  	})
  3938  }
  3939  
  3940  func (s *uniterNetworkInfoSuite) TestNetworkInfoForImplicitlyBoundEndpoint(c *gc.C) {
  3941  	// Since wordpressUnit has explicit binding for "db", switch the API to
  3942  	// mysqlUnit and check "mysql:server" uses the machine preferred private
  3943  	// address.
  3944  	s.setupUniterAPIForUnit(c, s.mysqlUnit)
  3945  	rel := s.addRelation(c, "mysql", "wordpress")
  3946  	mysqlRelUnit, err := rel.Unit(s.mysqlUnit)
  3947  	c.Assert(err, jc.ErrorIsNil)
  3948  	err = mysqlRelUnit.EnterScope(nil)
  3949  	c.Assert(err, jc.ErrorIsNil)
  3950  	s.assertInScope(c, mysqlRelUnit, true)
  3951  
  3952  	args := params.NetworkInfoParams{
  3953  		Unit:     s.mysqlUnit.Tag().String(),
  3954  		Bindings: []string{"server"},
  3955  	}
  3956  
  3957  	expectedInfo := params.NetworkInfoResult{
  3958  		Info: []params.NetworkInfo{
  3959  			{
  3960  				MACAddress:    "00:11:22:33:20:54",
  3961  				InterfaceName: "eth4",
  3962  				Addresses: []params.InterfaceAddress{
  3963  					{Address: "192.168.1.20", CIDR: "192.168.1.0/24"},
  3964  				},
  3965  			},
  3966  		},
  3967  		EgressSubnets:    []string{"192.168.1.20/32"},
  3968  		IngressAddresses: []string{"192.168.1.20"},
  3969  	}
  3970  
  3971  	result, err := s.uniter.NetworkInfo(args)
  3972  	c.Assert(err, jc.ErrorIsNil)
  3973  	c.Check(result, jc.DeepEquals, params.NetworkInfoResults{
  3974  		Results: map[string]params.NetworkInfoResult{
  3975  			"server": expectedInfo,
  3976  		},
  3977  	})
  3978  }
  3979  
  3980  func (s *uniterNetworkInfoSuite) TestNetworkInfoUsesRelationAddressNonDefaultBinding(c *gc.C) {
  3981  	// If a network info call is made in the context of a relation, and the
  3982  	// endpoint of that relation is bound to the non default space, we
  3983  	// provide the ingress addresses as those belonging to the space.
  3984  	s.setupUniterAPIForUnit(c, s.mysqlUnit)
  3985  	_, err := s.State.AddRemoteApplication(state.AddRemoteApplicationParams{
  3986  		SourceModel: coretesting.ModelTag,
  3987  		Name:        "wordpress-remote",
  3988  		Endpoints:   []charm.Relation{{Name: "db", Interface: "mysql", Role: "requirer"}},
  3989  	})
  3990  	c.Assert(err, jc.ErrorIsNil)
  3991  	rel := s.addRelation(c, "mysql", "wordpress-remote")
  3992  	mysqlRelUnit, err := rel.Unit(s.mysqlUnit)
  3993  	c.Assert(err, jc.ErrorIsNil)
  3994  	err = mysqlRelUnit.EnterScope(nil)
  3995  	c.Assert(err, jc.ErrorIsNil)
  3996  	s.assertInScope(c, mysqlRelUnit, true)
  3997  
  3998  	// Relation specific egress subnets override model config.
  3999  	err = s.JujuConnSuite.Model.UpdateModelConfig(map[string]interface{}{config.EgressSubnets: "10.0.0.0/8"}, nil)
  4000  	c.Assert(err, jc.ErrorIsNil)
  4001  	relEgress := state.NewRelationEgressNetworks(s.State)
  4002  	_, err = relEgress.Save(rel.Tag().Id(), false, []string{"192.168.1.0/24"})
  4003  	c.Assert(err, jc.ErrorIsNil)
  4004  
  4005  	relId := rel.Id()
  4006  	args := params.NetworkInfoParams{
  4007  		Unit:       s.mysqlUnit.Tag().String(),
  4008  		Bindings:   []string{"server"},
  4009  		RelationId: &relId,
  4010  	}
  4011  
  4012  	expectedInfo := params.NetworkInfoResult{
  4013  		Info: []params.NetworkInfo{
  4014  			{
  4015  				MACAddress:    "00:11:22:33:20:54",
  4016  				InterfaceName: "eth4",
  4017  				Addresses: []params.InterfaceAddress{
  4018  					{Address: "192.168.1.20", CIDR: "192.168.1.0/24"},
  4019  				},
  4020  			},
  4021  		},
  4022  		EgressSubnets:    []string{"192.168.1.0/24"},
  4023  		IngressAddresses: []string{"192.168.1.20"},
  4024  	}
  4025  
  4026  	result, err := s.uniter.NetworkInfo(args)
  4027  	c.Assert(err, jc.ErrorIsNil)
  4028  	c.Check(result, jc.DeepEquals, params.NetworkInfoResults{
  4029  		Results: map[string]params.NetworkInfoResult{
  4030  			"server": expectedInfo,
  4031  		},
  4032  	})
  4033  }
  4034  
  4035  func (s *uniterNetworkInfoSuite) TestNetworkInfoUsesRelationAddressDefaultBinding(c *gc.C) {
  4036  	// If a network info call is made in the context of a relation, and the
  4037  	// endpoint of that relation is not bound, or bound to the default space, we
  4038  	// provide the ingress address relevant to the relation: public for CMR.
  4039  	_, err := s.State.AddRemoteApplication(state.AddRemoteApplicationParams{
  4040  		SourceModel: coretesting.ModelTag,
  4041  		Name:        "wordpress-remote",
  4042  		Endpoints:   []charm.Relation{{Name: "db", Interface: "mysql", Role: "requirer"}},
  4043  	})
  4044  	c.Assert(err, jc.ErrorIsNil)
  4045  
  4046  	// Recreate mysql app without endpoint binding.
  4047  	s.mysql = s.Factory.MakeApplication(c, &factory.ApplicationParams{
  4048  		Name:  "mysql-default",
  4049  		Charm: s.mysqlCharm,
  4050  	})
  4051  	s.mysqlUnit = s.Factory.MakeUnit(c, &factory.UnitParams{
  4052  		Application: s.mysql,
  4053  		Machine:     s.machine1,
  4054  	})
  4055  	s.setupUniterAPIForUnit(c, s.mysqlUnit)
  4056  
  4057  	rel := s.addRelation(c, "mysql-default", "wordpress-remote")
  4058  	mysqlRelUnit, err := rel.Unit(s.mysqlUnit)
  4059  	c.Assert(err, jc.ErrorIsNil)
  4060  	err = mysqlRelUnit.EnterScope(nil)
  4061  	c.Assert(err, jc.ErrorIsNil)
  4062  	s.assertInScope(c, mysqlRelUnit, true)
  4063  
  4064  	// Relation specific egress subnets override model config.
  4065  	err = s.JujuConnSuite.Model.UpdateModelConfig(map[string]interface{}{config.EgressSubnets: "10.0.0.0/8"}, nil)
  4066  	c.Assert(err, jc.ErrorIsNil)
  4067  	relEgress := state.NewRelationEgressNetworks(s.State)
  4068  	_, err = relEgress.Save(rel.Tag().Id(), false, []string{"192.168.1.0/24"})
  4069  	c.Assert(err, jc.ErrorIsNil)
  4070  
  4071  	relId := rel.Id()
  4072  	args := params.NetworkInfoParams{
  4073  		Unit:       s.mysqlUnit.Tag().String(),
  4074  		Bindings:   []string{"server"},
  4075  		RelationId: &relId,
  4076  	}
  4077  
  4078  	// Since it is a remote relation, the expected ingress address is set to the
  4079  	// machine's public address.
  4080  	expectedIngressAddress, err := s.machine1.PublicAddress()
  4081  	c.Assert(err, jc.ErrorIsNil)
  4082  
  4083  	expectedInfo := params.NetworkInfoResult{
  4084  		Info: []params.NetworkInfo{
  4085  			{
  4086  				MACAddress:    "00:11:22:33:20:50",
  4087  				InterfaceName: "eth0.100",
  4088  				Addresses: []params.InterfaceAddress{
  4089  					{Address: "10.0.0.20", CIDR: "10.0.0.0/24"},
  4090  				},
  4091  			},
  4092  		},
  4093  		EgressSubnets:    []string{"192.168.1.0/24"},
  4094  		IngressAddresses: []string{expectedIngressAddress.Value},
  4095  	}
  4096  
  4097  	result, err := s.uniter.NetworkInfo(args)
  4098  	c.Assert(err, jc.ErrorIsNil)
  4099  	c.Check(result, jc.DeepEquals, params.NetworkInfoResults{
  4100  		Results: map[string]params.NetworkInfoResult{
  4101  			"server": expectedInfo,
  4102  		},
  4103  	})
  4104  }
  4105  
  4106  func (s *uniterNetworkInfoSuite) TestNetworkInfoV6Results(c *gc.C) {
  4107  	s.addRelationAndAssertInScope(c)
  4108  
  4109  	args := params.NetworkInfoParams{
  4110  		Unit:     s.wordpressUnit.Tag().String(),
  4111  		Bindings: []string{"db"},
  4112  	}
  4113  
  4114  	expectedResult := params.NetworkInfoResultsV6{
  4115  		Results: map[string]params.NetworkInfoResultV6{
  4116  			"db": {
  4117  				Info: []params.NetworkInfo{
  4118  					{
  4119  						MACAddress:    "00:11:22:33:10:50",
  4120  						InterfaceName: "eth0.100",
  4121  						Addresses:     []params.InterfaceAddress{{Address: "10.0.0.10", CIDR: "10.0.0.0/24"}},
  4122  					}, {
  4123  						MACAddress:    "00:11:22:33:10:51",
  4124  						InterfaceName: "eth1.100",
  4125  						Addresses:     []params.InterfaceAddress{{Address: "10.0.0.11", CIDR: "10.0.0.0/24"}},
  4126  					},
  4127  				},
  4128  			},
  4129  		},
  4130  	}
  4131  
  4132  	apiV6, err := uniter.NewUniterAPIV6(facadetest.Context{
  4133  		State_:             s.State,
  4134  		Resources_:         s.resources,
  4135  		Auth_:              s.authorizer,
  4136  		LeadershipChecker_: s.State.LeadershipChecker(),
  4137  	})
  4138  	c.Assert(err, jc.ErrorIsNil)
  4139  
  4140  	result, err := apiV6.NetworkInfo(args)
  4141  	c.Assert(err, jc.ErrorIsNil)
  4142  
  4143  	c.Check(result, jc.DeepEquals, expectedResult)
  4144  }
  4145  
  4146  func (s *uniterSuite) TestNetworkInfoCAASModelRelation(c *gc.C) {
  4147  	_, cm, gitlab, gitlabUnit := s.setupCAASModel(c)
  4148  
  4149  	st := cm.State()
  4150  	f := factory.NewFactory(st, s.StatePool)
  4151  	ch := f.MakeCharm(c, &factory.CharmParams{Name: "mariadb", Series: "kubernetes"})
  4152  	f.MakeApplication(c, &factory.ApplicationParams{Name: "mariadb", Charm: ch})
  4153  	eps, err := st.InferEndpoints("gitlab", "mariadb")
  4154  	c.Assert(err, jc.ErrorIsNil)
  4155  	rel, err := st.AddRelation(eps...)
  4156  	c.Assert(err, jc.ErrorIsNil)
  4157  	wpRelUnit, err := rel.Unit(gitlabUnit)
  4158  	c.Assert(err, jc.ErrorIsNil)
  4159  	err = wpRelUnit.EnterScope(nil)
  4160  	c.Assert(err, jc.ErrorIsNil)
  4161  
  4162  	var updateUnits state.UpdateUnitsOperation
  4163  	addr := "10.0.0.1"
  4164  	updateUnits.Updates = []*state.UpdateUnitOperation{gitlabUnit.UpdateOperation(state.UnitUpdateProperties{
  4165  		Address: &addr,
  4166  		Ports:   &[]string{"443"},
  4167  	})}
  4168  	err = gitlab.UpdateUnits(&updateUnits)
  4169  	c.Assert(err, jc.ErrorIsNil)
  4170  
  4171  	err = gitlab.UpdateCloudService("", []network.Address{
  4172  		{Value: "192.168.1.2", Scope: network.ScopeCloudLocal},
  4173  		{Value: "54.32.1.2", Scope: network.ScopePublic},
  4174  	})
  4175  	c.Assert(err, jc.ErrorIsNil)
  4176  
  4177  	relId := rel.Id()
  4178  	args := params.NetworkInfoParams{
  4179  		Unit:       gitlabUnit.Tag().String(),
  4180  		Bindings:   []string{"db"},
  4181  		RelationId: &relId,
  4182  	}
  4183  
  4184  	expectedResult := params.NetworkInfoResult{
  4185  		Info: []params.NetworkInfo{
  4186  			{
  4187  				Addresses: []params.InterfaceAddress{
  4188  					{Address: "10.0.0.1"},
  4189  				},
  4190  			},
  4191  		},
  4192  		EgressSubnets:    []string{"54.32.1.2/32"},
  4193  		IngressAddresses: []string{"54.32.1.2", "192.168.1.2", "10.0.0.1"},
  4194  	}
  4195  
  4196  	uniterAPI, err := uniter.NewUniterAPI(facadetest.Context{
  4197  		State_:             st,
  4198  		Resources_:         s.resources,
  4199  		Auth_:              s.authorizer,
  4200  		LeadershipChecker_: s.State.LeadershipChecker(),
  4201  	})
  4202  	c.Assert(err, jc.ErrorIsNil)
  4203  
  4204  	result, err := uniterAPI.NetworkInfo(args)
  4205  	c.Assert(err, jc.ErrorIsNil)
  4206  	c.Check(result.Results["db"], jc.DeepEquals, expectedResult)
  4207  }
  4208  
  4209  func (s *uniterSuite) TestNetworkInfoCAASModelNoRelation(c *gc.C) {
  4210  	_, cm, wp, wpUnit := s.setupCAASModel(c)
  4211  
  4212  	st := cm.State()
  4213  	f := factory.NewFactory(st, s.StatePool)
  4214  	ch := f.MakeCharm(c, &factory.CharmParams{Name: "mariadb", Series: "kubernetes"})
  4215  	f.MakeApplication(c, &factory.ApplicationParams{Name: "mariadb", Charm: ch})
  4216  
  4217  	var updateUnits state.UpdateUnitsOperation
  4218  	addr := "10.0.0.1"
  4219  	updateUnits.Updates = []*state.UpdateUnitOperation{wpUnit.UpdateOperation(state.UnitUpdateProperties{
  4220  		Address: &addr,
  4221  		Ports:   &[]string{"443"},
  4222  	})}
  4223  	err := wp.UpdateUnits(&updateUnits)
  4224  	c.Assert(err, jc.ErrorIsNil)
  4225  
  4226  	err = wp.UpdateCloudService("", []network.Address{
  4227  		{Value: "192.168.1.2", Scope: network.ScopeCloudLocal},
  4228  		{Value: "54.32.1.2", Scope: network.ScopePublic},
  4229  	})
  4230  	c.Assert(err, jc.ErrorIsNil)
  4231  
  4232  	args := params.NetworkInfoParams{
  4233  		Unit:     wpUnit.Tag().String(),
  4234  		Bindings: []string{"db"},
  4235  	}
  4236  
  4237  	expectedResult := params.NetworkInfoResult{
  4238  		Info: []params.NetworkInfo{
  4239  			{
  4240  				Addresses: []params.InterfaceAddress{
  4241  					{Address: "10.0.0.1"},
  4242  				},
  4243  			},
  4244  		},
  4245  		EgressSubnets:    []string{"54.32.1.2/32"},
  4246  		IngressAddresses: []string{"54.32.1.2", "192.168.1.2", "10.0.0.1"},
  4247  	}
  4248  
  4249  	uniterAPI, err := uniter.NewUniterAPI(facadetest.Context{
  4250  		State_:             st,
  4251  		Resources_:         s.resources,
  4252  		Auth_:              s.authorizer,
  4253  		LeadershipChecker_: s.State.LeadershipChecker(),
  4254  	})
  4255  	c.Assert(err, jc.ErrorIsNil)
  4256  
  4257  	result, err := uniterAPI.NetworkInfo(args)
  4258  	c.Assert(err, jc.ErrorIsNil)
  4259  	c.Check(result.Results["db"], jc.DeepEquals, expectedResult)
  4260  }
  4261  
  4262  func (s *uniterSuite) TestGetCloudSpecDeniesAccessWhenNotTrusted(c *gc.C) {
  4263  	result, err := s.uniter.CloudSpec()
  4264  	c.Assert(err, jc.ErrorIsNil)
  4265  	c.Assert(result, gc.DeepEquals, params.CloudSpecResult{Error: apiservertesting.ErrUnauthorized})
  4266  }
  4267  
  4268  type cloudSpecUniterSuite struct {
  4269  	uniterSuiteBase
  4270  }
  4271  
  4272  var _ = gc.Suite(&cloudSpecUniterSuite{})
  4273  
  4274  func (s *cloudSpecUniterSuite) SetUpTest(c *gc.C) {
  4275  	s.uniterSuiteBase.SetUpTest(c)
  4276  
  4277  	// Update the application config for wordpress so that it is authorised to
  4278  	// retrieve its cloud spec.
  4279  	conf := map[string]interface{}{application.TrustConfigOptionName: true}
  4280  	fields := map[string]environschema.Attr{application.TrustConfigOptionName: {Type: environschema.Tbool}}
  4281  	defaults := map[string]interface{}{application.TrustConfigOptionName: false}
  4282  	err := s.wordpress.UpdateApplicationConfig(conf, nil, fields, defaults)
  4283  	c.Assert(err, jc.ErrorIsNil)
  4284  }
  4285  
  4286  func (s *cloudSpecUniterSuite) TestGetCloudSpecReturnsSpecWhenTrusted(c *gc.C) {
  4287  	result, err := s.uniter.CloudSpec()
  4288  	c.Assert(err, jc.ErrorIsNil)
  4289  	c.Assert(result.Error, gc.IsNil)
  4290  	c.Assert(result.Result.Name, gc.Equals, "dummy")
  4291  
  4292  	exp := map[string]string{
  4293  		"username": "dummy",
  4294  		"password": "secret",
  4295  	}
  4296  	c.Assert(result.Result.Credential.Attributes, gc.DeepEquals, exp)
  4297  }
  4298  
  4299  type uniterV8Suite struct {
  4300  	uniterSuiteBase
  4301  	uniterV8 *uniter.UniterAPIV8
  4302  }
  4303  
  4304  var _ = gc.Suite(&uniterV8Suite{})
  4305  
  4306  func (s *uniterV8Suite) SetUpTest(c *gc.C) {
  4307  	s.uniterSuiteBase.SetUpTest(c)
  4308  
  4309  	uniterV8, err := uniter.NewUniterAPIV8(facadetest.Context{
  4310  		State_:             s.State,
  4311  		Resources_:         s.resources,
  4312  		Auth_:              s.authorizer,
  4313  		LeadershipChecker_: s.State.LeadershipChecker(),
  4314  	})
  4315  	c.Assert(err, jc.ErrorIsNil)
  4316  	s.uniterV8 = uniterV8
  4317  }
  4318  
  4319  func (s *uniterV8Suite) TestWatchConfigSettings(c *gc.C) {
  4320  	err := s.wordpressUnit.SetCharmURL(s.wpCharm.URL())
  4321  	c.Assert(err, jc.ErrorIsNil)
  4322  
  4323  	c.Assert(s.resources.Count(), gc.Equals, 0)
  4324  
  4325  	args := params.Entities{Entities: []params.Entity{
  4326  		{Tag: "unit-mysql-0"},
  4327  		{Tag: "unit-wordpress-0"},
  4328  		{Tag: "unit-foo-42"},
  4329  	}}
  4330  	result, err := s.uniterV8.WatchConfigSettings(args)
  4331  	c.Assert(err, jc.ErrorIsNil)
  4332  	c.Assert(result, gc.DeepEquals, params.NotifyWatchResults{
  4333  		Results: []params.NotifyWatchResult{
  4334  			{Error: apiservertesting.ErrUnauthorized},
  4335  			{NotifyWatcherId: "1"},
  4336  			{Error: apiservertesting.ErrUnauthorized},
  4337  		},
  4338  	})
  4339  
  4340  	// Verify the resource was registered and stop when done
  4341  	c.Assert(s.resources.Count(), gc.Equals, 1)
  4342  	resource := s.resources.Get("1")
  4343  	defer statetesting.AssertStop(c, resource)
  4344  
  4345  	// Check that the Watch has consumed the initial event ("returned" in
  4346  	// the Watch call)
  4347  	wc := statetesting.NewNotifyWatcherC(c, s.State, resource.(state.NotifyWatcher))
  4348  	wc.AssertNoChange()
  4349  }
  4350  
  4351  func (s *uniterV8Suite) TestWatchUnitAddresses(c *gc.C) {
  4352  	c.Assert(s.resources.Count(), gc.Equals, 0)
  4353  
  4354  	args := params.Entities{Entities: []params.Entity{
  4355  		{Tag: "unit-mysql-0"},
  4356  		{Tag: "unit-wordpress-0"},
  4357  		{Tag: "unit-foo-42"},
  4358  		{Tag: "machine-0"},
  4359  		{Tag: "application-wordpress"},
  4360  	}}
  4361  	result, err := s.uniterV8.WatchUnitAddresses(args)
  4362  	c.Assert(err, jc.ErrorIsNil)
  4363  	c.Assert(result, gc.DeepEquals, params.NotifyWatchResults{
  4364  		Results: []params.NotifyWatchResult{
  4365  			{Error: apiservertesting.ErrUnauthorized},
  4366  			{NotifyWatcherId: "1"},
  4367  			{Error: apiservertesting.ErrUnauthorized},
  4368  			{Error: apiservertesting.ErrUnauthorized},
  4369  			{Error: apiservertesting.ErrUnauthorized},
  4370  		},
  4371  	})
  4372  
  4373  	// Verify the resource was registered and stop when done
  4374  	c.Assert(s.resources.Count(), gc.Equals, 1)
  4375  	resource := s.resources.Get("1")
  4376  	defer statetesting.AssertStop(c, resource)
  4377  
  4378  	// Check that the Watch has consumed the initial event ("returned" in
  4379  	// the Watch call)
  4380  	wc := statetesting.NewNotifyWatcherC(c, s.State, resource.(state.NotifyWatcher))
  4381  	wc.AssertNoChange()
  4382  }
  4383  
  4384  func (s *uniterV8Suite) TestWatchCAASUnitAddresses(c *gc.C) {
  4385  	_, cm, _, _ := s.setupCAASModel(c)
  4386  	c.Assert(s.resources.Count(), gc.Equals, 0)
  4387  
  4388  	args := params.Entities{Entities: []params.Entity{
  4389  		{Tag: "unit-mysql-0"},
  4390  		{Tag: "unit-gitlab-0"},
  4391  		{Tag: "unit-foo-42"},
  4392  		{Tag: "machine-0"},
  4393  		{Tag: "application-gitlab"},
  4394  	}}
  4395  
  4396  	uniterAPI, err := uniter.NewUniterAPIV8(facadetest.Context{
  4397  		State_:             cm.State(),
  4398  		Resources_:         s.resources,
  4399  		Auth_:              s.authorizer,
  4400  		LeadershipChecker_: s.State.LeadershipChecker(),
  4401  	})
  4402  	c.Assert(err, jc.ErrorIsNil)
  4403  
  4404  	result, err := uniterAPI.WatchUnitAddresses(args)
  4405  	c.Assert(err, jc.ErrorIsNil)
  4406  	c.Assert(result, gc.DeepEquals, params.NotifyWatchResults{
  4407  		Results: []params.NotifyWatchResult{
  4408  			{Error: apiservertesting.ErrUnauthorized},
  4409  			{NotifyWatcherId: "1"},
  4410  			{Error: apiservertesting.ErrUnauthorized},
  4411  			{Error: apiservertesting.ErrUnauthorized},
  4412  			{Error: apiservertesting.ErrUnauthorized},
  4413  		},
  4414  	})
  4415  
  4416  	// Verify the resource was registered and stop when done
  4417  	c.Assert(s.resources.Count(), gc.Equals, 1)
  4418  	resource := s.resources.Get("1")
  4419  	defer statetesting.AssertStop(c, resource)
  4420  
  4421  	// Check that the Watch has consumed the initial event ("returned" in
  4422  	// the Watch call)
  4423  	wc := statetesting.NewNotifyWatcherC(c, s.State, resource.(state.NotifyWatcher))
  4424  	wc.AssertNoChange()
  4425  }