github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/container/lxd/initialisation_test.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  // +build go1.3, linux
     5  
     6  package lxd
     7  
     8  import (
     9  	"errors"
    10  	"fmt"
    11  	"net"
    12  	"runtime"
    13  
    14  	"github.com/juju/testing"
    15  	jc "github.com/juju/testing/checkers"
    16  	"github.com/juju/utils/packaging/commands"
    17  	"github.com/juju/utils/packaging/manager"
    18  	"github.com/juju/utils/proxy"
    19  	"github.com/juju/utils/series"
    20  	gc "gopkg.in/check.v1"
    21  
    22  	coretesting "github.com/juju/juju/testing"
    23  )
    24  
    25  type InitialiserSuite struct {
    26  	coretesting.BaseSuite
    27  	calledCmds []string
    28  	testing.PatchExecHelper
    29  }
    30  
    31  var _ = gc.Suite(&InitialiserSuite{})
    32  
    33  const lxdBridgeContent = `# WARNING: Don't modify this file by hand, it is generated by debconf!
    34  # To update those values, please run "dpkg-reconfigure lxd"
    35  
    36  # Whether to setup a new bridge
    37  USE_LXD_BRIDGE="true"
    38  EXISTING_BRIDGE=""
    39  
    40  # Bridge name
    41  LXD_BRIDGE="lxdbr0"
    42  
    43  # dnsmasq configuration path
    44  LXD_CONFILE=""
    45  
    46  # dnsmasq domain
    47  LXD_DOMAIN="lxd"
    48  
    49  # IPv4
    50  LXD_IPV4_ADDR="10.0.4.1"
    51  LXD_IPV4_NETMASK="255.255.255.0"
    52  LXD_IPV4_NETWORK="10.0.4.1/24"
    53  LXD_IPV4_DHCP_RANGE="10.0.4.2,10.0.4.100"
    54  LXD_IPV4_DHCP_MAX="50"
    55  LXD_IPV4_NAT="true"
    56  
    57  # IPv6
    58  LXD_IPV6_ADDR="2001:470:b2b5:9999::1"
    59  LXD_IPV6_MASK="64"
    60  LXD_IPV6_NETWORK="2001:470:b2b5:9999::1/64"
    61  LXD_IPV6_NAT="true"
    62  
    63  # Proxy server
    64  LXD_IPV6_PROXY="true"
    65  `
    66  
    67  // getMockRunCommandWithRetry is a helper function which returns a function
    68  // with an identical signature to manager.RunCommandWithRetry which saves each
    69  // command it recieves in a slice and always returns no output, error code 0
    70  // and a nil error.
    71  func getMockRunCommandWithRetry(calledCmds *[]string) func(string, func(string) error) (string, int, error) {
    72  	return func(cmd string, fatalError func(string) error) (string, int, error) {
    73  		*calledCmds = append(*calledCmds, cmd)
    74  		return "", 0, nil
    75  	}
    76  }
    77  
    78  func (s *InitialiserSuite) SetUpTest(c *gc.C) {
    79  	s.BaseSuite.SetUpTest(c)
    80  	s.calledCmds = []string{}
    81  	s.PatchValue(&manager.RunCommandWithRetry, getMockRunCommandWithRetry(&s.calledCmds))
    82  	s.PatchValue(&configureLXDBridge, func() error { return nil })
    83  	s.PatchValue(&getLXDConfigSetter, func() (configSetter, error) {
    84  		return &mockConfigSetter{}, nil
    85  	})
    86  	// Fake the lxc executable for all the tests.
    87  	testing.PatchExecutableAsEchoArgs(c, s, "lxc")
    88  	testing.PatchExecutableAsEchoArgs(c, s, "lxd")
    89  }
    90  
    91  func (s *InitialiserSuite) TestLTSSeriesPackages(c *gc.C) {
    92  	// Momentarily, the only series with a dedicated cloud archive is precise,
    93  	// which we will use for the following test:
    94  	paccmder, err := commands.NewPackageCommander("trusty")
    95  	c.Assert(err, jc.ErrorIsNil)
    96  
    97  	s.PatchValue(&series.HostSeries, func() string { return "trusty" })
    98  	container := NewContainerInitialiser("trusty")
    99  
   100  	err = container.Initialise()
   101  	c.Assert(err, jc.ErrorIsNil)
   102  
   103  	c.Assert(s.calledCmds, gc.DeepEquals, []string{
   104  		paccmder.InstallCmd("--target-release", "trusty-backports", "lxd"),
   105  	})
   106  }
   107  
   108  func (s *InitialiserSuite) TestNoSeriesPackages(c *gc.C) {
   109  	// Here we want to test for any other series whilst avoiding the
   110  	// possibility of hitting a cloud archive-requiring release.
   111  	// As such, we simply pass an empty series.
   112  	paccmder, err := commands.NewPackageCommander("xenial")
   113  	c.Assert(err, jc.ErrorIsNil)
   114  
   115  	container := NewContainerInitialiser("")
   116  
   117  	err = container.Initialise()
   118  	c.Assert(err, jc.ErrorIsNil)
   119  
   120  	c.Assert(s.calledCmds, gc.DeepEquals, []string{
   121  		paccmder.InstallCmd("lxd"),
   122  	})
   123  }
   124  
   125  func (s *InitialiserSuite) TestLXDInitZFS(c *gc.C) {
   126  	// Patch df so it always returns 100GB
   127  	df100 := func(path string) (uint64, error) {
   128  		return 100 * 1024 * 1024 * 1024, nil
   129  	}
   130  	s.PatchValue(&df, df100)
   131  
   132  	container := NewContainerInitialiser("xenial")
   133  	err := container.Initialise()
   134  	c.Assert(err, jc.ErrorIsNil)
   135  
   136  	testing.AssertEchoArgs(c, "lxd", "init", "--auto", "--storage-backend",
   137  		"zfs", "--storage-pool", "lxd", "--storage-create-loop", "90")
   138  }
   139  
   140  type mockConfigSetter struct {
   141  	keys   []string
   142  	values []string
   143  }
   144  
   145  func (m *mockConfigSetter) SetConfig(key, value string) error {
   146  	m.keys = append(m.keys, key)
   147  	m.values = append(m.values, value)
   148  	return nil
   149  }
   150  
   151  func (s *InitialiserSuite) TestConfigureProxies(c *gc.C) {
   152  	// This test is safe on windows because it mocks out all lxd moving parts.
   153  	setter := &mockConfigSetter{}
   154  	s.PatchValue(&getLXDConfigSetter, func() (configSetter, error) {
   155  		return setter, nil
   156  	})
   157  
   158  	proxies := proxy.Settings{
   159  		Http:    "http://test.local/http/proxy",
   160  		Https:   "http://test.local/https/proxy",
   161  		NoProxy: "test.local,localhost",
   162  	}
   163  	err := ConfigureLXDProxies(proxies)
   164  	c.Assert(err, jc.ErrorIsNil)
   165  
   166  	c.Check(setter.keys, jc.DeepEquals, []string{
   167  		"core.proxy_http", "core.proxy_https", "core.proxy_ignore_hosts",
   168  	})
   169  	c.Check(setter.values, jc.DeepEquals, []string{
   170  		"http://test.local/http/proxy", "http://test.local/https/proxy", "test.local,localhost",
   171  	})
   172  }
   173  
   174  func (s *InitialiserSuite) TestInitializeSetsProxies(c *gc.C) {
   175  	if runtime.GOOS == "windows" {
   176  		c.Skip("no lxd on windows")
   177  	}
   178  
   179  	setter := &mockConfigSetter{}
   180  	s.PatchValue(&getLXDConfigSetter, func() (configSetter, error) {
   181  		return setter, nil
   182  	})
   183  
   184  	s.PatchEnvironment("http_proxy", "http://test.local/http/proxy")
   185  	s.PatchEnvironment("https_proxy", "http://test.local/https/proxy")
   186  	s.PatchEnvironment("no_proxy", "test.local,localhost")
   187  
   188  	container := NewContainerInitialiser("")
   189  	err := container.Initialise()
   190  	c.Assert(err, jc.ErrorIsNil)
   191  
   192  	c.Check(setter.keys, jc.DeepEquals, []string{
   193  		"core.proxy_http", "core.proxy_https", "core.proxy_ignore_hosts",
   194  	})
   195  	c.Check(setter.values, jc.DeepEquals, []string{
   196  		"http://test.local/http/proxy", "http://test.local/https/proxy", "test.local,localhost",
   197  	})
   198  }
   199  
   200  func (s *InitialiserSuite) TestFindAvailableSubnetWithInterfaceAddrsError(c *gc.C) {
   201  	s.PatchValue(&interfaceAddrs, func() ([]net.Addr, error) {
   202  		return nil, errors.New("boom!")
   203  	})
   204  	subnet, err := findNextAvailableIPv4Subnet()
   205  	c.Assert(err, gc.ErrorMatches, "cannot get network interface addresses: boom!")
   206  	c.Assert(subnet, gc.Equals, "")
   207  }
   208  
   209  type testFindSubnetAddr struct {
   210  	val string
   211  }
   212  
   213  func (a testFindSubnetAddr) Network() string {
   214  	return "ip+net"
   215  }
   216  
   217  func (a testFindSubnetAddr) String() string {
   218  	return a.val
   219  }
   220  
   221  func testAddresses(c *gc.C, networks ...string) ([]net.Addr, error) {
   222  	addrs := make([]net.Addr, 0)
   223  	for _, n := range networks {
   224  		_, _, err := net.ParseCIDR(n)
   225  		if err != nil {
   226  			return nil, err
   227  		}
   228  		c.Assert(err, gc.IsNil)
   229  		addrs = append(addrs, testFindSubnetAddr{n})
   230  	}
   231  	return addrs, nil
   232  }
   233  
   234  func (s *InitialiserSuite) TestFindAvailableSubnetWithNoAddresses(c *gc.C) {
   235  	s.PatchValue(&interfaceAddrs, func() ([]net.Addr, error) {
   236  		return testAddresses(c)
   237  	})
   238  	subnet, err := findNextAvailableIPv4Subnet()
   239  	c.Assert(err, gc.IsNil)
   240  	c.Assert(subnet, gc.Equals, "0")
   241  }
   242  
   243  func (s *InitialiserSuite) TestFindAvailableSubnetWithIPv6Only(c *gc.C) {
   244  	s.PatchValue(&interfaceAddrs, func() ([]net.Addr, error) {
   245  		return testAddresses(c, "fe80::aa8e:a275:7ae0:34af/64")
   246  	})
   247  	subnet, err := findNextAvailableIPv4Subnet()
   248  	c.Assert(err, gc.IsNil)
   249  	c.Assert(subnet, gc.Equals, "0")
   250  }
   251  
   252  func (s *InitialiserSuite) TestFindAvailableSubnetWithIPv4OnlyAndNo10xSubnet(c *gc.C) {
   253  	s.PatchValue(&interfaceAddrs, func() ([]net.Addr, error) {
   254  		return testAddresses(c, "192.168.1.64/24")
   255  	})
   256  	subnet, err := findNextAvailableIPv4Subnet()
   257  	c.Assert(err, gc.IsNil)
   258  	c.Assert(subnet, gc.Equals, "0")
   259  }
   260  
   261  func (s *InitialiserSuite) TestFindAvailableSubnetWithInvalidCIDR(c *gc.C) {
   262  	s.PatchValue(&interfaceAddrs, func() ([]net.Addr, error) {
   263  		return []net.Addr{
   264  			testFindSubnetAddr{"10.0.0.1"},
   265  			testFindSubnetAddr{"10.0.5.1/24"}}, nil
   266  	})
   267  	subnet, err := findNextAvailableIPv4Subnet()
   268  	c.Assert(err, gc.IsNil)
   269  	c.Assert(subnet, gc.Equals, "6")
   270  }
   271  
   272  func (s *InitialiserSuite) TestFindAvailableSubnetWithIPv4AndExisting10xNetwork(c *gc.C) {
   273  	s.PatchValue(&interfaceAddrs, func() ([]net.Addr, error) {
   274  		return testAddresses(c, "192.168.1.64/24", "10.0.0.1/24")
   275  	})
   276  	subnet, err := findNextAvailableIPv4Subnet()
   277  	c.Assert(err, gc.IsNil)
   278  	c.Assert(subnet, gc.Equals, "1")
   279  }
   280  
   281  func (s *InitialiserSuite) TestFindAvailableSubnetWithExisting10xNetworks(c *gc.C) {
   282  	s.PatchValue(&interfaceAddrs, func() ([]net.Addr, error) {
   283  		return testAddresses(c, "192.168.1.0/24", "10.0.4.1/24", "::1/128", "10.0.3.1/24", "fe80::aa8e:a275:7ae0:34af/64")
   284  	})
   285  	subnet, err := findNextAvailableIPv4Subnet()
   286  	c.Assert(err, gc.IsNil)
   287  	c.Assert(subnet, gc.Equals, "5")
   288  }
   289  
   290  func (s *InitialiserSuite) TestFindAvailableSubnetUpperBoundInUse(c *gc.C) {
   291  	s.PatchValue(&interfaceAddrs, func() ([]net.Addr, error) {
   292  		return testAddresses(c, "10.0.255.1/24")
   293  	})
   294  	subnet, err := findNextAvailableIPv4Subnet()
   295  	c.Assert(err, gc.IsNil)
   296  	c.Assert(subnet, gc.Equals, "0")
   297  }
   298  
   299  func (s *InitialiserSuite) TestFindAvailableSubnetUpperBoundAndLowerBoundInUse(c *gc.C) {
   300  	s.PatchValue(&interfaceAddrs, func() ([]net.Addr, error) {
   301  		return testAddresses(c, "10.0.255.1/24", "10.0.0.1/24")
   302  	})
   303  	subnet, err := findNextAvailableIPv4Subnet()
   304  	c.Assert(err, gc.IsNil)
   305  	c.Assert(subnet, gc.Equals, "1")
   306  }
   307  
   308  func (s *InitialiserSuite) TestFindAvailableSubnetWithFull10xSubnet(c *gc.C) {
   309  	s.PatchValue(&interfaceAddrs, func() ([]net.Addr, error) {
   310  		addrs := make([]net.Addr, 256)
   311  		for i := 0; i < 256; i++ {
   312  			subnet := fmt.Sprintf("10.0.%v.1/24", i)
   313  			addrs[i] = testFindSubnetAddr{subnet}
   314  		}
   315  		return addrs, nil
   316  	})
   317  	subnet, err := findNextAvailableIPv4Subnet()
   318  	c.Assert(err, gc.ErrorMatches, "could not find unused subnet")
   319  	c.Assert(subnet, gc.Equals, "")
   320  }
   321  
   322  func (s *InitialiserSuite) TestParseLXDBridgeFileValues(c *gc.C) {
   323  	insignificantContent := `
   324  # Comment 1, followed by empty line.
   325  
   326  # Comment 2, followed by empty line.
   327  
   328    And a line that has content, but is not a comment, nor a key/value pair.
   329  `
   330  	for i, test := range []struct {
   331  		desc     string
   332  		content  string
   333  		expected map[string]string
   334  	}{{
   335  		desc:     "empty content",
   336  		content:  "",
   337  		expected: map[string]string{},
   338  	}, {
   339  		desc:     "only comments and empty lines",
   340  		content:  insignificantContent,
   341  		expected: map[string]string{},
   342  	}, {
   343  		desc:     "missing key",
   344  		content:  "=a",
   345  		expected: map[string]string{},
   346  	}, {
   347  		desc:    "empty value",
   348  		content: "a=",
   349  		expected: map[string]string{
   350  			"a": "",
   351  		},
   352  	}, {
   353  		desc:    "value defined, but empty",
   354  		content: `a=""`,
   355  		expected: map[string]string{
   356  			"a": "",
   357  		},
   358  	}, {
   359  		desc:    "multiple entries",
   360  		content: "a=b\nc=d\ne=f",
   361  		expected: map[string]string{
   362  			"a": "b",
   363  			"c": "d",
   364  			"e": "f",
   365  		},
   366  	}, {
   367  		desc:    "comment with leading whitespace",
   368  		content: " #a=b\nc=d\ne=f",
   369  		expected: map[string]string{
   370  			"c": "d",
   371  			"e": "f",
   372  		},
   373  	}, {
   374  		desc:    "key/value pairs with leading and trailing whitespace",
   375  		content: " a=b\n c=d \ne=f ",
   376  		expected: map[string]string{
   377  			"a": "b",
   378  			"c": "d",
   379  			"e": "f",
   380  		},
   381  	}} {
   382  		c.Logf("test #%d - %s", i, test.desc)
   383  		values := parseLXDBridgeConfigValues(test.content)
   384  		c.Check(values, gc.DeepEquals, test.expected)
   385  	}
   386  }
   387  
   388  func (s *InitialiserSuite) TestParseLXDBridgeFileValuesWithRealWorldContent(c *gc.C) {
   389  	expected := map[string]string{
   390  		"USE_LXD_BRIDGE":      "true",
   391  		"EXISTING_BRIDGE":     "",
   392  		"LXD_BRIDGE":          "lxdbr0",
   393  		"LXD_CONFILE":         "",
   394  		"LXD_DOMAIN":          "lxd",
   395  		"LXD_IPV4_ADDR":       "10.0.4.1",
   396  		"LXD_IPV4_NETMASK":    "255.255.255.0",
   397  		"LXD_IPV4_NETWORK":    "10.0.4.1/24",
   398  		"LXD_IPV4_DHCP_RANGE": "10.0.4.2,10.0.4.100",
   399  		"LXD_IPV4_DHCP_MAX":   "50",
   400  		"LXD_IPV4_NAT":        "true",
   401  		"LXD_IPV6_ADDR":       "2001:470:b2b5:9999::1",
   402  		"LXD_IPV6_MASK":       "64",
   403  		"LXD_IPV6_NETWORK":    "2001:470:b2b5:9999::1/64",
   404  		"LXD_IPV6_NAT":        "true",
   405  		"LXD_IPV6_PROXY":      "true",
   406  	}
   407  	values := parseLXDBridgeConfigValues(lxdBridgeContent)
   408  	c.Check(values, gc.DeepEquals, expected)
   409  }
   410  
   411  func (s *InitialiserSuite) TestBridgeConfigurationWithNoChangeRequired(c *gc.C) {
   412  	result, err := bridgeConfiguration(lxdBridgeContent)
   413  	c.Assert(err, gc.IsNil)
   414  	c.Assert(lxdBridgeContent, gc.Equals, result)
   415  }
   416  
   417  func (s *InitialiserSuite) TestBridgeConfigurationWithInterfacesError(c *gc.C) {
   418  	s.PatchValue(&interfaceAddrs, func() ([]net.Addr, error) {
   419  		return nil, errors.New("boom!")
   420  	})
   421  	result, err := bridgeConfiguration("")
   422  	c.Assert(err, gc.ErrorMatches, "cannot get network interface addresses: boom!")
   423  	c.Assert(result, gc.Equals, "")
   424  }
   425  
   426  func (s *InitialiserSuite) TestBridgeConfigurationWithNewSubnet(c *gc.C) {
   427  	s.PatchValue(&interfaceAddrs, func() ([]net.Addr, error) {
   428  		return testAddresses(c, "10.0.4.1/24")
   429  	})
   430  
   431  	expectedValues := map[string]string{
   432  		"USE_LXD_BRIDGE":      "true",
   433  		"EXISTING_BRIDGE":     "",
   434  		"LXD_BRIDGE":          "lxdbr0",
   435  		"LXD_IPV4_ADDR":       "10.0.5.1",
   436  		"LXD_IPV4_NETMASK":    "255.255.255.0",
   437  		"LXD_IPV4_NETWORK":    "10.0.5.1/24",
   438  		"LXD_IPV4_DHCP_RANGE": "10.0.5.2,10.0.5.254",
   439  		"LXD_IPV4_DHCP_MAX":   "253",
   440  		"LXD_IPV4_NAT":        "true",
   441  		"LXD_IPV6_PROXY":      "false",
   442  	}
   443  
   444  	result, err := bridgeConfiguration(`LXD_IPV4_ADDR=""`)
   445  	c.Assert(err, gc.IsNil)
   446  	actualValues := parseLXDBridgeConfigValues(result)
   447  	c.Assert(expectedValues, gc.DeepEquals, actualValues)
   448  }