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