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