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