github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/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 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(&configureZFS, func() {}) 83 s.PatchValue(&configureLXDBridge, func() error { return nil }) 84 s.PatchValue(&getLXDConfigSetter, func() (configSetter, error) { 85 return &mockConfigSetter{}, nil 86 }) 87 // Fake the lxc executable for all the tests. 88 testing.PatchExecutableAsEchoArgs(c, s, "lxc") 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 type mockConfigSetter struct { 126 keys []string 127 values []string 128 } 129 130 func (m *mockConfigSetter) SetConfig(key, value string) error { 131 m.keys = append(m.keys, key) 132 m.values = append(m.values, value) 133 return nil 134 } 135 136 func (s *InitialiserSuite) TestConfigureProxies(c *gc.C) { 137 // This test is safe on windows because it mocks out all lxd moving parts. 138 setter := &mockConfigSetter{} 139 s.PatchValue(&getLXDConfigSetter, func() (configSetter, error) { 140 return setter, nil 141 }) 142 143 proxies := proxy.Settings{ 144 Http: "http://test.local/http/proxy", 145 Https: "http://test.local/https/proxy", 146 NoProxy: "test.local,localhost", 147 } 148 err := ConfigureLXDProxies(proxies) 149 c.Assert(err, jc.ErrorIsNil) 150 151 c.Check(setter.keys, jc.DeepEquals, []string{ 152 "core.proxy_http", "core.proxy_https", "core.proxy_ignore_hosts", 153 }) 154 c.Check(setter.values, jc.DeepEquals, []string{ 155 "http://test.local/http/proxy", "http://test.local/https/proxy", "test.local,localhost", 156 }) 157 } 158 159 func (s *InitialiserSuite) TestInitializeSetsProxies(c *gc.C) { 160 if runtime.GOOS == "windows" { 161 c.Skip("no lxd on windows") 162 } 163 164 setter := &mockConfigSetter{} 165 s.PatchValue(&getLXDConfigSetter, func() (configSetter, error) { 166 return setter, nil 167 }) 168 169 s.PatchEnvironment("http_proxy", "http://test.local/http/proxy") 170 s.PatchEnvironment("https_proxy", "http://test.local/https/proxy") 171 s.PatchEnvironment("no_proxy", "test.local,localhost") 172 173 container := NewContainerInitialiser("") 174 err := container.Initialise() 175 c.Assert(err, jc.ErrorIsNil) 176 177 c.Check(setter.keys, jc.DeepEquals, []string{ 178 "core.proxy_http", "core.proxy_https", "core.proxy_ignore_hosts", 179 }) 180 c.Check(setter.values, jc.DeepEquals, []string{ 181 "http://test.local/http/proxy", "http://test.local/https/proxy", "test.local,localhost", 182 }) 183 } 184 185 func (s *InitialiserSuite) TestFindAvailableSubnetWithInterfaceAddrsError(c *gc.C) { 186 s.PatchValue(&interfaceAddrs, func() ([]net.Addr, error) { 187 return nil, errors.New("boom!") 188 }) 189 subnet, err := findNextAvailableIPv4Subnet() 190 c.Assert(err, gc.ErrorMatches, "cannot get network interface addresses: boom!") 191 c.Assert(subnet, gc.Equals, "") 192 } 193 194 type testFindSubnetAddr struct { 195 val string 196 } 197 198 func (a testFindSubnetAddr) Network() string { 199 return "ip+net" 200 } 201 202 func (a testFindSubnetAddr) String() string { 203 return a.val 204 } 205 206 func testAddresses(c *gc.C, networks ...string) ([]net.Addr, error) { 207 addrs := make([]net.Addr, 0) 208 for _, n := range networks { 209 _, _, err := net.ParseCIDR(n) 210 if err != nil { 211 return nil, err 212 } 213 c.Assert(err, gc.IsNil) 214 addrs = append(addrs, testFindSubnetAddr{n}) 215 } 216 return addrs, nil 217 } 218 219 func (s *InitialiserSuite) TestFindAvailableSubnetWithNoAddresses(c *gc.C) { 220 s.PatchValue(&interfaceAddrs, func() ([]net.Addr, error) { 221 return testAddresses(c) 222 }) 223 subnet, err := findNextAvailableIPv4Subnet() 224 c.Assert(err, gc.IsNil) 225 c.Assert(subnet, gc.Equals, "0") 226 } 227 228 func (s *InitialiserSuite) TestFindAvailableSubnetWithIPv6Only(c *gc.C) { 229 s.PatchValue(&interfaceAddrs, func() ([]net.Addr, error) { 230 return testAddresses(c, "fe80::aa8e:a275:7ae0:34af/64") 231 }) 232 subnet, err := findNextAvailableIPv4Subnet() 233 c.Assert(err, gc.IsNil) 234 c.Assert(subnet, gc.Equals, "0") 235 } 236 237 func (s *InitialiserSuite) TestFindAvailableSubnetWithIPv4OnlyAndNo10xSubnet(c *gc.C) { 238 s.PatchValue(&interfaceAddrs, func() ([]net.Addr, error) { 239 return testAddresses(c, "192.168.1.64/24") 240 }) 241 subnet, err := findNextAvailableIPv4Subnet() 242 c.Assert(err, gc.IsNil) 243 c.Assert(subnet, gc.Equals, "0") 244 } 245 246 func (s *InitialiserSuite) TestFindAvailableSubnetWithInvalidCIDR(c *gc.C) { 247 s.PatchValue(&interfaceAddrs, func() ([]net.Addr, error) { 248 return []net.Addr{ 249 testFindSubnetAddr{"10.0.0.1"}, 250 testFindSubnetAddr{"10.0.5.1/24"}}, nil 251 }) 252 subnet, err := findNextAvailableIPv4Subnet() 253 c.Assert(err, gc.IsNil) 254 c.Assert(subnet, gc.Equals, "6") 255 } 256 257 func (s *InitialiserSuite) TestFindAvailableSubnetWithIPv4AndExisting10xNetwork(c *gc.C) { 258 s.PatchValue(&interfaceAddrs, func() ([]net.Addr, error) { 259 return testAddresses(c, "192.168.1.64/24", "10.0.0.1/24") 260 }) 261 subnet, err := findNextAvailableIPv4Subnet() 262 c.Assert(err, gc.IsNil) 263 c.Assert(subnet, gc.Equals, "1") 264 } 265 266 func (s *InitialiserSuite) TestFindAvailableSubnetWithExisting10xNetworks(c *gc.C) { 267 s.PatchValue(&interfaceAddrs, func() ([]net.Addr, error) { 268 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") 269 }) 270 subnet, err := findNextAvailableIPv4Subnet() 271 c.Assert(err, gc.IsNil) 272 c.Assert(subnet, gc.Equals, "5") 273 } 274 275 func (s *InitialiserSuite) TestFindAvailableSubnetUpperBoundInUse(c *gc.C) { 276 s.PatchValue(&interfaceAddrs, func() ([]net.Addr, error) { 277 return testAddresses(c, "10.0.255.1/24") 278 }) 279 subnet, err := findNextAvailableIPv4Subnet() 280 c.Assert(err, gc.IsNil) 281 c.Assert(subnet, gc.Equals, "0") 282 } 283 284 func (s *InitialiserSuite) TestFindAvailableSubnetUpperBoundAndLowerBoundInUse(c *gc.C) { 285 s.PatchValue(&interfaceAddrs, func() ([]net.Addr, error) { 286 return testAddresses(c, "10.0.255.1/24", "10.0.0.1/24") 287 }) 288 subnet, err := findNextAvailableIPv4Subnet() 289 c.Assert(err, gc.IsNil) 290 c.Assert(subnet, gc.Equals, "1") 291 } 292 293 func (s *InitialiserSuite) TestFindAvailableSubnetWithFull10xSubnet(c *gc.C) { 294 s.PatchValue(&interfaceAddrs, func() ([]net.Addr, error) { 295 addrs := make([]net.Addr, 256) 296 for i := 0; i < 256; i++ { 297 subnet := fmt.Sprintf("10.0.%v.1/24", i) 298 addrs[i] = testFindSubnetAddr{subnet} 299 } 300 return addrs, nil 301 }) 302 subnet, err := findNextAvailableIPv4Subnet() 303 c.Assert(err, gc.ErrorMatches, "could not find unused subnet") 304 c.Assert(subnet, gc.Equals, "") 305 } 306 307 func (s *InitialiserSuite) TestParseLXDBridgeFileValues(c *gc.C) { 308 insignificantContent := ` 309 # Comment 1, followed by empty line. 310 311 # Comment 2, followed by empty line. 312 313 And a line that has content, but is not a comment, nor a key/value pair. 314 ` 315 for i, test := range []struct { 316 desc string 317 content string 318 expected map[string]string 319 }{{ 320 desc: "empty content", 321 content: "", 322 expected: map[string]string{}, 323 }, { 324 desc: "only comments and empty lines", 325 content: insignificantContent, 326 expected: map[string]string{}, 327 }, { 328 desc: "missing key", 329 content: "=a", 330 expected: map[string]string{}, 331 }, { 332 desc: "empty value", 333 content: "a=", 334 expected: map[string]string{ 335 "a": "", 336 }, 337 }, { 338 desc: "value defined, but empty", 339 content: `a=""`, 340 expected: map[string]string{ 341 "a": "", 342 }, 343 }, { 344 desc: "multiple entries", 345 content: "a=b\nc=d\ne=f", 346 expected: map[string]string{ 347 "a": "b", 348 "c": "d", 349 "e": "f", 350 }, 351 }, { 352 desc: "comment with leading whitespace", 353 content: " #a=b\nc=d\ne=f", 354 expected: map[string]string{ 355 "c": "d", 356 "e": "f", 357 }, 358 }, { 359 desc: "key/value pairs with leading and trailing whitespace", 360 content: " a=b\n c=d \ne=f ", 361 expected: map[string]string{ 362 "a": "b", 363 "c": "d", 364 "e": "f", 365 }, 366 }} { 367 c.Logf("test #%d - %s", i, test.desc) 368 values := parseLXDBridgeConfigValues(test.content) 369 c.Check(values, gc.DeepEquals, test.expected) 370 } 371 } 372 373 func (s *InitialiserSuite) TestParseLXDBridgeFileValuesWithRealWorldContent(c *gc.C) { 374 expected := map[string]string{ 375 "USE_LXD_BRIDGE": "true", 376 "EXISTING_BRIDGE": "", 377 "LXD_BRIDGE": "lxdbr0", 378 "LXD_CONFILE": "", 379 "LXD_DOMAIN": "lxd", 380 "LXD_IPV4_ADDR": "10.0.4.1", 381 "LXD_IPV4_NETMASK": "255.255.255.0", 382 "LXD_IPV4_NETWORK": "10.0.4.1/24", 383 "LXD_IPV4_DHCP_RANGE": "10.0.4.2,10.0.4.100", 384 "LXD_IPV4_DHCP_MAX": "50", 385 "LXD_IPV4_NAT": "true", 386 "LXD_IPV6_ADDR": "2001:470:b2b5:9999::1", 387 "LXD_IPV6_MASK": "64", 388 "LXD_IPV6_NETWORK": "2001:470:b2b5:9999::1/64", 389 "LXD_IPV6_NAT": "true", 390 "LXD_IPV6_PROXY": "true", 391 } 392 values := parseLXDBridgeConfigValues(lxdBridgeContent) 393 c.Check(values, gc.DeepEquals, expected) 394 } 395 396 func (s *InitialiserSuite) TestBridgeConfigurationWithNoChangeRequired(c *gc.C) { 397 result, err := bridgeConfiguration(lxdBridgeContent) 398 c.Assert(err, gc.IsNil) 399 c.Assert(lxdBridgeContent, gc.Equals, result) 400 } 401 402 func (s *InitialiserSuite) TestBridgeConfigurationWithInterfacesError(c *gc.C) { 403 s.PatchValue(&interfaceAddrs, func() ([]net.Addr, error) { 404 return nil, errors.New("boom!") 405 }) 406 result, err := bridgeConfiguration("") 407 c.Assert(err, gc.ErrorMatches, "cannot get network interface addresses: boom!") 408 c.Assert(result, gc.Equals, "") 409 } 410 411 func (s *InitialiserSuite) TestBridgeConfigurationWithNewSubnet(c *gc.C) { 412 s.PatchValue(&interfaceAddrs, func() ([]net.Addr, error) { 413 return testAddresses(c, "10.0.4.1/24") 414 }) 415 416 expectedValues := map[string]string{ 417 "USE_LXD_BRIDGE": "true", 418 "EXISTING_BRIDGE": "", 419 "LXD_BRIDGE": "lxdbr0", 420 "LXD_IPV4_ADDR": "10.0.5.1", 421 "LXD_IPV4_NETMASK": "255.255.255.0", 422 "LXD_IPV4_NETWORK": "10.0.5.1/24", 423 "LXD_IPV4_DHCP_RANGE": "10.0.5.2,10.0.5.254", 424 "LXD_IPV4_DHCP_MAX": "253", 425 "LXD_IPV4_NAT": "true", 426 "LXD_IPV6_PROXY": "false", 427 } 428 429 result, err := bridgeConfiguration(`LXD_IPV4_ADDR=""`) 430 c.Assert(err, gc.IsNil) 431 actualValues := parseLXDBridgeConfigValues(result) 432 c.Assert(expectedValues, gc.DeepEquals, actualValues) 433 }