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