github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/container/lxc/lxc_test.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package lxc_test
     5  
     6  import (
     7  	"fmt"
     8  	"io/ioutil"
     9  	"net"
    10  	"os"
    11  	"path/filepath"
    12  	"runtime"
    13  	"strings"
    14  	stdtesting "testing"
    15  	"time"
    16  
    17  	"github.com/juju/errors"
    18  	"github.com/juju/loggo"
    19  	gitjujutesting "github.com/juju/testing"
    20  	jc "github.com/juju/testing/checkers"
    21  	ft "github.com/juju/testing/filetesting"
    22  	"github.com/juju/utils/proxy"
    23  	"github.com/juju/utils/set"
    24  	"github.com/juju/utils/symlink"
    25  	gc "gopkg.in/check.v1"
    26  	goyaml "gopkg.in/yaml.v1"
    27  	"launchpad.net/golxc"
    28  
    29  	"github.com/juju/juju/agent"
    30  	"github.com/juju/juju/container"
    31  	"github.com/juju/juju/container/lxc"
    32  	"github.com/juju/juju/container/lxc/mock"
    33  	lxctesting "github.com/juju/juju/container/lxc/testing"
    34  	containertesting "github.com/juju/juju/container/testing"
    35  	"github.com/juju/juju/environs/config"
    36  	"github.com/juju/juju/instance"
    37  	instancetest "github.com/juju/juju/instance/testing"
    38  	"github.com/juju/juju/network"
    39  	"github.com/juju/juju/provider/dummy"
    40  	coretesting "github.com/juju/juju/testing"
    41  )
    42  
    43  func Test(t *stdtesting.T) {
    44  	gc.TestingT(t)
    45  }
    46  
    47  type LxcSuite struct {
    48  	lxctesting.TestSuite
    49  
    50  	events   chan mock.Event
    51  	useClone bool
    52  	useAUFS  bool
    53  	logDir   string
    54  }
    55  
    56  var _ = gc.Suite(&LxcSuite{})
    57  
    58  var lxcCgroupContents = `11:hugetlb:/lxc/juju-machine-1-lxc-0
    59  10:perf_event:/lxc/juju-machine-1-lxc-0
    60  9:blkio:/lxc/juju-machine-1-lxc-0
    61  8:freezer:/lxc/juju-machine-1-lxc-0
    62  7:devices:/lxc/juju-machine-1-lxc-0
    63  6:memory:/lxc/juju-machine-1-lxc-0
    64  5:cpuacct:/lxc/juju-machine-1-lxc-0
    65  4:cpu:/lxc/juju-machine-1-lxc-0
    66  3:cpuset:/lxc/juju-machine-1-lxc-0
    67  2:name=systemd:/lxc/juju-machine-1-lxc-0
    68  `
    69  
    70  var hostCgroupContents = `11:hugetlb:/
    71  10:perf_event:/
    72  9:blkio:/
    73  8:freezer:/
    74  7:devices:/
    75  6:memory:/
    76  5:cpuacct:/
    77  4:cpu:/
    78  3:cpuset:/
    79  2:name=systemd:/
    80  `
    81  
    82  var malformedCgroupFile = `some bogus content
    83  more bogus content`
    84  
    85  func (s *LxcSuite) SetUpTest(c *gc.C) {
    86  	s.TestSuite.SetUpTest(c)
    87  	s.logDir = c.MkDir()
    88  	loggo.GetLogger("juju.container.lxc").SetLogLevel(loggo.TRACE)
    89  	s.events = make(chan mock.Event, 25)
    90  	s.TestSuite.ContainerFactory.AddListener(s.events)
    91  	s.PatchValue(&lxc.TemplateLockDir, c.MkDir())
    92  	s.PatchValue(&lxc.TemplateStopTimeout, 500*time.Millisecond)
    93  	fakeMAC, err := net.ParseMAC("aa:bb:cc:dd:ee:ff")
    94  	c.Assert(err, jc.ErrorIsNil)
    95  	s.PatchValue(lxc.DiscoverHostNIC, func() (net.Interface, error) {
    96  		return net.Interface{
    97  			Index:        1,
    98  			MTU:          4321,
    99  			Name:         "eth0",
   100  			HardwareAddr: fakeMAC,
   101  			Flags:        net.FlagUp,
   102  		}, nil
   103  	})
   104  }
   105  
   106  func (s *LxcSuite) TearDownTest(c *gc.C) {
   107  	s.TestSuite.ContainerFactory.RemoveListener(s.events)
   108  	close(s.events)
   109  	s.TestSuite.TearDownTest(c)
   110  }
   111  
   112  func (t *LxcSuite) TestPreferFastLXC(c *gc.C) {
   113  	for i, test := range []struct {
   114  		message        string
   115  		releaseVersion string
   116  		expected       bool
   117  	}{{
   118  		message: "missing release file",
   119  	}, {
   120  		message:        "precise release",
   121  		releaseVersion: "12.04",
   122  	}, {
   123  		message:        "trusty release",
   124  		releaseVersion: "14.04",
   125  		expected:       true,
   126  	}, {
   127  		message:        "unstable unicorn",
   128  		releaseVersion: "14.10",
   129  		expected:       true,
   130  	}, {
   131  		message:        "lucid",
   132  		releaseVersion: "10.04",
   133  	}} {
   134  		c.Logf("%v: %v", i, test.message)
   135  		value := lxc.PreferFastLXC(test.releaseVersion)
   136  		c.Assert(value, gc.Equals, test.expected)
   137  	}
   138  }
   139  
   140  func (s *LxcSuite) TestContainerManagerLXCClone(c *gc.C) {
   141  	type test struct {
   142  		releaseVersion string
   143  		useClone       string
   144  		expectClone    bool
   145  	}
   146  	tests := []test{{
   147  		releaseVersion: "12.04",
   148  		useClone:       "true",
   149  		expectClone:    true,
   150  	}, {
   151  		releaseVersion: "14.04",
   152  		expectClone:    true,
   153  	}, {
   154  		releaseVersion: "12.04",
   155  		useClone:       "false",
   156  	}, {
   157  		releaseVersion: "14.04",
   158  		useClone:       "false",
   159  	}}
   160  
   161  	for i, test := range tests {
   162  		c.Logf("test %d: %v", i, test)
   163  		s.PatchValue(lxc.ReleaseVersion, func() string { return test.releaseVersion })
   164  
   165  		mgr, err := lxc.NewContainerManager(container.ManagerConfig{
   166  			container.ConfigName: "juju",
   167  			"use-clone":          test.useClone,
   168  		}, &containertesting.MockURLGetter{})
   169  		c.Assert(err, jc.ErrorIsNil)
   170  		c.Check(lxc.GetCreateWithCloneValue(mgr), gc.Equals, test.expectClone)
   171  	}
   172  }
   173  
   174  func (s *LxcSuite) TestContainerDirFilesystem(c *gc.C) {
   175  	for i, test := range []struct {
   176  		message    string
   177  		output     string
   178  		expected   string
   179  		errorMatch string
   180  	}{{
   181  		message:  "btrfs",
   182  		output:   "Type\nbtrfs\n",
   183  		expected: lxc.Btrfs,
   184  	}, {
   185  		message:  "ext4",
   186  		output:   "Type\next4\n",
   187  		expected: "ext4",
   188  	}, {
   189  		message:    "not enough output",
   190  		output:     "foo",
   191  		errorMatch: "could not determine filesystem type",
   192  	}} {
   193  		c.Logf("%v: %s", i, test.message)
   194  		s.HookCommandOutput(&lxc.FsCommandOutput, []byte(test.output), nil)
   195  		value, err := lxc.ContainerDirFilesystem()
   196  		if test.errorMatch == "" {
   197  			c.Check(err, jc.ErrorIsNil)
   198  			c.Check(value, gc.Equals, test.expected)
   199  		} else {
   200  			c.Check(err, gc.ErrorMatches, test.errorMatch)
   201  		}
   202  	}
   203  }
   204  
   205  func (*LxcSuite) TestParseConfigLine(c *gc.C) {
   206  	for i, test := range []struct {
   207  		about   string
   208  		input   string
   209  		setting string
   210  		value   string
   211  	}{{
   212  		about:   "empty line",
   213  		input:   "",
   214  		setting: "",
   215  		value:   "",
   216  	}, {
   217  		about:   "line with spaces",
   218  		input:   "  line  with spaces   ",
   219  		setting: "",
   220  		value:   "",
   221  	}, {
   222  		about:   "comments",
   223  		input:   "# comment",
   224  		setting: "",
   225  		value:   "",
   226  	}, {
   227  		about:   "commented setting",
   228  		input:   "#lxc.flag = disabled",
   229  		setting: "",
   230  		value:   "",
   231  	}, {
   232  		about:   "comments with spaces",
   233  		input:   "  # comment  with   spaces ",
   234  		setting: "",
   235  		value:   "",
   236  	}, {
   237  		about:   "not a setting",
   238  		input:   "anything here",
   239  		setting: "",
   240  		value:   "",
   241  	}, {
   242  		about:   "valid setting, no whitespace",
   243  		input:   "lxc.setting=value",
   244  		setting: "lxc.setting",
   245  		value:   "value",
   246  	}, {
   247  		about:   "valid setting, with whitespace",
   248  		input:   "  lxc.setting  =  value  ",
   249  		setting: "lxc.setting",
   250  		value:   "value",
   251  	}, {
   252  		about:   "valid setting, with comment on the value",
   253  		input:   "lxc.setting = value  # comment # foo  ",
   254  		setting: "lxc.setting",
   255  		value:   "value",
   256  	}, {
   257  		about:   "valid setting, with comment, spaces and extra equals",
   258  		input:   "lxc.my.best.setting = foo=bar, but   # not really ",
   259  		setting: "lxc.my.best.setting",
   260  		value:   "foo=bar, but",
   261  	}} {
   262  		c.Logf("test %d: %s", i, test.about)
   263  		setting, value := lxc.ParseConfigLine(test.input)
   264  		c.Check(setting, gc.Equals, test.setting)
   265  		c.Check(value, gc.Equals, test.value)
   266  		if setting == "" {
   267  			c.Check(value, gc.Equals, "")
   268  		}
   269  	}
   270  }
   271  
   272  func (s *LxcSuite) TestUpdateContainerConfig(c *gc.C) {
   273  	networkConfig := container.BridgeNetworkConfig("nic42", []network.InterfaceInfo{{
   274  		DeviceIndex:    0,
   275  		CIDR:           "0.1.2.0/20",
   276  		InterfaceName:  "eth0",
   277  		MACAddress:     "aa:bb:cc:dd:ee:f0",
   278  		Address:        network.NewAddress("0.1.2.3", network.ScopeUnknown),
   279  		GatewayAddress: network.NewAddress("0.1.2.1", network.ScopeUnknown),
   280  	}, {
   281  		DeviceIndex:   1,
   282  		InterfaceName: "eth1",
   283  	}})
   284  
   285  	manager := s.makeManager(c, "test")
   286  	machineConfig, err := containertesting.MockMachineConfig("1/lxc/0")
   287  	c.Assert(err, jc.ErrorIsNil)
   288  	envConfig, err := config.New(config.NoDefaults, dummy.SampleConfig())
   289  	c.Assert(err, jc.ErrorIsNil)
   290  	machineConfig.Config = envConfig
   291  	instance := containertesting.CreateContainerWithMachineAndNetworkConfig(
   292  		c, manager, machineConfig, networkConfig,
   293  	)
   294  	name := string(instance.Id())
   295  
   296  	// Append a few extra lines to the config.
   297  	extraLines := []string{
   298  		"  lxc.rootfs =  /some/thing  # else ",
   299  		"",
   300  		"  # just comment  ",
   301  		"lxc.network.vlan.id=42",
   302  		"something else  # ignore  ",
   303  		"lxc.network.type=veth",
   304  		"lxc.network.link = foo  # comment",
   305  		"lxc.network.hwaddr = bar",
   306  	}
   307  	configPath := lxc.ContainerConfigFilename(name)
   308  	configFile, err := os.OpenFile(configPath, os.O_RDWR|os.O_APPEND, 0644)
   309  	c.Assert(err, jc.ErrorIsNil)
   310  	_, err = configFile.WriteString(strings.Join(extraLines, "\n") + "\n")
   311  	c.Assert(err, jc.ErrorIsNil)
   312  	err = configFile.Close()
   313  	c.Assert(err, jc.ErrorIsNil)
   314  
   315  	expectedConf := fmt.Sprintf(`
   316  # network config
   317  # interface "eth0"
   318  lxc.network.type = veth
   319  lxc.network.link = nic42
   320  lxc.network.flags = up
   321  lxc.network.name = eth0
   322  lxc.network.hwaddr = aa:bb:cc:dd:ee:f0
   323  lxc.network.ipv4 = 0.1.2.3/20
   324  lxc.network.ipv4.gateway = 0.1.2.1
   325  lxc.network.mtu = 4321
   326  
   327  # interface "eth1"
   328  lxc.network.type = veth
   329  lxc.network.link = nic42
   330  lxc.network.flags = up
   331  lxc.network.name = eth1
   332  lxc.network.mtu = 4321
   333  
   334  
   335  lxc.mount.entry = %s var/log/juju none defaults,bind 0 0
   336  `, s.logDir) + strings.Join(extraLines, "\n") + "\n"
   337  
   338  	lxcConfContents, err := ioutil.ReadFile(configPath)
   339  	c.Assert(err, jc.ErrorIsNil)
   340  	c.Assert(string(lxcConfContents), gc.Equals, expectedConf)
   341  
   342  	linesToReplace := []string{
   343  		"", // empty lines are ignored
   344  		"  lxc.network.type = bar # free drinks  !! ", // formatting is sanitized.
   345  		"  # comments are ignored",
   346  		"lxc.network.type=foo",                   // replace the second "type".
   347  		"lxc.network.name = em0 # renamed now",   // replace the first "name"
   348  		"lxc.network.name = em1",                 // replace the second "name"
   349  		"lxc.network.mtu = 1234",                 // replace only the first "mtu".
   350  		"lxc.network.hwaddr = ff:ee:dd:cc:bb:aa", // replace the first "hwaddr".
   351  		"lxc.network.hwaddr=deadbeef",            // replace second "hwaddr".
   352  		"lxc.network.hwaddr=nonsense",            // no third "hwaddr", so append.
   353  		"lxc.network.hwaddr = ",                  // no fourth "hwaddr" to remove - ignored.
   354  		"lxc.network.link=",                      // remove only the first "link"
   355  		"lxc.network.vlan.id=69",                 // replace.
   356  		"lxc.missing = appended",                 // missing - appended.
   357  		"lxc.network.type = phys",                // replace the third "type".
   358  		"lxc.mount.entry=",                       // delete existing "entry".
   359  		"lxc.rootfs = /foo/bar",                  // replace first "rootfs".
   360  		"lxc.rootfs = /bar/foo",                  // append new.
   361  	}
   362  	newConfig := strings.Join(linesToReplace, "\n")
   363  	updatedConfig := `
   364  # network config
   365  # interface "eth0"
   366  lxc.network.type = bar
   367  lxc.network.flags = up
   368  lxc.network.name = em0
   369  lxc.network.hwaddr = ff:ee:dd:cc:bb:aa
   370  lxc.network.ipv4 = 0.1.2.3/20
   371  lxc.network.ipv4.gateway = 0.1.2.1
   372  lxc.network.mtu = 1234
   373  
   374  # interface "eth1"
   375  lxc.network.type = foo
   376  lxc.network.link = nic42
   377  lxc.network.flags = up
   378  lxc.network.name = em1
   379  lxc.network.mtu = 4321
   380  
   381  
   382  lxc.rootfs = /foo/bar
   383  
   384    # just comment  
   385  lxc.network.vlan.id = 69
   386  something else  # ignore  
   387  lxc.network.type = phys
   388  lxc.network.link = foo  # comment
   389  lxc.network.hwaddr = deadbeef
   390  lxc.network.hwaddr = nonsense
   391  lxc.missing = appended
   392  lxc.rootfs = /bar/foo
   393  `
   394  	err = lxc.UpdateContainerConfig(name, newConfig)
   395  	c.Assert(err, jc.ErrorIsNil)
   396  	lxcConfContents, err = ioutil.ReadFile(configPath)
   397  	c.Assert(err, jc.ErrorIsNil)
   398  	c.Assert(string(lxcConfContents), gc.Equals, updatedConfig)
   399  
   400  	// Now test the example in updateContainerConfig's doc string.
   401  	oldConfig := `
   402  lxc.foo = off
   403  
   404  lxc.bar=42
   405  `
   406  	newConfig = `
   407  lxc.bar=
   408  lxc.foo = bar
   409  lxc.foo = baz # xx
   410  `
   411  	updatedConfig = `
   412  lxc.foo = bar
   413  
   414  lxc.foo = baz
   415  `
   416  	err = ioutil.WriteFile(configPath, []byte(oldConfig), 0644)
   417  	c.Assert(err, jc.ErrorIsNil)
   418  	err = lxc.UpdateContainerConfig(name, newConfig)
   419  	c.Assert(err, jc.ErrorIsNil)
   420  	lxcConfContents, err = ioutil.ReadFile(configPath)
   421  	c.Assert(err, jc.ErrorIsNil)
   422  	c.Assert(string(lxcConfContents), gc.Equals, updatedConfig)
   423  }
   424  
   425  func (*LxcSuite) TestReorderNetworkConfig(c *gc.C) {
   426  	path := c.MkDir()
   427  	configFile := filepath.Join(path, "config")
   428  	for i, test := range []struct {
   429  		about         string
   430  		input         string
   431  		shouldReorder bool
   432  		expectErr     string
   433  		output        string
   434  	}{{
   435  		about:         "empty input",
   436  		input:         "",
   437  		shouldReorder: false,
   438  		expectErr:     "",
   439  		output:        "",
   440  	}, {
   441  		about: "no network settings",
   442  		input: `
   443  # comment
   444  lxc.foo = bar
   445  
   446    lxc.test=# none
   447  lxc.bar.foo=42 # comment
   448  `,
   449  		shouldReorder: false,
   450  	}, {
   451  		about: "just one lxc.network.type",
   452  		input: `
   453  # comment
   454  lxc.foo = bar
   455  
   456  lxc.network.type = veth
   457  
   458    lxc.test=# none
   459  lxc.bar.foo=42 # comment
   460  `,
   461  		shouldReorder: false,
   462  	}, {
   463  		about: "correctly ordered network config",
   464  		input: `
   465  # Network configuration
   466  lxc.network.type = veth
   467  lxc.network.hwaddr = aa:bb:cc:dd:ee:f0
   468  lxc.network.flags = up
   469  lxc.network.link = br0
   470  lxc.network.type = veth
   471  lxc.network.flags = up
   472  lxc.network.link = br2
   473  lxc.network.hwaddr = aa:bb:cc:dd:ee:f1
   474  lxc.network.name = eth1
   475  lxc.network.type = veth
   476  lxc.network.flags = up
   477  lxc.network.link = br3
   478  lxc.network.hwaddr = aa:bb:cc:dd:ee:f2
   479  lxc.network.name = eth2
   480  lxc.hook.mount = /usr/share/lxc/config/hook.sh
   481  `,
   482  		shouldReorder: false,
   483  	}, {
   484  		about: "1 hwaddr before first type",
   485  		input: `
   486  lxc.foo = bar # stays here
   487  # Network configuration
   488    lxc.network.hwaddr = aa:bb:cc:dd:ee:f0 # comment
   489  lxc.network.type = veth    # comment2  
   490  lxc.network.flags = up # all the rest..
   491  lxc.network.link = br0 # ..is kept...
   492  lxc.network.type = veth # ..as it is.
   493  lxc.network.flags = up
   494  lxc.network.link = br2
   495  lxc.hook.mount = /usr/share/lxc/config/hook.sh
   496  `,
   497  		shouldReorder: true,
   498  		output: `
   499  lxc.foo = bar # stays here
   500  # Network configuration
   501  lxc.network.type = veth    # comment2  
   502    lxc.network.hwaddr = aa:bb:cc:dd:ee:f0 # comment
   503  lxc.network.flags = up # all the rest..
   504  lxc.network.link = br0 # ..is kept...
   505  lxc.network.type = veth # ..as it is.
   506  lxc.network.flags = up
   507  lxc.network.link = br2
   508  lxc.hook.mount = /usr/share/lxc/config/hook.sh
   509  `,
   510  	}, {
   511  		about: "several network lines before first type",
   512  		input: `
   513  lxc.foo = bar # stays here
   514  # Network configuration
   515    lxc.network.hwaddr = aa:bb:cc:dd:ee:f0 # comment
   516  lxc.network.flags = up # first up
   517  lxc.network.link = br0
   518  lxc.network.type = veth    # comment2  
   519  lxc.network.type = vlan
   520  lxc.network.flags = up # all the rest..
   521  lxc.network.link = br1 # ...is kept...
   522  lxc.network.vlan.id = 42 # ...as it is.
   523  lxc.hook.mount = /usr/share/lxc/config/hook.sh
   524  `,
   525  		shouldReorder: true,
   526  		output: `
   527  lxc.foo = bar # stays here
   528  # Network configuration
   529  lxc.network.type = veth    # comment2  
   530    lxc.network.hwaddr = aa:bb:cc:dd:ee:f0 # comment
   531  lxc.network.flags = up # first up
   532  lxc.network.link = br0
   533  lxc.network.type = vlan
   534  lxc.network.flags = up # all the rest..
   535  lxc.network.link = br1 # ...is kept...
   536  lxc.network.vlan.id = 42 # ...as it is.
   537  lxc.hook.mount = /usr/share/lxc/config/hook.sh
   538  `,
   539  	}, {
   540  		about: "one network setting without lxc.network.type",
   541  		input: `
   542  # comment
   543  lxc.foo = bar
   544  
   545  lxc.network.anything=goes#badly
   546  
   547    lxc.test=# none
   548  lxc.bar.foo=42 # comment
   549  `,
   550  		expectErr: `cannot have line\(s\) ".*" without lxc.network.type in config ".*"`,
   551  	}, {
   552  		about: "several network settings without lxc.network.type",
   553  		input: `
   554  # comment
   555  lxc.foo = bar
   556  
   557  lxc.network.anything=goes#badly
   558  lxc.network.vlan.id = 42
   559  lxc.network.name = foo
   560  
   561    lxc.test=# none
   562  lxc.bar.foo=42 # comment
   563  `,
   564  		expectErr: `cannot have line\(s\) ".*" without lxc.network.type in config ".*"`,
   565  	}} {
   566  		c.Logf("test %d: %q", i, test.about)
   567  		err := ioutil.WriteFile(configFile, []byte(test.input), 0644)
   568  		c.Assert(err, jc.ErrorIsNil)
   569  		wasReordered, err := lxc.ReorderNetworkConfig(configFile)
   570  		if test.expectErr != "" {
   571  			c.Check(err, gc.ErrorMatches, test.expectErr)
   572  			c.Check(wasReordered, gc.Equals, test.shouldReorder)
   573  			continue
   574  		}
   575  		data, err := ioutil.ReadFile(configFile)
   576  		c.Assert(err, jc.ErrorIsNil)
   577  		if test.shouldReorder {
   578  			c.Check(string(data), gc.Equals, test.output)
   579  			c.Check(wasReordered, jc.IsTrue)
   580  		} else {
   581  			c.Check(string(data), gc.Equals, test.input)
   582  			c.Check(wasReordered, jc.IsFalse)
   583  		}
   584  	}
   585  }
   586  
   587  func (s *LxcSuite) makeManager(c *gc.C, name string) container.Manager {
   588  	params := container.ManagerConfig{
   589  		container.ConfigName: name,
   590  	}
   591  	// Need to ensure use-clone is explicitly set to avoid it
   592  	// being set based on the OS version.
   593  	params["use-clone"] = fmt.Sprintf("%v", s.useClone)
   594  	params["log-dir"] = s.logDir
   595  	if s.useAUFS {
   596  		params["use-aufs"] = "true"
   597  	}
   598  	manager, err := lxc.NewContainerManager(params, &containertesting.MockURLGetter{})
   599  	c.Assert(err, jc.ErrorIsNil)
   600  	return manager
   601  }
   602  
   603  func (*LxcSuite) TestManagerWarnsAboutUnknownOption(c *gc.C) {
   604  	_, err := lxc.NewContainerManager(container.ManagerConfig{
   605  		container.ConfigName: "BillyBatson",
   606  		"shazam":             "Captain Marvel",
   607  	}, &containertesting.MockURLGetter{})
   608  	c.Assert(err, jc.ErrorIsNil)
   609  	c.Assert(c.GetTestLog(), jc.Contains, `WARNING juju.container unused config option: "shazam" -> "Captain Marvel"`)
   610  }
   611  
   612  func (s *LxcSuite) TestCreateContainer(c *gc.C) {
   613  	manager := s.makeManager(c, "test")
   614  	instance := containertesting.CreateContainer(c, manager, "1/lxc/0")
   615  
   616  	name := string(instance.Id())
   617  	// Check our container config files: initial lxc.conf, the
   618  	// run-time effective config, and cloud-init userdata.
   619  	lxcConfContents, err := ioutil.ReadFile(filepath.Join(s.ContainerDir, name, "lxc.conf"))
   620  	c.Assert(err, jc.ErrorIsNil)
   621  	c.Assert(string(lxcConfContents), jc.Contains, "lxc.network.link = nic42")
   622  	lxcConfContents, err = ioutil.ReadFile(lxc.ContainerConfigFilename(name))
   623  	c.Assert(err, jc.ErrorIsNil)
   624  	c.Assert(string(lxcConfContents), jc.Contains, "lxc.network.link = nic42")
   625  
   626  	cloudInitFilename := filepath.Join(s.ContainerDir, name, "cloud-init")
   627  	data := containertesting.AssertCloudInit(c, cloudInitFilename)
   628  
   629  	x := make(map[interface{}]interface{})
   630  	err = goyaml.Unmarshal(data, &x)
   631  	c.Assert(err, jc.ErrorIsNil)
   632  
   633  	var scripts []string
   634  	for _, s := range x["runcmd"].([]interface{}) {
   635  		scripts = append(scripts, s.(string))
   636  	}
   637  
   638  	c.Assert(scripts[len(scripts)-3:], gc.DeepEquals, []string{
   639  		"start jujud-machine-1-lxc-0",
   640  		"rm $bin/tools.tar.gz && rm $bin/juju2.3.4-quantal-amd64.sha256",
   641  		"ifconfig",
   642  	})
   643  
   644  	// Check the mount point has been created inside the container.
   645  	c.Assert(filepath.Join(s.LxcDir, name, "rootfs", agent.DefaultLogDir), jc.IsDirectory)
   646  	// Check that the config file is linked in the restart dir.
   647  	expectedLinkLocation := filepath.Join(s.RestartDir, name+".conf")
   648  	expectedTarget := filepath.Join(s.LxcDir, name, "config")
   649  	linkInfo, err := os.Lstat(expectedLinkLocation)
   650  	c.Assert(err, jc.ErrorIsNil)
   651  	c.Assert(linkInfo.Mode()&os.ModeSymlink, gc.Equals, os.ModeSymlink)
   652  
   653  	location, err := symlink.Read(expectedLinkLocation)
   654  	c.Assert(err, jc.ErrorIsNil)
   655  	c.Assert(location, gc.Equals, expectedTarget)
   656  }
   657  
   658  func (s *LxcSuite) TestCreateContainerFailsWithInjectedError(c *gc.C) {
   659  	errorChannel := make(chan error, 1)
   660  	cleanup := mock.PatchTransientErrorInjectionChannel(errorChannel)
   661  	defer cleanup()
   662  
   663  	// One injected error means the container creation will fail
   664  	// but the destroy function will clean up the remaining container
   665  	// resulting in a RetryableCreationError
   666  	errorChannel <- errors.New("start error")
   667  
   668  	manager := s.makeManager(c, "test")
   669  	_, err := containertesting.CreateContainerTest(c, manager, "1/lxc/0")
   670  	c.Assert(err, gc.NotNil)
   671  
   672  	// this should be a retryable error
   673  	isRetryable := instance.IsRetryableCreationError(errors.Cause(err))
   674  	c.Assert(isRetryable, jc.IsTrue)
   675  }
   676  
   677  func (s *LxcSuite) TestCreateContainerWithInjectedErrorDestroyFails(c *gc.C) {
   678  	errorChannel := make(chan error, 2)
   679  	cleanup := mock.PatchTransientErrorInjectionChannel(errorChannel)
   680  	defer cleanup()
   681  
   682  	// Two injected errors mean that the container creation and subsequent
   683  	// destroy will fail. This should not result in a RetryableCreationError
   684  	// as the container was left in an error state
   685  	errorChannel <- errors.New("create error")
   686  	errorChannel <- errors.New("destroy error")
   687  
   688  	manager := s.makeManager(c, "test")
   689  	_, err := containertesting.CreateContainerTest(c, manager, "1/lxc/0")
   690  	c.Assert(err, gc.NotNil)
   691  
   692  	// this should not be a retryable error
   693  	isRetryable := instance.IsRetryableCreationError(errors.Cause(err))
   694  	c.Assert(isRetryable, jc.IsFalse)
   695  }
   696  
   697  func (s *LxcSuite) ensureTemplateStopped(name string) <-chan struct{} {
   698  	ch := make(chan struct{}, 1)
   699  	go func() {
   700  		for {
   701  			template := s.ContainerFactory.New(name)
   702  			if template.IsRunning() {
   703  				template.Stop()
   704  				close(ch)
   705  				return
   706  			}
   707  			time.Sleep(50 * time.Millisecond)
   708  		}
   709  	}()
   710  	return ch
   711  }
   712  
   713  func (s *LxcSuite) AssertEvent(c *gc.C, event mock.Event, expected mock.Action, id string) {
   714  	c.Assert(event.Action, gc.Equals, expected)
   715  	c.Assert(event.InstanceId, gc.Equals, id)
   716  	if expected == mock.Created {
   717  		c.Assert(event.EnvArgs, gc.Not(gc.HasLen), 0)
   718  	}
   719  }
   720  
   721  func (s *LxcSuite) TestCreateContainerEvents(c *gc.C) {
   722  	manager := s.makeManager(c, "test")
   723  	instance := containertesting.CreateContainer(c, manager, "1")
   724  	id := string(instance.Id())
   725  	s.AssertEvent(c, <-s.events, mock.Created, id)
   726  	s.AssertEvent(c, <-s.events, mock.Started, id)
   727  }
   728  
   729  func (s *LxcSuite) TestCreateContainerEventsWithClone(c *gc.C) {
   730  	s.PatchValue(&s.useClone, true)
   731  	// The template containers are created with an upstart job that
   732  	// stops them once cloud init has finished.  We emulate that here.
   733  	template := "juju-quantal-lxc-template"
   734  	ch := s.ensureTemplateStopped(template)
   735  	defer func() { <-ch }()
   736  	manager := s.makeManager(c, "test")
   737  	instance := containertesting.CreateContainer(c, manager, "1")
   738  	id := string(instance.Id())
   739  	s.AssertEvent(c, <-s.events, mock.Created, template)
   740  	s.AssertEvent(c, <-s.events, mock.Started, template)
   741  	s.AssertEvent(c, <-s.events, mock.Stopped, template)
   742  	s.AssertEvent(c, <-s.events, mock.Cloned, template)
   743  	s.AssertEvent(c, <-s.events, mock.Started, id)
   744  }
   745  
   746  func (s *LxcSuite) createTemplate(c *gc.C) golxc.Container {
   747  	name := "juju-quantal-lxc-template"
   748  	ch := s.ensureTemplateStopped(name)
   749  	defer func() { <-ch }()
   750  	network := container.BridgeNetworkConfig("nic42", nil)
   751  	authorizedKeys := "authorized keys list"
   752  	aptProxy := proxy.Settings{}
   753  	aptMirror := "http://my.archive.ubuntu.com/ubuntu"
   754  	template, err := lxc.EnsureCloneTemplate(
   755  		"ext4",
   756  		"quantal",
   757  		network,
   758  		authorizedKeys,
   759  		aptProxy,
   760  		aptMirror,
   761  		true,
   762  		true,
   763  		&containertesting.MockURLGetter{},
   764  	)
   765  	c.Assert(err, jc.ErrorIsNil)
   766  	c.Assert(template.Name(), gc.Equals, name)
   767  
   768  	createEvent := <-s.events
   769  	c.Assert(createEvent.Action, gc.Equals, mock.Created)
   770  	c.Assert(createEvent.InstanceId, gc.Equals, name)
   771  	argsSet := set.NewStrings(createEvent.TemplateArgs...)
   772  	c.Assert(argsSet.Contains("imageURL"), jc.IsTrue)
   773  	s.AssertEvent(c, <-s.events, mock.Started, name)
   774  	s.AssertEvent(c, <-s.events, mock.Stopped, name)
   775  
   776  	autostartLink := lxc.RestartSymlink(name)
   777  	config, err := ioutil.ReadFile(lxc.ContainerConfigFilename(name))
   778  	c.Assert(err, jc.ErrorIsNil)
   779  	expected := `
   780  # network config
   781  # interface "eth0"
   782  lxc.network.type = veth
   783  lxc.network.link = nic42
   784  lxc.network.flags = up
   785  lxc.network.mtu = 4321
   786  
   787  `
   788  	// NOTE: no autostart, no mounting the log dir
   789  	c.Assert(string(config), gc.Equals, expected)
   790  	c.Assert(autostartLink, jc.DoesNotExist)
   791  
   792  	return template
   793  }
   794  
   795  func (s *LxcSuite) TestCreateContainerEventsWithCloneExistingTemplate(c *gc.C) {
   796  	s.createTemplate(c)
   797  	s.PatchValue(&s.useClone, true)
   798  	manager := s.makeManager(c, "test")
   799  	instance := containertesting.CreateContainer(c, manager, "1")
   800  	name := string(instance.Id())
   801  	cloned := <-s.events
   802  	s.AssertEvent(c, cloned, mock.Cloned, "juju-quantal-lxc-template")
   803  	c.Assert(cloned.Args, gc.IsNil)
   804  	s.AssertEvent(c, <-s.events, mock.Started, name)
   805  }
   806  
   807  func (s *LxcSuite) TestCreateContainerEventsWithCloneExistingTemplateAUFS(c *gc.C) {
   808  	s.createTemplate(c)
   809  	s.PatchValue(&s.useClone, true)
   810  	s.PatchValue(&s.useAUFS, true)
   811  	manager := s.makeManager(c, "test")
   812  	instance := containertesting.CreateContainer(c, manager, "1")
   813  	name := string(instance.Id())
   814  	cloned := <-s.events
   815  	s.AssertEvent(c, cloned, mock.Cloned, "juju-quantal-lxc-template")
   816  	c.Assert(cloned.Args, gc.DeepEquals, []string{"--snapshot", "--backingstore", "aufs"})
   817  	s.AssertEvent(c, <-s.events, mock.Started, name)
   818  }
   819  
   820  func (s *LxcSuite) TestCreateContainerWithCloneMountsAndAutostarts(c *gc.C) {
   821  	s.createTemplate(c)
   822  	s.PatchValue(&s.useClone, true)
   823  	manager := s.makeManager(c, "test")
   824  	instance := containertesting.CreateContainer(c, manager, "1")
   825  	name := string(instance.Id())
   826  
   827  	autostartLink := lxc.RestartSymlink(name)
   828  	config, err := ioutil.ReadFile(lxc.ContainerConfigFilename(name))
   829  	c.Assert(err, jc.ErrorIsNil)
   830  	mountLine := fmt.Sprintf("lxc.mount.entry = %s var/log/juju none defaults,bind 0 0", s.logDir)
   831  	c.Assert(string(config), jc.Contains, mountLine)
   832  	c.Assert(autostartLink, jc.IsSymlink)
   833  }
   834  
   835  func (s *LxcSuite) TestContainerState(c *gc.C) {
   836  	manager := s.makeManager(c, "test")
   837  	c.Logf("%#v", manager)
   838  	instance := containertesting.CreateContainer(c, manager, "1/lxc/0")
   839  
   840  	// The mock container will be immediately "running".
   841  	c.Assert(instance.Status(), gc.Equals, string(golxc.StateRunning))
   842  
   843  	// DestroyContainer stops and then destroys the container, putting it
   844  	// into "unknown" state.
   845  	err := manager.DestroyContainer(instance.Id())
   846  	c.Assert(err, jc.ErrorIsNil)
   847  	c.Assert(instance.Status(), gc.Equals, string(golxc.StateUnknown))
   848  }
   849  
   850  func (s *LxcSuite) TestDestroyContainer(c *gc.C) {
   851  	manager := s.makeManager(c, "test")
   852  	instance := containertesting.CreateContainer(c, manager, "1/lxc/0")
   853  
   854  	err := manager.DestroyContainer(instance.Id())
   855  	c.Assert(err, jc.ErrorIsNil)
   856  
   857  	name := string(instance.Id())
   858  	// Check that the container dir is no longer in the container dir
   859  	c.Assert(filepath.Join(s.ContainerDir, name), jc.DoesNotExist)
   860  	// but instead, in the removed container dir
   861  	c.Assert(filepath.Join(s.RemovedDir, name), jc.IsDirectory)
   862  }
   863  
   864  func (s *LxcSuite) TestDestroyContainerNameClash(c *gc.C) {
   865  	manager := s.makeManager(c, "test")
   866  	instance := containertesting.CreateContainer(c, manager, "1/lxc/0")
   867  
   868  	name := string(instance.Id())
   869  	targetDir := filepath.Join(s.RemovedDir, name)
   870  	err := os.MkdirAll(targetDir, 0755)
   871  	c.Assert(err, jc.ErrorIsNil)
   872  
   873  	err = manager.DestroyContainer(instance.Id())
   874  	c.Assert(err, jc.ErrorIsNil)
   875  
   876  	// Check that the container dir is no longer in the container dir
   877  	c.Assert(filepath.Join(s.ContainerDir, name), jc.DoesNotExist)
   878  	// but instead, in the removed container dir with a ".1" suffix as there was already a directory there.
   879  	c.Assert(filepath.Join(s.RemovedDir, fmt.Sprintf("%s.1", name)), jc.IsDirectory)
   880  }
   881  
   882  func (s *LxcSuite) TestNamedManagerPrefix(c *gc.C) {
   883  	manager := s.makeManager(c, "eric")
   884  	instance := containertesting.CreateContainer(c, manager, "1/lxc/0")
   885  	c.Assert(string(instance.Id()), gc.Equals, "eric-machine-1-lxc-0")
   886  }
   887  
   888  func (s *LxcSuite) TestListContainers(c *gc.C) {
   889  	foo := s.makeManager(c, "foo")
   890  	bar := s.makeManager(c, "bar")
   891  
   892  	foo1 := containertesting.CreateContainer(c, foo, "1/lxc/0")
   893  	foo2 := containertesting.CreateContainer(c, foo, "1/lxc/1")
   894  	foo3 := containertesting.CreateContainer(c, foo, "1/lxc/2")
   895  
   896  	bar1 := containertesting.CreateContainer(c, bar, "1/lxc/0")
   897  	bar2 := containertesting.CreateContainer(c, bar, "1/lxc/1")
   898  
   899  	result, err := foo.ListContainers()
   900  	c.Assert(err, jc.ErrorIsNil)
   901  	instancetest.MatchInstances(c, result, foo1, foo2, foo3)
   902  
   903  	result, err = bar.ListContainers()
   904  	c.Assert(err, jc.ErrorIsNil)
   905  	instancetest.MatchInstances(c, result, bar1, bar2)
   906  }
   907  
   908  func (s *LxcSuite) TestCreateContainerAutostarts(c *gc.C) {
   909  	manager := s.makeManager(c, "test")
   910  	instance := containertesting.CreateContainer(c, manager, "1/lxc/0")
   911  	autostartLink := lxc.RestartSymlink(string(instance.Id()))
   912  	c.Assert(autostartLink, jc.IsSymlink)
   913  }
   914  
   915  func (s *LxcSuite) TestCreateContainerNoRestartDir(c *gc.C) {
   916  	err := os.Remove(s.RestartDir)
   917  	c.Assert(err, jc.ErrorIsNil)
   918  
   919  	manager := s.makeManager(c, "test")
   920  	instance := containertesting.CreateContainer(c, manager, "1/lxc/0")
   921  	name := string(instance.Id())
   922  	autostartLink := lxc.RestartSymlink(name)
   923  	config, err := ioutil.ReadFile(lxc.ContainerConfigFilename(name))
   924  	c.Assert(err, jc.ErrorIsNil)
   925  	expected := fmt.Sprintf(`
   926  # network config
   927  # interface "eth0"
   928  lxc.network.type = veth
   929  lxc.network.link = nic42
   930  lxc.network.flags = up
   931  lxc.network.mtu = 4321
   932  
   933  lxc.start.auto = 1
   934  lxc.mount.entry = %s var/log/juju none defaults,bind 0 0
   935  `, s.logDir)
   936  	c.Assert(string(config), gc.Equals, expected)
   937  	c.Assert(autostartLink, jc.DoesNotExist)
   938  }
   939  
   940  func (s *LxcSuite) TestDestroyContainerRemovesAutostartLink(c *gc.C) {
   941  	manager := s.makeManager(c, "test")
   942  	instance := containertesting.CreateContainer(c, manager, "1/lxc/0")
   943  	err := manager.DestroyContainer(instance.Id())
   944  	c.Assert(err, jc.ErrorIsNil)
   945  	autostartLink := lxc.RestartSymlink(string(instance.Id()))
   946  	c.Assert(autostartLink, jc.SymlinkDoesNotExist)
   947  }
   948  
   949  func (s *LxcSuite) TestDestroyContainerNoRestartDir(c *gc.C) {
   950  	err := os.Remove(s.RestartDir)
   951  	c.Assert(err, jc.ErrorIsNil)
   952  
   953  	manager := s.makeManager(c, "test")
   954  	instance := containertesting.CreateContainer(c, manager, "1/lxc/0")
   955  	err = manager.DestroyContainer(instance.Id())
   956  	c.Assert(err, jc.ErrorIsNil)
   957  }
   958  
   959  type NetworkSuite struct {
   960  	coretesting.BaseSuite
   961  }
   962  
   963  var _ = gc.Suite(&NetworkSuite{})
   964  
   965  func (*NetworkSuite) TestGenerateNetworkConfig(c *gc.C) {
   966  	dhcpNIC := network.InterfaceInfo{
   967  		DeviceIndex:   0,
   968  		MACAddress:    "aa:bb:cc:dd:ee:f0",
   969  		InterfaceName: "eth0",
   970  		// The following is not part of the LXC config, but cause the
   971  		// generated cloud-init user-data to change accordingly.
   972  		ConfigType: network.ConfigDHCP,
   973  	}
   974  	staticNIC := network.InterfaceInfo{
   975  		DeviceIndex:    1,
   976  		CIDR:           "0.1.2.0/20", // used to infer the subnet mask.
   977  		MACAddress:     "aa:bb:cc:dd:ee:f1",
   978  		InterfaceName:  "eth1",
   979  		Address:        network.NewAddress("0.1.2.3", network.ScopeUnknown),
   980  		GatewayAddress: network.NewAddress("0.1.2.1", network.ScopeUnknown),
   981  		// The rest is passed to cloud-init.
   982  		ConfigType: network.ConfigStatic,
   983  		DNSServers: network.NewAddresses("ns1.invalid", "ns2.invalid"),
   984  	}
   985  	extraConfigNIC := network.InterfaceInfo{
   986  		DeviceIndex:   2,
   987  		MACAddress:    "aa:bb:cc:dd:ee:f2",
   988  		InterfaceName: "eth2",
   989  		VLANTag:       42,
   990  		NoAutoStart:   true,
   991  		// The rest is passed to cloud-init.
   992  		ConfigType: network.ConfigManual,
   993  		DNSServers: network.NewAddresses("ns1.invalid", "ns2.invalid"),
   994  		ExtraConfig: map[string]string{
   995  			"pre-up":   "ip route add default via 0.1.2.1",
   996  			"up":       "ip route add 0.1.2.1 dev eth2",
   997  			"pre-down": "ip route del 0.1.2.1 dev eth2",
   998  			"down":     "ip route del default via 0.1.2.1",
   999  		},
  1000  	}
  1001  	// Test /24 is used by default when the CIDR is invalid or empty.
  1002  	staticNICNoCIDR, staticNICBadCIDR := staticNIC, staticNIC
  1003  	staticNICNoCIDR.CIDR = ""
  1004  	staticNICBadCIDR.CIDR = "bad"
  1005  	// Test when NoAutoStart is true gateway is not added, even if there.
  1006  	staticNICNoAutoWithGW := staticNIC
  1007  	staticNICNoAutoWithGW.NoAutoStart = true
  1008  
  1009  	allNICs := []network.InterfaceInfo{dhcpNIC, staticNIC, extraConfigNIC}
  1010  	for _, test := range []struct {
  1011  		config            *container.NetworkConfig
  1012  		mtu               int
  1013  		nics              []network.InterfaceInfo
  1014  		rendered          []string
  1015  		logContains       string
  1016  		logDoesNotContain string
  1017  	}{{
  1018  		config: nil,
  1019  		mtu:    0,
  1020  		rendered: []string{
  1021  			"lxc.network.type = veth",
  1022  			"lxc.network.link = lxcbr0",
  1023  			"lxc.network.flags = up",
  1024  		},
  1025  		logContains:       `WARNING juju.container.lxc network type missing, using the default "bridge" config`,
  1026  		logDoesNotContain: `INFO juju.container.lxc setting MTU to 0 for all container network interfaces`,
  1027  	}, {
  1028  		config: lxc.DefaultNetworkConfig(),
  1029  		mtu:    42,
  1030  		rendered: []string{
  1031  			"lxc.network.type = veth",
  1032  			"lxc.network.link = lxcbr0",
  1033  			"lxc.network.flags = up",
  1034  			"lxc.network.mtu = 42",
  1035  		},
  1036  		logContains: `INFO juju.container.lxc setting MTU to 42 for all container network interfaces`,
  1037  	}, {
  1038  		config: container.BridgeNetworkConfig("foo", nil),
  1039  		mtu:    1500,
  1040  		rendered: []string{
  1041  			"lxc.network.type = veth",
  1042  			"lxc.network.link = foo",
  1043  			"lxc.network.flags = up",
  1044  			"lxc.network.mtu = 1500",
  1045  		},
  1046  		logContains: `INFO juju.container.lxc setting MTU to 1500 for all container network interfaces`,
  1047  	}, {
  1048  		config: container.PhysicalNetworkConfig("foo", nil),
  1049  		mtu:    9000,
  1050  		rendered: []string{
  1051  			"lxc.network.type = phys",
  1052  			"lxc.network.link = foo",
  1053  			"lxc.network.flags = up",
  1054  			"lxc.network.mtu = 9000",
  1055  		},
  1056  		logContains: `INFO juju.container.lxc setting MTU to 9000 for all container network interfaces`,
  1057  	}, {
  1058  		config: container.BridgeNetworkConfig("foo", allNICs),
  1059  		mtu:    9000,
  1060  		nics:   allNICs,
  1061  		rendered: []string{
  1062  			"lxc.network.type = veth",
  1063  			"lxc.network.link = foo",
  1064  			"lxc.network.flags = up",
  1065  			"lxc.network.name = eth0",
  1066  			"lxc.network.hwaddr = aa:bb:cc:dd:ee:f0",
  1067  			"lxc.network.mtu = 9000",
  1068  
  1069  			"lxc.network.type = veth",
  1070  			"lxc.network.link = foo",
  1071  			"lxc.network.flags = up",
  1072  			"lxc.network.name = eth1",
  1073  			"lxc.network.hwaddr = aa:bb:cc:dd:ee:f1",
  1074  			"lxc.network.ipv4 = 0.1.2.3/20",
  1075  			"lxc.network.ipv4.gateway = 0.1.2.1",
  1076  			"lxc.network.mtu = 9000",
  1077  
  1078  			"lxc.network.type = vlan",
  1079  			"lxc.network.vlan.id = 42",
  1080  			"lxc.network.link = foo",
  1081  			"lxc.network.name = eth2",
  1082  			"lxc.network.hwaddr = aa:bb:cc:dd:ee:f2",
  1083  			"lxc.network.mtu = 9000",
  1084  		},
  1085  		logContains: `INFO juju.container.lxc setting MTU to 9000 for all container network interfaces`,
  1086  	}, {
  1087  		config: container.BridgeNetworkConfig("foo", []network.InterfaceInfo{staticNICNoCIDR}),
  1088  		nics:   []network.InterfaceInfo{staticNICNoCIDR},
  1089  		rendered: []string{
  1090  			"lxc.network.type = veth",
  1091  			"lxc.network.link = foo",
  1092  			"lxc.network.flags = up",
  1093  			"lxc.network.name = eth1",
  1094  			"lxc.network.hwaddr = aa:bb:cc:dd:ee:f1",
  1095  			"lxc.network.ipv4 = 0.1.2.3/24",
  1096  			"lxc.network.ipv4.gateway = 0.1.2.1",
  1097  		},
  1098  		logContains: `WARNING juju.container.lxc invalid CIDR "" for interface "eth1", using /24 as fallback`,
  1099  	}, {
  1100  		config: container.BridgeNetworkConfig("foo", []network.InterfaceInfo{staticNICBadCIDR}),
  1101  		nics:   []network.InterfaceInfo{staticNICBadCIDR},
  1102  		rendered: []string{
  1103  			"lxc.network.type = veth",
  1104  			"lxc.network.link = foo",
  1105  			"lxc.network.flags = up",
  1106  			"lxc.network.name = eth1",
  1107  			"lxc.network.hwaddr = aa:bb:cc:dd:ee:f1",
  1108  			"lxc.network.ipv4 = 0.1.2.3/24",
  1109  			"lxc.network.ipv4.gateway = 0.1.2.1",
  1110  		},
  1111  		logContains: `WARNING juju.container.lxc invalid CIDR "bad" for interface "eth1", using /24 as fallback`,
  1112  	}, {
  1113  		config: container.BridgeNetworkConfig("foo", []network.InterfaceInfo{staticNICNoAutoWithGW}),
  1114  		nics:   []network.InterfaceInfo{staticNICNoAutoWithGW},
  1115  		rendered: []string{
  1116  			"lxc.network.type = veth",
  1117  			"lxc.network.link = foo",
  1118  			"lxc.network.name = eth1",
  1119  			"lxc.network.hwaddr = aa:bb:cc:dd:ee:f1",
  1120  			"lxc.network.ipv4 = 0.1.2.3/20",
  1121  		},
  1122  		logContains: `WARNING juju.container.lxc not setting IPv4 gateway "0.1.2.1" for non-auto start interface "eth1"`,
  1123  	}} {
  1124  		restorer := gitjujutesting.PatchValue(lxc.DiscoverHostNIC, func() (net.Interface, error) {
  1125  			return net.Interface{
  1126  				Index: 1,
  1127  				Name:  "eth0",
  1128  				MTU:   test.mtu,
  1129  				Flags: net.FlagUp,
  1130  			}, nil
  1131  		})
  1132  		config := lxc.GenerateNetworkConfig(test.config)
  1133  		// Parse the config to drop comments and empty lines. This is
  1134  		// needed to ensure the order of all settings match what we
  1135  		// expect to get rendered, as the order matters.
  1136  		var configLines []string
  1137  		for _, line := range strings.Split(config, "\n") {
  1138  			line = strings.TrimSpace(line)
  1139  			if line == "" || strings.HasPrefix(line, "#") {
  1140  				continue
  1141  			}
  1142  			configLines = append(configLines, line)
  1143  		}
  1144  		c.Check(configLines, jc.DeepEquals, test.rendered)
  1145  		if test.logContains != "" {
  1146  			c.Check(c.GetTestLog(), jc.Contains, test.logContains)
  1147  		}
  1148  		if test.logDoesNotContain != "" {
  1149  			c.Check(c.GetTestLog(), gc.Not(jc.Contains), test.logDoesNotContain)
  1150  		}
  1151  		// TODO(dimitern) In a follow-up, test the generated user-date
  1152  		// honors the other settings.
  1153  		restorer.Restore()
  1154  	}
  1155  }
  1156  
  1157  func (*NetworkSuite) TestNetworkConfigTemplate(c *gc.C) {
  1158  	restorer := gitjujutesting.PatchValue(lxc.DiscoverHostNIC, func() (net.Interface, error) {
  1159  		return net.Interface{
  1160  			Index: 1,
  1161  			Name:  "eth0",
  1162  			MTU:   4321,
  1163  			Flags: net.FlagUp,
  1164  		}, nil
  1165  	})
  1166  	defer restorer.Restore()
  1167  
  1168  	// Intentionally using an invalid type "foo" here to test it gets
  1169  	// changed to the default "veth" and a warning is logged.
  1170  	config := lxc.NetworkConfigTemplate(container.NetworkConfig{"foo", "bar", nil})
  1171  	// In the past, the entire lxc.conf file was just networking. With
  1172  	// the addition of the auto start, we now have to have better
  1173  	// isolate this test. As such, we parse the conf template results
  1174  	// and just get the results that start with 'lxc.network' as that
  1175  	// is what the test cares about.
  1176  	obtained := []string{}
  1177  	for _, value := range strings.Split(config, "\n") {
  1178  		if strings.HasPrefix(value, "lxc.network") {
  1179  			obtained = append(obtained, value)
  1180  		}
  1181  	}
  1182  	expected := []string{
  1183  		"lxc.network.type = veth",
  1184  		"lxc.network.link = bar",
  1185  		"lxc.network.flags = up",
  1186  		"lxc.network.mtu = 4321",
  1187  	}
  1188  	c.Assert(obtained, jc.DeepEquals, expected)
  1189  	c.Assert(
  1190  		c.GetTestLog(),
  1191  		jc.Contains,
  1192  		`WARNING juju.container.lxc unknown network type "foo", using the default "bridge" config`,
  1193  	)
  1194  	c.Assert(
  1195  		c.GetTestLog(),
  1196  		jc.Contains,
  1197  		`INFO juju.container.lxc setting MTU to 4321 for all container network interfaces`,
  1198  	)
  1199  }
  1200  
  1201  func (s *LxcSuite) TestIsLXCSupportedOnHost(c *gc.C) {
  1202  	baseDir := c.MkDir()
  1203  	cgroup := filepath.Join(baseDir, "cgroup")
  1204  
  1205  	ft.File{"cgroup", hostCgroupContents, 0400}.Create(c, baseDir)
  1206  
  1207  	s.PatchValue(lxc.InitProcessCgroupFile, cgroup)
  1208  	supports, err := lxc.IsLXCSupported()
  1209  	c.Assert(err, jc.ErrorIsNil)
  1210  	c.Assert(supports, jc.IsTrue)
  1211  
  1212  }
  1213  
  1214  func (s *LxcSuite) TestIsLXCSupportedOnLXCContainer(c *gc.C) {
  1215  	baseDir := c.MkDir()
  1216  	cgroup := filepath.Join(baseDir, "cgroup")
  1217  
  1218  	ft.File{"cgroup", lxcCgroupContents, 0400}.Create(c, baseDir)
  1219  
  1220  	s.PatchValue(lxc.InitProcessCgroupFile, cgroup)
  1221  	supports, err := lxc.IsLXCSupported()
  1222  	c.Assert(err, jc.ErrorIsNil)
  1223  	c.Assert(supports, jc.IsFalse)
  1224  
  1225  }
  1226  
  1227  func (s *LxcSuite) TestIsLXCSupportedMissingCgroupFile(c *gc.C) {
  1228  	s.PatchValue(lxc.InitProcessCgroupFile, "")
  1229  	supports, err := lxc.IsLXCSupported()
  1230  	c.Assert(err.Error(), gc.Matches, "open : no such file or directory")
  1231  	c.Assert(supports, jc.IsFalse)
  1232  }
  1233  
  1234  func (s *LxcSuite) TestIsLXCSupportedMalformedCgroupFile(c *gc.C) {
  1235  	baseDir := c.MkDir()
  1236  	cgroup := filepath.Join(baseDir, "cgroup")
  1237  
  1238  	ft.File{"cgroup", malformedCgroupFile, 0400}.Create(c, baseDir)
  1239  
  1240  	s.PatchValue(lxc.InitProcessCgroupFile, cgroup)
  1241  	supports, err := lxc.IsLXCSupported()
  1242  	c.Assert(err.Error(), gc.Equals, "Malformed cgroup file")
  1243  	c.Assert(supports, jc.IsFalse)
  1244  }
  1245  
  1246  func (s *LxcSuite) TestIsLXCSupportedNonLinuxSystem(c *gc.C) {
  1247  	if runtime.GOOS == "linux" {
  1248  		s.PatchValue(lxc.RuntimeGOOS, "windows")
  1249  	}
  1250  	supports, err := lxc.IsLXCSupported()
  1251  	c.Assert(err, jc.ErrorIsNil)
  1252  	c.Assert(supports, jc.IsFalse)
  1253  }