github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/api/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  	"time"
     9  
    10  	"github.com/juju/errors"
    11  	"github.com/juju/names/v5"
    12  	jc "github.com/juju/testing/checkers"
    13  	"github.com/juju/utils/v3"
    14  	"go.uber.org/mock/gomock"
    15  	gc "gopkg.in/check.v1"
    16  
    17  	"github.com/juju/juju/api"
    18  	"github.com/juju/juju/api/agent/provisioner"
    19  	apimocks "github.com/juju/juju/api/base/mocks"
    20  	apitesting "github.com/juju/juju/api/testing"
    21  	apiservererrors "github.com/juju/juju/apiserver/errors"
    22  	"github.com/juju/juju/container"
    23  	"github.com/juju/juju/core/arch"
    24  	"github.com/juju/juju/core/constraints"
    25  	"github.com/juju/juju/core/instance"
    26  	"github.com/juju/juju/core/life"
    27  	corenetwork "github.com/juju/juju/core/network"
    28  	"github.com/juju/juju/core/status"
    29  	"github.com/juju/juju/core/watcher/watchertest"
    30  	"github.com/juju/juju/environs/config"
    31  	"github.com/juju/juju/juju/testing"
    32  	"github.com/juju/juju/network"
    33  	"github.com/juju/juju/rpc/params"
    34  	"github.com/juju/juju/state"
    35  	"github.com/juju/juju/storage/poolmanager"
    36  	"github.com/juju/juju/storage/provider"
    37  	coretesting "github.com/juju/juju/testing"
    38  	coretools "github.com/juju/juju/tools"
    39  	jujuversion "github.com/juju/juju/version"
    40  )
    41  
    42  type provisionerSuite struct {
    43  	testing.JujuConnSuite
    44  	*apitesting.ModelWatcherTests
    45  	*apitesting.APIAddresserTests
    46  
    47  	st      api.Connection
    48  	machine *state.Machine
    49  
    50  	provisioner *provisioner.State
    51  }
    52  
    53  var _ = gc.Suite(&provisionerSuite{})
    54  
    55  func (s *provisionerSuite) SetUpTest(c *gc.C) {
    56  	s.JujuConnSuite.SetUpTest(c)
    57  
    58  	var err error
    59  	s.machine, err = s.State.AddMachine(state.UbuntuBase("12.10"), state.JobManageModel)
    60  	c.Assert(err, jc.ErrorIsNil)
    61  	password, err := utils.RandomPassword()
    62  	c.Assert(err, jc.ErrorIsNil)
    63  	err = s.machine.SetPassword(password)
    64  	c.Assert(err, jc.ErrorIsNil)
    65  	err = s.machine.SetInstanceInfo("i-manager", "", "fake_nonce", nil, nil, nil, nil, nil, nil)
    66  	c.Assert(err, jc.ErrorIsNil)
    67  	s.st = s.OpenAPIAsMachine(c, s.machine.Tag(), password, "fake_nonce")
    68  	c.Assert(s.st, gc.NotNil)
    69  	err = s.machine.SetProviderAddresses(corenetwork.NewSpaceAddress("0.1.2.3"))
    70  	c.Assert(err, jc.ErrorIsNil)
    71  
    72  	// Create the provisioner API facade.
    73  	s.provisioner = provisioner.NewState(s.st)
    74  	c.Assert(s.provisioner, gc.NotNil)
    75  
    76  	s.ModelWatcherTests = apitesting.NewModelWatcherTests(s.provisioner, s.BackingState, s.Model)
    77  	waitForModelWatchersIdle := func(c *gc.C) {
    78  		s.JujuConnSuite.WaitForModelWatchersIdle(c, s.BackingState.ModelUUID())
    79  	}
    80  	systemState, err := s.StatePool.SystemState()
    81  	c.Assert(err, jc.ErrorIsNil)
    82  	s.APIAddresserTests = apitesting.NewAPIAddresserTests(s.provisioner, systemState, s.BackingState, waitForModelWatchersIdle)
    83  }
    84  
    85  func (s *provisionerSuite) assertGetOneMachine(c *gc.C, tag names.MachineTag) provisioner.MachineProvisioner {
    86  	result, err := s.provisioner.Machines(tag)
    87  	c.Assert(err, jc.ErrorIsNil)
    88  	c.Assert(len(result), gc.Equals, 1)
    89  	c.Assert(result[0].Err, gc.IsNil)
    90  	return result[0].Machine
    91  }
    92  
    93  func (s *provisionerSuite) TestMachinesTagAndId(c *gc.C) {
    94  	result, err := s.provisioner.Machines(names.NewMachineTag("42"), s.machine.MachineTag())
    95  	c.Assert(err, jc.ErrorIsNil)
    96  	c.Assert(len(result), gc.Equals, 2)
    97  
    98  	c.Assert(result[0].Err, gc.ErrorMatches, "machine 42 not found")
    99  	c.Assert(result[0].Err, jc.Satisfies, params.IsCodeNotFound)
   100  	c.Assert(result[0].Machine, gc.IsNil)
   101  
   102  	c.Assert(result[1].Err, gc.IsNil)
   103  	c.Assert(result[1].Machine.Tag(), gc.Equals, s.machine.Tag())
   104  	c.Assert(result[1].Machine.Id(), gc.Equals, s.machine.Id())
   105  }
   106  
   107  func (s *provisionerSuite) TestGetSetStatus(c *gc.C) {
   108  	apiMachine := s.assertGetOneMachine(c, s.machine.MachineTag())
   109  
   110  	machineStatus, info, err := apiMachine.Status()
   111  	c.Assert(err, jc.ErrorIsNil)
   112  	c.Assert(machineStatus, gc.Equals, status.Pending)
   113  	c.Assert(info, gc.Equals, "")
   114  
   115  	err = apiMachine.SetStatus(status.Started, "blah", nil)
   116  	c.Assert(err, jc.ErrorIsNil)
   117  
   118  	machineStatus, info, err = apiMachine.Status()
   119  	c.Assert(err, jc.ErrorIsNil)
   120  	c.Assert(machineStatus, gc.Equals, status.Started)
   121  	c.Assert(info, gc.Equals, "blah")
   122  	statusInfo, err := s.machine.Status()
   123  	c.Assert(err, jc.ErrorIsNil)
   124  	c.Assert(statusInfo.Data, gc.HasLen, 0)
   125  }
   126  
   127  func (s *provisionerSuite) TestGetSetInstanceStatus(c *gc.C) {
   128  	apiMachine := s.assertGetOneMachine(c, s.machine.MachineTag())
   129  	instanceStatus, info, err := apiMachine.InstanceStatus()
   130  	c.Assert(err, jc.ErrorIsNil)
   131  	c.Assert(instanceStatus, gc.Equals, status.Pending)
   132  	c.Assert(info, gc.Equals, "")
   133  	err = apiMachine.SetInstanceStatus(status.Running, "blah", nil)
   134  	c.Assert(err, jc.ErrorIsNil)
   135  	instanceStatus, info, err = apiMachine.InstanceStatus()
   136  	c.Assert(err, jc.ErrorIsNil)
   137  	c.Assert(instanceStatus, gc.Equals, status.Running)
   138  	c.Assert(info, gc.Equals, "blah")
   139  	statusInfo, err := s.machine.InstanceStatus()
   140  	c.Assert(err, jc.ErrorIsNil)
   141  	c.Assert(statusInfo.Data, gc.HasLen, 0)
   142  }
   143  
   144  func (s *provisionerSuite) TestGetSetStatusWithData(c *gc.C) {
   145  	apiMachine := s.assertGetOneMachine(c, s.machine.MachineTag())
   146  	err := apiMachine.SetStatus(status.Error, "blah", map[string]interface{}{"foo": "bar"})
   147  	c.Assert(err, jc.ErrorIsNil)
   148  
   149  	machineStatus, info, err := apiMachine.Status()
   150  	c.Assert(err, jc.ErrorIsNil)
   151  	c.Assert(machineStatus, gc.Equals, status.Error)
   152  	c.Assert(info, gc.Equals, "blah")
   153  	statusInfo, err := s.machine.Status()
   154  	c.Assert(err, jc.ErrorIsNil)
   155  	c.Assert(statusInfo.Data, gc.DeepEquals, map[string]interface{}{"foo": "bar"})
   156  }
   157  
   158  func (s *provisionerSuite) TestMachinesWithTransientErrors(c *gc.C) {
   159  	machine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
   160  	c.Assert(err, jc.ErrorIsNil)
   161  	now := time.Now()
   162  	sInfo := status.StatusInfo{
   163  		Status:  status.ProvisioningError,
   164  		Message: "blah",
   165  		Data:    map[string]interface{}{"transient": true},
   166  		Since:   &now,
   167  	}
   168  	err = machine.SetInstanceStatus(sInfo)
   169  	c.Assert(err, jc.ErrorIsNil)
   170  	result, err := s.provisioner.MachinesWithTransientErrors()
   171  	c.Assert(err, jc.ErrorIsNil)
   172  	c.Assert(result, gc.HasLen, 1)
   173  
   174  	c.Assert(result[0].Machine.Id(), gc.Equals, "1")
   175  	c.Assert(result[0].Status, gc.DeepEquals, params.StatusResult{
   176  		Id:     "1",
   177  		Life:   "alive",
   178  		Status: "provisioning error",
   179  		Info:   "blah",
   180  		Data:   map[string]interface{}{"transient": true},
   181  	})
   182  }
   183  
   184  func (s *provisionerSuite) TestEnsureDeadAndRemove(c *gc.C) {
   185  	// Create a fresh machine to test the complete scenario.
   186  	otherMachine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
   187  	c.Assert(err, jc.ErrorIsNil)
   188  	c.Assert(otherMachine.Life(), gc.Equals, state.Alive)
   189  
   190  	apiMachine := s.assertGetOneMachine(c, otherMachine.MachineTag())
   191  	c.Assert(err, jc.ErrorIsNil)
   192  
   193  	err = apiMachine.Remove()
   194  	c.Assert(err, gc.ErrorMatches, `cannot remove entity "machine-1": still alive`)
   195  	err = apiMachine.EnsureDead()
   196  	c.Assert(err, jc.ErrorIsNil)
   197  
   198  	err = otherMachine.Refresh()
   199  	c.Assert(err, jc.ErrorIsNil)
   200  	c.Assert(otherMachine.Life(), gc.Equals, state.Dead)
   201  
   202  	err = apiMachine.EnsureDead()
   203  	c.Assert(err, jc.ErrorIsNil)
   204  	err = otherMachine.Refresh()
   205  	c.Assert(err, jc.ErrorIsNil)
   206  	c.Assert(otherMachine.Life(), gc.Equals, state.Dead)
   207  
   208  	err = apiMachine.Remove()
   209  	c.Assert(err, jc.ErrorIsNil)
   210  	err = otherMachine.Refresh()
   211  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   212  
   213  	err = apiMachine.EnsureDead()
   214  	c.Assert(err, gc.ErrorMatches, "machine 1 not found")
   215  	c.Assert(err, jc.Satisfies, params.IsCodeNotFound)
   216  
   217  	// Now try to EnsureDead machine 0 - should fail.
   218  	apiMachine = s.assertGetOneMachine(c, s.machine.MachineTag())
   219  	err = apiMachine.EnsureDead()
   220  	c.Assert(err, gc.ErrorMatches, "machine 0 is still a non-voting controller member")
   221  }
   222  
   223  func (s *provisionerSuite) TestMarkForRemoval(c *gc.C) {
   224  	machine, err := s.State.AddMachine(state.UbuntuBase("22.04"), state.JobHostUnits)
   225  	c.Assert(err, jc.ErrorIsNil)
   226  
   227  	apiMachine := s.assertGetOneMachine(c, machine.MachineTag())
   228  
   229  	err = apiMachine.MarkForRemoval()
   230  	c.Assert(err, gc.ErrorMatches, "cannot remove machine 1: machine is not dead")
   231  
   232  	err = machine.EnsureDead()
   233  	c.Assert(err, jc.ErrorIsNil)
   234  
   235  	err = apiMachine.MarkForRemoval()
   236  	c.Assert(err, jc.ErrorIsNil)
   237  
   238  	removals, err := s.State.AllMachineRemovals()
   239  	c.Assert(err, jc.ErrorIsNil)
   240  	c.Assert(removals, jc.SameContents, []string{"1"})
   241  }
   242  
   243  func (s *provisionerSuite) TestRefreshAndLife(c *gc.C) {
   244  	// Create a fresh machine to test the complete scenario.
   245  	otherMachine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
   246  	c.Assert(err, jc.ErrorIsNil)
   247  	c.Assert(otherMachine.Life(), gc.Equals, state.Alive)
   248  
   249  	apiMachine := s.assertGetOneMachine(c, otherMachine.MachineTag())
   250  	c.Assert(apiMachine.Life(), gc.Equals, life.Alive)
   251  
   252  	err = apiMachine.EnsureDead()
   253  	c.Assert(err, jc.ErrorIsNil)
   254  	c.Assert(apiMachine.Life(), gc.Equals, life.Alive)
   255  
   256  	err = apiMachine.Refresh()
   257  	c.Assert(err, jc.ErrorIsNil)
   258  	c.Assert(apiMachine.Life(), gc.Equals, life.Dead)
   259  }
   260  
   261  func (s *provisionerSuite) TestSetInstanceInfo(c *gc.C) {
   262  	pm := poolmanager.New(state.NewStateSettings(s.State), provider.CommonStorageProviders())
   263  	_, err := pm.Create("loop-pool", provider.LoopProviderType, map[string]interface{}{"foo": "bar"})
   264  	c.Assert(err, jc.ErrorIsNil)
   265  
   266  	// Create a fresh machine, since machine 0 is already provisioned.
   267  	template := state.MachineTemplate{
   268  		Base: state.UbuntuBase("12.10"),
   269  		Jobs: []state.MachineJob{state.JobHostUnits},
   270  		Volumes: []state.HostVolumeParams{{
   271  			Volume: state.VolumeParams{
   272  				Pool: "loop-pool",
   273  				Size: 123,
   274  			}},
   275  		},
   276  	}
   277  	notProvisionedMachine, err := s.State.AddOneMachine(template)
   278  	c.Assert(err, jc.ErrorIsNil)
   279  
   280  	apiMachine := s.assertGetOneMachine(c, notProvisionedMachine.MachineTag())
   281  
   282  	instanceId, err := apiMachine.InstanceId()
   283  	c.Assert(err, jc.Satisfies, params.IsCodeNotProvisioned)
   284  	c.Assert(err, gc.ErrorMatches, "machine 1 not provisioned")
   285  	c.Assert(instanceId, gc.Equals, instance.Id(""))
   286  
   287  	hwChars := instance.MustParseHardware("cores=123", "mem=4G")
   288  
   289  	volumes := []params.Volume{{
   290  		VolumeTag: "volume-1-0",
   291  		Info: params.VolumeInfo{
   292  			VolumeId: "vol-123",
   293  			Size:     124,
   294  		},
   295  	}}
   296  	volumeAttachments := map[string]params.VolumeAttachmentInfo{
   297  		"volume-1-0": {
   298  			DeviceName: "xvdf1",
   299  		},
   300  	}
   301  
   302  	err = apiMachine.SetInstanceInfo(
   303  		"i-will", "", "fake_nonce", &hwChars, nil, volumes, volumeAttachments, nil,
   304  	)
   305  	c.Assert(err, jc.ErrorIsNil)
   306  
   307  	instanceId, err = apiMachine.InstanceId()
   308  	c.Assert(err, jc.ErrorIsNil)
   309  	c.Assert(instanceId, gc.Equals, instance.Id("i-will"))
   310  
   311  	// Try it again - should fail.
   312  	err = apiMachine.SetInstanceInfo("i-wont", "", "fake", nil, nil, nil, nil, nil)
   313  	c.Assert(err, gc.ErrorMatches, `cannot record provisioning info for "i-wont": cannot set instance data for machine "1": already set`)
   314  
   315  	// Now try to get machine 0's instance id.
   316  	apiMachine = s.assertGetOneMachine(c, s.machine.MachineTag())
   317  	instanceId, err = apiMachine.InstanceId()
   318  	c.Assert(err, jc.ErrorIsNil)
   319  	c.Assert(instanceId, gc.Equals, instance.Id("i-manager"))
   320  
   321  	// Now check volumes and volume attachments.
   322  	sb, err := state.NewStorageBackend(s.State)
   323  	c.Assert(err, jc.ErrorIsNil)
   324  	volume, err := sb.Volume(names.NewVolumeTag("1/0"))
   325  	c.Assert(err, jc.ErrorIsNil)
   326  	volumeInfo, err := volume.Info()
   327  	c.Assert(err, jc.ErrorIsNil)
   328  	c.Assert(volumeInfo, gc.Equals, state.VolumeInfo{
   329  		VolumeId: "vol-123",
   330  		Pool:     "loop-pool",
   331  		Size:     124,
   332  	})
   333  	stateVolumeAttachments, err := sb.MachineVolumeAttachments(names.NewMachineTag("1"))
   334  	c.Assert(err, jc.ErrorIsNil)
   335  	c.Assert(stateVolumeAttachments, gc.HasLen, 1)
   336  	volumeAttachmentInfo, err := stateVolumeAttachments[0].Info()
   337  	c.Assert(err, jc.ErrorIsNil)
   338  	c.Assert(volumeAttachmentInfo, gc.Equals, state.VolumeAttachmentInfo{
   339  		DeviceName: "xvdf1",
   340  	})
   341  }
   342  
   343  func (s *provisionerSuite) TestAvailabilityZone(c *gc.C) {
   344  	// Create a fresh machine, since machine 0 is already provisioned.
   345  	template := state.MachineTemplate{
   346  		Base: state.UbuntuBase("16.04"),
   347  		Jobs: []state.MachineJob{state.JobHostUnits},
   348  	}
   349  	notProvisionedMachine, err := s.State.AddOneMachine(template)
   350  	c.Assert(err, jc.ErrorIsNil)
   351  
   352  	apiMachine := s.assertGetOneMachine(c, notProvisionedMachine.MachineTag())
   353  
   354  	instanceId, err := apiMachine.InstanceId()
   355  	c.Assert(err, jc.Satisfies, params.IsCodeNotProvisioned)
   356  	c.Assert(err, gc.ErrorMatches, "machine 1 not provisioned")
   357  	c.Assert(instanceId, gc.Equals, instance.Id(""))
   358  
   359  	availabilityZone := "ru-north-siberia"
   360  	hwChars := instance.MustParseHardware(fmt.Sprintf("availability-zone=%s", availabilityZone))
   361  
   362  	err = apiMachine.SetInstanceInfo(
   363  		"azinst", "", "nonce", &hwChars, nil, nil, nil, nil,
   364  	)
   365  	c.Assert(err, jc.ErrorIsNil)
   366  
   367  	retAvailabilityZone, err := apiMachine.AvailabilityZone()
   368  	c.Assert(err, jc.ErrorIsNil)
   369  	c.Assert(availabilityZone, gc.Equals, retAvailabilityZone)
   370  }
   371  
   372  func (s *provisionerSuite) TestSetInstanceInfoProfiles(c *gc.C) {
   373  	// Create a fresh machine, since machine 0 is already provisioned.
   374  	template := state.MachineTemplate{
   375  		Base: state.UbuntuBase("16.04"),
   376  		Jobs: []state.MachineJob{state.JobHostUnits},
   377  	}
   378  	notProvisionedMachine, err := s.State.AddOneMachine(template)
   379  	c.Assert(err, jc.ErrorIsNil)
   380  
   381  	apiMachine := s.assertGetOneMachine(c, notProvisionedMachine.MachineTag())
   382  
   383  	instanceId, err := apiMachine.InstanceId()
   384  	c.Assert(err, jc.Satisfies, params.IsCodeNotProvisioned)
   385  	c.Assert(err, gc.ErrorMatches, "machine 1 not provisioned")
   386  	c.Assert(instanceId, gc.Equals, instance.Id(""))
   387  
   388  	hwChars := instance.MustParseHardware("cores=123", "mem=4G")
   389  
   390  	profiles := []string{"juju-default-profile-0", "juju-default-lxd-2"}
   391  	err = apiMachine.SetInstanceInfo(
   392  		"profileinst", "", "nonce", &hwChars, nil, nil, nil, profiles,
   393  	)
   394  	c.Assert(err, jc.ErrorIsNil)
   395  
   396  	mach, err := s.State.Machine(apiMachine.Id())
   397  	c.Assert(err, jc.ErrorIsNil)
   398  	obtainedProfiles, err := mach.CharmProfiles()
   399  	c.Assert(err, jc.ErrorIsNil)
   400  	c.Assert(profiles, jc.SameContents, obtainedProfiles)
   401  }
   402  
   403  func (s *provisionerSuite) TestSetCharmProfiles(c *gc.C) {
   404  	apiMachine := s.assertGetOneMachine(c, s.machine.MachineTag())
   405  
   406  	profiles := []string{"juju-default-profile-0", "juju-default-lxd-2"}
   407  	err := apiMachine.SetCharmProfiles(profiles)
   408  	c.Assert(err, jc.ErrorIsNil)
   409  
   410  	mach, err := s.State.Machine(apiMachine.Id())
   411  	c.Assert(err, jc.ErrorIsNil)
   412  	obtainedProfiles, err := mach.CharmProfiles()
   413  	c.Assert(err, jc.ErrorIsNil)
   414  	c.Assert(profiles, jc.SameContents, obtainedProfiles)
   415  }
   416  
   417  func (s *provisionerSuite) TestKeepInstance(c *gc.C) {
   418  	err := s.machine.SetKeepInstance(true)
   419  	c.Assert(err, jc.ErrorIsNil)
   420  	apiMachine := s.assertGetOneMachine(c, s.machine.MachineTag())
   421  	keep, err := apiMachine.KeepInstance()
   422  	c.Assert(err, jc.ErrorIsNil)
   423  	c.Assert(keep, jc.IsTrue)
   424  }
   425  
   426  func (s *provisionerSuite) TestDistributionGroup(c *gc.C) {
   427  	apiMachine := s.assertGetOneMachine(c, s.machine.MachineTag())
   428  	instances, err := apiMachine.DistributionGroup()
   429  	c.Assert(err, jc.ErrorIsNil)
   430  	c.Assert(instances, gc.DeepEquals, []instance.Id{"i-manager"})
   431  
   432  	machine1, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
   433  	c.Assert(err, jc.ErrorIsNil)
   434  	apiMachine = s.assertGetOneMachine(c, machine1.MachineTag())
   435  	wordpress := s.AddTestingApplication(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
   436  
   437  	err = apiMachine.SetInstanceInfo("i-d", "", "fake", nil, nil, nil, nil, nil)
   438  	c.Assert(err, jc.ErrorIsNil)
   439  	instances, err = apiMachine.DistributionGroup()
   440  	c.Assert(err, jc.ErrorIsNil)
   441  	c.Assert(instances, gc.HasLen, 0) // no units assigned
   442  
   443  	var unitNames []string
   444  	for i := 0; i < 3; i++ {
   445  		unit, err := wordpress.AddUnit(state.AddUnitParams{})
   446  		c.Assert(err, jc.ErrorIsNil)
   447  		unitNames = append(unitNames, unit.Name())
   448  		err = unit.AssignToMachine(machine1)
   449  		c.Assert(err, jc.ErrorIsNil)
   450  		instances, err := apiMachine.DistributionGroup()
   451  		c.Assert(err, jc.ErrorIsNil)
   452  		c.Assert(instances, gc.DeepEquals, []instance.Id{"i-d"})
   453  	}
   454  }
   455  
   456  func (s *provisionerSuite) TestDistributionGroupMachineNotFound(c *gc.C) {
   457  	stateMachine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
   458  	c.Assert(err, jc.ErrorIsNil)
   459  	apiMachine := s.assertGetOneMachine(c, stateMachine.MachineTag())
   460  	err = apiMachine.EnsureDead()
   461  	c.Assert(err, jc.ErrorIsNil)
   462  	err = apiMachine.Remove()
   463  	c.Assert(err, jc.ErrorIsNil)
   464  	_, err = apiMachine.DistributionGroup()
   465  	c.Assert(err, gc.ErrorMatches, "machine 1 not found")
   466  	c.Assert(err, jc.Satisfies, params.IsCodeNotFound)
   467  }
   468  
   469  func (s *provisionerSuite) TestDistributionGroupByMachineId(c *gc.C) {
   470  	results, err := s.provisioner.DistributionGroupByMachineId(s.machine.MachineTag())
   471  	c.Assert(err, jc.ErrorIsNil)
   472  	c.Assert(len(results), gc.Equals, 1)
   473  	c.Assert(results, gc.DeepEquals, []provisioner.DistributionGroupResult{
   474  		{MachineIds: nil, Err: nil},
   475  	})
   476  
   477  	machine1, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
   478  	c.Assert(err, jc.ErrorIsNil)
   479  	wordpress := s.AddTestingApplication(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
   480  	unit, err := wordpress.AddUnit(state.AddUnitParams{})
   481  	c.Assert(err, jc.ErrorIsNil)
   482  	err = unit.AssignToMachine(machine1)
   483  	c.Assert(err, jc.ErrorIsNil)
   484  
   485  	results, err = s.provisioner.DistributionGroupByMachineId(
   486  		s.machine.MachineTag(),
   487  		machine1.MachineTag(),
   488  	)
   489  	c.Assert(err, jc.ErrorIsNil)
   490  	c.Assert(len(results), gc.Equals, 2)
   491  	c.Assert(results, gc.DeepEquals, []provisioner.DistributionGroupResult{
   492  		{MachineIds: nil, Err: nil},
   493  		{MachineIds: nil, Err: nil},
   494  	})
   495  
   496  	machine2, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
   497  	c.Assert(err, jc.ErrorIsNil)
   498  	unit2, err := wordpress.AddUnit(state.AddUnitParams{})
   499  	c.Assert(err, jc.ErrorIsNil)
   500  	err = unit2.AssignToMachine(machine2)
   501  	c.Assert(err, jc.ErrorIsNil)
   502  
   503  	results, err = s.provisioner.DistributionGroupByMachineId(
   504  		s.machine.MachineTag(),
   505  		machine1.MachineTag(),
   506  		machine2.MachineTag(),
   507  	)
   508  	c.Assert(err, jc.ErrorIsNil)
   509  	c.Assert(len(results), gc.Equals, 3)
   510  	c.Assert(results, gc.DeepEquals, []provisioner.DistributionGroupResult{
   511  		{MachineIds: nil, Err: nil},
   512  		{MachineIds: []string{"2"}, Err: nil},
   513  		{MachineIds: []string{"1"}, Err: nil},
   514  	})
   515  }
   516  
   517  func (s *provisionerSuite) TestDistributionGroupByMachineIdNotFound(c *gc.C) {
   518  	stateMachine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
   519  	c.Assert(err, jc.ErrorIsNil)
   520  	machineTag := stateMachine.MachineTag()
   521  	apiMachine := s.assertGetOneMachine(c, machineTag)
   522  	err = apiMachine.EnsureDead()
   523  	c.Assert(err, jc.ErrorIsNil)
   524  	err = apiMachine.Remove()
   525  	c.Assert(err, jc.ErrorIsNil)
   526  	results, err := s.provisioner.DistributionGroupByMachineId(machineTag)
   527  	c.Assert(err, jc.ErrorIsNil)
   528  	c.Assert(len(results), gc.Equals, 1)
   529  	c.Assert(results[0].Err, gc.ErrorMatches, "machine 1 not found")
   530  	c.Assert(results[0].Err, jc.Satisfies, params.IsCodeNotFound)
   531  }
   532  
   533  func (s *provisionerSuite) TestProvisioningInfo(c *gc.C) {
   534  	// Add a couple of spaces.
   535  	space1, err := s.State.AddSpace("space1", "", nil, true)
   536  	c.Assert(err, jc.ErrorIsNil)
   537  	space2, err := s.State.AddSpace("space2", "", nil, false)
   538  	c.Assert(err, jc.ErrorIsNil)
   539  	// Add 2 subnets into each space.
   540  	// Each subnet is in a matching zone (e.g "subnet-#" in "zone#").
   541  	testing.AddSubnetsWithTemplate(c, s.State, 4, corenetwork.SubnetInfo{
   542  		CIDR:              "10.{{.}}.0.0/16",
   543  		ProviderId:        "subnet-{{.}}",
   544  		AvailabilityZones: []string{"zone{{.}}"},
   545  		SpaceID:           fmt.Sprintf("{{if (lt . 2)}}%s{{else}}%s{{end}}", space1.Id(), space2.Id()),
   546  	})
   547  
   548  	cons := constraints.MustParse("cores=12 mem=8G spaces=^space1,space2")
   549  	template := state.MachineTemplate{
   550  		Base:        state.UbuntuBase("12.10"),
   551  		Jobs:        []state.MachineJob{state.JobHostUnits},
   552  		Placement:   "valid",
   553  		Constraints: cons,
   554  	}
   555  	machine, err := s.State.AddOneMachine(template)
   556  	c.Assert(err, jc.ErrorIsNil)
   557  
   558  	res, err := s.provisioner.ProvisioningInfo([]names.MachineTag{machine.MachineTag()})
   559  	c.Assert(err, jc.ErrorIsNil)
   560  
   561  	results := res.Results
   562  	c.Assert(results, gc.HasLen, 1)
   563  
   564  	provisioningInfo := results[0].Result
   565  	c.Assert(provisioningInfo.Base, jc.DeepEquals, params.Base{Name: "ubuntu", Channel: "12.10/stable"})
   566  	c.Assert(provisioningInfo.Placement, gc.Equals, template.Placement)
   567  	c.Assert(provisioningInfo.Constraints, jc.DeepEquals, template.Constraints)
   568  
   569  	c.Assert(provisioningInfo.SubnetAZs, jc.DeepEquals, map[string][]string{
   570  		"subnet-2": {"zone2"},
   571  		"subnet-3": {"zone3"},
   572  	})
   573  
   574  	c.Assert(provisioningInfo.SpaceSubnets, gc.HasLen, 1)
   575  	c.Assert(provisioningInfo.SpaceSubnets["space2"], jc.SameContents, []string{"subnet-2", "subnet-3"})
   576  }
   577  
   578  func (s *provisionerSuite) TestProvisioningInfoMachineNotFound(c *gc.C) {
   579  	res, err := s.provisioner.ProvisioningInfo([]names.MachineTag{names.NewMachineTag("1")})
   580  	c.Assert(err, jc.ErrorIsNil)
   581  
   582  	results := res.Results
   583  	c.Assert(results, gc.HasLen, 1)
   584  	c.Assert(results[0].Error, gc.ErrorMatches, "machine 1 not found")
   585  	c.Assert(results[0].Error, jc.Satisfies, params.IsCodeNotFound)
   586  }
   587  
   588  func (s *provisionerSuite) TestWatchContainers(c *gc.C) {
   589  	apiMachine := s.assertGetOneMachine(c, s.machine.MachineTag())
   590  
   591  	// Add one LXD container.
   592  	template := state.MachineTemplate{
   593  		Base: state.UbuntuBase("12.10"),
   594  		Jobs: []state.MachineJob{state.JobHostUnits},
   595  	}
   596  	container, err := s.State.AddMachineInsideMachine(template, s.machine.Id(), instance.LXD)
   597  	c.Assert(err, jc.ErrorIsNil)
   598  
   599  	w, err := apiMachine.WatchContainers(instance.LXD)
   600  	c.Assert(err, jc.ErrorIsNil)
   601  	wc := watchertest.NewStringsWatcherC(c, w)
   602  	defer wc.AssertStops()
   603  
   604  	// Initial event.
   605  	wc.AssertChange(container.Id())
   606  
   607  	// Change something other than the containers and make sure it's
   608  	// not detected.
   609  	err = apiMachine.SetStatus(status.Started, "not really", nil)
   610  	c.Assert(err, jc.ErrorIsNil)
   611  	wc.AssertNoChange()
   612  
   613  	// Add a KVM container and make sure it's not detected.
   614  	container, err = s.State.AddMachineInsideMachine(template, s.machine.Id(), instance.KVM)
   615  	c.Assert(err, jc.ErrorIsNil)
   616  	wc.AssertNoChange()
   617  
   618  	// Add another LXD container and make sure it's detected.
   619  	container, err = s.State.AddMachineInsideMachine(template, s.machine.Id(), instance.LXD)
   620  	c.Assert(err, jc.ErrorIsNil)
   621  	wc.AssertChange(container.Id())
   622  }
   623  
   624  func (s *provisionerSuite) TestWatchContainersAcceptsSupportedContainers(c *gc.C) {
   625  	apiMachine := s.assertGetOneMachine(c, s.machine.MachineTag())
   626  
   627  	for _, ctype := range instance.ContainerTypes {
   628  		w, err := apiMachine.WatchContainers(ctype)
   629  		c.Assert(w, gc.NotNil)
   630  		c.Assert(err, jc.ErrorIsNil)
   631  	}
   632  }
   633  
   634  func (s *provisionerSuite) TestWatchContainersErrors(c *gc.C) {
   635  	apiMachine := s.assertGetOneMachine(c, s.machine.MachineTag())
   636  
   637  	_, err := apiMachine.WatchContainers(instance.NONE)
   638  	c.Assert(err, gc.ErrorMatches, `unsupported container type "none"`)
   639  
   640  	_, err = apiMachine.WatchContainers("")
   641  	c.Assert(err, gc.ErrorMatches, "container type must be specified")
   642  }
   643  
   644  func (s *provisionerSuite) TestWatchModelMachines(c *gc.C) {
   645  	w, err := s.provisioner.WatchModelMachines()
   646  	c.Assert(err, jc.ErrorIsNil)
   647  	wc := watchertest.NewStringsWatcherC(c, w)
   648  	defer wc.AssertStops()
   649  
   650  	// Initial event.
   651  	wc.AssertChange(s.machine.Id())
   652  
   653  	// Add another 2 machines make sure they are detected.
   654  	_, err = s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
   655  	c.Assert(err, jc.ErrorIsNil)
   656  	otherMachine, err := s.State.AddMachine(state.UbuntuBase("12.10"), state.JobHostUnits)
   657  	c.Assert(err, jc.ErrorIsNil)
   658  	wc.AssertChange("1", "2")
   659  
   660  	// Change the lifecycle of last machine.
   661  	err = otherMachine.EnsureDead()
   662  	c.Assert(err, jc.ErrorIsNil)
   663  	wc.AssertChange("2")
   664  
   665  	// Add a container and make sure it's not detected.
   666  	template := state.MachineTemplate{
   667  		Base: state.UbuntuBase("12.10"),
   668  		Jobs: []state.MachineJob{state.JobHostUnits},
   669  	}
   670  	_, err = s.State.AddMachineInsideMachine(template, s.machine.Id(), instance.LXD)
   671  	c.Assert(err, jc.ErrorIsNil)
   672  	wc.AssertNoChange()
   673  }
   674  
   675  func (s *provisionerSuite) getManagerConfig(c *gc.C, typ instance.ContainerType) map[string]string {
   676  	args := params.ContainerManagerConfigParams{Type: typ}
   677  	result, err := s.provisioner.ContainerManagerConfig(args)
   678  	c.Assert(err, jc.ErrorIsNil)
   679  	return result.ManagerConfig
   680  }
   681  
   682  func (s *provisionerSuite) TestContainerManagerConfigKVM(c *gc.C) {
   683  	cfg := s.getManagerConfig(c, instance.KVM)
   684  	c.Assert(cfg, jc.DeepEquals, map[string]string{
   685  		container.ConfigModelUUID:        coretesting.ModelTag.Id(),
   686  		config.ContainerImageStreamKey:   "released",
   687  		config.ContainerNetworkingMethod: config.ConfigDefaults()[config.ContainerNetworkingMethod].(string),
   688  	})
   689  }
   690  
   691  func (s *provisionerSuite) TestContainerManagerConfigPermissive(c *gc.C) {
   692  	// ContainerManagerConfig is permissive of container types, and
   693  	// will just return the basic type-independent configuration.
   694  	cfg := s.getManagerConfig(c, "invalid")
   695  	c.Assert(cfg, jc.DeepEquals, map[string]string{
   696  		container.ConfigModelUUID:        coretesting.ModelTag.Id(),
   697  		config.ContainerImageStreamKey:   "released",
   698  		config.ContainerNetworkingMethod: config.ConfigDefaults()[config.ContainerNetworkingMethod].(string),
   699  	})
   700  }
   701  
   702  func (s *provisionerSuite) TestContainerConfig(c *gc.C) {
   703  	result, err := s.provisioner.ContainerConfig()
   704  	c.Assert(err, jc.ErrorIsNil)
   705  	c.Assert(result.ProviderType, gc.Equals, "dummy")
   706  	c.Assert(result.AuthorizedKeys, gc.Equals, s.Environ.Config().AuthorizedKeys())
   707  	c.Assert(result.SSLHostnameVerification, jc.IsTrue)
   708  }
   709  
   710  func (s *provisionerSuite) TestSetSupportedContainers(c *gc.C) {
   711  	apiMachine := s.assertGetOneMachine(c, s.machine.MachineTag())
   712  	err := apiMachine.SetSupportedContainers(instance.LXD, instance.KVM)
   713  	c.Assert(err, jc.ErrorIsNil)
   714  
   715  	err = s.machine.Refresh()
   716  	c.Assert(err, jc.ErrorIsNil)
   717  	containers, ok := s.machine.SupportedContainers()
   718  	c.Assert(ok, jc.IsTrue)
   719  	c.Assert(containers, gc.DeepEquals, []instance.ContainerType{instance.LXD, instance.KVM})
   720  }
   721  
   722  func (s *provisionerSuite) TestSupportedContainers(c *gc.C) {
   723  	apiMachine := s.assertGetOneMachine(c, s.machine.MachineTag())
   724  	err := apiMachine.SetSupportedContainers(instance.LXD, instance.KVM)
   725  	c.Assert(err, jc.ErrorIsNil)
   726  
   727  	containers, ok, err := apiMachine.SupportedContainers()
   728  	c.Assert(err, gc.IsNil)
   729  	c.Assert(ok, jc.IsTrue)
   730  	c.Assert(containers, gc.DeepEquals, []instance.ContainerType{instance.LXD, instance.KVM})
   731  }
   732  
   733  func (s *provisionerSuite) TestSupportsNoContainers(c *gc.C) {
   734  	apiMachine := s.assertGetOneMachine(c, s.machine.MachineTag())
   735  	err := apiMachine.SupportsNoContainers()
   736  	c.Assert(err, jc.ErrorIsNil)
   737  
   738  	err = s.machine.Refresh()
   739  	c.Assert(err, jc.ErrorIsNil)
   740  	containers, ok := s.machine.SupportedContainers()
   741  	c.Assert(ok, jc.IsTrue)
   742  	c.Assert(containers, gc.DeepEquals, []instance.ContainerType{})
   743  }
   744  
   745  func (s *provisionerSuite) TestFindToolsNoArch(c *gc.C) {
   746  	s.testFindTools(c, false, nil, nil)
   747  }
   748  
   749  func (s *provisionerSuite) TestFindToolsArch(c *gc.C) {
   750  	s.testFindTools(c, true, nil, nil)
   751  }
   752  
   753  func (s *provisionerSuite) TestFindToolsAPIError(c *gc.C) {
   754  	apiError := errors.New("everything's broken")
   755  	s.testFindTools(c, false, apiError, nil)
   756  }
   757  
   758  func (s *provisionerSuite) TestFindToolsLogicError(c *gc.C) {
   759  	logicError := errors.NotFoundf("tools")
   760  	s.testFindTools(c, false, nil, logicError)
   761  }
   762  
   763  func (s *provisionerSuite) testFindTools(c *gc.C, matchArch bool, apiError, logicError error) {
   764  	current := coretesting.CurrentVersion()
   765  	var toolsList = coretools.List{&coretools.Tools{Version: current}}
   766  	var called bool
   767  	var a string
   768  	if matchArch {
   769  		// if matchArch is true, this will be overwriten with the host's arch, otherwise
   770  		// leave a blank.
   771  		a = arch.HostArch()
   772  	}
   773  
   774  	provisioner.PatchFacadeCall(s, s.provisioner, func(request string, args, response interface{}) error {
   775  		called = true
   776  		c.Assert(request, gc.Equals, "FindTools")
   777  		expected := params.FindToolsParams{
   778  			Number: jujuversion.Current,
   779  			OSType: "ubuntu",
   780  			Arch:   a,
   781  		}
   782  		c.Assert(args, gc.Equals, expected)
   783  		result := response.(*params.FindToolsResult)
   784  		result.List = toolsList
   785  		if logicError != nil {
   786  			result.Error = apiservererrors.ServerError(logicError)
   787  		}
   788  		return apiError
   789  	})
   790  	apiList, err := s.provisioner.FindTools(jujuversion.Current, "ubuntu", a)
   791  	c.Assert(called, jc.IsTrue)
   792  	if apiError != nil {
   793  		c.Assert(err, gc.Equals, apiError)
   794  	} else if logicError != nil {
   795  		c.Assert(err.Error(), gc.Equals, logicError.Error())
   796  	} else {
   797  		c.Assert(err, jc.ErrorIsNil)
   798  		c.Assert(apiList, jc.SameContents, toolsList)
   799  	}
   800  }
   801  
   802  func (s *provisionerSuite) TestHostChangesForContainer(c *gc.C) {
   803  	// Create a machine, put it in "default" space with a single NIC. Create
   804  	// a container that is also in the "default" space, and request the
   805  	// HostChangesForContainer to see that it wants to bridge that NIC
   806  	space, err := s.State.AddSpace("default", corenetwork.Id("default"), nil, true)
   807  	c.Assert(err, jc.ErrorIsNil)
   808  	_, err = s.State.AddSubnet(corenetwork.SubnetInfo{
   809  		CIDR:    "10.0.0.0/24",
   810  		SpaceID: space.Id(),
   811  	})
   812  	c.Assert(err, jc.ErrorIsNil)
   813  	err = s.machine.SetLinkLayerDevices(
   814  		state.LinkLayerDeviceArgs{
   815  			Name:       "ens3",
   816  			Type:       corenetwork.EthernetDevice,
   817  			ParentName: "",
   818  			IsUp:       true,
   819  		},
   820  	)
   821  	c.Assert(err, jc.ErrorIsNil)
   822  	err = s.machine.SetDevicesAddresses(
   823  		state.LinkLayerDeviceAddress{
   824  			DeviceName:   "ens3",
   825  			CIDRAddress:  "10.0.0.10/24",
   826  			ConfigMethod: corenetwork.ConfigStatic,
   827  		},
   828  	)
   829  	c.Assert(err, jc.ErrorIsNil)
   830  	containerTemplate := state.MachineTemplate{
   831  		Base: state.UbuntuBase("12.10"),
   832  		Jobs: []state.MachineJob{state.JobHostUnits},
   833  	}
   834  	machine, err := s.State.AddMachineInsideMachine(containerTemplate, s.machine.Id(), instance.LXD)
   835  	c.Assert(err, jc.ErrorIsNil)
   836  
   837  	changes, reconfigureDelay, err := s.provisioner.HostChangesForContainer(machine.MachineTag())
   838  	c.Assert(err, gc.ErrorMatches, "dummy provider network config not supported.*")
   839  	c.Skip("can't test without network support")
   840  	c.Assert(err, jc.ErrorIsNil)
   841  	c.Check(changes, gc.DeepEquals, []network.DeviceToBridge{{
   842  		BridgeName: "br-ens3",
   843  		DeviceName: "ens3",
   844  	}})
   845  	c.Check(reconfigureDelay, gc.Equals, 0)
   846  }
   847  
   848  var _ = gc.Suite(&provisionerContainerSuite{})
   849  
   850  type provisionerContainerSuite struct {
   851  	containerTag names.MachineTag
   852  }
   853  
   854  func (s *provisionerContainerSuite) SetUpTest(c *gc.C) {
   855  	s.containerTag = names.NewMachineTag("0/lxd/0")
   856  }
   857  
   858  func (s *provisionerContainerSuite) TestPrepareContainerInterfaceInfoNoValues(c *gc.C) {
   859  	ctrl := gomock.NewController(c)
   860  	defer ctrl.Finish()
   861  
   862  	args := params.Entities{
   863  		Entities: []params.Entity{{Tag: s.containerTag.String()}},
   864  	}
   865  	results := params.MachineNetworkConfigResults{Results: []params.MachineNetworkConfigResult{{
   866  		Config: nil,
   867  		Error:  nil,
   868  	}}}
   869  
   870  	facadeCaller := apimocks.NewMockFacadeCaller(ctrl)
   871  	fExp := facadeCaller.EXPECT()
   872  	fExp.FacadeCall("PrepareContainerInterfaceInfo", args, gomock.Any()).SetArg(2, results).Return(nil)
   873  
   874  	provisionerApi := provisioner.NewStateFromFacade(facadeCaller)
   875  
   876  	networkInfo, err := provisionerApi.PrepareContainerInterfaceInfo(s.containerTag)
   877  	c.Assert(err, gc.IsNil)
   878  	c.Check(networkInfo, jc.DeepEquals, corenetwork.InterfaceInfos{})
   879  }
   880  
   881  func (s *provisionerContainerSuite) TestPrepareContainerInterfaceInfoSingleNIC(c *gc.C) {
   882  	ctrl := gomock.NewController(c)
   883  	defer ctrl.Finish()
   884  
   885  	args := params.Entities{
   886  		Entities: []params.Entity{{Tag: s.containerTag.String()}},
   887  	}
   888  	results := params.MachineNetworkConfigResults{
   889  		Results: []params.MachineNetworkConfigResult{{
   890  			Config: []params.NetworkConfig{{
   891  				DeviceIndex:         1,
   892  				MACAddress:          "de:ad:be:ff:11:22",
   893  				CIDR:                "192.168.0.5/24",
   894  				MTU:                 9000,
   895  				ProviderId:          "prov-id",
   896  				ProviderSubnetId:    "prov-sub-id",
   897  				ProviderSpaceId:     "prov-space-id",
   898  				ProviderAddressId:   "prov-address-id",
   899  				ProviderVLANId:      "prov-vlan-id",
   900  				VLANTag:             25,
   901  				InterfaceName:       "eth5",
   902  				ParentInterfaceName: "parent#br-eth5",
   903  				InterfaceType:       "ethernet",
   904  				Disabled:            false,
   905  				NoAutoStart:         false,
   906  				ConfigType:          "static",
   907  				Address:             "192.168.0.6",
   908  				DNSServers:          []string{"8.8.8.8"},
   909  				DNSSearchDomains:    []string{"mydomain"},
   910  				GatewayAddress:      "192.168.0.1",
   911  				Routes: []params.NetworkRoute{{
   912  					DestinationCIDR: "10.0.0.0/16",
   913  					GatewayIP:       "192.168.0.1",
   914  					Metric:          55,
   915  				}},
   916  			}},
   917  			Error: nil,
   918  		}},
   919  	}
   920  
   921  	facadeCaller := apimocks.NewMockFacadeCaller(ctrl)
   922  	fExp := facadeCaller.EXPECT()
   923  	fExp.FacadeCall("PrepareContainerInterfaceInfo", args, gomock.Any()).SetArg(2, results).Return(nil)
   924  
   925  	provisionerApi := provisioner.NewStateFromFacade(facadeCaller)
   926  	networkInfo, err := provisionerApi.PrepareContainerInterfaceInfo(s.containerTag)
   927  	c.Assert(err, gc.IsNil)
   928  	c.Check(networkInfo, jc.DeepEquals, corenetwork.InterfaceInfos{{
   929  		DeviceIndex:         1,
   930  		MACAddress:          "de:ad:be:ff:11:22",
   931  		MTU:                 9000,
   932  		ProviderId:          "prov-id",
   933  		ProviderSubnetId:    "prov-sub-id",
   934  		ProviderSpaceId:     "prov-space-id",
   935  		ProviderAddressId:   "prov-address-id",
   936  		ProviderVLANId:      "prov-vlan-id",
   937  		VLANTag:             25,
   938  		InterfaceName:       "eth5",
   939  		ParentInterfaceName: "parent#br-eth5",
   940  		InterfaceType:       "ethernet",
   941  		Disabled:            false,
   942  		NoAutoStart:         false,
   943  		ConfigType:          "static",
   944  		Addresses: corenetwork.ProviderAddresses{corenetwork.NewMachineAddress(
   945  			"192.168.0.6", corenetwork.WithCIDR("192.168.0.5/24"), corenetwork.WithConfigType(corenetwork.ConfigStatic),
   946  		).AsProviderAddress()},
   947  		DNSServers:       corenetwork.NewMachineAddresses([]string{"8.8.8.8"}).AsProviderAddresses(),
   948  		DNSSearchDomains: []string{"mydomain"},
   949  		GatewayAddress:   corenetwork.NewMachineAddress("192.168.0.1").AsProviderAddress(),
   950  		Routes: []corenetwork.Route{{
   951  			DestinationCIDR: "10.0.0.0/16",
   952  			GatewayIP:       "192.168.0.1",
   953  			Metric:          55,
   954  		}},
   955  	}})
   956  }
   957  
   958  func (s *provisionerContainerSuite) TestGetContainerProfileInfo(c *gc.C) {
   959  	ctrl := gomock.NewController(c)
   960  	defer ctrl.Finish()
   961  
   962  	args := params.Entities{
   963  		Entities: []params.Entity{{Tag: s.containerTag.String()}},
   964  	}
   965  	results := params.ContainerProfileResults{
   966  		Results: []params.ContainerProfileResult{
   967  			{
   968  				LXDProfiles: []*params.ContainerLXDProfile{
   969  					{
   970  						Profile: params.CharmLXDProfile{
   971  							Config: map[string]string{
   972  								"security.nesting":    "true",
   973  								"security.privileged": "true",
   974  							},
   975  						},
   976  						Name: "one",
   977  					},
   978  					{
   979  						Profile: params.CharmLXDProfile{
   980  							Devices: map[string]map[string]string{
   981  								"bdisk": {
   982  									"source": "/dev/loop0",
   983  									"type":   "unix-block",
   984  								},
   985  							},
   986  						},
   987  						Name: "two",
   988  					}},
   989  				Error: nil,
   990  			}},
   991  	}
   992  
   993  	facadeCaller := apimocks.NewMockFacadeCaller(ctrl)
   994  	fExp := facadeCaller.EXPECT()
   995  	fExp.FacadeCall("GetContainerProfileInfo", args, gomock.Any()).SetArg(2, results).Return(nil)
   996  
   997  	provisionerApi := provisioner.NewStateFromFacade(facadeCaller)
   998  
   999  	obtainedResults, err := provisionerApi.GetContainerProfileInfo(s.containerTag)
  1000  	c.Assert(err, jc.ErrorIsNil)
  1001  	c.Assert(obtainedResults, gc.DeepEquals, []*provisioner.LXDProfileResult{
  1002  		{
  1003  			Config: map[string]string{
  1004  				"security.nesting":    "true",
  1005  				"security.privileged": "true",
  1006  			},
  1007  			Name: "one",
  1008  		},
  1009  		{
  1010  			Devices: map[string]map[string]string{
  1011  				"bdisk": {
  1012  					"source": "/dev/loop0",
  1013  					"type":   "unix-block",
  1014  				},
  1015  			},
  1016  			Name: "two",
  1017  		}})
  1018  
  1019  }