github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/api/provisioner/provisioner_test.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  // TODO(anastasia) 2014-10-08 #1378716
     5  // Re-enable tests for PPC64/ARM64 when the fixed gccgo has been backported to trusty and the CI machines have been updated.
     6  
     7  // +build !gccgo
     8  
     9  package provisioner_test
    10  
    11  import (
    12  	"time"
    13  
    14  	"github.com/juju/errors"
    15  	jc "github.com/juju/testing/checkers"
    16  	"github.com/juju/utils"
    17  	"github.com/juju/utils/arch"
    18  	"github.com/juju/utils/series"
    19  	"github.com/juju/version"
    20  	gc "gopkg.in/check.v1"
    21  	"gopkg.in/juju/names.v2"
    22  
    23  	"github.com/juju/juju/api"
    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/constraints"
    29  	"github.com/juju/juju/container"
    30  	"github.com/juju/juju/instance"
    31  	"github.com/juju/juju/juju/testing"
    32  	"github.com/juju/juju/network"
    33  	"github.com/juju/juju/state"
    34  	"github.com/juju/juju/status"
    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  	"github.com/juju/juju/watcher/watchertest"
    41  )
    42  
    43  type provisionerSuite struct {
    44  	testing.JujuConnSuite
    45  	*apitesting.ModelWatcherTests
    46  	*apitesting.APIAddresserTests
    47  
    48  	st      api.Connection
    49  	machine *state.Machine
    50  
    51  	provisioner *provisioner.State
    52  }
    53  
    54  var _ = gc.Suite(&provisionerSuite{})
    55  
    56  func (s *provisionerSuite) SetUpTest(c *gc.C) {
    57  	s.JujuConnSuite.SetUpTest(c)
    58  
    59  	var err error
    60  	s.machine, err = s.State.AddMachine("quantal", state.JobManageModel)
    61  	c.Assert(err, jc.ErrorIsNil)
    62  	password, err := utils.RandomPassword()
    63  	c.Assert(err, jc.ErrorIsNil)
    64  	err = s.machine.SetPassword(password)
    65  	c.Assert(err, jc.ErrorIsNil)
    66  	err = s.machine.SetInstanceInfo("i-manager", "fake_nonce", nil, nil, nil, nil, nil)
    67  	c.Assert(err, jc.ErrorIsNil)
    68  	s.st = s.OpenAPIAsMachine(c, s.machine.Tag(), password, "fake_nonce")
    69  	c.Assert(s.st, gc.NotNil)
    70  	err = s.machine.SetProviderAddresses(network.NewAddress("0.1.2.3"))
    71  	c.Assert(err, jc.ErrorIsNil)
    72  
    73  	// Create the provisioner API facade.
    74  	s.provisioner = provisioner.NewState(s.st)
    75  	c.Assert(s.provisioner, gc.NotNil)
    76  
    77  	s.ModelWatcherTests = apitesting.NewModelWatcherTests(s.provisioner, s.BackingState)
    78  	s.APIAddresserTests = apitesting.NewAPIAddresserTests(s.provisioner, s.BackingState)
    79  }
    80  
    81  func (s *provisionerSuite) TestMachineTagAndId(c *gc.C) {
    82  	apiMachine, err := s.provisioner.Machine(names.NewMachineTag("42"))
    83  	c.Assert(err, gc.ErrorMatches, "machine 42 not found")
    84  	c.Assert(err, jc.Satisfies, params.IsCodeNotFound)
    85  	c.Assert(apiMachine, gc.IsNil)
    86  
    87  	// TODO(dfc) fix this type assertion
    88  	apiMachine, err = s.provisioner.Machine(s.machine.Tag().(names.MachineTag))
    89  	c.Assert(err, jc.ErrorIsNil)
    90  	c.Assert(apiMachine.Tag(), gc.Equals, s.machine.Tag())
    91  	c.Assert(apiMachine.Id(), gc.Equals, s.machine.Id())
    92  }
    93  
    94  func (s *provisionerSuite) TestGetSetStatus(c *gc.C) {
    95  	apiMachine, err := s.provisioner.Machine(s.machine.Tag().(names.MachineTag))
    96  	c.Assert(err, jc.ErrorIsNil)
    97  
    98  	machineStatus, info, err := apiMachine.Status()
    99  	c.Assert(err, jc.ErrorIsNil)
   100  	c.Assert(machineStatus, gc.Equals, status.Pending)
   101  	c.Assert(info, gc.Equals, "")
   102  
   103  	err = apiMachine.SetStatus(status.Started, "blah", nil)
   104  	c.Assert(err, jc.ErrorIsNil)
   105  
   106  	machineStatus, info, err = apiMachine.Status()
   107  	c.Assert(err, jc.ErrorIsNil)
   108  	c.Assert(machineStatus, gc.Equals, status.Started)
   109  	c.Assert(info, gc.Equals, "blah")
   110  	statusInfo, err := s.machine.Status()
   111  	c.Assert(err, jc.ErrorIsNil)
   112  	c.Assert(statusInfo.Data, gc.HasLen, 0)
   113  }
   114  
   115  func (s *provisionerSuite) TestGetSetInstanceStatus(c *gc.C) {
   116  	apiMachine, err := s.provisioner.Machine(s.machine.Tag().(names.MachineTag))
   117  	c.Assert(err, jc.ErrorIsNil)
   118  
   119  	instanceStatus, info, err := apiMachine.InstanceStatus()
   120  	c.Assert(err, jc.ErrorIsNil)
   121  	c.Assert(instanceStatus, gc.Equals, status.Pending)
   122  	c.Assert(info, gc.Equals, "")
   123  	err = apiMachine.SetInstanceStatus(status.Started, "blah", nil)
   124  	c.Assert(err, jc.ErrorIsNil)
   125  	instanceStatus, info, err = apiMachine.InstanceStatus()
   126  	c.Assert(err, jc.ErrorIsNil)
   127  	c.Assert(instanceStatus, gc.Equals, status.Started)
   128  	c.Assert(info, gc.Equals, "blah")
   129  	statusInfo, err := s.machine.InstanceStatus()
   130  	c.Assert(err, jc.ErrorIsNil)
   131  	c.Assert(statusInfo.Data, gc.HasLen, 0)
   132  }
   133  
   134  func (s *provisionerSuite) TestGetSetStatusWithData(c *gc.C) {
   135  	apiMachine, err := s.provisioner.Machine(s.machine.Tag().(names.MachineTag))
   136  	c.Assert(err, jc.ErrorIsNil)
   137  
   138  	err = apiMachine.SetStatus(status.Error, "blah", map[string]interface{}{"foo": "bar"})
   139  	c.Assert(err, jc.ErrorIsNil)
   140  
   141  	machineStatus, info, err := apiMachine.Status()
   142  	c.Assert(err, jc.ErrorIsNil)
   143  	c.Assert(machineStatus, gc.Equals, status.Error)
   144  	c.Assert(info, gc.Equals, "blah")
   145  	statusInfo, err := s.machine.Status()
   146  	c.Assert(err, jc.ErrorIsNil)
   147  	c.Assert(statusInfo.Data, gc.DeepEquals, map[string]interface{}{"foo": "bar"})
   148  }
   149  
   150  func (s *provisionerSuite) TestMachinesWithTransientErrors(c *gc.C) {
   151  	machine, err := s.State.AddMachine("quantal", state.JobHostUnits)
   152  	c.Assert(err, jc.ErrorIsNil)
   153  	now := time.Now()
   154  	sInfo := status.StatusInfo{
   155  		Status:  status.Error,
   156  		Message: "blah",
   157  		Data:    map[string]interface{}{"transient": true},
   158  		Since:   &now,
   159  	}
   160  	err = machine.SetStatus(sInfo)
   161  	c.Assert(err, jc.ErrorIsNil)
   162  	machines, info, err := s.provisioner.MachinesWithTransientErrors()
   163  	c.Assert(err, jc.ErrorIsNil)
   164  	c.Assert(machines, gc.HasLen, 1)
   165  	c.Assert(machines[0].Id(), gc.Equals, "1")
   166  	c.Assert(info, gc.HasLen, 1)
   167  	c.Assert(info[0], gc.DeepEquals, params.StatusResult{
   168  		Id:     "1",
   169  		Life:   "alive",
   170  		Status: "error",
   171  		Info:   "blah",
   172  		Data:   map[string]interface{}{"transient": true},
   173  	})
   174  }
   175  
   176  func (s *provisionerSuite) TestEnsureDeadAndRemove(c *gc.C) {
   177  	// Create a fresh machine to test the complete scenario.
   178  	otherMachine, err := s.State.AddMachine("quantal", state.JobHostUnits)
   179  	c.Assert(err, jc.ErrorIsNil)
   180  	c.Assert(otherMachine.Life(), gc.Equals, state.Alive)
   181  
   182  	apiMachine, err := s.provisioner.Machine(otherMachine.Tag().(names.MachineTag))
   183  	c.Assert(err, jc.ErrorIsNil)
   184  
   185  	err = apiMachine.Remove()
   186  	c.Assert(err, gc.ErrorMatches, `cannot remove entity "machine-1": still alive`)
   187  	err = apiMachine.EnsureDead()
   188  	c.Assert(err, jc.ErrorIsNil)
   189  
   190  	err = otherMachine.Refresh()
   191  	c.Assert(err, jc.ErrorIsNil)
   192  	c.Assert(otherMachine.Life(), gc.Equals, state.Dead)
   193  
   194  	err = apiMachine.EnsureDead()
   195  	c.Assert(err, jc.ErrorIsNil)
   196  	err = otherMachine.Refresh()
   197  	c.Assert(err, jc.ErrorIsNil)
   198  	c.Assert(otherMachine.Life(), gc.Equals, state.Dead)
   199  
   200  	err = apiMachine.Remove()
   201  	c.Assert(err, jc.ErrorIsNil)
   202  	err = otherMachine.Refresh()
   203  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
   204  
   205  	err = apiMachine.EnsureDead()
   206  	c.Assert(err, gc.ErrorMatches, "machine 1 not found")
   207  	c.Assert(err, jc.Satisfies, params.IsCodeNotFound)
   208  
   209  	// Now try to EnsureDead machine 0 - should fail.
   210  	apiMachine, err = s.provisioner.Machine(s.machine.Tag().(names.MachineTag))
   211  	c.Assert(err, jc.ErrorIsNil)
   212  	err = apiMachine.EnsureDead()
   213  	c.Assert(err, gc.ErrorMatches, "machine 0 is required by the model")
   214  }
   215  
   216  func (s *provisionerSuite) TestMarkForRemoval(c *gc.C) {
   217  	machine, err := s.State.AddMachine("xenial", state.JobHostUnits)
   218  	c.Assert(err, jc.ErrorIsNil)
   219  
   220  	apiMachine, err := s.provisioner.Machine(machine.Tag().(names.MachineTag))
   221  	c.Assert(err, jc.ErrorIsNil)
   222  
   223  	err = apiMachine.MarkForRemoval()
   224  	c.Assert(err, gc.ErrorMatches, "cannot remove machine 1: machine is not dead")
   225  
   226  	err = machine.EnsureDead()
   227  	c.Assert(err, jc.ErrorIsNil)
   228  
   229  	err = apiMachine.MarkForRemoval()
   230  	c.Assert(err, jc.ErrorIsNil)
   231  
   232  	removals, err := s.State.AllMachineRemovals()
   233  	c.Assert(err, jc.ErrorIsNil)
   234  	c.Assert(removals, jc.SameContents, []string{"1"})
   235  }
   236  
   237  func (s *provisionerSuite) TestRefreshAndLife(c *gc.C) {
   238  	// Create a fresh machine to test the complete scenario.
   239  	otherMachine, err := s.State.AddMachine("quantal", state.JobHostUnits)
   240  	c.Assert(err, jc.ErrorIsNil)
   241  	c.Assert(otherMachine.Life(), gc.Equals, state.Alive)
   242  
   243  	apiMachine, err := s.provisioner.Machine(otherMachine.Tag().(names.MachineTag))
   244  	c.Assert(err, jc.ErrorIsNil)
   245  	c.Assert(apiMachine.Life(), gc.Equals, params.Alive)
   246  
   247  	err = apiMachine.EnsureDead()
   248  	c.Assert(err, jc.ErrorIsNil)
   249  	c.Assert(apiMachine.Life(), gc.Equals, params.Alive)
   250  
   251  	err = apiMachine.Refresh()
   252  	c.Assert(err, jc.ErrorIsNil)
   253  	c.Assert(apiMachine.Life(), gc.Equals, params.Dead)
   254  }
   255  
   256  func (s *provisionerSuite) TestSetInstanceInfo(c *gc.C) {
   257  	pm := poolmanager.New(state.NewStateSettings(s.State), provider.CommonStorageProviders())
   258  	_, err := pm.Create("loop-pool", provider.LoopProviderType, map[string]interface{}{"foo": "bar"})
   259  	c.Assert(err, jc.ErrorIsNil)
   260  
   261  	// Create a fresh machine, since machine 0 is already provisioned.
   262  	template := state.MachineTemplate{
   263  		Series: "quantal",
   264  		Jobs:   []state.MachineJob{state.JobHostUnits},
   265  		Volumes: []state.MachineVolumeParams{{
   266  			Volume: state.VolumeParams{
   267  				Pool: "loop-pool",
   268  				Size: 123,
   269  			}},
   270  		},
   271  	}
   272  	notProvisionedMachine, err := s.State.AddOneMachine(template)
   273  	c.Assert(err, jc.ErrorIsNil)
   274  
   275  	apiMachine, err := s.provisioner.Machine(notProvisionedMachine.Tag().(names.MachineTag))
   276  	c.Assert(err, jc.ErrorIsNil)
   277  
   278  	instanceId, err := apiMachine.InstanceId()
   279  	c.Assert(err, jc.Satisfies, params.IsCodeNotProvisioned)
   280  	c.Assert(err, gc.ErrorMatches, "machine 1 not provisioned")
   281  	c.Assert(instanceId, gc.Equals, instance.Id(""))
   282  
   283  	hwChars := instance.MustParseHardware("cores=123", "mem=4G")
   284  
   285  	volumes := []params.Volume{{
   286  		VolumeTag: "volume-1-0",
   287  		Info: params.VolumeInfo{
   288  			VolumeId: "vol-123",
   289  			Size:     124,
   290  		},
   291  	}}
   292  	volumeAttachments := map[string]params.VolumeAttachmentInfo{
   293  		"volume-1-0": {
   294  			DeviceName: "xvdf1",
   295  		},
   296  	}
   297  
   298  	err = apiMachine.SetInstanceInfo(
   299  		"i-will", "fake_nonce", &hwChars, nil, volumes, volumeAttachments,
   300  	)
   301  	c.Assert(err, jc.ErrorIsNil)
   302  
   303  	instanceId, err = apiMachine.InstanceId()
   304  	c.Assert(err, jc.ErrorIsNil)
   305  	c.Assert(instanceId, gc.Equals, instance.Id("i-will"))
   306  
   307  	// Try it again - should fail.
   308  	err = apiMachine.SetInstanceInfo("i-wont", "fake", nil, nil, nil, nil)
   309  	c.Assert(err, gc.ErrorMatches, `cannot record provisioning info for "i-wont": cannot set instance data for machine "1": already set`)
   310  
   311  	// Now try to get machine 0's instance id.
   312  	apiMachine, err = s.provisioner.Machine(s.machine.Tag().(names.MachineTag))
   313  	c.Assert(err, jc.ErrorIsNil)
   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  	volume, err := s.State.Volume(names.NewVolumeTag("1/0"))
   320  	c.Assert(err, jc.ErrorIsNil)
   321  	volumeInfo, err := volume.Info()
   322  	c.Assert(err, jc.ErrorIsNil)
   323  	c.Assert(volumeInfo, gc.Equals, state.VolumeInfo{
   324  		VolumeId: "vol-123",
   325  		Pool:     "loop-pool",
   326  		Size:     124,
   327  	})
   328  	stateVolumeAttachments, err := s.State.MachineVolumeAttachments(names.NewMachineTag("1"))
   329  	c.Assert(err, jc.ErrorIsNil)
   330  	c.Assert(stateVolumeAttachments, gc.HasLen, 1)
   331  	volumeAttachmentInfo, err := stateVolumeAttachments[0].Info()
   332  	c.Assert(err, jc.ErrorIsNil)
   333  	c.Assert(volumeAttachmentInfo, gc.Equals, state.VolumeAttachmentInfo{
   334  		DeviceName: "xvdf1",
   335  	})
   336  }
   337  
   338  func (s *provisionerSuite) TestSeries(c *gc.C) {
   339  	// Create a fresh machine with different series.
   340  	foobarMachine, err := s.State.AddMachine("foobar", state.JobHostUnits)
   341  	c.Assert(err, jc.ErrorIsNil)
   342  
   343  	apiMachine, err := s.provisioner.Machine(foobarMachine.Tag().(names.MachineTag))
   344  	c.Assert(err, jc.ErrorIsNil)
   345  	series, err := apiMachine.Series()
   346  	c.Assert(err, jc.ErrorIsNil)
   347  	c.Assert(series, gc.Equals, "foobar")
   348  
   349  	// Now try machine 0.
   350  	apiMachine, err = s.provisioner.Machine(s.machine.Tag().(names.MachineTag))
   351  	c.Assert(err, jc.ErrorIsNil)
   352  	series, err = apiMachine.Series()
   353  	c.Assert(err, jc.ErrorIsNil)
   354  	c.Assert(series, gc.Equals, "quantal")
   355  }
   356  
   357  func (s *provisionerSuite) TestDistributionGroup(c *gc.C) {
   358  	apiMachine, err := s.provisioner.Machine(s.machine.Tag().(names.MachineTag))
   359  	c.Assert(err, jc.ErrorIsNil)
   360  	instances, err := apiMachine.DistributionGroup()
   361  	c.Assert(err, jc.ErrorIsNil)
   362  	c.Assert(instances, gc.DeepEquals, []instance.Id{"i-manager"})
   363  
   364  	machine1, err := s.State.AddMachine("quantal", state.JobHostUnits)
   365  	c.Assert(err, jc.ErrorIsNil)
   366  	apiMachine, err = s.provisioner.Machine(machine1.Tag().(names.MachineTag))
   367  	c.Assert(err, jc.ErrorIsNil)
   368  	wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
   369  
   370  	err = apiMachine.SetInstanceInfo("i-d", "fake", nil, nil, nil, nil)
   371  	c.Assert(err, jc.ErrorIsNil)
   372  	instances, err = apiMachine.DistributionGroup()
   373  	c.Assert(err, jc.ErrorIsNil)
   374  	c.Assert(instances, gc.HasLen, 0) // no units assigned
   375  
   376  	var unitNames []string
   377  	for i := 0; i < 3; i++ {
   378  		unit, err := wordpress.AddUnit()
   379  		c.Assert(err, jc.ErrorIsNil)
   380  		unitNames = append(unitNames, unit.Name())
   381  		err = unit.AssignToMachine(machine1)
   382  		c.Assert(err, jc.ErrorIsNil)
   383  		instances, err := apiMachine.DistributionGroup()
   384  		c.Assert(err, jc.ErrorIsNil)
   385  		c.Assert(instances, gc.DeepEquals, []instance.Id{"i-d"})
   386  	}
   387  }
   388  
   389  func (s *provisionerSuite) TestDistributionGroupMachineNotFound(c *gc.C) {
   390  	stateMachine, err := s.State.AddMachine("quantal", state.JobHostUnits)
   391  	c.Assert(err, jc.ErrorIsNil)
   392  	apiMachine, err := s.provisioner.Machine(stateMachine.Tag().(names.MachineTag))
   393  	c.Assert(err, jc.ErrorIsNil)
   394  	err = apiMachine.EnsureDead()
   395  	c.Assert(err, jc.ErrorIsNil)
   396  	err = apiMachine.Remove()
   397  	c.Assert(err, jc.ErrorIsNil)
   398  	_, err = apiMachine.DistributionGroup()
   399  	c.Assert(err, gc.ErrorMatches, "machine 1 not found")
   400  	c.Assert(err, jc.Satisfies, params.IsCodeNotFound)
   401  }
   402  
   403  func (s *provisionerSuite) TestProvisioningInfo(c *gc.C) {
   404  	// Add a couple of spaces.
   405  	_, err := s.State.AddSpace("space1", "", nil, true)
   406  	c.Assert(err, jc.ErrorIsNil)
   407  	_, err = s.State.AddSpace("space2", "", nil, false)
   408  	c.Assert(err, jc.ErrorIsNil)
   409  	// Add 2 subnets into each space.
   410  	// Each subnet is in a matching zone (e.g "subnet-#" in "zone#").
   411  	testing.AddSubnetsWithTemplate(c, s.State, 4, state.SubnetInfo{
   412  		CIDR:             "10.{{.}}.0.0/16",
   413  		ProviderId:       "subnet-{{.}}",
   414  		AvailabilityZone: "zone{{.}}",
   415  		SpaceName:        "{{if (lt . 2)}}space1{{else}}space2{{end}}",
   416  	})
   417  
   418  	cons := constraints.MustParse("cores=12 mem=8G spaces=^space1,space2")
   419  	template := state.MachineTemplate{
   420  		Series:      "quantal",
   421  		Jobs:        []state.MachineJob{state.JobHostUnits},
   422  		Placement:   "valid",
   423  		Constraints: cons,
   424  	}
   425  	machine, err := s.State.AddOneMachine(template)
   426  	c.Assert(err, jc.ErrorIsNil)
   427  	apiMachine, err := s.provisioner.Machine(machine.Tag().(names.MachineTag))
   428  	c.Assert(err, jc.ErrorIsNil)
   429  	provisioningInfo, err := apiMachine.ProvisioningInfo()
   430  	c.Assert(err, jc.ErrorIsNil)
   431  	c.Assert(provisioningInfo.Series, gc.Equals, template.Series)
   432  	c.Assert(provisioningInfo.Placement, gc.Equals, template.Placement)
   433  	c.Assert(provisioningInfo.Constraints, jc.DeepEquals, template.Constraints)
   434  	c.Assert(provisioningInfo.SubnetsToZones, jc.DeepEquals, map[string][]string{
   435  		"subnet-2": []string{"zone2"},
   436  		"subnet-3": []string{"zone3"},
   437  	})
   438  }
   439  
   440  func (s *provisionerSuite) TestProvisioningInfoMachineNotFound(c *gc.C) {
   441  	stateMachine, err := s.State.AddMachine("quantal", state.JobHostUnits)
   442  	c.Assert(err, jc.ErrorIsNil)
   443  	apiMachine, err := s.provisioner.Machine(stateMachine.Tag().(names.MachineTag))
   444  	c.Assert(err, jc.ErrorIsNil)
   445  	err = apiMachine.EnsureDead()
   446  	c.Assert(err, jc.ErrorIsNil)
   447  	err = apiMachine.Remove()
   448  	c.Assert(err, jc.ErrorIsNil)
   449  	_, err = apiMachine.ProvisioningInfo()
   450  	c.Assert(err, gc.ErrorMatches, "machine 1 not found")
   451  	c.Assert(err, jc.Satisfies, params.IsCodeNotFound)
   452  	// auth tests in apiserver
   453  }
   454  
   455  func (s *provisionerSuite) TestWatchContainers(c *gc.C) {
   456  	apiMachine, err := s.provisioner.Machine(s.machine.Tag().(names.MachineTag))
   457  	c.Assert(err, jc.ErrorIsNil)
   458  
   459  	// Add one LXD container.
   460  	template := state.MachineTemplate{
   461  		Series: "quantal",
   462  		Jobs:   []state.MachineJob{state.JobHostUnits},
   463  	}
   464  	container, err := s.State.AddMachineInsideMachine(template, s.machine.Id(), instance.LXD)
   465  	c.Assert(err, jc.ErrorIsNil)
   466  
   467  	w, err := apiMachine.WatchContainers(instance.LXD)
   468  	c.Assert(err, jc.ErrorIsNil)
   469  	wc := watchertest.NewStringsWatcherC(c, w, s.BackingState.StartSync)
   470  	defer wc.AssertStops()
   471  
   472  	// Initial event.
   473  	wc.AssertChange(container.Id())
   474  
   475  	// Change something other than the containers and make sure it's
   476  	// not detected.
   477  	err = apiMachine.SetStatus(status.Started, "not really", nil)
   478  	c.Assert(err, jc.ErrorIsNil)
   479  	wc.AssertNoChange()
   480  
   481  	// Add a KVM container and make sure it's not detected.
   482  	container, err = s.State.AddMachineInsideMachine(template, s.machine.Id(), instance.KVM)
   483  	c.Assert(err, jc.ErrorIsNil)
   484  	wc.AssertNoChange()
   485  
   486  	// Add another LXD container and make sure it's detected.
   487  	container, err = s.State.AddMachineInsideMachine(template, s.machine.Id(), instance.LXD)
   488  	c.Assert(err, jc.ErrorIsNil)
   489  	wc.AssertChange(container.Id())
   490  }
   491  
   492  func (s *provisionerSuite) TestWatchContainersAcceptsSupportedContainers(c *gc.C) {
   493  	apiMachine, err := s.provisioner.Machine(s.machine.Tag().(names.MachineTag))
   494  	c.Assert(err, jc.ErrorIsNil)
   495  
   496  	for _, ctype := range instance.ContainerTypes {
   497  		w, err := apiMachine.WatchContainers(ctype)
   498  		c.Assert(w, gc.NotNil)
   499  		c.Assert(err, jc.ErrorIsNil)
   500  	}
   501  }
   502  
   503  func (s *provisionerSuite) TestWatchContainersErrors(c *gc.C) {
   504  	apiMachine, err := s.provisioner.Machine(s.machine.Tag().(names.MachineTag))
   505  	c.Assert(err, jc.ErrorIsNil)
   506  
   507  	_, err = apiMachine.WatchContainers(instance.NONE)
   508  	c.Assert(err, gc.ErrorMatches, `unsupported container type "none"`)
   509  
   510  	_, err = apiMachine.WatchContainers("")
   511  	c.Assert(err, gc.ErrorMatches, "container type must be specified")
   512  }
   513  
   514  func (s *provisionerSuite) TestWatchModelMachines(c *gc.C) {
   515  	w, err := s.provisioner.WatchModelMachines()
   516  	c.Assert(err, jc.ErrorIsNil)
   517  	wc := watchertest.NewStringsWatcherC(c, w, s.BackingState.StartSync)
   518  	defer wc.AssertStops()
   519  
   520  	// Initial event.
   521  	wc.AssertChange(s.machine.Id())
   522  
   523  	// Add another 2 machines make sure they are detected.
   524  	otherMachine, err := s.State.AddMachine("quantal", state.JobHostUnits)
   525  	c.Assert(err, jc.ErrorIsNil)
   526  	otherMachine, err = s.State.AddMachine("quantal", state.JobHostUnits)
   527  	c.Assert(err, jc.ErrorIsNil)
   528  	wc.AssertChange("1", "2")
   529  
   530  	// Change the lifecycle of last machine.
   531  	err = otherMachine.EnsureDead()
   532  	c.Assert(err, jc.ErrorIsNil)
   533  	wc.AssertChange("2")
   534  
   535  	// Add a container and make sure it's not detected.
   536  	template := state.MachineTemplate{
   537  		Series: "quantal",
   538  		Jobs:   []state.MachineJob{state.JobHostUnits},
   539  	}
   540  	_, err = s.State.AddMachineInsideMachine(template, s.machine.Id(), instance.LXD)
   541  	c.Assert(err, jc.ErrorIsNil)
   542  	wc.AssertNoChange()
   543  }
   544  
   545  func (s *provisionerSuite) TestStateAddresses(c *gc.C) {
   546  	err := s.machine.SetProviderAddresses(network.NewAddress("0.1.2.3"))
   547  	c.Assert(err, jc.ErrorIsNil)
   548  
   549  	stateAddresses, err := s.State.Addresses()
   550  	c.Assert(err, jc.ErrorIsNil)
   551  
   552  	addresses, err := s.provisioner.StateAddresses()
   553  	c.Assert(err, jc.ErrorIsNil)
   554  	c.Assert(addresses, gc.DeepEquals, stateAddresses)
   555  }
   556  
   557  func (s *provisionerSuite) getManagerConfig(c *gc.C, typ instance.ContainerType) map[string]string {
   558  	args := params.ContainerManagerConfigParams{Type: typ}
   559  	result, err := s.provisioner.ContainerManagerConfig(args)
   560  	c.Assert(err, jc.ErrorIsNil)
   561  	return result.ManagerConfig
   562  }
   563  
   564  func (s *provisionerSuite) TestContainerManagerConfigKVM(c *gc.C) {
   565  	cfg := s.getManagerConfig(c, instance.KVM)
   566  	c.Assert(cfg, jc.DeepEquals, map[string]string{
   567  		container.ConfigModelUUID: coretesting.ModelTag.Id(),
   568  	})
   569  }
   570  
   571  func (s *provisionerSuite) TestContainerManagerConfigPermissive(c *gc.C) {
   572  	// ContainerManagerConfig is permissive of container types, and
   573  	// will just return the basic type-independent configuration.
   574  	cfg := s.getManagerConfig(c, "invalid")
   575  	c.Assert(cfg, jc.DeepEquals, map[string]string{
   576  		container.ConfigModelUUID: coretesting.ModelTag.Id(),
   577  	})
   578  }
   579  
   580  func (s *provisionerSuite) TestContainerConfig(c *gc.C) {
   581  	result, err := s.provisioner.ContainerConfig()
   582  	c.Assert(err, jc.ErrorIsNil)
   583  	c.Assert(result.ProviderType, gc.Equals, "dummy")
   584  	c.Assert(result.AuthorizedKeys, gc.Equals, s.Environ.Config().AuthorizedKeys())
   585  	c.Assert(result.SSLHostnameVerification, jc.IsTrue)
   586  }
   587  
   588  func (s *provisionerSuite) TestSetSupportedContainers(c *gc.C) {
   589  	apiMachine, err := s.provisioner.Machine(s.machine.Tag().(names.MachineTag))
   590  	c.Assert(err, jc.ErrorIsNil)
   591  	err = apiMachine.SetSupportedContainers(instance.LXD, instance.KVM)
   592  	c.Assert(err, jc.ErrorIsNil)
   593  
   594  	err = s.machine.Refresh()
   595  	c.Assert(err, jc.ErrorIsNil)
   596  	containers, ok := s.machine.SupportedContainers()
   597  	c.Assert(ok, jc.IsTrue)
   598  	c.Assert(containers, gc.DeepEquals, []instance.ContainerType{instance.LXD, instance.KVM})
   599  }
   600  
   601  func (s *provisionerSuite) TestSupportsNoContainers(c *gc.C) {
   602  	apiMachine, err := s.provisioner.Machine(s.machine.Tag().(names.MachineTag))
   603  	c.Assert(err, jc.ErrorIsNil)
   604  	err = apiMachine.SupportsNoContainers()
   605  	c.Assert(err, jc.ErrorIsNil)
   606  
   607  	err = s.machine.Refresh()
   608  	c.Assert(err, jc.ErrorIsNil)
   609  	containers, ok := s.machine.SupportedContainers()
   610  	c.Assert(ok, jc.IsTrue)
   611  	c.Assert(containers, gc.DeepEquals, []instance.ContainerType{})
   612  }
   613  
   614  func (s *provisionerSuite) TestFindToolsNoArch(c *gc.C) {
   615  	s.testFindTools(c, false, nil, nil)
   616  }
   617  
   618  func (s *provisionerSuite) TestFindToolsArch(c *gc.C) {
   619  	s.testFindTools(c, true, nil, nil)
   620  }
   621  
   622  func (s *provisionerSuite) TestFindToolsAPIError(c *gc.C) {
   623  	apiError := errors.New("everything's broken")
   624  	s.testFindTools(c, false, apiError, nil)
   625  }
   626  
   627  func (s *provisionerSuite) TestFindToolsLogicError(c *gc.C) {
   628  	logicError := errors.NotFoundf("tools")
   629  	s.testFindTools(c, false, nil, logicError)
   630  }
   631  
   632  func (s *provisionerSuite) testFindTools(c *gc.C, matchArch bool, apiError, logicError error) {
   633  	current := version.Binary{
   634  		Number: jujuversion.Current,
   635  		Arch:   arch.HostArch(),
   636  		Series: series.HostSeries(),
   637  	}
   638  	var toolsList = coretools.List{&coretools.Tools{Version: current}}
   639  	var called bool
   640  	var a string
   641  	if matchArch {
   642  		// if matchArch is true, this will be overwriten with the host's arch, otherwise
   643  		// leave a blank.
   644  		a = arch.HostArch()
   645  	}
   646  
   647  	provisioner.PatchFacadeCall(s, s.provisioner, func(request string, args, response interface{}) error {
   648  		called = true
   649  		c.Assert(request, gc.Equals, "FindTools")
   650  		expected := params.FindToolsParams{
   651  			Number:       jujuversion.Current,
   652  			Series:       series.HostSeries(),
   653  			Arch:         a,
   654  			MinorVersion: -1,
   655  			MajorVersion: -1,
   656  		}
   657  		c.Assert(args, gc.Equals, expected)
   658  		result := response.(*params.FindToolsResult)
   659  		result.List = toolsList
   660  		if logicError != nil {
   661  			result.Error = common.ServerError(logicError)
   662  		}
   663  		return apiError
   664  	})
   665  	apiList, err := s.provisioner.FindTools(jujuversion.Current, series.HostSeries(), a)
   666  	c.Assert(called, jc.IsTrue)
   667  	if apiError != nil {
   668  		c.Assert(err, gc.Equals, apiError)
   669  	} else if logicError != nil {
   670  		c.Assert(err.Error(), gc.Equals, logicError.Error())
   671  	} else {
   672  		c.Assert(err, jc.ErrorIsNil)
   673  		c.Assert(apiList, jc.SameContents, toolsList)
   674  	}
   675  }