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