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