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

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package provisioner_test
     5  
     6  import (
     7  	"fmt"
     8  	stdtesting "testing"
     9  	"time"
    10  
    11  	"github.com/golang/mock/gomock"
    12  	"github.com/juju/collections/set"
    13  	"github.com/juju/errors"
    14  	"github.com/juju/proxy"
    15  	jc "github.com/juju/testing/checkers"
    16  	gc "gopkg.in/check.v1"
    17  	"gopkg.in/juju/charm.v6"
    18  	"gopkg.in/juju/names.v2"
    19  
    20  	"github.com/juju/juju/apiserver/common"
    21  	commontesting "github.com/juju/juju/apiserver/common/testing"
    22  	"github.com/juju/juju/apiserver/facades/agent/provisioner"
    23  	"github.com/juju/juju/apiserver/facades/agent/provisioner/mocks"
    24  	"github.com/juju/juju/apiserver/params"
    25  	apiservertesting "github.com/juju/juju/apiserver/testing"
    26  	"github.com/juju/juju/container"
    27  	"github.com/juju/juju/core/constraints"
    28  	"github.com/juju/juju/core/instance"
    29  	"github.com/juju/juju/core/status"
    30  	"github.com/juju/juju/environs/config"
    31  	environtesting "github.com/juju/juju/environs/testing"
    32  	"github.com/juju/juju/juju/testing"
    33  	"github.com/juju/juju/network"
    34  	"github.com/juju/juju/network/containerizer"
    35  	"github.com/juju/juju/provider/dummy"
    36  	"github.com/juju/juju/state"
    37  	statetesting "github.com/juju/juju/state/testing"
    38  	"github.com/juju/juju/storage"
    39  	"github.com/juju/juju/storage/poolmanager"
    40  	"github.com/juju/juju/storage/provider"
    41  	coretesting "github.com/juju/juju/testing"
    42  	"github.com/juju/juju/testing/factory"
    43  )
    44  
    45  func TestPackage(t *stdtesting.T) {
    46  	coretesting.MgoTestPackage(t)
    47  }
    48  
    49  type provisionerSuite struct {
    50  	testing.JujuConnSuite
    51  
    52  	machines []*state.Machine
    53  
    54  	authorizer  apiservertesting.FakeAuthorizer
    55  	resources   *common.Resources
    56  	provisioner *provisioner.ProvisionerAPIV6
    57  }
    58  
    59  var _ = gc.Suite(&provisionerSuite{})
    60  
    61  func (s *provisionerSuite) SetUpTest(c *gc.C) {
    62  	s.setUpTest(c, false)
    63  }
    64  
    65  func (s *provisionerSuite) setUpTest(c *gc.C, withController bool) {
    66  	if s.JujuConnSuite.ConfigAttrs == nil {
    67  		s.JujuConnSuite.ConfigAttrs = make(map[string]interface{})
    68  	}
    69  	s.JujuConnSuite.ConfigAttrs["image-stream"] = "daily"
    70  	s.JujuConnSuite.SetUpTest(c)
    71  
    72  	// Reset previous machines (if any) and create 3 machines
    73  	// for the tests, plus an optional controller machine.
    74  	s.machines = nil
    75  	// Note that the specific machine ids allocated are assumed
    76  	// to be numerically consecutive from zero.
    77  	if withController {
    78  		s.machines = append(s.machines, testing.AddControllerMachine(c, s.State))
    79  	}
    80  	for i := 0; i < 5; i++ {
    81  		machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
    82  		c.Check(err, jc.ErrorIsNil)
    83  		s.machines = append(s.machines, machine)
    84  	}
    85  
    86  	// Create a FakeAuthorizer so we can check permissions,
    87  	// set up assuming we logged in as the controller.
    88  	s.authorizer = apiservertesting.FakeAuthorizer{
    89  		Controller: true,
    90  	}
    91  
    92  	// Create the resource registry separately to track invocations to
    93  	// Register, and to register the root for tools URLs.
    94  	s.resources = common.NewResources()
    95  
    96  	// Create a provisioner API for the machine.
    97  	provisionerAPI, err := provisioner.NewProvisionerAPIV6(
    98  		s.State,
    99  		s.resources,
   100  		s.authorizer,
   101  	)
   102  	c.Assert(err, jc.ErrorIsNil)
   103  	s.provisioner = provisionerAPI
   104  }
   105  
   106  type withoutControllerSuite struct {
   107  	provisionerSuite
   108  	*commontesting.ModelWatcherTest
   109  }
   110  
   111  var _ = gc.Suite(&withoutControllerSuite{})
   112  
   113  func (s *withoutControllerSuite) SetUpTest(c *gc.C) {
   114  	s.setUpTest(c, false)
   115  	s.ModelWatcherTest = commontesting.NewModelWatcherTest(s.provisioner, s.State, s.resources)
   116  }
   117  
   118  func (s *withoutControllerSuite) TestProvisionerFailsWithNonMachineAgentNonManagerUser(c *gc.C) {
   119  	anAuthorizer := s.authorizer
   120  	anAuthorizer.Controller = true
   121  	// Works with a controller, which is not a machine agent.
   122  	aProvisioner, err := provisioner.NewProvisionerAPI(s.State, s.resources, anAuthorizer)
   123  	c.Assert(err, jc.ErrorIsNil)
   124  	c.Assert(aProvisioner, gc.NotNil)
   125  
   126  	// But fails with neither a machine agent or a controller.
   127  	anAuthorizer.Controller = false
   128  	aProvisioner, err = provisioner.NewProvisionerAPI(s.State, s.resources, anAuthorizer)
   129  	c.Assert(err, gc.NotNil)
   130  	c.Assert(aProvisioner, gc.IsNil)
   131  	c.Assert(err, gc.ErrorMatches, "permission denied")
   132  }
   133  
   134  func (s *withoutControllerSuite) TestSetPasswords(c *gc.C) {
   135  	args := params.EntityPasswords{
   136  		Changes: []params.EntityPassword{
   137  			{Tag: s.machines[0].Tag().String(), Password: "xxx0-1234567890123457890"},
   138  			{Tag: s.machines[1].Tag().String(), Password: "xxx1-1234567890123457890"},
   139  			{Tag: s.machines[2].Tag().String(), Password: "xxx2-1234567890123457890"},
   140  			{Tag: s.machines[3].Tag().String(), Password: "xxx3-1234567890123457890"},
   141  			{Tag: s.machines[4].Tag().String(), Password: "xxx4-1234567890123457890"},
   142  			{Tag: "machine-42", Password: "foo"},
   143  			{Tag: "unit-foo-0", Password: "zzz"},
   144  			{Tag: "application-bar", Password: "abc"},
   145  		},
   146  	}
   147  	results, err := s.provisioner.SetPasswords(args)
   148  	c.Assert(err, jc.ErrorIsNil)
   149  	c.Assert(results, gc.DeepEquals, params.ErrorResults{
   150  		Results: []params.ErrorResult{
   151  			{nil},
   152  			{nil},
   153  			{nil},
   154  			{nil},
   155  			{nil},
   156  			{apiservertesting.NotFoundError("machine 42")},
   157  			{apiservertesting.ErrUnauthorized},
   158  			{apiservertesting.ErrUnauthorized},
   159  		},
   160  	})
   161  
   162  	// Verify the changes to both machines succeeded.
   163  	for i, machine := range s.machines {
   164  		c.Logf("trying %q password", machine.Tag())
   165  		err = machine.Refresh()
   166  		c.Assert(err, jc.ErrorIsNil)
   167  		changed := machine.PasswordValid(fmt.Sprintf("xxx%d-1234567890123457890", i))
   168  		c.Assert(changed, jc.IsTrue)
   169  	}
   170  }
   171  
   172  func (s *withoutControllerSuite) TestShortSetPasswords(c *gc.C) {
   173  	args := params.EntityPasswords{
   174  		Changes: []params.EntityPassword{
   175  			{Tag: s.machines[1].Tag().String(), Password: "xxx1"},
   176  		},
   177  	}
   178  	results, err := s.provisioner.SetPasswords(args)
   179  	c.Assert(err, jc.ErrorIsNil)
   180  	c.Assert(results.Results, gc.HasLen, 1)
   181  	c.Assert(results.Results[0].Error, gc.ErrorMatches,
   182  		"password is only 4 bytes long, and is not a valid Agent password")
   183  }
   184  
   185  func (s *withoutControllerSuite) TestLifeAsMachineAgent(c *gc.C) {
   186  	// NOTE: This and the next call serve to test the two
   187  	// different authorization schemes:
   188  	// 1. Machine agents can access their own machine and
   189  	// any container that has their own machine as parent;
   190  	// 2. Controllers can access any machine without
   191  	// a parent.
   192  	// There's no need to repeat this test for each method,
   193  	// because the authorization logic is common.
   194  
   195  	// Login as a machine agent for machine 0.
   196  	anAuthorizer := s.authorizer
   197  	anAuthorizer.Controller = false
   198  	anAuthorizer.Tag = s.machines[0].Tag()
   199  	aProvisioner, err := provisioner.NewProvisionerAPI(s.State, s.resources, anAuthorizer)
   200  	c.Assert(err, jc.ErrorIsNil)
   201  	c.Assert(aProvisioner, gc.NotNil)
   202  
   203  	// Make the machine dead before trying to add containers.
   204  	err = s.machines[0].EnsureDead()
   205  	c.Assert(err, jc.ErrorIsNil)
   206  
   207  	// Create some containers to work on.
   208  	template := state.MachineTemplate{
   209  		Series: "quantal",
   210  		Jobs:   []state.MachineJob{state.JobHostUnits},
   211  	}
   212  	var containers []*state.Machine
   213  	for i := 0; i < 3; i++ {
   214  		container, err := s.State.AddMachineInsideMachine(template, s.machines[0].Id(), instance.LXD)
   215  		c.Check(err, jc.ErrorIsNil)
   216  		containers = append(containers, container)
   217  	}
   218  	// Make one container dead.
   219  	err = containers[1].EnsureDead()
   220  	c.Assert(err, jc.ErrorIsNil)
   221  
   222  	args := params.Entities{Entities: []params.Entity{
   223  		{Tag: s.machines[0].Tag().String()},
   224  		{Tag: s.machines[1].Tag().String()},
   225  		{Tag: containers[0].Tag().String()},
   226  		{Tag: containers[1].Tag().String()},
   227  		{Tag: containers[2].Tag().String()},
   228  		{Tag: "machine-42"},
   229  		{Tag: "unit-foo-0"},
   230  		{Tag: "application-bar"},
   231  	}}
   232  	result, err := aProvisioner.Life(args)
   233  	c.Assert(err, jc.ErrorIsNil)
   234  	c.Assert(result, gc.DeepEquals, params.LifeResults{
   235  		Results: []params.LifeResult{
   236  			{Life: "dead"},
   237  			{Error: apiservertesting.ErrUnauthorized},
   238  			{Life: "alive"},
   239  			{Life: "dead"},
   240  			{Life: "alive"},
   241  			{Error: apiservertesting.ErrUnauthorized},
   242  			{Error: apiservertesting.ErrUnauthorized},
   243  			{Error: apiservertesting.ErrUnauthorized},
   244  		},
   245  	})
   246  }
   247  
   248  func (s *withoutControllerSuite) TestLifeAsController(c *gc.C) {
   249  	err := s.machines[1].EnsureDead()
   250  	c.Assert(err, jc.ErrorIsNil)
   251  	err = s.machines[1].Refresh()
   252  	c.Assert(err, jc.ErrorIsNil)
   253  	c.Assert(s.machines[0].Life(), gc.Equals, state.Alive)
   254  	c.Assert(s.machines[1].Life(), gc.Equals, state.Dead)
   255  	c.Assert(s.machines[2].Life(), gc.Equals, state.Alive)
   256  
   257  	args := params.Entities{Entities: []params.Entity{
   258  		{Tag: s.machines[0].Tag().String()},
   259  		{Tag: s.machines[1].Tag().String()},
   260  		{Tag: s.machines[2].Tag().String()},
   261  		{Tag: "machine-42"},
   262  		{Tag: "unit-foo-0"},
   263  		{Tag: "application-bar"},
   264  	}}
   265  	result, err := s.provisioner.Life(args)
   266  	c.Assert(err, jc.ErrorIsNil)
   267  	c.Assert(result, gc.DeepEquals, params.LifeResults{
   268  		Results: []params.LifeResult{
   269  			{Life: "alive"},
   270  			{Life: "dead"},
   271  			{Life: "alive"},
   272  			{Error: apiservertesting.NotFoundError("machine 42")},
   273  			{Error: apiservertesting.ErrUnauthorized},
   274  			{Error: apiservertesting.ErrUnauthorized},
   275  		},
   276  	})
   277  
   278  	// Remove the subordinate and make sure it's detected.
   279  	err = s.machines[1].Remove()
   280  	c.Assert(err, jc.ErrorIsNil)
   281  	err = s.machines[1].Refresh()
   282  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   283  
   284  	result, err = s.provisioner.Life(params.Entities{
   285  		Entities: []params.Entity{
   286  			{Tag: s.machines[1].Tag().String()},
   287  		},
   288  	})
   289  	c.Assert(err, jc.ErrorIsNil)
   290  	c.Assert(result, gc.DeepEquals, params.LifeResults{
   291  		Results: []params.LifeResult{
   292  			{Error: apiservertesting.NotFoundError("machine 1")},
   293  		},
   294  	})
   295  }
   296  
   297  func (s *withoutControllerSuite) TestRemove(c *gc.C) {
   298  	err := s.machines[1].EnsureDead()
   299  	c.Assert(err, jc.ErrorIsNil)
   300  	s.assertLife(c, 0, state.Alive)
   301  	s.assertLife(c, 1, state.Dead)
   302  	s.assertLife(c, 2, state.Alive)
   303  
   304  	args := params.Entities{Entities: []params.Entity{
   305  		{Tag: s.machines[0].Tag().String()},
   306  		{Tag: s.machines[1].Tag().String()},
   307  		{Tag: s.machines[2].Tag().String()},
   308  		{Tag: "machine-42"},
   309  		{Tag: "unit-foo-0"},
   310  		{Tag: "application-bar"},
   311  	}}
   312  	result, err := s.provisioner.Remove(args)
   313  	c.Assert(err, jc.ErrorIsNil)
   314  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   315  		Results: []params.ErrorResult{
   316  			{&params.Error{Message: `cannot remove entity "machine-0": still alive`}},
   317  			{nil},
   318  			{&params.Error{Message: `cannot remove entity "machine-2": still alive`}},
   319  			{apiservertesting.NotFoundError("machine 42")},
   320  			{apiservertesting.ErrUnauthorized},
   321  			{apiservertesting.ErrUnauthorized},
   322  		},
   323  	})
   324  
   325  	// Verify the changes.
   326  	s.assertLife(c, 0, state.Alive)
   327  	err = s.machines[2].Refresh()
   328  	c.Assert(err, jc.ErrorIsNil)
   329  	s.assertLife(c, 2, state.Alive)
   330  }
   331  
   332  func (s *withoutControllerSuite) TestSetStatus(c *gc.C) {
   333  	now := time.Now()
   334  	sInfo := status.StatusInfo{
   335  		Status:  status.Started,
   336  		Message: "blah",
   337  		Since:   &now,
   338  	}
   339  	err := s.machines[0].SetStatus(sInfo)
   340  	c.Assert(err, jc.ErrorIsNil)
   341  	sInfo = status.StatusInfo{
   342  		Status:  status.Stopped,
   343  		Message: "foo",
   344  		Since:   &now,
   345  	}
   346  	err = s.machines[1].SetStatus(sInfo)
   347  	c.Assert(err, jc.ErrorIsNil)
   348  	sInfo = status.StatusInfo{
   349  		Status:  status.Error,
   350  		Message: "not really",
   351  		Since:   &now,
   352  	}
   353  	err = s.machines[2].SetStatus(sInfo)
   354  	c.Assert(err, jc.ErrorIsNil)
   355  
   356  	args := params.SetStatus{
   357  		Entities: []params.EntityStatusArgs{
   358  			{Tag: s.machines[0].Tag().String(), Status: status.Error.String(), Info: "not really",
   359  				Data: map[string]interface{}{"foo": "bar"}},
   360  			{Tag: s.machines[1].Tag().String(), Status: status.Stopped.String(), Info: "foobar"},
   361  			{Tag: s.machines[2].Tag().String(), Status: status.Started.String(), Info: "again"},
   362  			{Tag: "machine-42", Status: status.Started.String(), Info: "blah"},
   363  			{Tag: "unit-foo-0", Status: status.Stopped.String(), Info: "foobar"},
   364  			{Tag: "application-bar", Status: status.Stopped.String(), Info: "foobar"},
   365  		}}
   366  	result, err := s.provisioner.SetStatus(args)
   367  	c.Assert(err, jc.ErrorIsNil)
   368  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   369  		Results: []params.ErrorResult{
   370  			{nil},
   371  			{nil},
   372  			{nil},
   373  			{apiservertesting.NotFoundError("machine 42")},
   374  			{apiservertesting.ErrUnauthorized},
   375  			{apiservertesting.ErrUnauthorized},
   376  		},
   377  	})
   378  
   379  	// Verify the changes.
   380  	s.assertStatus(c, 0, status.Error, "not really", map[string]interface{}{"foo": "bar"})
   381  	s.assertStatus(c, 1, status.Stopped, "foobar", map[string]interface{}{})
   382  	s.assertStatus(c, 2, status.Started, "again", map[string]interface{}{})
   383  }
   384  
   385  func (s *withoutControllerSuite) TestSetInstanceStatus(c *gc.C) {
   386  	now := time.Now()
   387  	sInfo := status.StatusInfo{
   388  		Status:  status.Provisioning,
   389  		Message: "blah",
   390  		Since:   &now,
   391  	}
   392  	err := s.machines[0].SetInstanceStatus(sInfo)
   393  	c.Assert(err, jc.ErrorIsNil)
   394  	sInfo = status.StatusInfo{
   395  		Status:  status.Running,
   396  		Message: "foo",
   397  		Since:   &now,
   398  	}
   399  	err = s.machines[1].SetInstanceStatus(sInfo)
   400  	c.Assert(err, jc.ErrorIsNil)
   401  	sInfo = status.StatusInfo{
   402  		Status:  status.Error,
   403  		Message: "not really",
   404  		Since:   &now,
   405  	}
   406  	err = s.machines[2].SetInstanceStatus(sInfo)
   407  	c.Assert(err, jc.ErrorIsNil)
   408  
   409  	args := params.SetStatus{
   410  		Entities: []params.EntityStatusArgs{
   411  			{Tag: s.machines[0].Tag().String(), Status: status.Provisioning.String(), Info: "not really",
   412  				Data: map[string]interface{}{"foo": "bar"}},
   413  			{Tag: s.machines[1].Tag().String(), Status: status.Running.String(), Info: "foobar"},
   414  			{Tag: s.machines[2].Tag().String(), Status: status.ProvisioningError.String(), Info: "again"},
   415  			{Tag: "machine-42", Status: status.Provisioning.String(), Info: "blah"},
   416  			{Tag: "unit-foo-0", Status: status.Error.String(), Info: "foobar"},
   417  			{Tag: "application-bar", Status: status.ProvisioningError.String(), Info: "foobar"},
   418  		}}
   419  	result, err := s.provisioner.SetInstanceStatus(args)
   420  	c.Assert(err, jc.ErrorIsNil)
   421  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   422  		Results: []params.ErrorResult{
   423  			{nil},
   424  			{nil},
   425  			{nil},
   426  			{apiservertesting.NotFoundError("machine 42")},
   427  			{apiservertesting.ErrUnauthorized},
   428  			{apiservertesting.ErrUnauthorized},
   429  		},
   430  	})
   431  
   432  	// Verify the changes.
   433  	s.assertInstanceStatus(c, 0, status.Provisioning, "not really", map[string]interface{}{"foo": "bar"})
   434  	s.assertInstanceStatus(c, 1, status.Running, "foobar", map[string]interface{}{})
   435  	s.assertInstanceStatus(c, 2, status.ProvisioningError, "again", map[string]interface{}{})
   436  	// ProvisioningError also has a special case which is to set the machine to Error
   437  	s.assertStatus(c, 2, status.Error, "again", map[string]interface{}{})
   438  }
   439  
   440  func (s *withoutControllerSuite) TestMachinesWithTransientErrors(c *gc.C) {
   441  	now := time.Now()
   442  	sInfo := status.StatusInfo{
   443  		Status:  status.Provisioning,
   444  		Message: "blah",
   445  		Since:   &now,
   446  	}
   447  	err := s.machines[0].SetInstanceStatus(sInfo)
   448  	c.Assert(err, jc.ErrorIsNil)
   449  	sInfo = status.StatusInfo{
   450  		Status:  status.ProvisioningError,
   451  		Message: "transient error",
   452  		Data:    map[string]interface{}{"transient": true, "foo": "bar"},
   453  		Since:   &now,
   454  	}
   455  	err = s.machines[1].SetInstanceStatus(sInfo)
   456  	c.Assert(err, jc.ErrorIsNil)
   457  	sInfo = status.StatusInfo{
   458  		Status:  status.ProvisioningError,
   459  		Message: "error",
   460  		Data:    map[string]interface{}{"transient": false},
   461  		Since:   &now,
   462  	}
   463  	err = s.machines[2].SetInstanceStatus(sInfo)
   464  	c.Assert(err, jc.ErrorIsNil)
   465  	sInfo = status.StatusInfo{
   466  		Status:  status.Error,
   467  		Message: "error",
   468  		Since:   &now,
   469  	}
   470  	err = s.machines[3].SetInstanceStatus(sInfo)
   471  	c.Assert(err, jc.ErrorIsNil)
   472  	// Machine 4 is provisioned but error not reset yet.
   473  	sInfo = status.StatusInfo{
   474  		Status:  status.Error,
   475  		Message: "transient error",
   476  		Data:    map[string]interface{}{"transient": true, "foo": "bar"},
   477  		Since:   &now,
   478  	}
   479  	err = s.machines[4].SetInstanceStatus(sInfo)
   480  	c.Assert(err, jc.ErrorIsNil)
   481  	hwChars := instance.MustParseHardware("arch=i386", "mem=4G")
   482  	err = s.machines[4].SetProvisioned("i-am", "", "fake_nonce", &hwChars)
   483  	c.Assert(err, jc.ErrorIsNil)
   484  
   485  	result, err := s.provisioner.MachinesWithTransientErrors()
   486  	c.Assert(err, jc.ErrorIsNil)
   487  	c.Assert(result, gc.DeepEquals, params.StatusResults{
   488  		Results: []params.StatusResult{
   489  			{Id: "1", Life: "alive", Status: "provisioning error", Info: "transient error",
   490  				Data: map[string]interface{}{"transient": true, "foo": "bar"}},
   491  		},
   492  	})
   493  }
   494  
   495  func (s *withoutControllerSuite) TestMachinesWithTransientErrorsPermission(c *gc.C) {
   496  	// Machines where there's permission issues are omitted.
   497  	anAuthorizer := s.authorizer
   498  	anAuthorizer.Controller = false
   499  	anAuthorizer.Tag = names.NewMachineTag("1")
   500  	aProvisioner, err := provisioner.NewProvisionerAPI(s.State, s.resources,
   501  		anAuthorizer)
   502  	now := time.Now()
   503  	sInfo := status.StatusInfo{
   504  		Status:  status.Running,
   505  		Message: "blah",
   506  		Since:   &now,
   507  	}
   508  	err = s.machines[0].SetInstanceStatus(sInfo)
   509  	c.Assert(err, jc.ErrorIsNil)
   510  	sInfo = status.StatusInfo{
   511  		Status:  status.ProvisioningError,
   512  		Message: "transient error",
   513  		Data:    map[string]interface{}{"transient": true, "foo": "bar"},
   514  		Since:   &now,
   515  	}
   516  	err = s.machines[1].SetInstanceStatus(sInfo)
   517  	c.Assert(err, jc.ErrorIsNil)
   518  	sInfo = status.StatusInfo{
   519  		Status:  status.ProvisioningError,
   520  		Message: "error",
   521  		Data:    map[string]interface{}{"transient": false},
   522  		Since:   &now,
   523  	}
   524  	err = s.machines[2].SetInstanceStatus(sInfo)
   525  	c.Assert(err, jc.ErrorIsNil)
   526  	sInfo = status.StatusInfo{
   527  		Status:  status.ProvisioningError,
   528  		Message: "error",
   529  		Since:   &now,
   530  	}
   531  	err = s.machines[3].SetInstanceStatus(sInfo)
   532  	c.Assert(err, jc.ErrorIsNil)
   533  
   534  	result, err := aProvisioner.MachinesWithTransientErrors()
   535  	c.Assert(err, jc.ErrorIsNil)
   536  	c.Assert(result, gc.DeepEquals, params.StatusResults{
   537  		Results: []params.StatusResult{{
   538  			Id: "1", Life: "alive", Status: "provisioning error",
   539  			Info: "transient error",
   540  			Data: map[string]interface{}{"transient": true, "foo": "bar"},
   541  		},
   542  		},
   543  	})
   544  }
   545  
   546  func (s *withoutControllerSuite) TestEnsureDead(c *gc.C) {
   547  	err := s.machines[1].EnsureDead()
   548  	c.Assert(err, jc.ErrorIsNil)
   549  	s.assertLife(c, 0, state.Alive)
   550  	s.assertLife(c, 1, state.Dead)
   551  	s.assertLife(c, 2, state.Alive)
   552  
   553  	args := params.Entities{Entities: []params.Entity{
   554  		{Tag: s.machines[0].Tag().String()},
   555  		{Tag: s.machines[1].Tag().String()},
   556  		{Tag: s.machines[2].Tag().String()},
   557  		{Tag: "machine-42"},
   558  		{Tag: "unit-foo-0"},
   559  		{Tag: "application-bar"},
   560  	}}
   561  	result, err := s.provisioner.EnsureDead(args)
   562  	c.Assert(err, jc.ErrorIsNil)
   563  	c.Assert(result, gc.DeepEquals, params.ErrorResults{
   564  		Results: []params.ErrorResult{
   565  			{nil},
   566  			{nil},
   567  			{nil},
   568  			{apiservertesting.NotFoundError("machine 42")},
   569  			{apiservertesting.ErrUnauthorized},
   570  			{apiservertesting.ErrUnauthorized},
   571  		},
   572  	})
   573  
   574  	// Verify the changes.
   575  	s.assertLife(c, 0, state.Dead)
   576  	s.assertLife(c, 1, state.Dead)
   577  	s.assertLife(c, 2, state.Dead)
   578  }
   579  
   580  func (s *withoutControllerSuite) assertLife(c *gc.C, index int, expectLife state.Life) {
   581  	err := s.machines[index].Refresh()
   582  	c.Assert(err, jc.ErrorIsNil)
   583  	c.Assert(s.machines[index].Life(), gc.Equals, expectLife)
   584  }
   585  
   586  func (s *withoutControllerSuite) assertStatus(c *gc.C, index int, expectStatus status.Status, expectInfo string,
   587  	expectData map[string]interface{}) {
   588  
   589  	statusInfo, err := s.machines[index].Status()
   590  	c.Assert(err, jc.ErrorIsNil)
   591  	c.Assert(statusInfo.Status, gc.Equals, expectStatus)
   592  	c.Assert(statusInfo.Message, gc.Equals, expectInfo)
   593  	c.Assert(statusInfo.Data, gc.DeepEquals, expectData)
   594  }
   595  
   596  func (s *withoutControllerSuite) assertInstanceStatus(c *gc.C, index int, expectStatus status.Status, expectInfo string,
   597  	expectData map[string]interface{}) {
   598  
   599  	statusInfo, err := s.machines[index].InstanceStatus()
   600  	c.Assert(err, jc.ErrorIsNil)
   601  	c.Assert(statusInfo.Status, gc.Equals, expectStatus)
   602  	c.Assert(statusInfo.Message, gc.Equals, expectInfo)
   603  	c.Assert(statusInfo.Data, gc.DeepEquals, expectData)
   604  }
   605  
   606  func (s *withoutControllerSuite) TestWatchContainers(c *gc.C) {
   607  	c.Assert(s.resources.Count(), gc.Equals, 0)
   608  
   609  	args := params.WatchContainers{Params: []params.WatchContainer{
   610  		{MachineTag: s.machines[0].Tag().String(), ContainerType: string(instance.LXD)},
   611  		{MachineTag: s.machines[1].Tag().String(), ContainerType: string(instance.KVM)},
   612  		{MachineTag: "machine-42", ContainerType: ""},
   613  		{MachineTag: "unit-foo-0", ContainerType: ""},
   614  		{MachineTag: "application-bar", ContainerType: ""},
   615  	}}
   616  	result, err := s.provisioner.WatchContainers(args)
   617  	c.Assert(err, jc.ErrorIsNil)
   618  	c.Assert(result, gc.DeepEquals, params.StringsWatchResults{
   619  		Results: []params.StringsWatchResult{
   620  			{StringsWatcherId: "1", Changes: []string{}},
   621  			{StringsWatcherId: "2", Changes: []string{}},
   622  			{Error: apiservertesting.NotFoundError("machine 42")},
   623  			{Error: apiservertesting.ErrUnauthorized},
   624  			{Error: apiservertesting.ErrUnauthorized},
   625  		},
   626  	})
   627  
   628  	// Verify the resources were registered and stop them when done.
   629  	c.Assert(s.resources.Count(), gc.Equals, 2)
   630  	m0Watcher := s.resources.Get("1")
   631  	defer statetesting.AssertStop(c, m0Watcher)
   632  	m1Watcher := s.resources.Get("2")
   633  	defer statetesting.AssertStop(c, m1Watcher)
   634  
   635  	// Check that the Watch has consumed the initial event ("returned"
   636  	// in the Watch call)
   637  	wc0 := statetesting.NewStringsWatcherC(c, s.State, m0Watcher.(state.StringsWatcher))
   638  	wc0.AssertNoChange()
   639  	wc1 := statetesting.NewStringsWatcherC(c, s.State, m1Watcher.(state.StringsWatcher))
   640  	wc1.AssertNoChange()
   641  }
   642  
   643  func (s *withoutControllerSuite) TestWatchAllContainers(c *gc.C) {
   644  	c.Assert(s.resources.Count(), gc.Equals, 0)
   645  
   646  	args := params.WatchContainers{Params: []params.WatchContainer{
   647  		{MachineTag: s.machines[0].Tag().String()},
   648  		{MachineTag: s.machines[1].Tag().String()},
   649  		{MachineTag: "machine-42"},
   650  		{MachineTag: "unit-foo-0"},
   651  		{MachineTag: "application-bar"},
   652  	}}
   653  	result, err := s.provisioner.WatchAllContainers(args)
   654  	c.Assert(err, jc.ErrorIsNil)
   655  	c.Assert(result, gc.DeepEquals, params.StringsWatchResults{
   656  		Results: []params.StringsWatchResult{
   657  			{StringsWatcherId: "1", Changes: []string{}},
   658  			{StringsWatcherId: "2", Changes: []string{}},
   659  			{Error: apiservertesting.NotFoundError("machine 42")},
   660  			{Error: apiservertesting.ErrUnauthorized},
   661  			{Error: apiservertesting.ErrUnauthorized},
   662  		},
   663  	})
   664  
   665  	// Verify the resources were registered and stop them when done.
   666  	c.Assert(s.resources.Count(), gc.Equals, 2)
   667  	m0Watcher := s.resources.Get("1")
   668  	defer statetesting.AssertStop(c, m0Watcher)
   669  	m1Watcher := s.resources.Get("2")
   670  	defer statetesting.AssertStop(c, m1Watcher)
   671  
   672  	// Check that the Watch has consumed the initial event ("returned"
   673  	// in the Watch call)
   674  	wc0 := statetesting.NewStringsWatcherC(c, s.State, m0Watcher.(state.StringsWatcher))
   675  	wc0.AssertNoChange()
   676  	wc1 := statetesting.NewStringsWatcherC(c, s.State, m1Watcher.(state.StringsWatcher))
   677  	wc1.AssertNoChange()
   678  }
   679  
   680  func (s *withoutControllerSuite) TestWatchContainersCharmProfiles(c *gc.C) {
   681  	c.Assert(s.resources.Count(), gc.Equals, 0)
   682  
   683  	args := params.WatchContainers{Params: []params.WatchContainer{
   684  		{MachineTag: s.machines[0].Tag().String(), ContainerType: string(instance.LXD)},
   685  		{MachineTag: s.machines[1].Tag().String(), ContainerType: string(instance.KVM)},
   686  		{MachineTag: "machine-42", ContainerType: ""},
   687  		{MachineTag: "unit-foo-0", ContainerType: ""},
   688  		{MachineTag: "application-bar", ContainerType: ""},
   689  	}}
   690  	result, err := s.provisioner.WatchContainersCharmProfiles(args)
   691  	c.Assert(err, jc.ErrorIsNil)
   692  	c.Assert(result, gc.DeepEquals, params.StringsWatchResults{
   693  		Results: []params.StringsWatchResult{
   694  			{StringsWatcherId: "1", Changes: []string{}},
   695  			{StringsWatcherId: "2", Changes: []string{}},
   696  			{Error: apiservertesting.NotFoundError("machine 42")},
   697  			{Error: apiservertesting.ErrUnauthorized},
   698  			{Error: apiservertesting.ErrUnauthorized},
   699  		},
   700  	})
   701  
   702  	// Verify the resources were registered and stop them when done.
   703  	c.Assert(s.resources.Count(), gc.Equals, 2)
   704  	m0Watcher := s.resources.Get("1")
   705  	defer statetesting.AssertStop(c, m0Watcher)
   706  	m1Watcher := s.resources.Get("2")
   707  	defer statetesting.AssertStop(c, m1Watcher)
   708  
   709  	// Check that the Watch has consumed the initial event "returned"
   710  	// in the Watch call)
   711  	wc0 := statetesting.NewStringsWatcherC(c, s.State, m0Watcher.(state.StringsWatcher))
   712  	wc0.AssertNoChange()
   713  	wc1 := statetesting.NewStringsWatcherC(c, s.State, m1Watcher.(state.StringsWatcher))
   714  	wc1.AssertNoChange()
   715  }
   716  
   717  func (s *withoutControllerSuite) TestWatchModelMachinesCharmProfiles(c *gc.C) {
   718  	c.Assert(s.resources.Count(), gc.Equals, 0)
   719  
   720  	got, err := s.provisioner.WatchModelMachinesCharmProfiles()
   721  	c.Assert(err, jc.ErrorIsNil)
   722  	want := params.StringsWatchResult{
   723  		StringsWatcherId: "1",
   724  		Changes:          []string{},
   725  	}
   726  	c.Assert(got.StringsWatcherId, gc.Equals, want.StringsWatcherId)
   727  	c.Assert(got.Changes, jc.SameContents, want.Changes)
   728  
   729  	// Verify the resources were registered and stop them when done.
   730  	c.Assert(s.resources.Count(), gc.Equals, 1)
   731  	resource := s.resources.Get("1")
   732  	defer statetesting.AssertStop(c, resource)
   733  
   734  	// Check that the Watch has consumed the initial event "returned"
   735  	// in the Watch call)
   736  	wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher))
   737  	wc.AssertNoChange()
   738  
   739  	// Make sure WatchModelMachines fails with a machine agent login.
   740  	anAuthorizer := s.authorizer
   741  	anAuthorizer.Tag = names.NewMachineTag("1")
   742  	anAuthorizer.Controller = false
   743  	aProvisioner, err := provisioner.NewProvisionerAPI(s.State, s.resources, anAuthorizer)
   744  	c.Assert(err, jc.ErrorIsNil)
   745  
   746  	result, err := aProvisioner.WatchModelMachinesCharmProfiles()
   747  	c.Assert(err, gc.ErrorMatches, "permission denied")
   748  	c.Assert(result, gc.DeepEquals, params.StringsWatchResult{})
   749  }
   750  
   751  func (s *withoutControllerSuite) TestModelConfigNonManager(c *gc.C) {
   752  	// Now test it with a non-controller and make sure
   753  	// the secret attributes are masked.
   754  	anAuthorizer := s.authorizer
   755  	anAuthorizer.Tag = names.NewMachineTag("1")
   756  	anAuthorizer.Controller = false
   757  	aProvisioner, err := provisioner.NewProvisionerAPI(s.State, s.resources,
   758  		anAuthorizer)
   759  	c.Assert(err, jc.ErrorIsNil)
   760  	s.AssertModelConfig(c, aProvisioner)
   761  }
   762  
   763  func (s *withoutControllerSuite) TestStatus(c *gc.C) {
   764  	now := time.Now()
   765  	sInfo := status.StatusInfo{
   766  		Status:  status.Started,
   767  		Message: "blah",
   768  		Since:   &now,
   769  	}
   770  	err := s.machines[0].SetStatus(sInfo)
   771  	c.Assert(err, jc.ErrorIsNil)
   772  	sInfo = status.StatusInfo{
   773  		Status:  status.Stopped,
   774  		Message: "foo",
   775  		Since:   &now,
   776  	}
   777  	err = s.machines[1].SetStatus(sInfo)
   778  	c.Assert(err, jc.ErrorIsNil)
   779  	sInfo = status.StatusInfo{
   780  		Status:  status.Error,
   781  		Message: "not really",
   782  		Data:    map[string]interface{}{"foo": "bar"},
   783  		Since:   &now,
   784  	}
   785  	err = s.machines[2].SetStatus(sInfo)
   786  	c.Assert(err, jc.ErrorIsNil)
   787  
   788  	args := params.Entities{Entities: []params.Entity{
   789  		{Tag: s.machines[0].Tag().String()},
   790  		{Tag: s.machines[1].Tag().String()},
   791  		{Tag: s.machines[2].Tag().String()},
   792  		{Tag: "machine-42"},
   793  		{Tag: "unit-foo-0"},
   794  		{Tag: "application-bar"},
   795  	}}
   796  	result, err := s.provisioner.Status(args)
   797  	c.Assert(err, jc.ErrorIsNil)
   798  	// Zero out the updated timestamps so we can easily check the results.
   799  	for i, statusResult := range result.Results {
   800  		r := statusResult
   801  		if r.Status != "" {
   802  			c.Assert(r.Since, gc.NotNil)
   803  		}
   804  		r.Since = nil
   805  		result.Results[i] = r
   806  	}
   807  	c.Assert(result, gc.DeepEquals, params.StatusResults{
   808  		Results: []params.StatusResult{
   809  			{Status: status.Started.String(), Info: "blah", Data: map[string]interface{}{}},
   810  			{Status: status.Stopped.String(), Info: "foo", Data: map[string]interface{}{}},
   811  			{Status: status.Error.String(), Info: "not really", Data: map[string]interface{}{"foo": "bar"}},
   812  			{Error: apiservertesting.NotFoundError("machine 42")},
   813  			{Error: apiservertesting.ErrUnauthorized},
   814  			{Error: apiservertesting.ErrUnauthorized},
   815  		},
   816  	})
   817  }
   818  
   819  func (s *withoutControllerSuite) TestInstanceStatus(c *gc.C) {
   820  	now := time.Now()
   821  	sInfo := status.StatusInfo{
   822  		Status:  status.Provisioning,
   823  		Message: "blah",
   824  		Since:   &now,
   825  	}
   826  	err := s.machines[0].SetInstanceStatus(sInfo)
   827  	c.Assert(err, jc.ErrorIsNil)
   828  	sInfo = status.StatusInfo{
   829  		Status:  status.Running,
   830  		Message: "foo",
   831  		Since:   &now,
   832  	}
   833  	err = s.machines[1].SetInstanceStatus(sInfo)
   834  	c.Assert(err, jc.ErrorIsNil)
   835  	sInfo = status.StatusInfo{
   836  		Status:  status.ProvisioningError,
   837  		Message: "not really",
   838  		Data:    map[string]interface{}{"foo": "bar"},
   839  		Since:   &now,
   840  	}
   841  	err = s.machines[2].SetInstanceStatus(sInfo)
   842  	c.Assert(err, jc.ErrorIsNil)
   843  
   844  	args := params.Entities{Entities: []params.Entity{
   845  		{Tag: s.machines[0].Tag().String()},
   846  		{Tag: s.machines[1].Tag().String()},
   847  		{Tag: s.machines[2].Tag().String()},
   848  		{Tag: "machine-42"},
   849  		{Tag: "unit-foo-0"},
   850  		{Tag: "application-bar"},
   851  	}}
   852  	result, err := s.provisioner.InstanceStatus(args)
   853  	c.Assert(err, jc.ErrorIsNil)
   854  	// Zero out the updated timestamps so we can easily check the results.
   855  	for i, statusResult := range result.Results {
   856  		r := statusResult
   857  		if r.Status != "" {
   858  			c.Assert(r.Since, gc.NotNil)
   859  		}
   860  		r.Since = nil
   861  		result.Results[i] = r
   862  	}
   863  	c.Assert(result, gc.DeepEquals, params.StatusResults{
   864  		Results: []params.StatusResult{
   865  			{Status: status.Provisioning.String(), Info: "blah", Data: map[string]interface{}{}},
   866  			{Status: status.Running.String(), Info: "foo", Data: map[string]interface{}{}},
   867  			{Status: status.ProvisioningError.String(), Info: "not really", Data: map[string]interface{}{"foo": "bar"}},
   868  			{Error: apiservertesting.NotFoundError("machine 42")},
   869  			{Error: apiservertesting.ErrUnauthorized},
   870  			{Error: apiservertesting.ErrUnauthorized},
   871  		},
   872  	})
   873  }
   874  
   875  func (s *withoutControllerSuite) TestSeries(c *gc.C) {
   876  	// Add a machine with different series.
   877  	foobarMachine := s.Factory.MakeMachine(c, &factory.MachineParams{Series: "foobar"})
   878  	args := params.Entities{Entities: []params.Entity{
   879  		{Tag: s.machines[0].Tag().String()},
   880  		{Tag: foobarMachine.Tag().String()},
   881  		{Tag: s.machines[2].Tag().String()},
   882  		{Tag: "machine-42"},
   883  		{Tag: "unit-foo-0"},
   884  		{Tag: "application-bar"},
   885  	}}
   886  	result, err := s.provisioner.Series(args)
   887  	c.Assert(err, jc.ErrorIsNil)
   888  	c.Assert(result, gc.DeepEquals, params.StringResults{
   889  		Results: []params.StringResult{
   890  			{Result: s.machines[0].Series()},
   891  			{Result: foobarMachine.Series()},
   892  			{Result: s.machines[2].Series()},
   893  			{Error: apiservertesting.NotFoundError("machine 42")},
   894  			{Error: apiservertesting.ErrUnauthorized},
   895  			{Error: apiservertesting.ErrUnauthorized},
   896  		},
   897  	})
   898  }
   899  
   900  func (s *withoutControllerSuite) TestAvailabilityZone(c *gc.C) {
   901  	availabilityZone := "ru-north-siberia"
   902  	emptyAz := ""
   903  	hcWithAZ := instance.HardwareCharacteristics{AvailabilityZone: &availabilityZone}
   904  	hcWithEmptyAZ := instance.HardwareCharacteristics{AvailabilityZone: &emptyAz}
   905  	hcWithNilAz := instance.HardwareCharacteristics{AvailabilityZone: nil}
   906  
   907  	// add machines with different availability zones: string, empty string, nil
   908  	azMachine, _ := s.Factory.MakeMachineReturningPassword(c, &factory.MachineParams{
   909  		Characteristics: &hcWithAZ,
   910  	})
   911  
   912  	emptyAzMachine, _ := s.Factory.MakeMachineReturningPassword(c, &factory.MachineParams{
   913  		Characteristics: &hcWithEmptyAZ,
   914  	})
   915  
   916  	nilAzMachine, _ := s.Factory.MakeMachineReturningPassword(c, &factory.MachineParams{
   917  		Characteristics: &hcWithNilAz,
   918  	})
   919  	args := params.Entities{Entities: []params.Entity{
   920  		{Tag: azMachine.Tag().String()},
   921  		{Tag: emptyAzMachine.Tag().String()},
   922  		{Tag: nilAzMachine.Tag().String()},
   923  	}}
   924  	result, err := s.provisioner.AvailabilityZone(args)
   925  	c.Assert(err, jc.ErrorIsNil)
   926  	c.Assert(result, gc.DeepEquals, params.StringResults{
   927  		Results: []params.StringResult{
   928  			{Result: availabilityZone},
   929  			{Result: emptyAz},
   930  			{Result: emptyAz},
   931  		},
   932  	})
   933  }
   934  
   935  func (s *withoutControllerSuite) TestKeepInstance(c *gc.C) {
   936  	// Add a machine with keep-instance = true.
   937  	foobarMachine := s.Factory.MakeMachine(c, &factory.MachineParams{InstanceId: "1234"})
   938  	err := foobarMachine.SetKeepInstance(true)
   939  	c.Assert(err, jc.ErrorIsNil)
   940  
   941  	args := params.Entities{Entities: []params.Entity{
   942  		{Tag: s.machines[0].Tag().String()},
   943  		{Tag: foobarMachine.Tag().String()},
   944  		{Tag: s.machines[2].Tag().String()},
   945  		{Tag: "machine-42"},
   946  		{Tag: "unit-foo-0"},
   947  		{Tag: "application-bar"},
   948  	}}
   949  	result, err := s.provisioner.KeepInstance(args)
   950  	c.Assert(err, jc.ErrorIsNil)
   951  	c.Assert(result, gc.DeepEquals, params.BoolResults{
   952  		Results: []params.BoolResult{
   953  			{Result: false},
   954  			{Result: true},
   955  			{Result: false},
   956  			{Error: apiservertesting.NotFoundError("machine 42")},
   957  			{Error: apiservertesting.ErrUnauthorized},
   958  			{Error: apiservertesting.ErrUnauthorized},
   959  		},
   960  	})
   961  }
   962  
   963  func (s *withoutControllerSuite) TestDistributionGroup(c *gc.C) {
   964  	addUnits := func(name string, machines ...*state.Machine) (units []*state.Unit) {
   965  		app := s.AddTestingApplication(c, name, s.AddTestingCharm(c, name))
   966  		for _, m := range machines {
   967  			unit, err := app.AddUnit(state.AddUnitParams{})
   968  			c.Assert(err, jc.ErrorIsNil)
   969  			err = unit.AssignToMachine(m)
   970  			c.Assert(err, jc.ErrorIsNil)
   971  			units = append(units, unit)
   972  		}
   973  		return units
   974  	}
   975  	setProvisioned := func(id string) {
   976  		m, err := s.State.Machine(id)
   977  		c.Assert(err, jc.ErrorIsNil)
   978  		err = m.SetProvisioned(instance.Id("machine-"+id+"-inst"), "", "nonce", nil)
   979  		c.Assert(err, jc.ErrorIsNil)
   980  	}
   981  
   982  	mysqlUnit := addUnits("mysql", s.machines[0], s.machines[3])[0]
   983  	wordpressUnits := addUnits("wordpress", s.machines[0], s.machines[1], s.machines[2])
   984  
   985  	// Unassign wordpress/1 from machine-1.
   986  	// The unit should not show up in the results.
   987  	err := wordpressUnits[1].UnassignFromMachine()
   988  	c.Assert(err, jc.ErrorIsNil)
   989  
   990  	// Provision machines 1, 2 and 3. Machine-0 remains
   991  	// unprovisioned, and machine-1 has no units, and so
   992  	// neither will show up in the results.
   993  	setProvisioned("1")
   994  	setProvisioned("2")
   995  	setProvisioned("3")
   996  
   997  	// Add a few controllers, provision two of them.
   998  	_, err = s.State.EnableHA(3, constraints.Value{}, "quantal", nil)
   999  	c.Assert(err, jc.ErrorIsNil)
  1000  	setProvisioned("5")
  1001  	setProvisioned("7")
  1002  
  1003  	// Create a logging service, subordinate to mysql.
  1004  	s.AddTestingApplication(c, "logging", s.AddTestingCharm(c, "logging"))
  1005  	eps, err := s.State.InferEndpoints("mysql", "logging")
  1006  	c.Assert(err, jc.ErrorIsNil)
  1007  	rel, err := s.State.AddRelation(eps...)
  1008  	c.Assert(err, jc.ErrorIsNil)
  1009  	ru, err := rel.Unit(mysqlUnit)
  1010  	c.Assert(err, jc.ErrorIsNil)
  1011  	err = ru.EnterScope(nil)
  1012  	c.Assert(err, jc.ErrorIsNil)
  1013  
  1014  	args := params.Entities{Entities: []params.Entity{
  1015  		{Tag: s.machines[0].Tag().String()},
  1016  		{Tag: s.machines[1].Tag().String()},
  1017  		{Tag: s.machines[2].Tag().String()},
  1018  		{Tag: s.machines[3].Tag().String()},
  1019  		{Tag: "machine-5"},
  1020  	}}
  1021  	result, err := s.provisioner.DistributionGroup(args)
  1022  	c.Assert(err, jc.ErrorIsNil)
  1023  	c.Assert(result, gc.DeepEquals, params.DistributionGroupResults{
  1024  		Results: []params.DistributionGroupResult{
  1025  			{Result: []instance.Id{"machine-2-inst", "machine-3-inst"}},
  1026  			{Result: []instance.Id{}},
  1027  			{Result: []instance.Id{"machine-2-inst"}},
  1028  			{Result: []instance.Id{"machine-3-inst"}},
  1029  			{Result: []instance.Id{"machine-5-inst", "machine-7-inst"}},
  1030  		},
  1031  	})
  1032  }
  1033  
  1034  func (s *withoutControllerSuite) TestDistributionGroupControllerAuth(c *gc.C) {
  1035  	args := params.Entities{Entities: []params.Entity{
  1036  		{Tag: "machine-0"},
  1037  		{Tag: "machine-42"},
  1038  		{Tag: "machine-0-lxd-99"},
  1039  		{Tag: "unit-foo-0"},
  1040  		{Tag: "application-bar"},
  1041  	}}
  1042  	result, err := s.provisioner.DistributionGroup(args)
  1043  	c.Assert(err, jc.ErrorIsNil)
  1044  	c.Assert(result, gc.DeepEquals, params.DistributionGroupResults{
  1045  		Results: []params.DistributionGroupResult{
  1046  			// controller may access any top-level machines.
  1047  			{Result: []instance.Id{}},
  1048  			{Error: apiservertesting.NotFoundError("machine 42")},
  1049  			// only a machine agent for the container or its
  1050  			// parent may access it.
  1051  			{Error: apiservertesting.ErrUnauthorized},
  1052  			// non-machines always unauthorized
  1053  			{Error: apiservertesting.ErrUnauthorized},
  1054  			{Error: apiservertesting.ErrUnauthorized},
  1055  		},
  1056  	})
  1057  }
  1058  
  1059  func (s *withoutControllerSuite) TestDistributionGroupMachineAgentAuth(c *gc.C) {
  1060  	anAuthorizer := s.authorizer
  1061  	anAuthorizer.Tag = names.NewMachineTag("1")
  1062  	anAuthorizer.Controller = false
  1063  	provisioner, err := provisioner.NewProvisionerAPI(s.State, s.resources, anAuthorizer)
  1064  	c.Check(err, jc.ErrorIsNil)
  1065  	args := params.Entities{Entities: []params.Entity{
  1066  		{Tag: "machine-0"},
  1067  		{Tag: "machine-1"},
  1068  		{Tag: "machine-42"},
  1069  		{Tag: "machine-0-lxd-99"},
  1070  		{Tag: "machine-1-lxd-99"},
  1071  		{Tag: "machine-1-lxd-99-lxd-100"},
  1072  	}}
  1073  	result, err := provisioner.DistributionGroup(args)
  1074  	c.Assert(err, jc.ErrorIsNil)
  1075  	c.Assert(result, gc.DeepEquals, params.DistributionGroupResults{
  1076  		Results: []params.DistributionGroupResult{
  1077  			{Error: apiservertesting.ErrUnauthorized},
  1078  			{Result: []instance.Id{}},
  1079  			{Error: apiservertesting.ErrUnauthorized},
  1080  			// only a machine agent for the container or its
  1081  			// parent may access it.
  1082  			{Error: apiservertesting.ErrUnauthorized},
  1083  			{Error: apiservertesting.NotFoundError("machine 1/lxd/99")},
  1084  			{Error: apiservertesting.ErrUnauthorized},
  1085  		},
  1086  	})
  1087  }
  1088  
  1089  func (s *withoutControllerSuite) TestDistributionGroupByMachineId(c *gc.C) {
  1090  	addUnits := func(name string, machines ...*state.Machine) (units []*state.Unit) {
  1091  		app := s.AddTestingApplication(c, name, s.AddTestingCharm(c, name))
  1092  		for _, m := range machines {
  1093  			unit, err := app.AddUnit(state.AddUnitParams{})
  1094  			c.Assert(err, jc.ErrorIsNil)
  1095  			err = unit.AssignToMachine(m)
  1096  			c.Assert(err, jc.ErrorIsNil)
  1097  			units = append(units, unit)
  1098  		}
  1099  		return units
  1100  	}
  1101  	setProvisioned := func(id string) {
  1102  		m, err := s.State.Machine(id)
  1103  		c.Assert(err, jc.ErrorIsNil)
  1104  		err = m.SetProvisioned(instance.Id("machine-"+id+"-inst"), "", "nonce", nil)
  1105  		c.Assert(err, jc.ErrorIsNil)
  1106  	}
  1107  
  1108  	_ = addUnits("mysql", s.machines[0], s.machines[3])[0]
  1109  	wordpressUnits := addUnits("wordpress", s.machines[0], s.machines[1], s.machines[2])
  1110  
  1111  	// Unassign wordpress/1 from machine-1.
  1112  	// The unit should not show up in the results.
  1113  	err := wordpressUnits[1].UnassignFromMachine()
  1114  	c.Assert(err, jc.ErrorIsNil)
  1115  
  1116  	// Provision machines 1, 2 and 3. Machine-0 remains
  1117  	// unprovisioned.
  1118  	setProvisioned("1")
  1119  	setProvisioned("2")
  1120  	setProvisioned("3")
  1121  
  1122  	// Add a few controllers, provision two of them.
  1123  	_, err = s.State.EnableHA(3, constraints.Value{}, "quantal", nil)
  1124  	c.Assert(err, jc.ErrorIsNil)
  1125  	setProvisioned("5")
  1126  	setProvisioned("7")
  1127  
  1128  	args := params.Entities{Entities: []params.Entity{
  1129  		{Tag: s.machines[0].Tag().String()},
  1130  		{Tag: s.machines[1].Tag().String()},
  1131  		{Tag: s.machines[2].Tag().String()},
  1132  		{Tag: s.machines[3].Tag().String()},
  1133  		{Tag: "machine-5"},
  1134  	}}
  1135  	provisionerV5 := provisioner.ProvisionerAPIV5{s.provisioner}
  1136  	result, err := provisionerV5.DistributionGroupByMachineId(args)
  1137  	c.Assert(err, jc.ErrorIsNil)
  1138  	c.Assert(result, gc.DeepEquals, params.StringsResults{
  1139  		Results: []params.StringsResult{
  1140  			{Result: []string{"2", "3"}},
  1141  			{Result: []string{}},
  1142  			{Result: []string{"0"}},
  1143  			{Result: []string{"0"}},
  1144  			{Result: []string{"6", "7"}},
  1145  		},
  1146  	})
  1147  }
  1148  
  1149  func (s *withoutControllerSuite) TestDistributionGroupByMachineIdControllerAuth(c *gc.C) {
  1150  	args := params.Entities{Entities: []params.Entity{
  1151  		{Tag: "machine-0"},
  1152  		{Tag: "machine-42"},
  1153  		{Tag: "machine-0-lxd-99"},
  1154  		{Tag: "unit-foo-0"},
  1155  		{Tag: "application-bar"},
  1156  	}}
  1157  	provisionerV5 := provisioner.ProvisionerAPIV5{s.provisioner}
  1158  	result, err := provisionerV5.DistributionGroupByMachineId(args)
  1159  	c.Assert(err, jc.ErrorIsNil)
  1160  	c.Assert(result, gc.DeepEquals, params.StringsResults{
  1161  		Results: []params.StringsResult{
  1162  			// controller may access any top-level machines.
  1163  			{Result: []string{}, Error: nil},
  1164  			{Result: nil, Error: apiservertesting.NotFoundError("machine 42")},
  1165  			// only a machine agent for the container or its
  1166  			// parent may access it.
  1167  			{Result: nil, Error: apiservertesting.ErrUnauthorized},
  1168  			// non-machines always unauthorized
  1169  			{Result: nil, Error: apiservertesting.ErrUnauthorized},
  1170  			{Result: nil, Error: apiservertesting.ErrUnauthorized},
  1171  		},
  1172  	})
  1173  }
  1174  
  1175  func (s *withoutControllerSuite) TestDistributionGroupByMachineIdMachineAgentAuth(c *gc.C) {
  1176  	anAuthorizer := s.authorizer
  1177  	anAuthorizer.Tag = names.NewMachineTag("1")
  1178  	anAuthorizer.Controller = false
  1179  	provisionerV5, err := provisioner.NewProvisionerAPIV5(s.State, s.resources, anAuthorizer)
  1180  	c.Check(err, jc.ErrorIsNil)
  1181  	args := params.Entities{Entities: []params.Entity{
  1182  		{Tag: "machine-0"},
  1183  		{Tag: "machine-1"},
  1184  		{Tag: "machine-42"},
  1185  		{Tag: "machine-0-lxd-99"},
  1186  		{Tag: "machine-1-lxd-99"},
  1187  		{Tag: "machine-1-lxd-99-lxd-100"},
  1188  	}}
  1189  	result, err := provisionerV5.DistributionGroupByMachineId(args)
  1190  	c.Assert(err, jc.ErrorIsNil)
  1191  	c.Assert(result, gc.DeepEquals, params.StringsResults{
  1192  		Results: []params.StringsResult{
  1193  			{Result: nil, Error: apiservertesting.ErrUnauthorized},
  1194  			{Result: []string{}, Error: nil},
  1195  			{Result: nil, Error: apiservertesting.ErrUnauthorized},
  1196  			// only a machine agent for the container or its
  1197  			// parent may access it.
  1198  			{Result: nil, Error: apiservertesting.ErrUnauthorized},
  1199  			{Result: nil, Error: apiservertesting.NotFoundError("machine 1/lxd/99")},
  1200  			{Result: nil, Error: apiservertesting.ErrUnauthorized},
  1201  		},
  1202  	})
  1203  }
  1204  
  1205  func (s *provisionerSuite) TestConstraints(c *gc.C) {
  1206  	// Add a machine with some constraints.
  1207  	cons := constraints.MustParse("cores=123", "mem=8G")
  1208  	template := state.MachineTemplate{
  1209  		Series:      "quantal",
  1210  		Jobs:        []state.MachineJob{state.JobHostUnits},
  1211  		Constraints: cons,
  1212  	}
  1213  	consMachine, err := s.State.AddOneMachine(template)
  1214  	c.Assert(err, jc.ErrorIsNil)
  1215  
  1216  	machine0Constraints, err := s.machines[0].Constraints()
  1217  	c.Assert(err, jc.ErrorIsNil)
  1218  
  1219  	args := params.Entities{Entities: []params.Entity{
  1220  		{Tag: s.machines[0].Tag().String()},
  1221  		{Tag: consMachine.Tag().String()},
  1222  		{Tag: "machine-42"},
  1223  		{Tag: "unit-foo-0"},
  1224  		{Tag: "application-bar"},
  1225  	}}
  1226  	result, err := s.provisioner.Constraints(args)
  1227  	c.Assert(err, jc.ErrorIsNil)
  1228  	c.Assert(result, gc.DeepEquals, params.ConstraintsResults{
  1229  		Results: []params.ConstraintsResult{
  1230  			{Constraints: machine0Constraints},
  1231  			{Constraints: template.Constraints},
  1232  			{Error: apiservertesting.NotFoundError("machine 42")},
  1233  			{Error: apiservertesting.ErrUnauthorized},
  1234  			{Error: apiservertesting.ErrUnauthorized},
  1235  		},
  1236  	})
  1237  }
  1238  
  1239  func (s *withoutControllerSuite) TestSetInstanceInfo(c *gc.C) {
  1240  	pm := poolmanager.New(state.NewStateSettings(s.State), storage.ChainedProviderRegistry{
  1241  		dummy.StorageProviders(),
  1242  		provider.CommonStorageProviders(),
  1243  	})
  1244  	_, err := pm.Create("static-pool", "static", map[string]interface{}{"foo": "bar"})
  1245  	c.Assert(err, jc.ErrorIsNil)
  1246  	err = s.Model.UpdateModelConfig(map[string]interface{}{
  1247  		"storage-default-block-source": "static-pool",
  1248  	}, nil)
  1249  	c.Assert(err, jc.ErrorIsNil)
  1250  
  1251  	// Provision machine 0 first.
  1252  	hwChars := instance.MustParseHardware("arch=i386", "mem=4G")
  1253  	err = s.machines[0].SetInstanceInfo("i-am", "", "fake_nonce", &hwChars, nil, nil, nil, nil, nil)
  1254  	c.Assert(err, jc.ErrorIsNil)
  1255  
  1256  	volumesMachine, err := s.State.AddOneMachine(state.MachineTemplate{
  1257  		Series: "quantal",
  1258  		Jobs:   []state.MachineJob{state.JobHostUnits},
  1259  		Volumes: []state.HostVolumeParams{{
  1260  			Volume: state.VolumeParams{Size: 1000},
  1261  		}},
  1262  	})
  1263  	c.Assert(err, jc.ErrorIsNil)
  1264  
  1265  	args := params.InstancesInfo{Machines: []params.InstanceInfo{{
  1266  		Tag:        s.machines[0].Tag().String(),
  1267  		InstanceId: "i-was",
  1268  		Nonce:      "fake_nonce",
  1269  	}, {
  1270  		Tag:             s.machines[1].Tag().String(),
  1271  		InstanceId:      "i-will",
  1272  		Nonce:           "fake_nonce",
  1273  		Characteristics: &hwChars,
  1274  	}, {
  1275  		Tag:             s.machines[2].Tag().String(),
  1276  		InstanceId:      "i-am-too",
  1277  		Nonce:           "fake",
  1278  		Characteristics: nil,
  1279  	}, {
  1280  		Tag:        volumesMachine.Tag().String(),
  1281  		InstanceId: "i-am-also",
  1282  		Nonce:      "fake",
  1283  		Volumes: []params.Volume{{
  1284  			VolumeTag: "volume-0",
  1285  			Info: params.VolumeInfo{
  1286  				VolumeId: "vol-0",
  1287  				Size:     1234,
  1288  			},
  1289  		}},
  1290  		VolumeAttachments: map[string]params.VolumeAttachmentInfo{
  1291  			"volume-0": {
  1292  				DeviceName: "sda",
  1293  			},
  1294  		},
  1295  	},
  1296  		{Tag: "machine-42"},
  1297  		{Tag: "unit-foo-0"},
  1298  		{Tag: "application-bar"},
  1299  	}}
  1300  	result, err := s.provisioner.SetInstanceInfo(args)
  1301  	c.Assert(err, jc.ErrorIsNil)
  1302  	c.Assert(result, jc.DeepEquals, params.ErrorResults{
  1303  		Results: []params.ErrorResult{
  1304  			{&params.Error{
  1305  				Message: `cannot record provisioning info for "i-was": cannot set instance data for machine "0": already set`,
  1306  			}},
  1307  			{nil},
  1308  			{nil},
  1309  			{nil},
  1310  			{apiservertesting.NotFoundError("machine 42")},
  1311  			{apiservertesting.ErrUnauthorized},
  1312  			{apiservertesting.ErrUnauthorized},
  1313  		},
  1314  	})
  1315  
  1316  	// Verify machine 1 and 2 were provisioned.
  1317  	c.Assert(s.machines[1].Refresh(), gc.IsNil)
  1318  	c.Assert(s.machines[2].Refresh(), gc.IsNil)
  1319  
  1320  	instanceId, err := s.machines[1].InstanceId()
  1321  	c.Assert(err, jc.ErrorIsNil)
  1322  	c.Check(instanceId, gc.Equals, instance.Id("i-will"))
  1323  	instanceId, err = s.machines[2].InstanceId()
  1324  	c.Assert(err, jc.ErrorIsNil)
  1325  	c.Check(instanceId, gc.Equals, instance.Id("i-am-too"))
  1326  	c.Check(s.machines[1].CheckProvisioned("fake_nonce"), jc.IsTrue)
  1327  	c.Check(s.machines[2].CheckProvisioned("fake"), jc.IsTrue)
  1328  	gotHardware, err := s.machines[1].HardwareCharacteristics()
  1329  	c.Assert(err, jc.ErrorIsNil)
  1330  	c.Check(gotHardware, gc.DeepEquals, &hwChars)
  1331  
  1332  	// Verify the machine with requested volumes was provisioned, and the
  1333  	// volume information recorded in state.
  1334  	sb, err := state.NewStorageBackend(s.State)
  1335  	c.Assert(err, jc.ErrorIsNil)
  1336  	volumeAttachments, err := sb.MachineVolumeAttachments(volumesMachine.MachineTag())
  1337  	c.Assert(err, jc.ErrorIsNil)
  1338  	c.Assert(volumeAttachments, gc.HasLen, 1)
  1339  	volumeAttachmentInfo, err := volumeAttachments[0].Info()
  1340  	c.Assert(err, jc.ErrorIsNil)
  1341  	c.Assert(volumeAttachmentInfo, gc.Equals, state.VolumeAttachmentInfo{DeviceName: "sda"})
  1342  	volume, err := sb.Volume(volumeAttachments[0].Volume())
  1343  	c.Assert(err, jc.ErrorIsNil)
  1344  	volumeInfo, err := volume.Info()
  1345  	c.Assert(err, jc.ErrorIsNil)
  1346  	c.Assert(volumeInfo, gc.Equals, state.VolumeInfo{VolumeId: "vol-0", Pool: "static-pool", Size: 1234})
  1347  
  1348  	// Verify the machine without requested volumes still has no volume
  1349  	// attachments recorded in state.
  1350  	volumeAttachments, err = sb.MachineVolumeAttachments(s.machines[1].MachineTag())
  1351  	c.Assert(err, jc.ErrorIsNil)
  1352  	c.Assert(volumeAttachments, gc.HasLen, 0)
  1353  }
  1354  
  1355  func (s *withoutControllerSuite) TestInstanceId(c *gc.C) {
  1356  	// Provision 2 machines first.
  1357  	err := s.machines[0].SetProvisioned("i-am", "", "fake_nonce", nil)
  1358  	c.Assert(err, jc.ErrorIsNil)
  1359  	hwChars := instance.MustParseHardware("arch=i386", "mem=4G")
  1360  	err = s.machines[1].SetProvisioned("i-am-not", "", "fake_nonce", &hwChars)
  1361  	c.Assert(err, jc.ErrorIsNil)
  1362  
  1363  	args := params.Entities{Entities: []params.Entity{
  1364  		{Tag: s.machines[0].Tag().String()},
  1365  		{Tag: s.machines[1].Tag().String()},
  1366  		{Tag: s.machines[2].Tag().String()},
  1367  		{Tag: "machine-42"},
  1368  		{Tag: "unit-foo-0"},
  1369  		{Tag: "application-bar"},
  1370  	}}
  1371  	result, err := s.provisioner.InstanceId(args)
  1372  	c.Assert(err, jc.ErrorIsNil)
  1373  	c.Assert(result, gc.DeepEquals, params.StringResults{
  1374  		Results: []params.StringResult{
  1375  			{Result: "i-am"},
  1376  			{Result: "i-am-not"},
  1377  			{Error: apiservertesting.NotProvisionedError("2")},
  1378  			{Error: apiservertesting.NotFoundError("machine 42")},
  1379  			{Error: apiservertesting.ErrUnauthorized},
  1380  			{Error: apiservertesting.ErrUnauthorized},
  1381  		},
  1382  	})
  1383  }
  1384  
  1385  func (s *withoutControllerSuite) TestWatchModelMachines(c *gc.C) {
  1386  	c.Assert(s.resources.Count(), gc.Equals, 0)
  1387  
  1388  	got, err := s.provisioner.WatchModelMachines()
  1389  	c.Assert(err, jc.ErrorIsNil)
  1390  	want := params.StringsWatchResult{
  1391  		StringsWatcherId: "1",
  1392  		Changes:          []string{"0", "1", "2", "3", "4"},
  1393  	}
  1394  	c.Assert(got.StringsWatcherId, gc.Equals, want.StringsWatcherId)
  1395  	c.Assert(got.Changes, jc.SameContents, want.Changes)
  1396  
  1397  	// Verify the resources were registered and stop them when done.
  1398  	c.Assert(s.resources.Count(), gc.Equals, 1)
  1399  	resource := s.resources.Get("1")
  1400  	defer statetesting.AssertStop(c, resource)
  1401  
  1402  	// Check that the Watch has consumed the initial event ("returned"
  1403  	// in the Watch call)
  1404  	wc := statetesting.NewStringsWatcherC(c, s.State, resource.(state.StringsWatcher))
  1405  	wc.AssertNoChange()
  1406  
  1407  	// Make sure WatchModelMachines fails with a machine agent login.
  1408  	anAuthorizer := s.authorizer
  1409  	anAuthorizer.Tag = names.NewMachineTag("1")
  1410  	anAuthorizer.Controller = false
  1411  	aProvisioner, err := provisioner.NewProvisionerAPI(s.State, s.resources, anAuthorizer)
  1412  	c.Assert(err, jc.ErrorIsNil)
  1413  
  1414  	result, err := aProvisioner.WatchModelMachines()
  1415  	c.Assert(err, gc.ErrorMatches, "permission denied")
  1416  	c.Assert(result, gc.DeepEquals, params.StringsWatchResult{})
  1417  }
  1418  
  1419  func (s *provisionerSuite) getManagerConfig(c *gc.C, typ instance.ContainerType) map[string]string {
  1420  	args := params.ContainerManagerConfigParams{Type: typ}
  1421  	results, err := s.provisioner.ContainerManagerConfig(args)
  1422  	c.Assert(err, jc.ErrorIsNil)
  1423  	return results.ManagerConfig
  1424  }
  1425  
  1426  func (s *withoutControllerSuite) TestContainerManagerConfigDefaults(c *gc.C) {
  1427  	cfg := s.getManagerConfig(c, instance.KVM)
  1428  	c.Assert(cfg, jc.DeepEquals, map[string]string{
  1429  		container.ConfigModelUUID:      coretesting.ModelTag.Id(),
  1430  		config.ContainerImageStreamKey: "released",
  1431  	})
  1432  }
  1433  
  1434  type withImageMetadataSuite struct {
  1435  	provisionerSuite
  1436  }
  1437  
  1438  var _ = gc.Suite(&withImageMetadataSuite{})
  1439  
  1440  func (s *withImageMetadataSuite) SetUpTest(c *gc.C) {
  1441  	s.ConfigAttrs = map[string]interface{}{
  1442  		config.ContainerImageStreamKey:      "daily",
  1443  		config.ContainerImageMetadataURLKey: "https://images.linuxcontainers.org/",
  1444  	}
  1445  	s.setUpTest(c, false)
  1446  }
  1447  
  1448  func (s *withImageMetadataSuite) TestContainerManagerConfigImageMetadata(c *gc.C) {
  1449  	cfg := s.getManagerConfig(c, instance.LXD)
  1450  	c.Assert(cfg, jc.DeepEquals, map[string]string{
  1451  		container.ConfigModelUUID:           coretesting.ModelTag.Id(),
  1452  		config.ContainerImageStreamKey:      "daily",
  1453  		config.ContainerImageMetadataURLKey: "https://images.linuxcontainers.org/",
  1454  	})
  1455  }
  1456  func (s *withoutControllerSuite) TestContainerConfig(c *gc.C) {
  1457  	attrs := map[string]interface{}{
  1458  		"juju-http-proxy":              "http://proxy.example.com:9000",
  1459  		"apt-https-proxy":              "https://proxy.example.com:9000",
  1460  		"allow-lxd-loop-mounts":        true,
  1461  		"apt-mirror":                   "http://example.mirror.com",
  1462  		"cloudinit-userdata":           validCloudInitUserData,
  1463  		"container-inherit-properties": "ca-certs,apt-primary",
  1464  	}
  1465  	err := s.Model.UpdateModelConfig(attrs, nil)
  1466  	c.Assert(err, jc.ErrorIsNil)
  1467  	expectedAPTProxy := proxy.Settings{
  1468  		Http:    "http://proxy.example.com:9000",
  1469  		Https:   "https://proxy.example.com:9000",
  1470  		NoProxy: "127.0.0.1,localhost,::1",
  1471  	}
  1472  
  1473  	expectedProxy := proxy.Settings{
  1474  		Http:    "http://proxy.example.com:9000",
  1475  		NoProxy: "127.0.0.1,localhost,::1",
  1476  	}
  1477  
  1478  	results, err := s.provisioner.ContainerConfig()
  1479  	c.Check(err, jc.ErrorIsNil)
  1480  	c.Check(results.UpdateBehavior, gc.Not(gc.IsNil))
  1481  	c.Check(results.ProviderType, gc.Equals, "dummy")
  1482  	c.Check(results.AuthorizedKeys, gc.Equals, s.Environ.Config().AuthorizedKeys())
  1483  	c.Check(results.SSLHostnameVerification, jc.IsTrue)
  1484  	c.Check(results.LegacyProxy.HasProxySet(), jc.IsFalse)
  1485  	c.Check(results.JujuProxy, gc.DeepEquals, expectedProxy)
  1486  	c.Check(results.AptProxy, gc.DeepEquals, expectedAPTProxy)
  1487  	c.Check(results.AptMirror, gc.DeepEquals, "http://example.mirror.com")
  1488  	c.Check(results.CloudInitUserData, gc.DeepEquals, map[string]interface{}{
  1489  		"packages":        []interface{}{"python-keystoneclient", "python-glanceclient"},
  1490  		"preruncmd":       []interface{}{"mkdir /tmp/preruncmd", "mkdir /tmp/preruncmd2"},
  1491  		"postruncmd":      []interface{}{"mkdir /tmp/postruncmd", "mkdir /tmp/postruncmd2"},
  1492  		"package_upgrade": false})
  1493  	c.Check(results.ContainerInheritProperties, gc.DeepEquals, "ca-certs,apt-primary")
  1494  }
  1495  
  1496  func (s *withoutControllerSuite) TestContainerConfigLegacy(c *gc.C) {
  1497  	attrs := map[string]interface{}{
  1498  		"http-proxy":                   "http://proxy.example.com:9000",
  1499  		"apt-https-proxy":              "https://proxy.example.com:9000",
  1500  		"allow-lxd-loop-mounts":        true,
  1501  		"apt-mirror":                   "http://example.mirror.com",
  1502  		"cloudinit-userdata":           validCloudInitUserData,
  1503  		"container-inherit-properties": "ca-certs,apt-primary",
  1504  	}
  1505  	err := s.Model.UpdateModelConfig(attrs, nil)
  1506  	c.Assert(err, jc.ErrorIsNil)
  1507  	expectedAPTProxy := proxy.Settings{
  1508  		Http:    "http://proxy.example.com:9000",
  1509  		Https:   "https://proxy.example.com:9000",
  1510  		NoProxy: "127.0.0.1,localhost,::1",
  1511  	}
  1512  
  1513  	expectedProxy := proxy.Settings{
  1514  		Http:    "http://proxy.example.com:9000",
  1515  		NoProxy: "127.0.0.1,localhost,::1",
  1516  	}
  1517  
  1518  	results, err := s.provisioner.ContainerConfig()
  1519  	c.Check(err, jc.ErrorIsNil)
  1520  	c.Check(results.UpdateBehavior, gc.Not(gc.IsNil))
  1521  	c.Check(results.ProviderType, gc.Equals, "dummy")
  1522  	c.Check(results.AuthorizedKeys, gc.Equals, s.Environ.Config().AuthorizedKeys())
  1523  	c.Check(results.SSLHostnameVerification, jc.IsTrue)
  1524  	c.Check(results.LegacyProxy, gc.DeepEquals, expectedProxy)
  1525  	c.Check(results.JujuProxy.HasProxySet(), jc.IsFalse)
  1526  	c.Check(results.AptProxy, gc.DeepEquals, expectedAPTProxy)
  1527  	c.Check(results.AptMirror, gc.DeepEquals, "http://example.mirror.com")
  1528  	c.Check(results.CloudInitUserData, gc.DeepEquals, map[string]interface{}{
  1529  		"packages":        []interface{}{"python-keystoneclient", "python-glanceclient"},
  1530  		"preruncmd":       []interface{}{"mkdir /tmp/preruncmd", "mkdir /tmp/preruncmd2"},
  1531  		"postruncmd":      []interface{}{"mkdir /tmp/postruncmd", "mkdir /tmp/postruncmd2"},
  1532  		"package_upgrade": false})
  1533  	c.Check(results.ContainerInheritProperties, gc.DeepEquals, "ca-certs,apt-primary")
  1534  }
  1535  
  1536  func (s *withoutControllerSuite) TestContainerConfigV5(c *gc.C) {
  1537  	attrs := map[string]interface{}{
  1538  		"http-proxy":                   "http://proxy.example.com:9000",
  1539  		"apt-https-proxy":              "https://proxy.example.com:9000",
  1540  		"allow-lxd-loop-mounts":        true,
  1541  		"apt-mirror":                   "http://example.mirror.com",
  1542  		"cloudinit-userdata":           validCloudInitUserData,
  1543  		"container-inherit-properties": "ca-certs,apt-primary",
  1544  	}
  1545  	err := s.Model.UpdateModelConfig(attrs, nil)
  1546  	c.Assert(err, jc.ErrorIsNil)
  1547  	expectedAPTProxy := proxy.Settings{
  1548  		Http:    "http://proxy.example.com:9000",
  1549  		Https:   "https://proxy.example.com:9000",
  1550  		NoProxy: "127.0.0.1,localhost,::1",
  1551  	}
  1552  
  1553  	expectedProxy := proxy.Settings{
  1554  		Http:    "http://proxy.example.com:9000",
  1555  		NoProxy: "127.0.0.1,localhost,::1",
  1556  	}
  1557  
  1558  	provisionerV5, err := provisioner.NewProvisionerAPIV5(s.State, s.resources, s.authorizer)
  1559  	c.Check(err, jc.ErrorIsNil)
  1560  
  1561  	var results params.ContainerConfigV5
  1562  	results, err = provisionerV5.ContainerConfig()
  1563  	c.Check(err, jc.ErrorIsNil)
  1564  
  1565  	c.Check(results.UpdateBehavior, gc.Not(gc.IsNil))
  1566  	c.Check(results.ProviderType, gc.Equals, "dummy")
  1567  	c.Check(results.AuthorizedKeys, gc.Equals, s.Environ.Config().AuthorizedKeys())
  1568  	c.Check(results.SSLHostnameVerification, jc.IsTrue)
  1569  	c.Check(results.Proxy, gc.DeepEquals, expectedProxy)
  1570  	c.Check(results.AptProxy, gc.DeepEquals, expectedAPTProxy)
  1571  	c.Check(results.AptMirror, gc.DeepEquals, "http://example.mirror.com")
  1572  	c.Check(results.CloudInitUserData, gc.DeepEquals, map[string]interface{}{
  1573  		"packages":        []interface{}{"python-keystoneclient", "python-glanceclient"},
  1574  		"preruncmd":       []interface{}{"mkdir /tmp/preruncmd", "mkdir /tmp/preruncmd2"},
  1575  		"postruncmd":      []interface{}{"mkdir /tmp/postruncmd", "mkdir /tmp/postruncmd2"},
  1576  		"package_upgrade": false})
  1577  	c.Check(results.ContainerInheritProperties, gc.DeepEquals, "ca-certs,apt-primary")
  1578  }
  1579  
  1580  func (s *withoutControllerSuite) TestSetSupportedContainers(c *gc.C) {
  1581  	args := params.MachineContainersParams{Params: []params.MachineContainers{{
  1582  		MachineTag:     "machine-0",
  1583  		ContainerTypes: []instance.ContainerType{instance.LXD},
  1584  	}, {
  1585  		MachineTag:     "machine-1",
  1586  		ContainerTypes: []instance.ContainerType{instance.LXD, instance.KVM},
  1587  	}}}
  1588  	results, err := s.provisioner.SetSupportedContainers(args)
  1589  	c.Assert(err, jc.ErrorIsNil)
  1590  	c.Assert(results.Results, gc.HasLen, 2)
  1591  	for _, result := range results.Results {
  1592  		c.Assert(result.Error, gc.IsNil)
  1593  	}
  1594  	m0, err := s.State.Machine("0")
  1595  	c.Assert(err, jc.ErrorIsNil)
  1596  	containers, ok := m0.SupportedContainers()
  1597  	c.Assert(ok, jc.IsTrue)
  1598  	c.Assert(containers, gc.DeepEquals, []instance.ContainerType{instance.LXD})
  1599  	m1, err := s.State.Machine("1")
  1600  	c.Assert(err, jc.ErrorIsNil)
  1601  	containers, ok = m1.SupportedContainers()
  1602  	c.Assert(ok, jc.IsTrue)
  1603  	c.Assert(containers, gc.DeepEquals, []instance.ContainerType{instance.LXD, instance.KVM})
  1604  }
  1605  
  1606  func (s *withoutControllerSuite) TestSetSupportedContainersPermissions(c *gc.C) {
  1607  	// Login as a machine agent for machine 0.
  1608  	anAuthorizer := s.authorizer
  1609  	anAuthorizer.Controller = false
  1610  	anAuthorizer.Tag = s.machines[0].Tag()
  1611  	aProvisioner, err := provisioner.NewProvisionerAPI(s.State, s.resources, anAuthorizer)
  1612  	c.Assert(err, jc.ErrorIsNil)
  1613  	c.Assert(aProvisioner, gc.NotNil)
  1614  
  1615  	args := params.MachineContainersParams{
  1616  		Params: []params.MachineContainers{{
  1617  			MachineTag:     "machine-0",
  1618  			ContainerTypes: []instance.ContainerType{instance.LXD},
  1619  		}, {
  1620  			MachineTag:     "machine-1",
  1621  			ContainerTypes: []instance.ContainerType{instance.LXD},
  1622  		}, {
  1623  			MachineTag:     "machine-42",
  1624  			ContainerTypes: []instance.ContainerType{instance.LXD},
  1625  		},
  1626  		},
  1627  	}
  1628  	// Only machine 0 can have it's containers updated.
  1629  	results, err := aProvisioner.SetSupportedContainers(args)
  1630  	c.Assert(results, gc.DeepEquals, params.ErrorResults{
  1631  		Results: []params.ErrorResult{
  1632  			{Error: nil},
  1633  			{Error: apiservertesting.ErrUnauthorized},
  1634  			{Error: apiservertesting.ErrUnauthorized},
  1635  		},
  1636  	})
  1637  }
  1638  
  1639  func (s *withoutControllerSuite) TestSupportsNoContainers(c *gc.C) {
  1640  	args := params.MachineContainersParams{
  1641  		Params: []params.MachineContainers{
  1642  			{
  1643  				MachineTag: "machine-0",
  1644  			},
  1645  		},
  1646  	}
  1647  	results, err := s.provisioner.SetSupportedContainers(args)
  1648  	c.Assert(err, jc.ErrorIsNil)
  1649  	c.Assert(results.Results, gc.HasLen, 1)
  1650  	c.Assert(results.Results[0].Error, gc.IsNil)
  1651  	m0, err := s.State.Machine("0")
  1652  	c.Assert(err, jc.ErrorIsNil)
  1653  	containers, ok := m0.SupportedContainers()
  1654  	c.Assert(ok, jc.IsTrue)
  1655  	c.Assert(containers, gc.DeepEquals, []instance.ContainerType{})
  1656  }
  1657  
  1658  var _ = gc.Suite(&withControllerSuite{})
  1659  
  1660  type withControllerSuite struct {
  1661  	provisionerSuite
  1662  }
  1663  
  1664  func (s *withControllerSuite) SetUpTest(c *gc.C) {
  1665  	s.provisionerSuite.setUpTest(c, true)
  1666  }
  1667  
  1668  func (s *withControllerSuite) TestAPIAddresses(c *gc.C) {
  1669  	hostPorts := [][]network.HostPort{
  1670  		network.NewHostPorts(1234, "0.1.2.3"),
  1671  	}
  1672  	err := s.State.SetAPIHostPorts(hostPorts)
  1673  	c.Assert(err, jc.ErrorIsNil)
  1674  
  1675  	result, err := s.provisioner.APIAddresses()
  1676  	c.Assert(err, jc.ErrorIsNil)
  1677  	c.Assert(result, gc.DeepEquals, params.StringsResult{
  1678  		Result: []string{"0.1.2.3:1234"},
  1679  	})
  1680  }
  1681  
  1682  func (s *withControllerSuite) TestStateAddresses(c *gc.C) {
  1683  	addresses, err := s.State.Addresses()
  1684  	c.Assert(err, jc.ErrorIsNil)
  1685  
  1686  	result, err := s.provisioner.StateAddresses()
  1687  	c.Assert(err, jc.ErrorIsNil)
  1688  	c.Assert(result, gc.DeepEquals, params.StringsResult{
  1689  		Result: addresses,
  1690  	})
  1691  }
  1692  
  1693  func (s *withControllerSuite) TestCACert(c *gc.C) {
  1694  	result, err := s.provisioner.CACert()
  1695  	c.Assert(err, jc.ErrorIsNil)
  1696  	c.Assert(result, gc.DeepEquals, params.BytesResult{
  1697  		Result: []byte(coretesting.CACert),
  1698  	})
  1699  }
  1700  
  1701  func (s *withoutControllerSuite) TestWatchMachineErrorRetry(c *gc.C) {
  1702  	s.WaitForModelWatchersIdle(c, s.Model.UUID())
  1703  	s.PatchValue(&provisioner.ErrorRetryWaitDelay, 2*coretesting.ShortWait)
  1704  	c.Assert(s.resources.Count(), gc.Equals, 0)
  1705  
  1706  	_, err := s.provisioner.WatchMachineErrorRetry()
  1707  	c.Assert(err, jc.ErrorIsNil)
  1708  
  1709  	// Verify the resources were registered and stop them when done.
  1710  	c.Assert(s.resources.Count(), gc.Equals, 1)
  1711  	resource := s.resources.Get("1")
  1712  	defer statetesting.AssertStop(c, resource)
  1713  
  1714  	// Check that the Watch has consumed the initial event ("returned"
  1715  	// in the Watch call)
  1716  	wc := statetesting.NewNotifyWatcherC(c, s.State, resource.(state.NotifyWatcher))
  1717  	wc.AssertNoChange()
  1718  
  1719  	// We should now get a time triggered change.
  1720  	wc.AssertOneChange()
  1721  
  1722  	// Make sure WatchMachineErrorRetry fails with a machine agent login.
  1723  	anAuthorizer := s.authorizer
  1724  	anAuthorizer.Tag = names.NewMachineTag("1")
  1725  	anAuthorizer.Controller = false
  1726  	aProvisioner, err := provisioner.NewProvisionerAPI(s.State, s.resources, anAuthorizer)
  1727  	c.Assert(err, jc.ErrorIsNil)
  1728  
  1729  	result, err := aProvisioner.WatchMachineErrorRetry()
  1730  	c.Assert(err, gc.ErrorMatches, "permission denied")
  1731  	c.Assert(result, gc.DeepEquals, params.NotifyWatchResult{})
  1732  }
  1733  
  1734  func (s *withoutControllerSuite) TestFindTools(c *gc.C) {
  1735  	args := params.FindToolsParams{
  1736  		MajorVersion: -1,
  1737  		MinorVersion: -1,
  1738  	}
  1739  	result, err := s.provisioner.FindTools(args)
  1740  	c.Assert(err, jc.ErrorIsNil)
  1741  	c.Assert(result.Error, gc.IsNil)
  1742  	c.Assert(result.List, gc.Not(gc.HasLen), 0)
  1743  	for _, tools := range result.List {
  1744  		url := fmt.Sprintf("https://%s/model/%s/tools/%s",
  1745  			s.APIState.Addr(), coretesting.ModelTag.Id(), tools.Version)
  1746  		c.Assert(tools.URL, gc.Equals, url)
  1747  	}
  1748  }
  1749  
  1750  func (s *withoutControllerSuite) TestMarkMachinesForRemoval(c *gc.C) {
  1751  	err := s.machines[0].EnsureDead()
  1752  	c.Assert(err, jc.ErrorIsNil)
  1753  	err = s.machines[2].EnsureDead()
  1754  	c.Assert(err, jc.ErrorIsNil)
  1755  
  1756  	res, err := s.provisioner.MarkMachinesForRemoval(params.Entities{
  1757  		Entities: []params.Entity{
  1758  			{Tag: "machine-2"},         // ok
  1759  			{Tag: "machine-100"},       // not found
  1760  			{Tag: "machine-0"},         // ok
  1761  			{Tag: "machine-1"},         // not dead
  1762  			{Tag: "machine-0-lxd-5"},   // unauthorised
  1763  			{Tag: "application-thing"}, // only machines allowed
  1764  		},
  1765  	})
  1766  	c.Assert(err, jc.ErrorIsNil)
  1767  	results := res.Results
  1768  	c.Assert(results, gc.HasLen, 6)
  1769  	c.Check(results[0].Error, gc.IsNil)
  1770  	c.Check(*results[1].Error, gc.Equals,
  1771  		*common.ServerError(errors.NotFoundf("machine 100")))
  1772  	c.Check(*results[1].Error, jc.Satisfies, params.IsCodeNotFound)
  1773  	c.Check(results[2].Error, gc.IsNil)
  1774  	c.Check(*results[3].Error, gc.Equals,
  1775  		*common.ServerError(errors.New("cannot remove machine 1: machine is not dead")))
  1776  	c.Check(*results[4].Error, gc.Equals, *apiservertesting.ErrUnauthorized)
  1777  	c.Check(*results[5].Error, gc.Equals,
  1778  		*common.ServerError(errors.New(`"application-thing" is not a valid machine tag`)))
  1779  
  1780  	removals, err := s.State.AllMachineRemovals()
  1781  	c.Assert(err, jc.ErrorIsNil)
  1782  	c.Check(removals, jc.SameContents, []string{"0", "2"})
  1783  }
  1784  
  1785  // TODO(jam): 2017-02-15 We seem to be lacking most of direct unit tests around ProcessOneContainer
  1786  // Some of the use cases we need to be testing are:
  1787  // 1) Provider can allocate addresses, should result in a container with
  1788  //    addresses requested from the provider, and 'static' configuration on those
  1789  //    devices.
  1790  // 2) Provider cannot allocate addresses, currently this should make us use
  1791  //    'lxdbr0' and DHCP allocated addresses.
  1792  // 3) Provider could allocate DHCP based addresses on the host device, which would let us
  1793  //    use a bridge on the device and DHCP. (Currently not supported, but desirable for
  1794  //    vSphere and Manual and probably LXD providers.)
  1795  // Addition (manadart 2018-10-09): To begin accommodating the deficiencies noted
  1796  // above, the new suite below uses mocks for tests ill-suited to the dummy
  1797  // provider. We could reasonably re-write the tests above over time to use the
  1798  // new suite.
  1799  
  1800  type provisionerMockSuite struct {
  1801  	coretesting.BaseSuite
  1802  
  1803  	environ      *environtesting.MockNetworkingEnviron
  1804  	host         *mocks.MockMachine
  1805  	container    *mocks.MockMachine
  1806  	device       *mocks.MockLinkLayerDevice
  1807  	parentDevice *mocks.MockLinkLayerDevice
  1808  
  1809  	unit        *mocks.MockUnit
  1810  	application *mocks.MockApplication
  1811  	charm       *mocks.MockCharm
  1812  }
  1813  
  1814  var _ = gc.Suite(&provisionerMockSuite{})
  1815  
  1816  // Even when the provider supports container addresses, manually provisioned
  1817  // machines should fall back to DHCP.
  1818  func (s *provisionerMockSuite) TestManuallyProvisionedHostsUseDHCPForContainers(c *gc.C) {
  1819  	defer s.setup(c).Finish()
  1820  
  1821  	s.expectManuallyProvisionedHostsUseDHCPForContainers()
  1822  
  1823  	res := params.MachineNetworkConfigResults{
  1824  		Results: []params.MachineNetworkConfigResult{{}},
  1825  	}
  1826  	ctx := provisioner.NewPrepareOrGetContext(res, false)
  1827  
  1828  	// ProviderCallContext is not required by this logical path; we pass nil.
  1829  	err := ctx.ProcessOneContainer(s.environ, nil, 0, s.host, s.container)
  1830  	c.Assert(err, jc.ErrorIsNil)
  1831  	c.Assert(res.Results[0].Config, gc.HasLen, 1)
  1832  
  1833  	cfg := res.Results[0].Config[0]
  1834  	c.Check(cfg.ConfigType, gc.Equals, "dhcp")
  1835  	c.Check(cfg.ProviderSubnetId, gc.Equals, "")
  1836  	c.Check(cfg.VLANTag, gc.Equals, 0)
  1837  }
  1838  
  1839  func (s *provisionerMockSuite) expectManuallyProvisionedHostsUseDHCPForContainers() {
  1840  	s.expectNetworkingEnviron()
  1841  	s.expectLinkLayerDevices()
  1842  
  1843  	emptySpace := ""
  1844  
  1845  	cExp := s.container.EXPECT()
  1846  	cExp.InstanceId().Return(instance.UnknownId, errors.NotProvisionedf("idk-lol"))
  1847  	cExp.DesiredSpaces().Return(set.NewStrings(emptySpace), nil)
  1848  	cExp.Id().Return("lxd/0").AnyTimes()
  1849  	cExp.SetLinkLayerDevices(gomock.Any()).Return(nil)
  1850  	cExp.AllLinkLayerDevices().Return([]containerizer.LinkLayerDevice{s.device}, nil)
  1851  
  1852  	hExp := s.host.EXPECT()
  1853  	hExp.Id().Return("0").AnyTimes()
  1854  	hExp.LinkLayerDevicesForSpaces(gomock.Any()).Return(
  1855  		map[string][]containerizer.LinkLayerDevice{emptySpace: {s.device}}, nil)
  1856  	// Crucial behavioural trait. Set false to test failure.
  1857  	hExp.IsManual().Return(true, nil)
  1858  	hExp.InstanceId().Return(instance.Id("manual:10.0.0.66"), nil)
  1859  
  1860  }
  1861  
  1862  // expectNetworkingEnviron stubs an environ that supports container networking.
  1863  func (s *provisionerMockSuite) expectNetworkingEnviron() {
  1864  	eExp := s.environ.EXPECT()
  1865  	eExp.Config().Return(&config.Config{}).AnyTimes()
  1866  	eExp.SupportsContainerAddresses(gomock.Any()).Return(true, nil).AnyTimes()
  1867  }
  1868  
  1869  // expectLinkLayerDevices mocks a link-layer device and its parent,
  1870  // suitable for use as a bridge network for containers.
  1871  func (s *provisionerMockSuite) expectLinkLayerDevices() {
  1872  	devName := "eth0"
  1873  	mtu := uint(1500)
  1874  	mac := network.GenerateVirtualMACAddress()
  1875  	deviceArgs := state.LinkLayerDeviceArgs{
  1876  		Name:       devName,
  1877  		Type:       state.EthernetDevice,
  1878  		MACAddress: mac,
  1879  		MTU:        mtu,
  1880  	}
  1881  
  1882  	dExp := s.device.EXPECT()
  1883  	dExp.Name().Return(devName).AnyTimes()
  1884  	dExp.Type().Return(state.BridgeDevice).AnyTimes()
  1885  	dExp.MTU().Return(mtu).AnyTimes()
  1886  	dExp.EthernetDeviceForBridge(devName).Return(deviceArgs, nil).MinTimes(1)
  1887  	dExp.ParentDevice().Return(s.parentDevice, nil)
  1888  	dExp.MACAddress().Return(mac)
  1889  	dExp.IsAutoStart().Return(true)
  1890  	dExp.IsUp().Return(true)
  1891  
  1892  	pExp := s.parentDevice.EXPECT()
  1893  	// The address itself is unimportant, so we can use an empty one.
  1894  	// What is important is that there is one there to flex the path we are
  1895  	// testing.
  1896  	pExp.Addresses().Return([]*state.Address{{}}, nil)
  1897  	pExp.Name().Return(devName).MinTimes(1)
  1898  }
  1899  
  1900  func (s *provisionerMockSuite) TestContainerAlreadyProvisionedError(c *gc.C) {
  1901  	defer s.setup(c).Finish()
  1902  
  1903  	exp := s.container.EXPECT()
  1904  	exp.InstanceId().Return(instance.Id("juju-8ebd6c-0"), nil)
  1905  	exp.Id().Return("0/lxd/0")
  1906  
  1907  	res := params.MachineNetworkConfigResults{
  1908  		Results: []params.MachineNetworkConfigResult{{}},
  1909  	}
  1910  	ctx := provisioner.NewPrepareOrGetContext(res, true)
  1911  
  1912  	// ProviderCallContext is not required by this logical path; we pass nil.
  1913  	err := ctx.ProcessOneContainer(s.environ, nil, 0, s.host, s.container)
  1914  	c.Assert(err, gc.ErrorMatches, `container "0/lxd/0" already provisioned as "juju-8ebd6c-0"`)
  1915  }
  1916  
  1917  func (s *provisionerMockSuite) TestGetContainerProfileInfo(c *gc.C) {
  1918  	ctrl := s.setup(c)
  1919  	defer ctrl.Finish()
  1920  	s.expectCharmLXDProfiles(ctrl)
  1921  
  1922  	s.application.EXPECT().Name().Return("application")
  1923  	s.charm.EXPECT().Revision().Return(3)
  1924  	s.charm.EXPECT().LXDProfile().Return(
  1925  		&charm.LXDProfile{
  1926  			Config: map[string]string{
  1927  				"security.nesting":    "true",
  1928  				"security.privileged": "true",
  1929  			},
  1930  		})
  1931  
  1932  	res := params.ContainerProfileResults{
  1933  		Results: []params.ContainerProfileResult{{}},
  1934  	}
  1935  	ctx := provisioner.NewContainerProfileContext(res, "testme")
  1936  
  1937  	// ProviderCallContext is not required by this logical path; we pass nil.
  1938  	err := ctx.ProcessOneContainer(s.environ, nil, 0, s.host, s.container)
  1939  	c.Assert(err, jc.ErrorIsNil)
  1940  	c.Assert(res.Results, gc.HasLen, 1)
  1941  	c.Assert(res.Results[0].Error, gc.IsNil)
  1942  	c.Assert(res.Results[0].LXDProfiles, gc.HasLen, 1)
  1943  	profile := res.Results[0].LXDProfiles[0]
  1944  	c.Check(profile.Name, gc.Equals, "juju-testme-application-3")
  1945  	c.Check(profile.Profile.Config, gc.DeepEquals,
  1946  		map[string]string{
  1947  			"security.nesting":    "true",
  1948  			"security.privileged": "true",
  1949  		},
  1950  	)
  1951  }
  1952  
  1953  func (s *provisionerMockSuite) TestGetContainerProfileInfoNoProfile(c *gc.C) {
  1954  	ctrl := s.setup(c)
  1955  	defer ctrl.Finish()
  1956  	s.expectCharmLXDProfiles(ctrl)
  1957  
  1958  	s.charm.EXPECT().LXDProfile().Return(nil)
  1959  	s.unit.EXPECT().Name().Return("application/0")
  1960  
  1961  	res := params.ContainerProfileResults{
  1962  		Results: []params.ContainerProfileResult{{}},
  1963  	}
  1964  	ctx := provisioner.NewContainerProfileContext(res, "testme")
  1965  
  1966  	// ProviderCallContext is not required by this logical path; we pass nil.
  1967  	err := ctx.ProcessOneContainer(s.environ, nil, 0, s.host, s.container)
  1968  	c.Assert(err, jc.ErrorIsNil)
  1969  	c.Assert(res.Results, gc.HasLen, 1)
  1970  	c.Assert(res.Results[0].Error, gc.IsNil)
  1971  	c.Assert(res.Results[0].LXDProfiles, gc.HasLen, 0)
  1972  }
  1973  
  1974  func (s *provisionerMockSuite) expectCharmLXDProfiles(ctrl *gomock.Controller) {
  1975  	s.unit = mocks.NewMockUnit(ctrl)
  1976  	s.application = mocks.NewMockApplication(ctrl)
  1977  	s.charm = mocks.NewMockCharm(ctrl)
  1978  
  1979  	s.container.EXPECT().Units().Return([]containerizer.Unit{s.unit}, nil)
  1980  	s.unit.EXPECT().Application().Return(s.application, nil)
  1981  	s.application.EXPECT().Charm().Return(s.charm, false, nil)
  1982  }
  1983  
  1984  func (s *provisionerMockSuite) setup(c *gc.C) *gomock.Controller {
  1985  	ctrl := gomock.NewController(c)
  1986  
  1987  	s.environ = environtesting.NewMockNetworkingEnviron(ctrl)
  1988  	s.host = mocks.NewMockMachine(ctrl)
  1989  	s.container = mocks.NewMockMachine(ctrl)
  1990  	s.device = mocks.NewMockLinkLayerDevice(ctrl)
  1991  	s.parentDevice = mocks.NewMockLinkLayerDevice(ctrl)
  1992  
  1993  	return ctrl
  1994  }
  1995  
  1996  type provisionerProfileMockSuite struct {
  1997  	coretesting.BaseSuite
  1998  
  1999  	backend *mocks.MockProfileBackend
  2000  	charm   *mocks.MockProfileCharm
  2001  	machine *mocks.MockProfileMachine
  2002  }
  2003  
  2004  var _ = gc.Suite(&provisionerProfileMockSuite{})
  2005  
  2006  func (s *provisionerProfileMockSuite) TestMachineChangeProfileChangeInfoRemoveUnit(c *gc.C) {
  2007  	defer s.setup(c).Finish()
  2008  
  2009  	mExp := s.machine.EXPECT()
  2010  	mExp.CharmProfiles().Return([]string{
  2011  		"default",
  2012  		"juju-testme",
  2013  		"juju-testme-lxd-profile-alt-2",
  2014  		"juju-testme-application-1",
  2015  	}, nil)
  2016  	mExp.UpgradeCharmProfileCharmURL().Return("", nil)
  2017  	mExp.UpgradeCharmProfileApplication().Return("lxd-profile-alt", nil)
  2018  
  2019  	result, err := provisioner.MachineChangeProfileChangeInfo(s.machine, s.backend)
  2020  	c.Assert(err, gc.IsNil)
  2021  	c.Assert(result.Error, gc.IsNil)
  2022  	c.Assert(result.OldProfileName, gc.Equals, "juju-testme-lxd-profile-alt-2")
  2023  }
  2024  
  2025  func (s *provisionerProfileMockSuite) TestMachineChangeProfileChangeInfoRemoveProfile(c *gc.C) {
  2026  	defer s.setup(c).Finish()
  2027  
  2028  	charmURLString := "local:bionic/lxd-profile-alt-3"
  2029  	mExp := s.machine.EXPECT()
  2030  	mExp.CharmProfiles().Return([]string{
  2031  		"default",
  2032  		"juju-testme",
  2033  		"juju-testme-lxd-profile-alt-2",
  2034  		"juju-testme-application-1",
  2035  	}, nil)
  2036  	mExp.UpgradeCharmProfileCharmURL().Return(charmURLString, nil)
  2037  	mExp.UpgradeCharmProfileApplication().Return("lxd-profile-alt", nil)
  2038  	mExp.Id().Return("2")
  2039  
  2040  	cExp := s.charm.EXPECT()
  2041  	cExp.Revision().Return(3)
  2042  	cExp.LXDProfile().Return(&charm.LXDProfile{})
  2043  	cExp.Meta().Return(&charm.Meta{Subordinate: false})
  2044  
  2045  	chURL, err := charm.ParseURL(charmURLString)
  2046  	c.Assert(err, jc.ErrorIsNil)
  2047  	s.backend.EXPECT().Charm(gomock.Eq(chURL)).Return(s.charm, nil)
  2048  
  2049  	result, err := provisioner.MachineChangeProfileChangeInfo(s.machine, s.backend)
  2050  	c.Assert(err, gc.IsNil)
  2051  	c.Assert(result.Error, gc.IsNil)
  2052  	c.Assert(result.OldProfileName, gc.Equals, "juju-testme-lxd-profile-alt-2")
  2053  	c.Assert(result.NewProfileName, gc.Equals, "")
  2054  }
  2055  
  2056  func (s *provisionerProfileMockSuite) TestMachineChangeProfileChangeInfoAddProfile(c *gc.C) {
  2057  	defer s.setup(c).Finish()
  2058  
  2059  	charmURLString := "local:bionic/lxd-profile-alt-3"
  2060  	mExp := s.machine.EXPECT()
  2061  	mExp.CharmProfiles().Return([]string{
  2062  		"default",
  2063  		"juju-testme",
  2064  		"juju-testme-application-1",
  2065  	}, nil)
  2066  	mExp.UpgradeCharmProfileCharmURL().Return(charmURLString, nil)
  2067  	mExp.UpgradeCharmProfileApplication().Return("lxd-profile-alt", nil)
  2068  	mExp.Id().Return("2")
  2069  	mExp.ModelName().Return("testme")
  2070  
  2071  	cExp := s.charm.EXPECT()
  2072  	cExp.Revision().Return(3)
  2073  	cExp.LXDProfile().Return(&charm.LXDProfile{
  2074  		Config:      map[string]string{"security.privilaged": "true"},
  2075  		Description: "profile to test",
  2076  	})
  2077  	cExp.Meta().Return(&charm.Meta{Subordinate: false})
  2078  
  2079  	chURL, err := charm.ParseURL(charmURLString)
  2080  	c.Assert(err, jc.ErrorIsNil)
  2081  	s.backend.EXPECT().Charm(gomock.Eq(chURL)).Return(s.charm, nil)
  2082  
  2083  	result, err := provisioner.MachineChangeProfileChangeInfo(s.machine, s.backend)
  2084  	c.Assert(err, gc.IsNil)
  2085  	c.Assert(result.Error, gc.IsNil)
  2086  	c.Assert(result.NewProfileName, gc.Equals, "juju-testme-lxd-profile-alt-3")
  2087  	c.Assert(result.OldProfileName, gc.Equals, "")
  2088  	c.Assert(result.Profile, gc.DeepEquals, &params.CharmLXDProfile{
  2089  		Config:      map[string]string{"security.privilaged": "true"},
  2090  		Description: "profile to test",
  2091  	})
  2092  	c.Assert(result.Subordinate, jc.IsFalse)
  2093  }
  2094  
  2095  func (s *provisionerProfileMockSuite) TestMachineChangeProfileChangeInfoChangeProfile(c *gc.C) {
  2096  	defer s.setup(c).Finish()
  2097  
  2098  	charmURLString := "local:bionic/lxd-profile-alt-3"
  2099  	mExp := s.machine.EXPECT()
  2100  	mExp.CharmProfiles().Return([]string{
  2101  		"default",
  2102  		"juju-testme",
  2103  		"juju-testme-lxd-profile-alt-2",
  2104  		"juju-testme-application-1",
  2105  	}, nil)
  2106  	mExp.UpgradeCharmProfileCharmURL().Return(charmURLString, nil)
  2107  	mExp.UpgradeCharmProfileApplication().Return("lxd-profile-alt", nil)
  2108  	mExp.Id().Return("2")
  2109  
  2110  	cExp := s.charm.EXPECT()
  2111  	cExp.Revision().Return(3)
  2112  	cExp.LXDProfile().Return(&charm.LXDProfile{
  2113  		Config:      map[string]string{"security.privilaged": "true"},
  2114  		Description: "profile to test",
  2115  	})
  2116  	cExp.Meta().Return(&charm.Meta{Subordinate: true})
  2117  
  2118  	chURL, err := charm.ParseURL(charmURLString)
  2119  	c.Assert(err, jc.ErrorIsNil)
  2120  	s.backend.EXPECT().Charm(gomock.Eq(chURL)).Return(s.charm, nil)
  2121  
  2122  	result, err := provisioner.MachineChangeProfileChangeInfo(s.machine, s.backend)
  2123  	c.Assert(err, gc.IsNil)
  2124  	c.Assert(result.Error, gc.IsNil)
  2125  	c.Assert(result.OldProfileName, gc.Equals, "juju-testme-lxd-profile-alt-2")
  2126  	c.Assert(result.NewProfileName, gc.Equals, "juju-testme-lxd-profile-alt-3")
  2127  	c.Assert(result.Profile, gc.DeepEquals, &params.CharmLXDProfile{
  2128  		Config:      map[string]string{"security.privilaged": "true"},
  2129  		Description: "profile to test",
  2130  	})
  2131  	c.Assert(result.Subordinate, jc.IsTrue)
  2132  }
  2133  
  2134  func (s *provisionerProfileMockSuite) setup(c *gc.C) *gomock.Controller {
  2135  	ctrl := gomock.NewController(c)
  2136  
  2137  	s.backend = mocks.NewMockProfileBackend(ctrl)
  2138  	s.charm = mocks.NewMockProfileCharm(ctrl)
  2139  	s.machine = mocks.NewMockProfileMachine(ctrl)
  2140  
  2141  	return ctrl
  2142  }