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 }