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