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