github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/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(&arch.HostArch, func() string { return 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.EnsureLXCRootFSEtcNetwork(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  
   238  	machineId := "1/lxc/0"
   239  	lxc := s.startInstance(c, machineId, []storage.VolumeParams{{Provider: provider.LoopProviderType}})
   240  	s.api.CheckCalls(c, []gitjujutesting.StubCall{{
   241  		FuncName: "PrepareContainerInterfaceInfo",
   242  		Args:     []interface{}{names.NewMachineTag("1-lxc-0")},
   243  	}, {
   244  		FuncName: "ContainerConfig",
   245  	}})
   246  	c.Assert(lxc.Id(), gc.Equals, instance.Id("juju-machine-1-lxc-0"))
   247  	c.Assert(s.lxcContainerDir(lxc), jc.IsDirectory)
   248  	s.assertInstances(c, lxc)
   249  	// Check storage config.
   250  	config := filepath.Join(s.LxcDir, string(lxc.Id()), "config")
   251  	AssertFileContents(c, jc.Contains, config, "lxc.aa_profile = lxc-container-default-with-mounting")
   252  }
   253  
   254  func (s *lxcBrokerSuite) TestStartInstanceLoopMountsDisallowed(c *gc.C) {
   255  	s.api.fakeContainerConfig.AllowLXCLoopMounts = false
   256  	s.SetFeatureFlags(feature.AddressAllocation)
   257  	machineId := "1/lxc/0"
   258  	lxc := s.startInstance(c, machineId, []storage.VolumeParams{{Provider: provider.LoopProviderType}})
   259  	s.api.CheckCalls(c, []gitjujutesting.StubCall{{
   260  		FuncName: "PrepareContainerInterfaceInfo",
   261  		Args:     []interface{}{names.NewMachineTag("1-lxc-0")},
   262  	}, {
   263  		FuncName: "ContainerConfig",
   264  	}})
   265  	c.Assert(lxc.Id(), gc.Equals, instance.Id("juju-machine-1-lxc-0"))
   266  	c.Assert(s.lxcContainerDir(lxc), jc.IsDirectory)
   267  	s.assertInstances(c, lxc)
   268  	s.assertDefaultStorageConfig(c, lxc)
   269  }
   270  
   271  func (s *lxcBrokerSuite) TestStartInstanceHostArch(c *gc.C) {
   272  	instanceConfig := s.instanceConfig(c, "1/lxc/0")
   273  
   274  	// Patch the host's arch, so the LXC broker will filter tools.
   275  	s.PatchValue(&arch.HostArch, func() string { return 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.
   296  	s.PatchValue(&arch.HostArch, func() string { return arch.PPC64EL })
   297  	possibleTools := coretools.List{&coretools.Tools{
   298  		Version: version.MustParseBinary("2.3.4-quantal-amd64"),
   299  		URL:     "http://tools.testing.invalid/2.3.4-quantal-amd64.tgz",
   300  	}}
   301  	_, err := s.broker.StartInstance(environs.StartInstanceParams{
   302  		Constraints:    constraints.Value{},
   303  		Tools:          possibleTools,
   304  		InstanceConfig: instanceConfig,
   305  	})
   306  	c.Assert(err, gc.ErrorMatches, "need tools for arch ppc64el, only found \\[amd64\\]")
   307  }
   308  
   309  func (s *lxcBrokerSuite) TestStartInstanceWithBridgeEnviron(c *gc.C) {
   310  	s.agentConfig.SetValue(agent.LxcBridge, "br0")
   311  	machineId := "1/lxc/0"
   312  	lxc := s.startInstance(c, machineId, nil)
   313  	s.api.CheckCalls(c, []gitjujutesting.StubCall{{
   314  		FuncName: "ContainerConfig",
   315  	}})
   316  	c.Assert(lxc.Id(), gc.Equals, instance.Id("juju-machine-1-lxc-0"))
   317  	c.Assert(s.lxcContainerDir(lxc), jc.IsDirectory)
   318  	s.assertInstances(c, lxc)
   319  	// Uses default network config
   320  	lxc_conf := filepath.Join(s.ContainerDir, string(lxc.Id()), "lxc.conf")
   321  	expect := []string{
   322  		"lxc.network.type = veth",
   323  		"lxc.network.link = br0",
   324  	}
   325  	AssertFileContains(c, lxc_conf, expect...)
   326  }
   327  
   328  func (s *lxcBrokerSuite) TestStartInstancePopulatesNetworkInfo(c *gc.C) {
   329  	s.SetFeatureFlags(feature.AddressAllocation)
   330  	s.PatchValue(provisioner.InterfaceAddrs, func(i *net.Interface) ([]net.Addr, error) {
   331  		return []net.Addr{&fakeAddr{"0.1.2.1/24"}}, nil
   332  	})
   333  	fakeResolvConf := filepath.Join(c.MkDir(), "resolv.conf")
   334  	err := ioutil.WriteFile(fakeResolvConf, []byte("nameserver ns1.dummy\n"), 0644)
   335  	c.Assert(err, jc.ErrorIsNil)
   336  	s.PatchValue(provisioner.ResolvConf, fakeResolvConf)
   337  
   338  	instanceConfig := s.instanceConfig(c, "42")
   339  	possibleTools := coretools.List{&coretools.Tools{
   340  		Version: version.MustParseBinary("2.3.4-quantal-amd64"),
   341  		URL:     "http://tools.testing.invalid/2.3.4-quantal-amd64.tgz",
   342  	}}
   343  	result, err := s.broker.StartInstance(environs.StartInstanceParams{
   344  		Constraints:    constraints.Value{},
   345  		Tools:          possibleTools,
   346  		InstanceConfig: instanceConfig,
   347  	})
   348  	c.Assert(err, jc.ErrorIsNil)
   349  	c.Assert(result.NetworkInfo, gc.HasLen, 1)
   350  	iface := result.NetworkInfo[0]
   351  	c.Assert(err, jc.ErrorIsNil)
   352  	c.Assert(iface, jc.DeepEquals, network.InterfaceInfo{
   353  		DeviceIndex:    0,
   354  		CIDR:           "0.1.2.0/24",
   355  		ConfigType:     network.ConfigStatic,
   356  		InterfaceName:  "eth0", // generated from the device index.
   357  		MACAddress:     "aa:bb:cc:dd:ee:ff",
   358  		DNSServers:     network.NewAddresses("ns1.dummy"),
   359  		Address:        network.NewAddress("0.1.2.3"),
   360  		GatewayAddress: network.NewAddress("0.1.2.1"),
   361  		NetworkName:    network.DefaultPrivate,
   362  		ProviderId:     network.DefaultProviderId,
   363  	})
   364  }
   365  
   366  func (s *lxcBrokerSuite) TestStopInstance(c *gc.C) {
   367  	lxc0 := s.startInstance(c, "1/lxc/0", nil)
   368  	lxc1 := s.startInstance(c, "1/lxc/1", nil)
   369  	lxc2 := s.startInstance(c, "1/lxc/2", nil)
   370  
   371  	s.assertInstances(c, lxc0, lxc1, lxc2)
   372  	err := s.broker.StopInstances(lxc0.Id())
   373  	c.Assert(err, jc.ErrorIsNil)
   374  	s.assertInstances(c, lxc1, lxc2)
   375  	c.Assert(s.lxcContainerDir(lxc0), jc.DoesNotExist)
   376  	c.Assert(s.lxcRemovedContainerDir(lxc0), jc.IsDirectory)
   377  
   378  	err = s.broker.StopInstances(lxc1.Id(), lxc2.Id())
   379  	c.Assert(err, jc.ErrorIsNil)
   380  	s.assertInstances(c)
   381  }
   382  
   383  func (s *lxcBrokerSuite) TestAllInstances(c *gc.C) {
   384  	lxc0 := s.startInstance(c, "1/lxc/0", nil)
   385  	lxc1 := s.startInstance(c, "1/lxc/1", nil)
   386  	s.assertInstances(c, lxc0, lxc1)
   387  
   388  	err := s.broker.StopInstances(lxc1.Id())
   389  	c.Assert(err, jc.ErrorIsNil)
   390  	lxc2 := s.startInstance(c, "1/lxc/2", nil)
   391  	s.assertInstances(c, lxc0, lxc2)
   392  }
   393  
   394  func (s *lxcBrokerSuite) assertInstances(c *gc.C, inst ...instance.Instance) {
   395  	results, err := s.broker.AllInstances()
   396  	c.Assert(err, jc.ErrorIsNil)
   397  	instancetest.MatchInstances(c, results, inst...)
   398  }
   399  
   400  func (s *lxcBrokerSuite) lxcContainerDir(inst instance.Instance) string {
   401  	return filepath.Join(s.ContainerDir, string(inst.Id()))
   402  }
   403  
   404  func (s *lxcBrokerSuite) lxcRemovedContainerDir(inst instance.Instance) string {
   405  	return filepath.Join(s.RemovedDir, string(inst.Id()))
   406  }
   407  
   408  func (s *lxcBrokerSuite) TestLocalDNSServers(c *gc.C) {
   409  	fakeConf := filepath.Join(c.MkDir(), "resolv.conf")
   410  	s.PatchValue(provisioner.ResolvConf, fakeConf)
   411  
   412  	// If config is missing, that's OK.
   413  	dnses, dnsSearch, err := provisioner.LocalDNSServers()
   414  	c.Assert(err, jc.ErrorIsNil)
   415  	c.Assert(dnses, gc.HasLen, 0)
   416  	c.Assert(dnsSearch, gc.Equals, "")
   417  
   418  	// Enter some data in fakeConf.
   419  	data := `
   420   anything else is ignored
   421    # comments are ignored
   422    nameserver  0.1.2.3  # that's parsed
   423  search  foo.baz # comment ignored
   424  # nameserver 42.42.42.42 - ignored as well
   425  nameserver 8.8.8.8
   426  nameserver example.com # comment after is ok
   427  `
   428  	err = ioutil.WriteFile(fakeConf, []byte(data), 0644)
   429  	c.Assert(err, jc.ErrorIsNil)
   430  
   431  	dnses, dnsSearch, err = provisioner.LocalDNSServers()
   432  	c.Assert(err, jc.ErrorIsNil)
   433  	c.Assert(dnses, jc.DeepEquals, network.NewAddresses(
   434  		"0.1.2.3", "8.8.8.8", "example.com",
   435  	))
   436  	c.Assert(dnsSearch, gc.Equals, "foo.baz")
   437  }
   438  
   439  func (s *lxcBrokerSuite) TestMustParseTemplate(c *gc.C) {
   440  	f := func() { provisioner.MustParseTemplate("", "{{invalid}") }
   441  	c.Assert(f, gc.PanicMatches, `template: :1: function "invalid" not defined`)
   442  
   443  	tmpl := provisioner.MustParseTemplate("name", "X={{.X}}")
   444  	c.Assert(tmpl, gc.NotNil)
   445  	c.Assert(tmpl.Name(), gc.Equals, "name")
   446  
   447  	var buf bytes.Buffer
   448  	err := tmpl.Execute(&buf, struct{ X string }{"42"})
   449  	c.Assert(err, jc.ErrorIsNil)
   450  	c.Assert(buf.String(), gc.Equals, "X=42")
   451  }
   452  
   453  func (s *lxcBrokerSuite) TestRunTemplateCommand(c *gc.C) {
   454  	for i, test := range []struct {
   455  		source        string
   456  		exitNonZeroOK bool
   457  		data          interface{}
   458  		exitCode      int
   459  		expectErr     string
   460  	}{{
   461  		source:        "echo {{.Name}}",
   462  		exitNonZeroOK: false,
   463  		data:          struct{ Name string }{"foo"},
   464  		exitCode:      0,
   465  	}, {
   466  		source:        "exit {{.Code}}",
   467  		exitNonZeroOK: false,
   468  		data:          struct{ Code int }{123},
   469  		exitCode:      123,
   470  		expectErr:     `command "exit 123" failed with exit code 123`,
   471  	}, {
   472  		source:        "exit {{.Code}}",
   473  		exitNonZeroOK: true,
   474  		data:          struct{ Code int }{56},
   475  		exitCode:      56,
   476  	}, {
   477  		source:        "exit 42",
   478  		exitNonZeroOK: true,
   479  		exitCode:      42,
   480  	}, {
   481  		source:        "some-invalid-command",
   482  		exitNonZeroOK: false,
   483  		exitCode:      127, // returned by bash.
   484  		expectErr:     `command "some-invalid-command" failed with exit code 127`,
   485  	}} {
   486  		c.Logf("test %d: %q -> %d", i, test.source, test.exitCode)
   487  		t, err := template.New(fmt.Sprintf("test %d", i)).Parse(test.source)
   488  		if !c.Check(err, jc.ErrorIsNil, gc.Commentf("parsing %q", test.source)) {
   489  			continue
   490  		}
   491  		exitCode, err := provisioner.RunTemplateCommand(t, test.exitNonZeroOK, test.data)
   492  		if test.expectErr != "" {
   493  			c.Check(err, gc.ErrorMatches, test.expectErr)
   494  		} else {
   495  			c.Check(err, jc.ErrorIsNil)
   496  		}
   497  		c.Check(exitCode, gc.Equals, test.exitCode)
   498  	}
   499  }
   500  
   501  func (s *lxcBrokerSuite) TestSetupRoutesAndIPTablesInvalidArgs(c *gc.C) {
   502  	// Isolate the test from the host machine.
   503  	gitjujutesting.PatchExecutableThrowError(c, s, "iptables", 42)
   504  	gitjujutesting.PatchExecutableThrowError(c, s, "ip", 123)
   505  
   506  	// Check that all the arguments are verified to be non-empty.
   507  	expectStartupErr := "primaryNIC, primaryAddr, bridgeName, and ifaceInfo must be all set"
   508  	emptyIfaceInfo := []network.InterfaceInfo{}
   509  	for i, test := range []struct {
   510  		about       string
   511  		primaryNIC  string
   512  		primaryAddr network.Address
   513  		bridgeName  string
   514  		ifaceInfo   []network.InterfaceInfo
   515  		expectErr   string
   516  	}{{
   517  		about:       "all empty",
   518  		primaryNIC:  "",
   519  		primaryAddr: network.Address{},
   520  		bridgeName:  "",
   521  		ifaceInfo:   nil,
   522  		expectErr:   expectStartupErr,
   523  	}, {
   524  		about:       "all but primaryNIC empty",
   525  		primaryNIC:  "nic",
   526  		primaryAddr: network.Address{},
   527  		bridgeName:  "",
   528  		ifaceInfo:   nil,
   529  		expectErr:   expectStartupErr,
   530  	}, {
   531  		about:       "all but primaryAddr empty",
   532  		primaryNIC:  "",
   533  		primaryAddr: network.NewAddress("0.1.2.1"),
   534  		bridgeName:  "",
   535  		ifaceInfo:   nil,
   536  		expectErr:   expectStartupErr,
   537  	}, {
   538  		about:       "all but bridgeName empty",
   539  		primaryNIC:  "",
   540  		primaryAddr: network.Address{},
   541  		bridgeName:  "bridge",
   542  		ifaceInfo:   nil,
   543  		expectErr:   expectStartupErr,
   544  	}, {
   545  		about:       "all but primaryNIC and bridgeName empty",
   546  		primaryNIC:  "nic",
   547  		primaryAddr: network.Address{},
   548  		bridgeName:  "bridge",
   549  		ifaceInfo:   nil,
   550  		expectErr:   expectStartupErr,
   551  	}, {
   552  		about:       "all but primaryNIC and primaryAddr empty",
   553  		primaryNIC:  "nic",
   554  		primaryAddr: network.NewAddress("0.1.2.1"),
   555  		bridgeName:  "",
   556  		ifaceInfo:   nil,
   557  		expectErr:   expectStartupErr,
   558  	}, {
   559  		about:       "all but primaryAddr and bridgeName empty",
   560  		primaryNIC:  "",
   561  		primaryAddr: network.NewAddress("0.1.2.1"),
   562  		bridgeName:  "bridge",
   563  		ifaceInfo:   nil,
   564  		expectErr:   expectStartupErr,
   565  	}, {
   566  		about:       "all set except ifaceInfo",
   567  		primaryNIC:  "nic",
   568  		primaryAddr: network.NewAddress("0.1.2.1"),
   569  		bridgeName:  "bridge",
   570  		ifaceInfo:   nil,
   571  		expectErr:   expectStartupErr,
   572  	}, {
   573  		about:       "all empty (ifaceInfo set but empty)",
   574  		primaryNIC:  "",
   575  		primaryAddr: network.Address{},
   576  		bridgeName:  "",
   577  		ifaceInfo:   emptyIfaceInfo,
   578  		expectErr:   expectStartupErr,
   579  	}, {
   580  		about:       "all but primaryNIC empty (ifaceInfo set but empty)",
   581  		primaryNIC:  "nic",
   582  		primaryAddr: network.Address{},
   583  		bridgeName:  "",
   584  		ifaceInfo:   emptyIfaceInfo,
   585  		expectErr:   expectStartupErr,
   586  	}, {
   587  		about:       "all but primaryAddr empty (ifaceInfo set but empty)",
   588  		primaryNIC:  "",
   589  		primaryAddr: network.NewAddress("0.1.2.1"),
   590  		bridgeName:  "",
   591  		ifaceInfo:   emptyIfaceInfo,
   592  		expectErr:   expectStartupErr,
   593  	}, {
   594  		about:       "all but bridgeName empty (ifaceInfo set but empty)",
   595  		primaryNIC:  "",
   596  		primaryAddr: network.Address{},
   597  		bridgeName:  "bridge",
   598  		ifaceInfo:   emptyIfaceInfo,
   599  		expectErr:   expectStartupErr,
   600  	}, {
   601  		about:       "just primaryAddr is empty and ifaceInfo set but empty",
   602  		primaryNIC:  "nic",
   603  		primaryAddr: network.Address{},
   604  		bridgeName:  "bridge",
   605  		ifaceInfo:   emptyIfaceInfo,
   606  		expectErr:   expectStartupErr,
   607  	}, {
   608  		about:       "just bridgeName is empty and ifaceInfo set but empty",
   609  		primaryNIC:  "nic",
   610  		primaryAddr: network.NewAddress("0.1.2.1"),
   611  		bridgeName:  "",
   612  		ifaceInfo:   emptyIfaceInfo,
   613  		expectErr:   expectStartupErr,
   614  	}, {
   615  		about:       "just primaryNIC is empty and ifaceInfo set but empty",
   616  		primaryNIC:  "",
   617  		primaryAddr: network.NewAddress("0.1.2.1"),
   618  		bridgeName:  "bridge",
   619  		ifaceInfo:   emptyIfaceInfo,
   620  		expectErr:   expectStartupErr,
   621  	}, {
   622  		about:       "all set except ifaceInfo, which is set but empty",
   623  		primaryNIC:  "nic",
   624  		primaryAddr: network.NewAddress("0.1.2.1"),
   625  		bridgeName:  "bridge",
   626  		ifaceInfo:   emptyIfaceInfo,
   627  		expectErr:   expectStartupErr,
   628  	}, {
   629  		about:       "all set, but ifaceInfo has empty Address",
   630  		primaryNIC:  "nic",
   631  		primaryAddr: network.NewAddress("0.1.2.1"),
   632  		bridgeName:  "bridge",
   633  		// No Address set.
   634  		ifaceInfo: []network.InterfaceInfo{{DeviceIndex: 0}},
   635  		expectErr: `container IP "" must be set`,
   636  	}} {
   637  		c.Logf("test %d: %s", i, test.about)
   638  		err := provisioner.SetupRoutesAndIPTables(
   639  			test.primaryNIC,
   640  			test.primaryAddr,
   641  			test.bridgeName,
   642  			test.ifaceInfo,
   643  			false, // TODO(dimitern): Untested.
   644  		)
   645  		c.Check(err, gc.ErrorMatches, test.expectErr)
   646  	}
   647  }
   648  
   649  func (s *lxcBrokerSuite) TestSetupRoutesAndIPTablesIPTablesCheckError(c *gc.C) {
   650  	// Isolate the test from the host machine.
   651  	gitjujutesting.PatchExecutableThrowError(c, s, "iptables", 42)
   652  	gitjujutesting.PatchExecutableThrowError(c, s, "ip", 123)
   653  
   654  	ifaceInfo := []network.InterfaceInfo{{
   655  		Address: network.NewAddress("0.1.2.3"),
   656  	}}
   657  
   658  	addr := network.NewAddress("0.1.2.1")
   659  	err := provisioner.SetupRoutesAndIPTables("nic", addr, "bridge", ifaceInfo, false)
   660  	c.Assert(err, gc.ErrorMatches, "iptables failed with unexpected exit code 42")
   661  }
   662  
   663  func (s *lxcBrokerSuite) TestSetupRoutesAndIPTablesIPTablesAddError(c *gc.C) {
   664  	// Isolate the test from the host machine. Patch iptables with a
   665  	// script which returns code=1 for the check but fails when adding
   666  	// the rule.
   667  	script := `if [[ "$3" == "-C" ]]; then exit 1; else exit 42; fi`
   668  	gitjujutesting.PatchExecutable(c, s, "iptables", script)
   669  	gitjujutesting.PatchExecutableThrowError(c, s, "ip", 123)
   670  
   671  	fakeptablesRules := map[string]provisioner.IptablesRule{
   672  		"IPTablesSNAT": {
   673  			"nat",
   674  			"POSTROUTING",
   675  			"{{.HostIF}} {{.HostIP}}",
   676  		},
   677  	}
   678  	s.PatchValue(provisioner.IptablesRules, fakeptablesRules)
   679  
   680  	ifaceInfo := []network.InterfaceInfo{{
   681  		Address: network.NewAddress("0.1.2.3"),
   682  	}}
   683  
   684  	addr := network.NewAddress("0.1.2.1")
   685  	err := provisioner.SetupRoutesAndIPTables("nic", addr, "bridge", ifaceInfo, false)
   686  	c.Assert(err, gc.ErrorMatches, `command "iptables -t nat -I .*" failed with exit code 42`)
   687  }
   688  
   689  func (s *lxcBrokerSuite) TestSetupRoutesAndIPTablesIPRouteError(c *gc.C) {
   690  	// Isolate the test from the host machine.
   691  	// Returning code=0 from iptables means we won't add a rule.
   692  	gitjujutesting.PatchExecutableThrowError(c, s, "iptables", 0)
   693  	gitjujutesting.PatchExecutableThrowError(c, s, "ip", 123)
   694  
   695  	ifaceInfo := []network.InterfaceInfo{{
   696  		Address: network.NewAddress("0.1.2.3"),
   697  	}}
   698  
   699  	addr := network.NewAddress("0.1.2.1")
   700  	err := provisioner.SetupRoutesAndIPTables("nic", addr, "bridge", ifaceInfo, false)
   701  	c.Assert(err, gc.ErrorMatches,
   702  		`command "ip route add 0.1.2.3 dev bridge" failed with exit code 123`,
   703  	)
   704  }
   705  
   706  func (s *lxcBrokerSuite) TestSetupRoutesAndIPTablesAddsRuleIfMissing(c *gc.C) {
   707  	// Isolate the test from the host machine. Because PatchExecutable
   708  	// does not allow us to assert on subsequent executions of the
   709  	// same binary, we need to replace the iptables commands with
   710  	// separate ones. The check returns code=1 to trigger calling
   711  	// add.
   712  	fakeptablesRules := map[string]provisioner.IptablesRule{
   713  		"IPTablesSNAT": {
   714  			"nat",
   715  			"POSTROUTING",
   716  			"{{.HostIF}} {{.HostIP}}",
   717  		},
   718  	}
   719  	s.PatchValue(provisioner.IptablesRules, fakeptablesRules)
   720  
   721  	gitjujutesting.PatchExecutableAsEchoArgs(c, s, "iptables", 1, 0)
   722  	gitjujutesting.PatchExecutableAsEchoArgs(c, s, "ip")
   723  
   724  	ifaceInfo := []network.InterfaceInfo{{
   725  		Address: network.NewAddress("0.1.2.3"),
   726  	}}
   727  
   728  	addr := network.NewAddress("0.1.2.1")
   729  	err := provisioner.SetupRoutesAndIPTables("nic", addr, "bridge", ifaceInfo, false)
   730  	c.Assert(err, jc.ErrorIsNil)
   731  
   732  	// Now verify the expected commands - since check returns 1, add
   733  	// will be called before ip route add.
   734  
   735  	gitjujutesting.AssertEchoArgs(c, "iptables", "-t", "nat", "-C", "POSTROUTING", "nic", "0.1.2.1")
   736  	gitjujutesting.AssertEchoArgs(c, "iptables", "-t", "nat", "-I", "POSTROUTING", "1", "nic", "0.1.2.1")
   737  	gitjujutesting.AssertEchoArgs(c, "ip", "route", "add", "0.1.2.3", "dev", "bridge")
   738  }
   739  
   740  func (s *lxcBrokerSuite) TestDiscoverPrimaryNICNetInterfacesError(c *gc.C) {
   741  	s.PatchValue(provisioner.NetInterfaces, func() ([]net.Interface, error) {
   742  		return nil, errors.New("boom!")
   743  	})
   744  
   745  	nic, addr, err := provisioner.DiscoverPrimaryNIC()
   746  	c.Assert(err, gc.ErrorMatches, "cannot get network interfaces: boom!")
   747  	c.Assert(nic, gc.Equals, "")
   748  	c.Assert(addr, jc.DeepEquals, network.Address{})
   749  }
   750  
   751  func (s *lxcBrokerSuite) TestDiscoverPrimaryNICInterfaceAddrsError(c *gc.C) {
   752  	s.PatchValue(provisioner.NetInterfaces, func() ([]net.Interface, error) {
   753  		return []net.Interface{{
   754  			Index: 0,
   755  			Name:  "fake",
   756  			Flags: net.FlagUp,
   757  		}}, nil
   758  	})
   759  	s.PatchValue(provisioner.InterfaceAddrs, func(i *net.Interface) ([]net.Addr, error) {
   760  		return nil, errors.New("boom!")
   761  	})
   762  
   763  	nic, addr, err := provisioner.DiscoverPrimaryNIC()
   764  	c.Assert(err, gc.ErrorMatches, `cannot get "fake" addresses: boom!`)
   765  	c.Assert(nic, gc.Equals, "")
   766  	c.Assert(addr, jc.DeepEquals, network.Address{})
   767  }
   768  
   769  func (s *lxcBrokerSuite) TestDiscoverPrimaryNICInvalidAddr(c *gc.C) {
   770  	s.PatchValue(provisioner.NetInterfaces, func() ([]net.Interface, error) {
   771  		return []net.Interface{{
   772  			Index: 0,
   773  			Name:  "fake",
   774  			Flags: net.FlagUp,
   775  		}}, nil
   776  	})
   777  	s.PatchValue(provisioner.InterfaceAddrs, func(i *net.Interface) ([]net.Addr, error) {
   778  		return []net.Addr{&fakeAddr{}}, nil
   779  	})
   780  
   781  	nic, addr, err := provisioner.DiscoverPrimaryNIC()
   782  	c.Assert(err, gc.ErrorMatches, `cannot parse address "fakeAddr": invalid CIDR address: fakeAddr`)
   783  	c.Assert(nic, gc.Equals, "")
   784  	c.Assert(addr, jc.DeepEquals, network.Address{})
   785  }
   786  
   787  func (s *lxcBrokerSuite) TestDiscoverPrimaryNICInterfaceNotFound(c *gc.C) {
   788  	s.PatchValue(provisioner.NetInterfaces, func() ([]net.Interface, error) {
   789  		return nil, nil
   790  	})
   791  
   792  	nic, addr, err := provisioner.DiscoverPrimaryNIC()
   793  	c.Assert(err, gc.ErrorMatches, "cannot detect the primary network interface")
   794  	c.Assert(nic, gc.Equals, "")
   795  	c.Assert(addr, jc.DeepEquals, network.Address{})
   796  }
   797  
   798  type fakeAddr struct{ value string }
   799  
   800  func (f *fakeAddr) Network() string { return "net" }
   801  func (f *fakeAddr) String() string {
   802  	if f.value != "" {
   803  		return f.value
   804  	}
   805  	return "fakeAddr"
   806  }
   807  
   808  var _ net.Addr = (*fakeAddr)(nil)
   809  
   810  func (s *lxcBrokerSuite) TestDiscoverPrimaryNICSuccess(c *gc.C) {
   811  	s.PatchValue(provisioner.NetInterfaces, func() ([]net.Interface, error) {
   812  		return []net.Interface{{
   813  			Index: 0,
   814  			Name:  "lo",
   815  			Flags: net.FlagUp | net.FlagLoopback, // up but loopback - ignored.
   816  		}, {
   817  			Index: 1,
   818  			Name:  "if0",
   819  			Flags: net.FlagPointToPoint, // not up - ignored.
   820  		}, {
   821  			Index: 2,
   822  			Name:  "if1",
   823  			Flags: net.FlagUp, // up but no addresses - ignored.
   824  		}, {
   825  			Index: 3,
   826  			Name:  "if2",
   827  			Flags: net.FlagUp, // up and has addresses - returned.
   828  		}}, nil
   829  	})
   830  	s.PatchValue(provisioner.InterfaceAddrs, func(i *net.Interface) ([]net.Addr, error) {
   831  		// We should be called only for the last two NICs. The first
   832  		// one (if1) won't have addresses, only the last one (if2).
   833  		c.Assert(i, gc.NotNil)
   834  		c.Assert(i.Name, gc.Matches, "if[12]")
   835  		if i.Name == "if2" {
   836  			return []net.Addr{&fakeAddr{"0.1.2.3/24"}}, nil
   837  		}
   838  		// For if1 we return no addresses.
   839  		return nil, nil
   840  	})
   841  
   842  	nic, addr, err := provisioner.DiscoverPrimaryNIC()
   843  	c.Assert(err, jc.ErrorIsNil)
   844  	c.Assert(nic, gc.Equals, "if2")
   845  	c.Assert(addr, jc.DeepEquals, network.NewAddress("0.1.2.3"))
   846  }
   847  
   848  func (s *lxcBrokerSuite) TestConfigureContainerNetwork(c *gc.C) {
   849  	// All the pieces used by this func are separately tested, we just
   850  	// test the integration between them.
   851  	s.PatchValue(provisioner.NetInterfaces, func() ([]net.Interface, error) {
   852  		return []net.Interface{{
   853  			Index: 0,
   854  			Name:  "fake0",
   855  			Flags: net.FlagUp,
   856  		}}, nil
   857  	})
   858  	s.PatchValue(provisioner.InterfaceAddrs, func(i *net.Interface) ([]net.Addr, error) {
   859  		return []net.Addr{&fakeAddr{"0.1.2.1/24"}}, nil
   860  	})
   861  	fakeResolvConf := filepath.Join(c.MkDir(), "resolv.conf")
   862  	err := ioutil.WriteFile(fakeResolvConf, []byte("nameserver ns1.dummy\n"), 0644)
   863  	c.Assert(err, jc.ErrorIsNil)
   864  	s.PatchValue(provisioner.ResolvConf, fakeResolvConf)
   865  
   866  	// When ifaceInfo is not empty it shouldn't do anything and both
   867  	// the error and the result are nil.
   868  	ifaceInfo := []network.InterfaceInfo{{DeviceIndex: 0}}
   869  	// First call as if we are configuring the container for the first time
   870  	result, err := provisioner.ConfigureContainerNetwork("42", "bridge", s.api, ifaceInfo, true, false)
   871  	c.Assert(err, jc.ErrorIsNil)
   872  	c.Assert(result, gc.IsNil)
   873  	s.api.CheckCalls(c, []gitjujutesting.StubCall{})
   874  
   875  	// Next call as if the container has already been configured.
   876  	s.api.ResetCalls()
   877  	result, err = provisioner.ConfigureContainerNetwork("42", "bridge", s.api, ifaceInfo, false, false)
   878  	c.Assert(err, jc.ErrorIsNil)
   879  	c.Assert(result, gc.IsNil)
   880  	s.api.CheckCalls(c, []gitjujutesting.StubCall{})
   881  
   882  	// Call as if the container already has a network configuration, but doesn't.
   883  	s.api.ResetCalls()
   884  	s.api.SetErrors(errors.NotProvisionedf("machine-42 has no network provisioning info"))
   885  	ifaceInfo = []network.InterfaceInfo{}
   886  	result, err = provisioner.ConfigureContainerNetwork("42", "bridge", s.api, ifaceInfo, false, false)
   887  	c.Assert(err, gc.ErrorMatches, "machine-42 has no network provisioning info not provisioned")
   888  	c.Assert(result, jc.DeepEquals, []network.InterfaceInfo{})
   889  	s.api.CheckCalls(c, []gitjujutesting.StubCall{{
   890  		FuncName: "GetContainerInterfaceInfo",
   891  		Args:     []interface{}{names.NewMachineTag("42")},
   892  	}})
   893  
   894  	// When it's not empty, result should be populated as expected.
   895  	s.api.ResetCalls()
   896  	result, err = provisioner.ConfigureContainerNetwork("42", "bridge", s.api, ifaceInfo, false, false)
   897  
   898  	c.Assert(err, jc.ErrorIsNil)
   899  	c.Assert(result, gc.HasLen, 1)
   900  	c.Assert(err, jc.ErrorIsNil)
   901  	c.Assert(result, jc.DeepEquals, []network.InterfaceInfo{{
   902  		DeviceIndex:    0,
   903  		CIDR:           "0.1.2.0/24",
   904  		ConfigType:     network.ConfigStatic,
   905  		InterfaceName:  "eth0", // generated from the device index.
   906  		MACAddress:     "aa:bb:cc:dd:ee:ff",
   907  		DNSServers:     network.NewAddresses("ns1.dummy"),
   908  		Address:        network.NewAddress("0.1.2.3"),
   909  		GatewayAddress: network.NewAddress("0.1.2.1"),
   910  		NetworkName:    network.DefaultPrivate,
   911  		ProviderId:     network.DefaultProviderId,
   912  	}})
   913  	s.api.CheckCalls(c, []gitjujutesting.StubCall{{
   914  		FuncName: "GetContainerInterfaceInfo",
   915  		Args:     []interface{}{names.NewMachineTag("42")},
   916  	}})
   917  
   918  	s.api.ResetCalls()
   919  	result, err = provisioner.ConfigureContainerNetwork("42", "bridge", s.api, ifaceInfo, false, false)
   920  	c.Assert(result, gc.HasLen, 1)
   921  	c.Assert(err, jc.ErrorIsNil)
   922  	c.Assert(result, jc.DeepEquals, []network.InterfaceInfo{{
   923  		DeviceIndex:    0,
   924  		CIDR:           "0.1.2.0/24",
   925  		ConfigType:     network.ConfigStatic,
   926  		InterfaceName:  "eth0", // generated from the device index.
   927  		MACAddress:     "aa:bb:cc:dd:ee:ff",
   928  		DNSServers:     network.NewAddresses("ns1.dummy"),
   929  		Address:        network.NewAddress("0.1.2.3"),
   930  		GatewayAddress: network.NewAddress("0.1.2.1"),
   931  		NetworkName:    network.DefaultPrivate,
   932  		ProviderId:     network.DefaultProviderId,
   933  	}})
   934  
   935  	s.api.CheckCalls(c, []gitjujutesting.StubCall{{
   936  		FuncName: "GetContainerInterfaceInfo",
   937  		Args:     []interface{}{names.NewMachineTag("42")},
   938  	}})
   939  }
   940  
   941  type lxcProvisionerSuite struct {
   942  	CommonProvisionerSuite
   943  	lxcSuite
   944  	events chan mock.Event
   945  }
   946  
   947  var _ = gc.Suite(&lxcProvisionerSuite{})
   948  
   949  func (s *lxcProvisionerSuite) SetUpSuite(c *gc.C) {
   950  	if runtime.GOOS == "windows" {
   951  		c.Skip("Skipping lxc tests on windows")
   952  	}
   953  	s.CommonProvisionerSuite.SetUpSuite(c)
   954  	s.lxcSuite.SetUpSuite(c)
   955  }
   956  
   957  func (s *lxcProvisionerSuite) TearDownSuite(c *gc.C) {
   958  	s.lxcSuite.TearDownSuite(c)
   959  	s.CommonProvisionerSuite.TearDownSuite(c)
   960  }
   961  
   962  func (s *lxcProvisionerSuite) SetUpTest(c *gc.C) {
   963  	s.CommonProvisionerSuite.SetUpTest(c)
   964  	s.lxcSuite.SetUpTest(c)
   965  
   966  	s.events = make(chan mock.Event, 25)
   967  	s.ContainerFactory.AddListener(s.events)
   968  }
   969  
   970  func (s *lxcProvisionerSuite) expectStarted(c *gc.C, machine *state.Machine) string {
   971  	// This check in particular leads to tests just hanging
   972  	// indefinitely quite often on i386.
   973  	coretesting.SkipIfI386(c, "lp:1425569")
   974  
   975  	var event mock.Event
   976  	s.State.StartSync()
   977  	select {
   978  	case event = <-s.events:
   979  		c.Assert(event.Action, gc.Equals, mock.Created)
   980  		argsSet := set.NewStrings(event.TemplateArgs...)
   981  		c.Assert(argsSet.Contains("imageURL"), jc.IsTrue)
   982  	case <-time.After(coretesting.LongWait):
   983  		c.Fatalf("timeout while waiting the mock container to get created")
   984  	}
   985  
   986  	select {
   987  	case event = <-s.events:
   988  		c.Assert(event.Action, gc.Equals, mock.Started)
   989  		err := machine.Refresh()
   990  		c.Assert(err, jc.ErrorIsNil)
   991  	case <-time.After(coretesting.LongWait):
   992  		c.Fatalf("timeout while waiting the mock container to start")
   993  	}
   994  
   995  	s.waitInstanceId(c, machine, instance.Id(event.InstanceId))
   996  	return event.InstanceId
   997  }
   998  
   999  func (s *lxcProvisionerSuite) expectStopped(c *gc.C, instId string) {
  1000  	// This check in particular leads to tests just hanging
  1001  	// indefinitely quite often on i386.
  1002  	coretesting.SkipIfI386(c, "lp:1425569")
  1003  
  1004  	s.State.StartSync()
  1005  	select {
  1006  	case event := <-s.events:
  1007  		c.Assert(event.Action, gc.Equals, mock.Stopped)
  1008  	case <-time.After(coretesting.LongWait):
  1009  		c.Fatalf("timeout while waiting the mock container to stop")
  1010  	}
  1011  
  1012  	select {
  1013  	case event := <-s.events:
  1014  		c.Assert(event.Action, gc.Equals, mock.Destroyed)
  1015  		c.Assert(event.InstanceId, gc.Equals, instId)
  1016  	case <-time.After(coretesting.LongWait):
  1017  		c.Fatalf("timeout while waiting the mock container to get destroyed")
  1018  	}
  1019  }
  1020  
  1021  func (s *lxcProvisionerSuite) expectNoEvents(c *gc.C) {
  1022  	select {
  1023  	case event := <-s.events:
  1024  		c.Fatalf("unexpected event %#v", event)
  1025  	case <-time.After(coretesting.ShortWait):
  1026  		return
  1027  	}
  1028  }
  1029  
  1030  func (s *lxcProvisionerSuite) TearDownTest(c *gc.C) {
  1031  	close(s.events)
  1032  	s.lxcSuite.TearDownTest(c)
  1033  	s.CommonProvisionerSuite.TearDownTest(c)
  1034  }
  1035  
  1036  func (s *lxcProvisionerSuite) newLxcProvisioner(c *gc.C) provisioner.Provisioner {
  1037  	parentMachineTag := names.NewMachineTag("0")
  1038  	agentConfig := s.AgentConfigForTag(c, parentMachineTag)
  1039  	managerConfig := container.ManagerConfig{
  1040  		container.ConfigName: "juju",
  1041  		"log-dir":            c.MkDir(),
  1042  		"use-clone":          "false",
  1043  	}
  1044  	broker, err := provisioner.NewLxcBroker(s.provisioner, agentConfig, managerConfig, &containertesting.MockURLGetter{}, false, 0)
  1045  	c.Assert(err, jc.ErrorIsNil)
  1046  	toolsFinder := (*provisioner.GetToolsFinder)(s.provisioner)
  1047  	return provisioner.NewContainerProvisioner(instance.LXC, s.provisioner, agentConfig, broker, toolsFinder)
  1048  }
  1049  
  1050  func (s *lxcProvisionerSuite) TestProvisionerStartStop(c *gc.C) {
  1051  	p := s.newLxcProvisioner(c)
  1052  	c.Assert(p.Stop(), gc.IsNil)
  1053  }
  1054  
  1055  func (s *lxcProvisionerSuite) TestDoesNotStartEnvironMachines(c *gc.C) {
  1056  	p := s.newLxcProvisioner(c)
  1057  	defer stop(c, p)
  1058  
  1059  	// Check that an instance is not provisioned when the machine is created.
  1060  	_, err := s.State.AddMachine(coretesting.FakeDefaultSeries, state.JobHostUnits)
  1061  	c.Assert(err, jc.ErrorIsNil)
  1062  
  1063  	s.expectNoEvents(c)
  1064  }
  1065  
  1066  func (s *lxcProvisionerSuite) TestDoesNotHaveRetryWatcher(c *gc.C) {
  1067  	p := s.newLxcProvisioner(c)
  1068  	defer stop(c, p)
  1069  
  1070  	w, err := provisioner.GetRetryWatcher(p)
  1071  	c.Assert(w, gc.IsNil)
  1072  	c.Assert(err, jc.Satisfies, errors.IsNotImplemented)
  1073  }
  1074  
  1075  func (s *lxcProvisionerSuite) addContainer(c *gc.C) *state.Machine {
  1076  	template := state.MachineTemplate{
  1077  		Series: coretesting.FakeDefaultSeries,
  1078  		Jobs:   []state.MachineJob{state.JobHostUnits},
  1079  	}
  1080  	container, err := s.State.AddMachineInsideMachine(template, "0", instance.LXC)
  1081  	c.Assert(err, jc.ErrorIsNil)
  1082  	return container
  1083  }
  1084  
  1085  func (s *lxcProvisionerSuite) TestContainerStartedAndStopped(c *gc.C) {
  1086  	coretesting.SkipIfI386(c, "lp:1425569")
  1087  
  1088  	p := s.newLxcProvisioner(c)
  1089  	defer stop(c, p)
  1090  
  1091  	container := s.addContainer(c)
  1092  	name := "juju-" + container.Tag().String()
  1093  	containertesting.EnsureLXCRootFSEtcNetwork(c, name)
  1094  	instId := s.expectStarted(c, container)
  1095  
  1096  	// ...and removed, along with the machine, when the machine is Dead.
  1097  	c.Assert(container.EnsureDead(), gc.IsNil)
  1098  	s.expectStopped(c, instId)
  1099  	s.waitRemoved(c, container)
  1100  }
  1101  
  1102  func (s *lxcProvisionerSuite) TestLXCProvisionerObservesConfigChanges(c *gc.C) {
  1103  	p := s.newLxcProvisioner(c)
  1104  	defer stop(c, p)
  1105  	s.assertProvisionerObservesConfigChanges(c, p)
  1106  }
  1107  
  1108  type fakeAPI struct {
  1109  	*gitjujutesting.Stub
  1110  
  1111  	fakeContainerConfig params.ContainerConfig
  1112  	fakeInterfaceInfo   network.InterfaceInfo
  1113  }
  1114  
  1115  var _ provisioner.APICalls = (*fakeAPI)(nil)
  1116  
  1117  var fakeInterfaceInfo network.InterfaceInfo = network.InterfaceInfo{
  1118  	DeviceIndex:    0,
  1119  	MACAddress:     "aa:bb:cc:dd:ee:ff",
  1120  	CIDR:           "0.1.2.0/24",
  1121  	InterfaceName:  "dummy0",
  1122  	Address:        network.NewAddress("0.1.2.3"),
  1123  	GatewayAddress: network.NewAddress("0.1.2.1"),
  1124  }
  1125  
  1126  var fakeContainerConfig = params.ContainerConfig{
  1127  	UpdateBehavior:          &params.UpdateBehavior{true, true},
  1128  	ProviderType:            "fake",
  1129  	AuthorizedKeys:          coretesting.FakeAuthKeys,
  1130  	SSLHostnameVerification: true,
  1131  }
  1132  
  1133  func NewFakeAPI() *fakeAPI {
  1134  	return &fakeAPI{
  1135  		Stub:                &gitjujutesting.Stub{},
  1136  		fakeContainerConfig: fakeContainerConfig,
  1137  		fakeInterfaceInfo:   fakeInterfaceInfo,
  1138  	}
  1139  }
  1140  
  1141  func (f *fakeAPI) ContainerConfig() (params.ContainerConfig, error) {
  1142  	f.MethodCall(f, "ContainerConfig")
  1143  	if err := f.NextErr(); err != nil {
  1144  		return params.ContainerConfig{}, err
  1145  	}
  1146  	return f.fakeContainerConfig, nil
  1147  }
  1148  
  1149  func (f *fakeAPI) PrepareContainerInterfaceInfo(tag names.MachineTag) ([]network.InterfaceInfo, error) {
  1150  	f.MethodCall(f, "PrepareContainerInterfaceInfo", tag)
  1151  	if err := f.NextErr(); err != nil {
  1152  		return nil, err
  1153  	}
  1154  	return []network.InterfaceInfo{f.fakeInterfaceInfo}, nil
  1155  }
  1156  
  1157  func (f *fakeAPI) GetContainerInterfaceInfo(tag names.MachineTag) ([]network.InterfaceInfo, error) {
  1158  	f.MethodCall(f, "GetContainerInterfaceInfo", tag)
  1159  	if err := f.NextErr(); err != nil {
  1160  		return nil, err
  1161  	}
  1162  	return []network.InterfaceInfo{f.fakeInterfaceInfo}, nil
  1163  }