github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/worker/provisioner/lxc-broker_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  	"bytes"
     8  	"fmt"
     9  	"io/ioutil"
    10  	"net"
    11  	"path/filepath"
    12  	"runtime"
    13  	"text/template"
    14  	"time"
    15  
    16  	"github.com/juju/errors"
    17  	"github.com/juju/names"
    18  	gitjujutesting "github.com/juju/testing"
    19  	jc "github.com/juju/testing/checkers"
    20  	"github.com/juju/utils/set"
    21  	gc "gopkg.in/check.v1"
    22  
    23  	"github.com/juju/juju/agent"
    24  	"github.com/juju/juju/apiserver/params"
    25  	"github.com/juju/juju/cloudconfig/instancecfg"
    26  	"github.com/juju/juju/constraints"
    27  	"github.com/juju/juju/container"
    28  	"github.com/juju/juju/container/lxc/mock"
    29  	lxctesting "github.com/juju/juju/container/lxc/testing"
    30  	containertesting "github.com/juju/juju/container/testing"
    31  	"github.com/juju/juju/environs"
    32  	"github.com/juju/juju/feature"
    33  	"github.com/juju/juju/instance"
    34  	instancetest "github.com/juju/juju/instance/testing"
    35  	"github.com/juju/juju/juju/arch"
    36  	jujutesting "github.com/juju/juju/juju/testing"
    37  	"github.com/juju/juju/network"
    38  	"github.com/juju/juju/state"
    39  	"github.com/juju/juju/storage"
    40  	"github.com/juju/juju/storage/provider"
    41  	coretesting "github.com/juju/juju/testing"
    42  	coretools "github.com/juju/juju/tools"
    43  	"github.com/juju/juju/version"
    44  	"github.com/juju/juju/worker/provisioner"
    45  )
    46  
    47  type lxcSuite struct {
    48  	lxctesting.TestSuite
    49  	events     chan mock.Event
    50  	eventsDone chan struct{}
    51  }
    52  
    53  type lxcBrokerSuite struct {
    54  	lxcSuite
    55  	broker      environs.InstanceBroker
    56  	agentConfig agent.ConfigSetterWriter
    57  	api         *fakeAPI
    58  }
    59  
    60  var _ = gc.Suite(&lxcBrokerSuite{})
    61  
    62  func (s *lxcSuite) SetUpTest(c *gc.C) {
    63  	s.TestSuite.SetUpTest(c)
    64  	if runtime.GOOS == "windows" {
    65  		c.Skip("Skipping lxc tests on windows")
    66  	}
    67  	s.events = make(chan mock.Event)
    68  	s.eventsDone = make(chan struct{})
    69  	go func() {
    70  		defer close(s.eventsDone)
    71  		for event := range s.events {
    72  			c.Output(3, fmt.Sprintf("lxc event: <%s, %s>", event.Action, event.InstanceId))
    73  		}
    74  	}()
    75  	s.TestSuite.ContainerFactory.AddListener(s.events)
    76  }
    77  
    78  func (s *lxcSuite) TearDownTest(c *gc.C) {
    79  	close(s.events)
    80  	<-s.eventsDone
    81  	s.TestSuite.TearDownTest(c)
    82  }
    83  
    84  func (s *lxcBrokerSuite) SetUpTest(c *gc.C) {
    85  	if runtime.GOOS == "windows" {
    86  		c.Skip("Skipping lxc tests on windows")
    87  	}
    88  	s.lxcSuite.SetUpTest(c)
    89  	var err error
    90  	s.agentConfig, err = agent.NewAgentConfig(
    91  		agent.AgentConfigParams{
    92  			DataDir:           "/not/used/here",
    93  			Tag:               names.NewMachineTag("1"),
    94  			UpgradedToVersion: version.Current.Number,
    95  			Password:          "dummy-secret",
    96  			Nonce:             "nonce",
    97  			APIAddresses:      []string{"10.0.0.1:1234"},
    98  			CACert:            coretesting.CACert,
    99  			Environment:       coretesting.EnvironmentTag,
   100  		})
   101  	c.Assert(err, jc.ErrorIsNil)
   102  	managerConfig := container.ManagerConfig{
   103  		container.ConfigName: "juju",
   104  		"log-dir":            c.MkDir(),
   105  		"use-clone":          "false",
   106  	}
   107  	s.api = NewFakeAPI()
   108  	s.broker, err = provisioner.NewLxcBroker(s.api, s.agentConfig, managerConfig, nil, false, 0)
   109  	c.Assert(err, jc.ErrorIsNil)
   110  }
   111  
   112  func (s *lxcBrokerSuite) instanceConfig(c *gc.C, machineId string) *instancecfg.InstanceConfig {
   113  	machineNonce := "fake-nonce"
   114  	// To isolate the tests from the host's architecture, we override it here.
   115  	s.PatchValue(&version.Current.Arch, arch.AMD64)
   116  	stateInfo := jujutesting.FakeStateInfo(machineId)
   117  	apiInfo := jujutesting.FakeAPIInfo(machineId)
   118  	instanceConfig, err := instancecfg.NewInstanceConfig(machineId, machineNonce, "released", "quantal", true, nil, stateInfo, apiInfo)
   119  	c.Assert(err, jc.ErrorIsNil)
   120  	// Ensure the <rootfs>/etc/network path exists.
   121  	containertesting.EnsureRootFSEtcNetwork(c, "juju-"+names.NewMachineTag(machineId).String())
   122  	return instanceConfig
   123  }
   124  
   125  func (s *lxcBrokerSuite) startInstance(c *gc.C, machineId string, volumes []storage.VolumeParams) instance.Instance {
   126  	instanceConfig := s.instanceConfig(c, machineId)
   127  	cons := constraints.Value{}
   128  	possibleTools := coretools.List{&coretools.Tools{
   129  		Version: version.MustParseBinary("2.3.4-quantal-amd64"),
   130  		URL:     "http://tools.testing.invalid/2.3.4-quantal-amd64.tgz",
   131  	}}
   132  	result, err := s.broker.StartInstance(environs.StartInstanceParams{
   133  		Constraints:    cons,
   134  		Tools:          possibleTools,
   135  		InstanceConfig: instanceConfig,
   136  		Volumes:        volumes,
   137  	})
   138  	c.Assert(err, jc.ErrorIsNil)
   139  	return result.Instance
   140  }
   141  
   142  func (s *lxcBrokerSuite) maintainInstance(c *gc.C, machineId string, volumes []storage.VolumeParams) {
   143  	instanceConfig := s.instanceConfig(c, machineId)
   144  	cons := constraints.Value{}
   145  	possibleTools := coretools.List{&coretools.Tools{
   146  		Version: version.MustParseBinary("2.3.4-quantal-amd64"),
   147  		URL:     "http://tools.testing.invalid/2.3.4-quantal-amd64.tgz",
   148  	}}
   149  	err := s.broker.MaintainInstance(environs.StartInstanceParams{
   150  		Constraints:    cons,
   151  		Tools:          possibleTools,
   152  		InstanceConfig: instanceConfig,
   153  		Volumes:        volumes,
   154  	})
   155  	c.Assert(err, jc.ErrorIsNil)
   156  }
   157  
   158  func (s *lxcBrokerSuite) assertDefaultStorageConfig(c *gc.C, lxc instance.Instance) {
   159  	config := filepath.Join(s.LxcDir, string(lxc.Id()), "config")
   160  	AssertFileContents(c, gc.Not(jc.Contains), config, "lxc.aa_profile = lxc-container-default-with-mounting")
   161  }
   162  
   163  func (s *lxcBrokerSuite) assertDefaultNetworkConfig(c *gc.C, lxc instance.Instance) {
   164  	lxc_conf := filepath.Join(s.ContainerDir, string(lxc.Id()), "lxc.conf")
   165  	expect := []string{
   166  		"lxc.network.type = veth",
   167  		"lxc.network.link = lxcbr0",
   168  	}
   169  	AssertFileContains(c, lxc_conf, expect...)
   170  }
   171  
   172  func (s *lxcBrokerSuite) TestStartInstance(c *gc.C) {
   173  	machineId := "1/lxc/0"
   174  	s.SetFeatureFlags(feature.AddressAllocation)
   175  	lxc := s.startInstance(c, machineId, nil)
   176  	s.api.CheckCalls(c, []gitjujutesting.StubCall{{
   177  		FuncName: "PrepareContainerInterfaceInfo",
   178  		Args:     []interface{}{names.NewMachineTag("1-lxc-0")},
   179  	}, {
   180  		FuncName: "ContainerConfig",
   181  	}})
   182  	c.Assert(lxc.Id(), gc.Equals, instance.Id("juju-machine-1-lxc-0"))
   183  	c.Assert(s.lxcContainerDir(lxc), jc.IsDirectory)
   184  	s.assertInstances(c, lxc)
   185  	s.assertDefaultNetworkConfig(c, lxc)
   186  	s.assertDefaultStorageConfig(c, lxc)
   187  }
   188  
   189  func (s *lxcBrokerSuite) TestStartInstanceAddressAllocationDisabled(c *gc.C) {
   190  	machineId := "1/lxc/0"
   191  	lxc := s.startInstance(c, machineId, nil)
   192  	s.api.CheckCalls(c, []gitjujutesting.StubCall{{
   193  		FuncName: "ContainerConfig",
   194  	}})
   195  	c.Assert(lxc.Id(), gc.Equals, instance.Id("juju-machine-1-lxc-0"))
   196  	c.Assert(s.lxcContainerDir(lxc), jc.IsDirectory)
   197  	s.assertInstances(c, lxc)
   198  	s.assertDefaultNetworkConfig(c, lxc)
   199  	s.assertDefaultStorageConfig(c, lxc)
   200  }
   201  
   202  func (s *lxcBrokerSuite) TestMaintainInstance(c *gc.C) {
   203  	machineId := "1/lxc/0"
   204  	s.SetFeatureFlags(feature.AddressAllocation)
   205  	lxc := s.startInstance(c, machineId, nil)
   206  	s.api.ResetCalls()
   207  
   208  	s.maintainInstance(c, machineId, nil)
   209  	s.api.CheckCalls(c, []gitjujutesting.StubCall{{
   210  		FuncName: "GetContainerInterfaceInfo",
   211  		Args:     []interface{}{names.NewMachineTag("1-lxc-0")},
   212  	}})
   213  	c.Assert(lxc.Id(), gc.Equals, instance.Id("juju-machine-1-lxc-0"))
   214  	c.Assert(s.lxcContainerDir(lxc), jc.IsDirectory)
   215  	s.assertInstances(c, lxc)
   216  	s.assertDefaultNetworkConfig(c, lxc)
   217  	s.assertDefaultStorageConfig(c, lxc)
   218  }
   219  
   220  func (s *lxcBrokerSuite) TestMaintainInstanceAddressAllocationDisabled(c *gc.C) {
   221  	machineId := "1/lxc/0"
   222  	lxc := s.startInstance(c, machineId, nil)
   223  	s.api.ResetCalls()
   224  
   225  	s.maintainInstance(c, machineId, nil)
   226  	s.api.CheckCalls(c, []gitjujutesting.StubCall{})
   227  	c.Assert(lxc.Id(), gc.Equals, instance.Id("juju-machine-1-lxc-0"))
   228  	c.Assert(s.lxcContainerDir(lxc), jc.IsDirectory)
   229  	s.assertInstances(c, lxc)
   230  	s.assertDefaultNetworkConfig(c, lxc)
   231  	s.assertDefaultStorageConfig(c, lxc)
   232  }
   233  
   234  func (s *lxcBrokerSuite) TestStartInstanceWithStorage(c *gc.C) {
   235  	s.api.fakeContainerConfig.AllowLXCLoopMounts = true
   236  	s.SetFeatureFlags(feature.AddressAllocation)
   237  	machineId := "1/lxc/0"
   238  	lxc := s.startInstance(c, machineId, []storage.VolumeParams{{Provider: provider.LoopProviderType}})
   239  	s.api.CheckCalls(c, []gitjujutesting.StubCall{{
   240  		FuncName: "PrepareContainerInterfaceInfo",
   241  		Args:     []interface{}{names.NewMachineTag("1-lxc-0")},
   242  	}, {
   243  		FuncName: "ContainerConfig",
   244  	}})
   245  	c.Assert(lxc.Id(), gc.Equals, instance.Id("juju-machine-1-lxc-0"))
   246  	c.Assert(s.lxcContainerDir(lxc), jc.IsDirectory)
   247  	s.assertInstances(c, lxc)
   248  	// Check storage config.
   249  	config := filepath.Join(s.LxcDir, string(lxc.Id()), "config")
   250  	AssertFileContents(c, jc.Contains, config, "lxc.aa_profile = lxc-container-default-with-mounting")
   251  }
   252  
   253  func (s *lxcBrokerSuite) TestStartInstanceLoopMountsDisallowed(c *gc.C) {
   254  	s.api.fakeContainerConfig.AllowLXCLoopMounts = false
   255  	s.SetFeatureFlags(feature.AddressAllocation)
   256  	machineId := "1/lxc/0"
   257  	lxc := s.startInstance(c, machineId, []storage.VolumeParams{{Provider: provider.LoopProviderType}})
   258  	s.api.CheckCalls(c, []gitjujutesting.StubCall{{
   259  		FuncName: "PrepareContainerInterfaceInfo",
   260  		Args:     []interface{}{names.NewMachineTag("1-lxc-0")},
   261  	}, {
   262  		FuncName: "ContainerConfig",
   263  	}})
   264  	c.Assert(lxc.Id(), gc.Equals, instance.Id("juju-machine-1-lxc-0"))
   265  	c.Assert(s.lxcContainerDir(lxc), jc.IsDirectory)
   266  	s.assertInstances(c, lxc)
   267  	s.assertDefaultStorageConfig(c, lxc)
   268  }
   269  
   270  func (s *lxcBrokerSuite) TestStartInstanceHostArch(c *gc.C) {
   271  	instanceConfig := s.instanceConfig(c, "1/lxc/0")
   272  
   273  	// Patch the host's arch, so the LXC broker will filter tools. We don't use PatchValue
   274  	// because instanceConfig already has, so it will restore version.Current.Arch during TearDownTest
   275  	version.Current.Arch = arch.PPC64EL
   276  	possibleTools := coretools.List{&coretools.Tools{
   277  		Version: version.MustParseBinary("2.3.4-quantal-amd64"),
   278  		URL:     "http://tools.testing.invalid/2.3.4-quantal-amd64.tgz",
   279  	}, {
   280  		Version: version.MustParseBinary("2.3.4-quantal-ppc64el"),
   281  		URL:     "http://tools.testing.invalid/2.3.4-quantal-ppc64el.tgz",
   282  	}}
   283  	_, err := s.broker.StartInstance(environs.StartInstanceParams{
   284  		Constraints:    constraints.Value{},
   285  		Tools:          possibleTools,
   286  		InstanceConfig: instanceConfig,
   287  	})
   288  	c.Assert(err, jc.ErrorIsNil)
   289  	c.Assert(instanceConfig.Tools.Version.Arch, gc.Equals, arch.PPC64EL)
   290  }
   291  
   292  func (s *lxcBrokerSuite) TestStartInstanceToolsArchNotFound(c *gc.C) {
   293  	instanceConfig := s.instanceConfig(c, "1/lxc/0")
   294  
   295  	// Patch the host's arch, so the LXC broker will filter tools. We don't use PatchValue
   296  	// because instanceConfig already has, so it will restore version.Current.Arch during TearDownTest
   297  	version.Current.Arch = arch.PPC64EL
   298  	possibleTools := coretools.List{&coretools.Tools{
   299  		Version: version.MustParseBinary("2.3.4-quantal-amd64"),
   300  		URL:     "http://tools.testing.invalid/2.3.4-quantal-amd64.tgz",
   301  	}}
   302  	_, err := s.broker.StartInstance(environs.StartInstanceParams{
   303  		Constraints:    constraints.Value{},
   304  		Tools:          possibleTools,
   305  		InstanceConfig: instanceConfig,
   306  	})
   307  	c.Assert(err, gc.ErrorMatches, "need tools for arch ppc64el, only found \\[amd64\\]")
   308  }
   309  
   310  func (s *lxcBrokerSuite) TestStartInstanceWithBridgeEnviron(c *gc.C) {
   311  	s.agentConfig.SetValue(agent.LxcBridge, "br0")
   312  	machineId := "1/lxc/0"
   313  	lxc := s.startInstance(c, machineId, nil)
   314  	s.api.CheckCalls(c, []gitjujutesting.StubCall{{
   315  		FuncName: "ContainerConfig",
   316  	}})
   317  	c.Assert(lxc.Id(), gc.Equals, instance.Id("juju-machine-1-lxc-0"))
   318  	c.Assert(s.lxcContainerDir(lxc), jc.IsDirectory)
   319  	s.assertInstances(c, lxc)
   320  	// Uses default network config
   321  	lxc_conf := filepath.Join(s.ContainerDir, string(lxc.Id()), "lxc.conf")
   322  	expect := []string{
   323  		"lxc.network.type = veth",
   324  		"lxc.network.link = br0",
   325  	}
   326  	AssertFileContains(c, lxc_conf, expect...)
   327  }
   328  
   329  func (s *lxcBrokerSuite) TestStopInstance(c *gc.C) {
   330  	lxc0 := s.startInstance(c, "1/lxc/0", nil)
   331  	lxc1 := s.startInstance(c, "1/lxc/1", nil)
   332  	lxc2 := s.startInstance(c, "1/lxc/2", nil)
   333  
   334  	s.assertInstances(c, lxc0, lxc1, lxc2)
   335  	err := s.broker.StopInstances(lxc0.Id())
   336  	c.Assert(err, jc.ErrorIsNil)
   337  	s.assertInstances(c, lxc1, lxc2)
   338  	c.Assert(s.lxcContainerDir(lxc0), jc.DoesNotExist)
   339  	c.Assert(s.lxcRemovedContainerDir(lxc0), jc.IsDirectory)
   340  
   341  	err = s.broker.StopInstances(lxc1.Id(), lxc2.Id())
   342  	c.Assert(err, jc.ErrorIsNil)
   343  	s.assertInstances(c)
   344  }
   345  
   346  func (s *lxcBrokerSuite) TestAllInstances(c *gc.C) {
   347  	lxc0 := s.startInstance(c, "1/lxc/0", nil)
   348  	lxc1 := s.startInstance(c, "1/lxc/1", nil)
   349  	s.assertInstances(c, lxc0, lxc1)
   350  
   351  	err := s.broker.StopInstances(lxc1.Id())
   352  	c.Assert(err, jc.ErrorIsNil)
   353  	lxc2 := s.startInstance(c, "1/lxc/2", nil)
   354  	s.assertInstances(c, lxc0, lxc2)
   355  }
   356  
   357  func (s *lxcBrokerSuite) assertInstances(c *gc.C, inst ...instance.Instance) {
   358  	results, err := s.broker.AllInstances()
   359  	c.Assert(err, jc.ErrorIsNil)
   360  	instancetest.MatchInstances(c, results, inst...)
   361  }
   362  
   363  func (s *lxcBrokerSuite) lxcContainerDir(inst instance.Instance) string {
   364  	return filepath.Join(s.ContainerDir, string(inst.Id()))
   365  }
   366  
   367  func (s *lxcBrokerSuite) lxcRemovedContainerDir(inst instance.Instance) string {
   368  	return filepath.Join(s.RemovedDir, string(inst.Id()))
   369  }
   370  
   371  func (s *lxcBrokerSuite) TestLocalDNSServers(c *gc.C) {
   372  	fakeConf := filepath.Join(c.MkDir(), "resolv.conf")
   373  	s.PatchValue(provisioner.ResolvConf, fakeConf)
   374  
   375  	// If config is missing, that's OK.
   376  	dnses, dnsSearch, err := provisioner.LocalDNSServers()
   377  	c.Assert(err, jc.ErrorIsNil)
   378  	c.Assert(dnses, gc.HasLen, 0)
   379  	c.Assert(dnsSearch, gc.Equals, "")
   380  
   381  	// Enter some data in fakeConf.
   382  	data := `
   383   anything else is ignored
   384    # comments are ignored
   385    nameserver  0.1.2.3  # that's parsed
   386  search  foo.baz # comment ignored
   387  # nameserver 42.42.42.42 - ignored as well
   388  nameserver 8.8.8.8
   389  nameserver example.com # comment after is ok
   390  `
   391  	err = ioutil.WriteFile(fakeConf, []byte(data), 0644)
   392  	c.Assert(err, jc.ErrorIsNil)
   393  
   394  	dnses, dnsSearch, err = provisioner.LocalDNSServers()
   395  	c.Assert(err, jc.ErrorIsNil)
   396  	c.Assert(dnses, jc.DeepEquals, network.NewAddresses(
   397  		"0.1.2.3", "8.8.8.8", "example.com",
   398  	))
   399  	c.Assert(dnsSearch, gc.Equals, "foo.baz")
   400  }
   401  
   402  func (s *lxcBrokerSuite) TestMustParseTemplate(c *gc.C) {
   403  	f := func() { provisioner.MustParseTemplate("", "{{invalid}") }
   404  	c.Assert(f, gc.PanicMatches, `template: :1: function "invalid" not defined`)
   405  
   406  	tmpl := provisioner.MustParseTemplate("name", "X={{.X}}")
   407  	c.Assert(tmpl, gc.NotNil)
   408  	c.Assert(tmpl.Name(), gc.Equals, "name")
   409  
   410  	var buf bytes.Buffer
   411  	err := tmpl.Execute(&buf, struct{ X string }{"42"})
   412  	c.Assert(err, jc.ErrorIsNil)
   413  	c.Assert(buf.String(), gc.Equals, "X=42")
   414  }
   415  
   416  func (s *lxcBrokerSuite) TestRunTemplateCommand(c *gc.C) {
   417  	for i, test := range []struct {
   418  		source        string
   419  		exitNonZeroOK bool
   420  		data          interface{}
   421  		exitCode      int
   422  		expectErr     string
   423  	}{{
   424  		source:        "echo {{.Name}}",
   425  		exitNonZeroOK: false,
   426  		data:          struct{ Name string }{"foo"},
   427  		exitCode:      0,
   428  	}, {
   429  		source:        "exit {{.Code}}",
   430  		exitNonZeroOK: false,
   431  		data:          struct{ Code int }{123},
   432  		exitCode:      123,
   433  		expectErr:     `command "exit 123" failed with exit code 123`,
   434  	}, {
   435  		source:        "exit {{.Code}}",
   436  		exitNonZeroOK: true,
   437  		data:          struct{ Code int }{56},
   438  		exitCode:      56,
   439  	}, {
   440  		source:        "exit 42",
   441  		exitNonZeroOK: true,
   442  		exitCode:      42,
   443  	}, {
   444  		source:        "some-invalid-command",
   445  		exitNonZeroOK: false,
   446  		exitCode:      127, // returned by bash.
   447  		expectErr:     `command "some-invalid-command" failed with exit code 127`,
   448  	}} {
   449  		c.Logf("test %d: %q -> %d", i, test.source, test.exitCode)
   450  		t, err := template.New(fmt.Sprintf("test %d", i)).Parse(test.source)
   451  		if !c.Check(err, jc.ErrorIsNil, gc.Commentf("parsing %q", test.source)) {
   452  			continue
   453  		}
   454  		exitCode, err := provisioner.RunTemplateCommand(t, test.exitNonZeroOK, test.data)
   455  		if test.expectErr != "" {
   456  			c.Check(err, gc.ErrorMatches, test.expectErr)
   457  		} else {
   458  			c.Check(err, jc.ErrorIsNil)
   459  		}
   460  		c.Check(exitCode, gc.Equals, test.exitCode)
   461  	}
   462  }
   463  
   464  func (s *lxcBrokerSuite) TestSetupRoutesAndIPTablesInvalidArgs(c *gc.C) {
   465  	// Isolate the test from the host machine.
   466  	gitjujutesting.PatchExecutableThrowError(c, s, "iptables", 42)
   467  	gitjujutesting.PatchExecutableThrowError(c, s, "ip", 123)
   468  
   469  	// Check that all the arguments are verified to be non-empty.
   470  	expectStartupErr := "primaryNIC, primaryAddr, bridgeName, and ifaceInfo must be all set"
   471  	emptyIfaceInfo := []network.InterfaceInfo{}
   472  	for i, test := range []struct {
   473  		about       string
   474  		primaryNIC  string
   475  		primaryAddr network.Address
   476  		bridgeName  string
   477  		ifaceInfo   []network.InterfaceInfo
   478  		expectErr   string
   479  	}{{
   480  		about:       "all empty",
   481  		primaryNIC:  "",
   482  		primaryAddr: network.Address{},
   483  		bridgeName:  "",
   484  		ifaceInfo:   nil,
   485  		expectErr:   expectStartupErr,
   486  	}, {
   487  		about:       "all but primaryNIC empty",
   488  		primaryNIC:  "nic",
   489  		primaryAddr: network.Address{},
   490  		bridgeName:  "",
   491  		ifaceInfo:   nil,
   492  		expectErr:   expectStartupErr,
   493  	}, {
   494  		about:       "all but primaryAddr empty",
   495  		primaryNIC:  "",
   496  		primaryAddr: network.NewAddress("0.1.2.1"),
   497  		bridgeName:  "",
   498  		ifaceInfo:   nil,
   499  		expectErr:   expectStartupErr,
   500  	}, {
   501  		about:       "all but bridgeName empty",
   502  		primaryNIC:  "",
   503  		primaryAddr: network.Address{},
   504  		bridgeName:  "bridge",
   505  		ifaceInfo:   nil,
   506  		expectErr:   expectStartupErr,
   507  	}, {
   508  		about:       "all but primaryNIC and bridgeName empty",
   509  		primaryNIC:  "nic",
   510  		primaryAddr: network.Address{},
   511  		bridgeName:  "bridge",
   512  		ifaceInfo:   nil,
   513  		expectErr:   expectStartupErr,
   514  	}, {
   515  		about:       "all but primaryNIC and primaryAddr empty",
   516  		primaryNIC:  "nic",
   517  		primaryAddr: network.NewAddress("0.1.2.1"),
   518  		bridgeName:  "",
   519  		ifaceInfo:   nil,
   520  		expectErr:   expectStartupErr,
   521  	}, {
   522  		about:       "all but primaryAddr and bridgeName empty",
   523  		primaryNIC:  "",
   524  		primaryAddr: network.NewAddress("0.1.2.1"),
   525  		bridgeName:  "bridge",
   526  		ifaceInfo:   nil,
   527  		expectErr:   expectStartupErr,
   528  	}, {
   529  		about:       "all set except ifaceInfo",
   530  		primaryNIC:  "nic",
   531  		primaryAddr: network.NewAddress("0.1.2.1"),
   532  		bridgeName:  "bridge",
   533  		ifaceInfo:   nil,
   534  		expectErr:   expectStartupErr,
   535  	}, {
   536  		about:       "all empty (ifaceInfo set but empty)",
   537  		primaryNIC:  "",
   538  		primaryAddr: network.Address{},
   539  		bridgeName:  "",
   540  		ifaceInfo:   emptyIfaceInfo,
   541  		expectErr:   expectStartupErr,
   542  	}, {
   543  		about:       "all but primaryNIC empty (ifaceInfo set but empty)",
   544  		primaryNIC:  "nic",
   545  		primaryAddr: network.Address{},
   546  		bridgeName:  "",
   547  		ifaceInfo:   emptyIfaceInfo,
   548  		expectErr:   expectStartupErr,
   549  	}, {
   550  		about:       "all but primaryAddr empty (ifaceInfo set but empty)",
   551  		primaryNIC:  "",
   552  		primaryAddr: network.NewAddress("0.1.2.1"),
   553  		bridgeName:  "",
   554  		ifaceInfo:   emptyIfaceInfo,
   555  		expectErr:   expectStartupErr,
   556  	}, {
   557  		about:       "all but bridgeName empty (ifaceInfo set but empty)",
   558  		primaryNIC:  "",
   559  		primaryAddr: network.Address{},
   560  		bridgeName:  "bridge",
   561  		ifaceInfo:   emptyIfaceInfo,
   562  		expectErr:   expectStartupErr,
   563  	}, {
   564  		about:       "just primaryAddr is empty and ifaceInfo set but empty",
   565  		primaryNIC:  "nic",
   566  		primaryAddr: network.Address{},
   567  		bridgeName:  "bridge",
   568  		ifaceInfo:   emptyIfaceInfo,
   569  		expectErr:   expectStartupErr,
   570  	}, {
   571  		about:       "just bridgeName is empty and ifaceInfo set but empty",
   572  		primaryNIC:  "nic",
   573  		primaryAddr: network.NewAddress("0.1.2.1"),
   574  		bridgeName:  "",
   575  		ifaceInfo:   emptyIfaceInfo,
   576  		expectErr:   expectStartupErr,
   577  	}, {
   578  		about:       "just primaryNIC is empty and ifaceInfo set but empty",
   579  		primaryNIC:  "",
   580  		primaryAddr: network.NewAddress("0.1.2.1"),
   581  		bridgeName:  "bridge",
   582  		ifaceInfo:   emptyIfaceInfo,
   583  		expectErr:   expectStartupErr,
   584  	}, {
   585  		about:       "all set except ifaceInfo, which is set but empty",
   586  		primaryNIC:  "nic",
   587  		primaryAddr: network.NewAddress("0.1.2.1"),
   588  		bridgeName:  "bridge",
   589  		ifaceInfo:   emptyIfaceInfo,
   590  		expectErr:   expectStartupErr,
   591  	}, {
   592  		about:       "all set, but ifaceInfo has empty Address",
   593  		primaryNIC:  "nic",
   594  		primaryAddr: network.NewAddress("0.1.2.1"),
   595  		bridgeName:  "bridge",
   596  		// No Address set.
   597  		ifaceInfo: []network.InterfaceInfo{{DeviceIndex: 0}},
   598  		expectErr: `container IP "" must be set`,
   599  	}} {
   600  		c.Logf("test %d: %s", i, test.about)
   601  		err := provisioner.SetupRoutesAndIPTables(
   602  			test.primaryNIC,
   603  			test.primaryAddr,
   604  			test.bridgeName,
   605  			test.ifaceInfo,
   606  			false, // TODO(dimitern): Untested.
   607  		)
   608  		c.Check(err, gc.ErrorMatches, test.expectErr)
   609  	}
   610  }
   611  
   612  func (s *lxcBrokerSuite) TestSetupRoutesAndIPTablesIPTablesCheckError(c *gc.C) {
   613  	// Isolate the test from the host machine.
   614  	gitjujutesting.PatchExecutableThrowError(c, s, "iptables", 42)
   615  	gitjujutesting.PatchExecutableThrowError(c, s, "ip", 123)
   616  
   617  	ifaceInfo := []network.InterfaceInfo{{
   618  		Address: network.NewAddress("0.1.2.3"),
   619  	}}
   620  
   621  	addr := network.NewAddress("0.1.2.1")
   622  	err := provisioner.SetupRoutesAndIPTables("nic", addr, "bridge", ifaceInfo, false)
   623  	c.Assert(err, gc.ErrorMatches, "iptables failed with unexpected exit code 42")
   624  }
   625  
   626  func (s *lxcBrokerSuite) TestSetupRoutesAndIPTablesIPTablesAddError(c *gc.C) {
   627  	// Isolate the test from the host machine. Patch iptables with a
   628  	// script which returns code=1 for the check but fails when adding
   629  	// the rule.
   630  	script := `if [[ "$3" == "-C" ]]; then exit 1; else exit 42; fi`
   631  	gitjujutesting.PatchExecutable(c, s, "iptables", script)
   632  	gitjujutesting.PatchExecutableThrowError(c, s, "ip", 123)
   633  
   634  	fakeptablesRules := map[string]provisioner.IptablesRule{
   635  		"IPTablesSNAT": {
   636  			"nat",
   637  			"POSTROUTING",
   638  			"{{.HostIF}} {{.HostIP}}",
   639  		},
   640  	}
   641  	s.PatchValue(provisioner.IptablesRules, fakeptablesRules)
   642  
   643  	ifaceInfo := []network.InterfaceInfo{{
   644  		Address: network.NewAddress("0.1.2.3"),
   645  	}}
   646  
   647  	addr := network.NewAddress("0.1.2.1")
   648  	err := provisioner.SetupRoutesAndIPTables("nic", addr, "bridge", ifaceInfo, false)
   649  	c.Assert(err, gc.ErrorMatches, `command "iptables -t nat -I .*" failed with exit code 42`)
   650  }
   651  
   652  func (s *lxcBrokerSuite) TestSetupRoutesAndIPTablesIPRouteError(c *gc.C) {
   653  	// Isolate the test from the host machine.
   654  	// Returning code=0 from iptables means we won't add a rule.
   655  	gitjujutesting.PatchExecutableThrowError(c, s, "iptables", 0)
   656  	gitjujutesting.PatchExecutableThrowError(c, s, "ip", 123)
   657  
   658  	ifaceInfo := []network.InterfaceInfo{{
   659  		Address: network.NewAddress("0.1.2.3"),
   660  	}}
   661  
   662  	addr := network.NewAddress("0.1.2.1")
   663  	err := provisioner.SetupRoutesAndIPTables("nic", addr, "bridge", ifaceInfo, false)
   664  	c.Assert(err, gc.ErrorMatches,
   665  		`command "ip route add 0.1.2.3 dev bridge" failed with exit code 123`,
   666  	)
   667  }
   668  
   669  func (s *lxcBrokerSuite) TestSetupRoutesAndIPTablesAddsRuleIfMissing(c *gc.C) {
   670  	// Isolate the test from the host machine. Because PatchExecutable
   671  	// does not allow us to assert on subsequent executions of the
   672  	// same binary, we need to replace the iptables commands with
   673  	// separate ones. The check returns code=1 to trigger calling
   674  	// add.
   675  	fakeptablesRules := map[string]provisioner.IptablesRule{
   676  		"IPTablesSNAT": {
   677  			"nat",
   678  			"POSTROUTING",
   679  			"{{.HostIF}} {{.HostIP}}",
   680  		},
   681  	}
   682  	s.PatchValue(provisioner.IptablesRules, fakeptablesRules)
   683  
   684  	gitjujutesting.PatchExecutableAsEchoArgs(c, s, "iptables", 1, 0)
   685  	gitjujutesting.PatchExecutableAsEchoArgs(c, s, "ip")
   686  
   687  	ifaceInfo := []network.InterfaceInfo{{
   688  		Address: network.NewAddress("0.1.2.3"),
   689  	}}
   690  
   691  	addr := network.NewAddress("0.1.2.1")
   692  	err := provisioner.SetupRoutesAndIPTables("nic", addr, "bridge", ifaceInfo, false)
   693  	c.Assert(err, jc.ErrorIsNil)
   694  
   695  	// Now verify the expected commands - since check returns 1, add
   696  	// will be called before ip route add.
   697  
   698  	gitjujutesting.AssertEchoArgs(c, "iptables", "-t", "nat", "-C", "POSTROUTING", "nic", "0.1.2.1")
   699  	gitjujutesting.AssertEchoArgs(c, "iptables", "-t", "nat", "-I", "POSTROUTING", "1", "nic", "0.1.2.1")
   700  	gitjujutesting.AssertEchoArgs(c, "ip", "route", "add", "0.1.2.3", "dev", "bridge")
   701  }
   702  
   703  func (s *lxcBrokerSuite) TestDiscoverPrimaryNICNetInterfacesError(c *gc.C) {
   704  	s.PatchValue(provisioner.NetInterfaces, func() ([]net.Interface, error) {
   705  		return nil, errors.New("boom!")
   706  	})
   707  
   708  	nic, addr, err := provisioner.DiscoverPrimaryNIC()
   709  	c.Assert(err, gc.ErrorMatches, "cannot get network interfaces: boom!")
   710  	c.Assert(nic, gc.Equals, "")
   711  	c.Assert(addr, jc.DeepEquals, network.Address{})
   712  }
   713  
   714  func (s *lxcBrokerSuite) TestDiscoverPrimaryNICInterfaceAddrsError(c *gc.C) {
   715  	s.PatchValue(provisioner.NetInterfaces, func() ([]net.Interface, error) {
   716  		return []net.Interface{{
   717  			Index: 0,
   718  			Name:  "fake",
   719  			Flags: net.FlagUp,
   720  		}}, nil
   721  	})
   722  	s.PatchValue(provisioner.InterfaceAddrs, func(i *net.Interface) ([]net.Addr, error) {
   723  		return nil, errors.New("boom!")
   724  	})
   725  
   726  	nic, addr, err := provisioner.DiscoverPrimaryNIC()
   727  	c.Assert(err, gc.ErrorMatches, `cannot get "fake" addresses: boom!`)
   728  	c.Assert(nic, gc.Equals, "")
   729  	c.Assert(addr, jc.DeepEquals, network.Address{})
   730  }
   731  
   732  func (s *lxcBrokerSuite) TestDiscoverPrimaryNICInvalidAddr(c *gc.C) {
   733  	s.PatchValue(provisioner.NetInterfaces, func() ([]net.Interface, error) {
   734  		return []net.Interface{{
   735  			Index: 0,
   736  			Name:  "fake",
   737  			Flags: net.FlagUp,
   738  		}}, nil
   739  	})
   740  	s.PatchValue(provisioner.InterfaceAddrs, func(i *net.Interface) ([]net.Addr, error) {
   741  		return []net.Addr{&fakeAddr{}}, nil
   742  	})
   743  
   744  	nic, addr, err := provisioner.DiscoverPrimaryNIC()
   745  	c.Assert(err, gc.ErrorMatches, `cannot parse address "fakeAddr": invalid CIDR address: fakeAddr`)
   746  	c.Assert(nic, gc.Equals, "")
   747  	c.Assert(addr, jc.DeepEquals, network.Address{})
   748  }
   749  
   750  func (s *lxcBrokerSuite) TestDiscoverPrimaryNICInterfaceNotFound(c *gc.C) {
   751  	s.PatchValue(provisioner.NetInterfaces, func() ([]net.Interface, error) {
   752  		return nil, nil
   753  	})
   754  
   755  	nic, addr, err := provisioner.DiscoverPrimaryNIC()
   756  	c.Assert(err, gc.ErrorMatches, "cannot detect the primary network interface")
   757  	c.Assert(nic, gc.Equals, "")
   758  	c.Assert(addr, jc.DeepEquals, network.Address{})
   759  }
   760  
   761  type fakeAddr struct{ value string }
   762  
   763  func (f *fakeAddr) Network() string { return "net" }
   764  func (f *fakeAddr) String() string {
   765  	if f.value != "" {
   766  		return f.value
   767  	}
   768  	return "fakeAddr"
   769  }
   770  
   771  var _ net.Addr = (*fakeAddr)(nil)
   772  
   773  func (s *lxcBrokerSuite) TestDiscoverPrimaryNICSuccess(c *gc.C) {
   774  	s.PatchValue(provisioner.NetInterfaces, func() ([]net.Interface, error) {
   775  		return []net.Interface{{
   776  			Index: 0,
   777  			Name:  "lo",
   778  			Flags: net.FlagUp | net.FlagLoopback, // up but loopback - ignored.
   779  		}, {
   780  			Index: 1,
   781  			Name:  "if0",
   782  			Flags: net.FlagPointToPoint, // not up - ignored.
   783  		}, {
   784  			Index: 2,
   785  			Name:  "if1",
   786  			Flags: net.FlagUp, // up but no addresses - ignored.
   787  		}, {
   788  			Index: 3,
   789  			Name:  "if2",
   790  			Flags: net.FlagUp, // up and has addresses - returned.
   791  		}}, nil
   792  	})
   793  	s.PatchValue(provisioner.InterfaceAddrs, func(i *net.Interface) ([]net.Addr, error) {
   794  		// We should be called only for the last two NICs. The first
   795  		// one (if1) won't have addresses, only the last one (if2).
   796  		c.Assert(i, gc.NotNil)
   797  		c.Assert(i.Name, gc.Matches, "if[12]")
   798  		if i.Name == "if2" {
   799  			return []net.Addr{&fakeAddr{"0.1.2.3/24"}}, nil
   800  		}
   801  		// For if1 we return no addresses.
   802  		return nil, nil
   803  	})
   804  
   805  	nic, addr, err := provisioner.DiscoverPrimaryNIC()
   806  	c.Assert(err, jc.ErrorIsNil)
   807  	c.Assert(nic, gc.Equals, "if2")
   808  	c.Assert(addr, jc.DeepEquals, network.NewAddress("0.1.2.3"))
   809  }
   810  
   811  func (s *lxcBrokerSuite) TestConfigureContainerNetwork(c *gc.C) {
   812  	// All the pieces used by this func are separately tested, we just
   813  	// test the integration between them.
   814  	s.PatchValue(provisioner.NetInterfaces, func() ([]net.Interface, error) {
   815  		return []net.Interface{{
   816  			Index: 0,
   817  			Name:  "fake0",
   818  			Flags: net.FlagUp,
   819  		}}, nil
   820  	})
   821  	s.PatchValue(provisioner.InterfaceAddrs, func(i *net.Interface) ([]net.Addr, error) {
   822  		return []net.Addr{&fakeAddr{"0.1.2.1/24"}}, nil
   823  	})
   824  	fakeResolvConf := filepath.Join(c.MkDir(), "resolv.conf")
   825  	err := ioutil.WriteFile(fakeResolvConf, []byte("nameserver ns1.dummy\n"), 0644)
   826  	c.Assert(err, jc.ErrorIsNil)
   827  	s.PatchValue(provisioner.ResolvConf, fakeResolvConf)
   828  
   829  	// When ifaceInfo is not empty it shouldn't do anything and both
   830  	// the error and the result are nil.
   831  	ifaceInfo := []network.InterfaceInfo{{DeviceIndex: 0}}
   832  	// First call as if we are configuring the container for the first time
   833  	result, err := provisioner.ConfigureContainerNetwork("42", "bridge", s.api, ifaceInfo, true, false)
   834  	c.Assert(err, jc.ErrorIsNil)
   835  	c.Assert(result, gc.IsNil)
   836  	s.api.CheckCalls(c, []gitjujutesting.StubCall{})
   837  
   838  	// Next call as if the container has already been configured.
   839  	s.api.ResetCalls()
   840  	result, err = provisioner.ConfigureContainerNetwork("42", "bridge", s.api, ifaceInfo, false, false)
   841  	c.Assert(err, jc.ErrorIsNil)
   842  	c.Assert(result, gc.IsNil)
   843  	s.api.CheckCalls(c, []gitjujutesting.StubCall{})
   844  
   845  	// Call as if the container already has a network configuration, but doesn't.
   846  	s.api.ResetCalls()
   847  	s.api.SetErrors(errors.NotProvisionedf("machine-42 has no network provisioning info"))
   848  	ifaceInfo = []network.InterfaceInfo{}
   849  	result, err = provisioner.ConfigureContainerNetwork("42", "bridge", s.api, ifaceInfo, false, false)
   850  	c.Assert(err, gc.ErrorMatches, "machine-42 has no network provisioning info not provisioned")
   851  	c.Assert(result, jc.DeepEquals, []network.InterfaceInfo{})
   852  	s.api.CheckCalls(c, []gitjujutesting.StubCall{{
   853  		FuncName: "GetContainerInterfaceInfo",
   854  		Args:     []interface{}{names.NewMachineTag("42")},
   855  	}})
   856  
   857  	// When it's not empty, result should be populated as expected.
   858  	s.api.ResetCalls()
   859  	result, err = provisioner.ConfigureContainerNetwork("42", "bridge", s.api, ifaceInfo, false, false)
   860  	c.Assert(err, jc.ErrorIsNil)
   861  	c.Assert(result, jc.DeepEquals, []network.InterfaceInfo{{
   862  		DeviceIndex:    0,
   863  		CIDR:           "0.1.2.0/24",
   864  		ConfigType:     network.ConfigStatic,
   865  		InterfaceName:  "eth0", // generated from the device index.
   866  		MACAddress:     provisioner.MACAddressTemplate,
   867  		DNSServers:     network.NewAddresses("ns1.dummy"),
   868  		Address:        network.NewAddress("0.1.2.3"),
   869  		GatewayAddress: network.NewAddress("0.1.2.1"),
   870  	}})
   871  	s.api.CheckCalls(c, []gitjujutesting.StubCall{{
   872  		FuncName: "GetContainerInterfaceInfo",
   873  		Args:     []interface{}{names.NewMachineTag("42")},
   874  	}})
   875  }
   876  
   877  type lxcProvisionerSuite struct {
   878  	CommonProvisionerSuite
   879  	lxcSuite
   880  	events chan mock.Event
   881  }
   882  
   883  var _ = gc.Suite(&lxcProvisionerSuite{})
   884  
   885  func (s *lxcProvisionerSuite) SetUpSuite(c *gc.C) {
   886  	if runtime.GOOS == "windows" {
   887  		c.Skip("Skipping lxc tests on windows")
   888  	}
   889  	s.CommonProvisionerSuite.SetUpSuite(c)
   890  	s.lxcSuite.SetUpSuite(c)
   891  }
   892  
   893  func (s *lxcProvisionerSuite) TearDownSuite(c *gc.C) {
   894  	s.lxcSuite.TearDownSuite(c)
   895  	s.CommonProvisionerSuite.TearDownSuite(c)
   896  }
   897  
   898  func (s *lxcProvisionerSuite) SetUpTest(c *gc.C) {
   899  	s.CommonProvisionerSuite.SetUpTest(c)
   900  	s.lxcSuite.SetUpTest(c)
   901  
   902  	s.events = make(chan mock.Event, 25)
   903  	s.ContainerFactory.AddListener(s.events)
   904  }
   905  
   906  func (s *lxcProvisionerSuite) expectStarted(c *gc.C, machine *state.Machine) string {
   907  	// This check in particular leads to tests just hanging
   908  	// indefinitely quite often on i386.
   909  	coretesting.SkipIfI386(c, "lp:1425569")
   910  
   911  	var event mock.Event
   912  	s.State.StartSync()
   913  	select {
   914  	case event = <-s.events:
   915  		c.Assert(event.Action, gc.Equals, mock.Created)
   916  		argsSet := set.NewStrings(event.TemplateArgs...)
   917  		c.Assert(argsSet.Contains("imageURL"), jc.IsTrue)
   918  	case <-time.After(coretesting.LongWait):
   919  		c.Fatalf("timeout while waiting the mock container to get created")
   920  	}
   921  
   922  	select {
   923  	case event = <-s.events:
   924  		c.Assert(event.Action, gc.Equals, mock.Started)
   925  		err := machine.Refresh()
   926  		c.Assert(err, jc.ErrorIsNil)
   927  	case <-time.After(coretesting.LongWait):
   928  		c.Fatalf("timeout while waiting the mock container to start")
   929  	}
   930  
   931  	s.waitInstanceId(c, machine, instance.Id(event.InstanceId))
   932  	return event.InstanceId
   933  }
   934  
   935  func (s *lxcProvisionerSuite) expectStopped(c *gc.C, instId string) {
   936  	// This check in particular leads to tests just hanging
   937  	// indefinitely quite often on i386.
   938  	coretesting.SkipIfI386(c, "lp:1425569")
   939  
   940  	s.State.StartSync()
   941  	select {
   942  	case event := <-s.events:
   943  		c.Assert(event.Action, gc.Equals, mock.Stopped)
   944  	case <-time.After(coretesting.LongWait):
   945  		c.Fatalf("timeout while waiting the mock container to stop")
   946  	}
   947  
   948  	select {
   949  	case event := <-s.events:
   950  		c.Assert(event.Action, gc.Equals, mock.Destroyed)
   951  		c.Assert(event.InstanceId, gc.Equals, instId)
   952  	case <-time.After(coretesting.LongWait):
   953  		c.Fatalf("timeout while waiting the mock container to get destroyed")
   954  	}
   955  }
   956  
   957  func (s *lxcProvisionerSuite) expectNoEvents(c *gc.C) {
   958  	select {
   959  	case event := <-s.events:
   960  		c.Fatalf("unexpected event %#v", event)
   961  	case <-time.After(coretesting.ShortWait):
   962  		return
   963  	}
   964  }
   965  
   966  func (s *lxcProvisionerSuite) TearDownTest(c *gc.C) {
   967  	close(s.events)
   968  	s.lxcSuite.TearDownTest(c)
   969  	s.CommonProvisionerSuite.TearDownTest(c)
   970  }
   971  
   972  func (s *lxcProvisionerSuite) newLxcProvisioner(c *gc.C) provisioner.Provisioner {
   973  	parentMachineTag := names.NewMachineTag("0")
   974  	agentConfig := s.AgentConfigForTag(c, parentMachineTag)
   975  	managerConfig := container.ManagerConfig{
   976  		container.ConfigName: "juju",
   977  		"log-dir":            c.MkDir(),
   978  		"use-clone":          "false",
   979  	}
   980  	broker, err := provisioner.NewLxcBroker(s.provisioner, agentConfig, managerConfig, &containertesting.MockURLGetter{}, false, 0)
   981  	c.Assert(err, jc.ErrorIsNil)
   982  	toolsFinder := (*provisioner.GetToolsFinder)(s.provisioner)
   983  	return provisioner.NewContainerProvisioner(instance.LXC, s.provisioner, agentConfig, broker, toolsFinder)
   984  }
   985  
   986  func (s *lxcProvisionerSuite) TestProvisionerStartStop(c *gc.C) {
   987  	p := s.newLxcProvisioner(c)
   988  	c.Assert(p.Stop(), gc.IsNil)
   989  }
   990  
   991  func (s *lxcProvisionerSuite) TestDoesNotStartEnvironMachines(c *gc.C) {
   992  	p := s.newLxcProvisioner(c)
   993  	defer stop(c, p)
   994  
   995  	// Check that an instance is not provisioned when the machine is created.
   996  	_, err := s.State.AddMachine(coretesting.FakeDefaultSeries, state.JobHostUnits)
   997  	c.Assert(err, jc.ErrorIsNil)
   998  
   999  	s.expectNoEvents(c)
  1000  }
  1001  
  1002  func (s *lxcProvisionerSuite) TestDoesNotHaveRetryWatcher(c *gc.C) {
  1003  	p := s.newLxcProvisioner(c)
  1004  	defer stop(c, p)
  1005  
  1006  	w, err := provisioner.GetRetryWatcher(p)
  1007  	c.Assert(w, gc.IsNil)
  1008  	c.Assert(err, jc.Satisfies, errors.IsNotImplemented)
  1009  }
  1010  
  1011  func (s *lxcProvisionerSuite) addContainer(c *gc.C) *state.Machine {
  1012  	template := state.MachineTemplate{
  1013  		Series: coretesting.FakeDefaultSeries,
  1014  		Jobs:   []state.MachineJob{state.JobHostUnits},
  1015  	}
  1016  	container, err := s.State.AddMachineInsideMachine(template, "0", instance.LXC)
  1017  	c.Assert(err, jc.ErrorIsNil)
  1018  	return container
  1019  }
  1020  
  1021  func (s *lxcProvisionerSuite) TestContainerStartedAndStopped(c *gc.C) {
  1022  	coretesting.SkipIfI386(c, "lp:1425569")
  1023  
  1024  	p := s.newLxcProvisioner(c)
  1025  	defer stop(c, p)
  1026  
  1027  	container := s.addContainer(c)
  1028  	name := "juju-" + container.Tag().String()
  1029  	containertesting.EnsureRootFSEtcNetwork(c, name)
  1030  	instId := s.expectStarted(c, container)
  1031  
  1032  	// ...and removed, along with the machine, when the machine is Dead.
  1033  	c.Assert(container.EnsureDead(), gc.IsNil)
  1034  	s.expectStopped(c, instId)
  1035  	s.waitRemoved(c, container)
  1036  }
  1037  
  1038  func (s *lxcProvisionerSuite) TestLXCProvisionerObservesConfigChanges(c *gc.C) {
  1039  	p := s.newLxcProvisioner(c)
  1040  	defer stop(c, p)
  1041  	s.assertProvisionerObservesConfigChanges(c, p)
  1042  }
  1043  
  1044  type fakeAPI struct {
  1045  	*gitjujutesting.Stub
  1046  
  1047  	fakeContainerConfig params.ContainerConfig
  1048  	fakeInterfaceInfo   network.InterfaceInfo
  1049  }
  1050  
  1051  var _ provisioner.APICalls = (*fakeAPI)(nil)
  1052  
  1053  var fakeInterfaceInfo network.InterfaceInfo = network.InterfaceInfo{
  1054  	DeviceIndex:    0,
  1055  	MACAddress:     "aa:bb:cc:dd:ee:ff",
  1056  	CIDR:           "0.1.2.0/24",
  1057  	InterfaceName:  "dummy0",
  1058  	Address:        network.NewAddress("0.1.2.3"),
  1059  	GatewayAddress: network.NewAddress("0.1.2.1"),
  1060  }
  1061  
  1062  var fakeContainerConfig = params.ContainerConfig{
  1063  	UpdateBehavior:          &params.UpdateBehavior{true, true},
  1064  	ProviderType:            "fake",
  1065  	AuthorizedKeys:          coretesting.FakeAuthKeys,
  1066  	SSLHostnameVerification: true,
  1067  }
  1068  
  1069  func NewFakeAPI() *fakeAPI {
  1070  	return &fakeAPI{
  1071  		Stub:                &gitjujutesting.Stub{},
  1072  		fakeContainerConfig: fakeContainerConfig,
  1073  		fakeInterfaceInfo:   fakeInterfaceInfo,
  1074  	}
  1075  }
  1076  
  1077  func (f *fakeAPI) ContainerConfig() (params.ContainerConfig, error) {
  1078  	f.MethodCall(f, "ContainerConfig")
  1079  	if err := f.NextErr(); err != nil {
  1080  		return params.ContainerConfig{}, err
  1081  	}
  1082  	return f.fakeContainerConfig, nil
  1083  }
  1084  
  1085  func (f *fakeAPI) PrepareContainerInterfaceInfo(tag names.MachineTag) ([]network.InterfaceInfo, error) {
  1086  	f.MethodCall(f, "PrepareContainerInterfaceInfo", tag)
  1087  	if err := f.NextErr(); err != nil {
  1088  		return nil, err
  1089  	}
  1090  	return []network.InterfaceInfo{f.fakeInterfaceInfo}, nil
  1091  }
  1092  
  1093  func (f *fakeAPI) GetContainerInterfaceInfo(tag names.MachineTag) ([]network.InterfaceInfo, error) {
  1094  	f.MethodCall(f, "GetContainerInterfaceInfo", tag)
  1095  	if err := f.NextErr(); err != nil {
  1096  		return nil, err
  1097  	}
  1098  	return []network.InterfaceInfo{f.fakeInterfaceInfo}, nil
  1099  }