github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/worker/provisioner/lxc-broker_test.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package provisioner_test 5 6 import ( 7 "bytes" 8 "fmt" 9 "io/ioutil" 10 "net" 11 "path/filepath" 12 "runtime" 13 "text/template" 14 "time" 15 16 "github.com/juju/errors" 17 "github.com/juju/names" 18 gitjujutesting "github.com/juju/testing" 19 jc "github.com/juju/testing/checkers" 20 "github.com/juju/utils/set" 21 gc "gopkg.in/check.v1" 22 23 "github.com/juju/juju/agent" 24 "github.com/juju/juju/apiserver/params" 25 "github.com/juju/juju/cloudconfig/instancecfg" 26 "github.com/juju/juju/constraints" 27 "github.com/juju/juju/container" 28 "github.com/juju/juju/container/lxc/mock" 29 lxctesting "github.com/juju/juju/container/lxc/testing" 30 containertesting "github.com/juju/juju/container/testing" 31 "github.com/juju/juju/environs" 32 "github.com/juju/juju/feature" 33 "github.com/juju/juju/instance" 34 instancetest "github.com/juju/juju/instance/testing" 35 "github.com/juju/juju/juju/arch" 36 jujutesting "github.com/juju/juju/juju/testing" 37 "github.com/juju/juju/network" 38 "github.com/juju/juju/state" 39 "github.com/juju/juju/storage" 40 "github.com/juju/juju/storage/provider" 41 coretesting "github.com/juju/juju/testing" 42 coretools "github.com/juju/juju/tools" 43 "github.com/juju/juju/version" 44 "github.com/juju/juju/worker/provisioner" 45 ) 46 47 type lxcSuite struct { 48 lxctesting.TestSuite 49 events chan mock.Event 50 eventsDone chan struct{} 51 } 52 53 type lxcBrokerSuite struct { 54 lxcSuite 55 broker environs.InstanceBroker 56 agentConfig agent.ConfigSetterWriter 57 api *fakeAPI 58 } 59 60 var _ = gc.Suite(&lxcBrokerSuite{}) 61 62 func (s *lxcSuite) SetUpTest(c *gc.C) { 63 s.TestSuite.SetUpTest(c) 64 if runtime.GOOS == "windows" { 65 c.Skip("Skipping lxc tests on windows") 66 } 67 s.events = make(chan mock.Event) 68 s.eventsDone = make(chan struct{}) 69 go func() { 70 defer close(s.eventsDone) 71 for event := range s.events { 72 c.Output(3, fmt.Sprintf("lxc event: <%s, %s>", event.Action, event.InstanceId)) 73 } 74 }() 75 s.TestSuite.ContainerFactory.AddListener(s.events) 76 } 77 78 func (s *lxcSuite) TearDownTest(c *gc.C) { 79 close(s.events) 80 <-s.eventsDone 81 s.TestSuite.TearDownTest(c) 82 } 83 84 func (s *lxcBrokerSuite) SetUpTest(c *gc.C) { 85 if runtime.GOOS == "windows" { 86 c.Skip("Skipping lxc tests on windows") 87 } 88 s.lxcSuite.SetUpTest(c) 89 var err error 90 s.agentConfig, err = agent.NewAgentConfig( 91 agent.AgentConfigParams{ 92 DataDir: "/not/used/here", 93 Tag: names.NewMachineTag("1"), 94 UpgradedToVersion: version.Current.Number, 95 Password: "dummy-secret", 96 Nonce: "nonce", 97 APIAddresses: []string{"10.0.0.1:1234"}, 98 CACert: coretesting.CACert, 99 Environment: coretesting.EnvironmentTag, 100 }) 101 c.Assert(err, jc.ErrorIsNil) 102 managerConfig := container.ManagerConfig{ 103 container.ConfigName: "juju", 104 "log-dir": c.MkDir(), 105 "use-clone": "false", 106 } 107 s.api = NewFakeAPI() 108 s.broker, err = provisioner.NewLxcBroker(s.api, s.agentConfig, managerConfig, nil, false, 0) 109 c.Assert(err, jc.ErrorIsNil) 110 } 111 112 func (s *lxcBrokerSuite) instanceConfig(c *gc.C, machineId string) *instancecfg.InstanceConfig { 113 machineNonce := "fake-nonce" 114 // To isolate the tests from the host's architecture, we override it here. 115 s.PatchValue(&arch.HostArch, func() string { return arch.AMD64 }) 116 stateInfo := jujutesting.FakeStateInfo(machineId) 117 apiInfo := jujutesting.FakeAPIInfo(machineId) 118 instanceConfig, err := instancecfg.NewInstanceConfig(machineId, machineNonce, "released", "quantal", true, nil, stateInfo, apiInfo) 119 c.Assert(err, jc.ErrorIsNil) 120 // Ensure the <rootfs>/etc/network path exists. 121 containertesting.EnsureLXCRootFSEtcNetwork(c, "juju-"+names.NewMachineTag(machineId).String()) 122 return instanceConfig 123 } 124 125 func (s *lxcBrokerSuite) startInstance(c *gc.C, machineId string, volumes []storage.VolumeParams) instance.Instance { 126 instanceConfig := s.instanceConfig(c, machineId) 127 cons := constraints.Value{} 128 possibleTools := coretools.List{&coretools.Tools{ 129 Version: version.MustParseBinary("2.3.4-quantal-amd64"), 130 URL: "http://tools.testing.invalid/2.3.4-quantal-amd64.tgz", 131 }} 132 result, err := s.broker.StartInstance(environs.StartInstanceParams{ 133 Constraints: cons, 134 Tools: possibleTools, 135 InstanceConfig: instanceConfig, 136 Volumes: volumes, 137 }) 138 c.Assert(err, jc.ErrorIsNil) 139 return result.Instance 140 } 141 142 func (s *lxcBrokerSuite) maintainInstance(c *gc.C, machineId string, volumes []storage.VolumeParams) { 143 instanceConfig := s.instanceConfig(c, machineId) 144 cons := constraints.Value{} 145 possibleTools := coretools.List{&coretools.Tools{ 146 Version: version.MustParseBinary("2.3.4-quantal-amd64"), 147 URL: "http://tools.testing.invalid/2.3.4-quantal-amd64.tgz", 148 }} 149 err := s.broker.MaintainInstance(environs.StartInstanceParams{ 150 Constraints: cons, 151 Tools: possibleTools, 152 InstanceConfig: instanceConfig, 153 Volumes: volumes, 154 }) 155 c.Assert(err, jc.ErrorIsNil) 156 } 157 158 func (s *lxcBrokerSuite) assertDefaultStorageConfig(c *gc.C, lxc instance.Instance) { 159 config := filepath.Join(s.LxcDir, string(lxc.Id()), "config") 160 AssertFileContents(c, gc.Not(jc.Contains), config, "lxc.aa_profile = lxc-container-default-with-mounting") 161 } 162 163 func (s *lxcBrokerSuite) assertDefaultNetworkConfig(c *gc.C, lxc instance.Instance) { 164 lxc_conf := filepath.Join(s.ContainerDir, string(lxc.Id()), "lxc.conf") 165 expect := []string{ 166 "lxc.network.type = veth", 167 "lxc.network.link = lxcbr0", 168 } 169 AssertFileContains(c, lxc_conf, expect...) 170 } 171 172 func (s *lxcBrokerSuite) TestStartInstance(c *gc.C) { 173 machineId := "1/lxc/0" 174 s.SetFeatureFlags(feature.AddressAllocation) 175 lxc := s.startInstance(c, machineId, nil) 176 s.api.CheckCalls(c, []gitjujutesting.StubCall{{ 177 FuncName: "PrepareContainerInterfaceInfo", 178 Args: []interface{}{names.NewMachineTag("1-lxc-0")}, 179 }, { 180 FuncName: "ContainerConfig", 181 }}) 182 c.Assert(lxc.Id(), gc.Equals, instance.Id("juju-machine-1-lxc-0")) 183 c.Assert(s.lxcContainerDir(lxc), jc.IsDirectory) 184 s.assertInstances(c, lxc) 185 s.assertDefaultNetworkConfig(c, lxc) 186 s.assertDefaultStorageConfig(c, lxc) 187 } 188 189 func (s *lxcBrokerSuite) TestStartInstanceAddressAllocationDisabled(c *gc.C) { 190 machineId := "1/lxc/0" 191 lxc := s.startInstance(c, machineId, nil) 192 s.api.CheckCalls(c, []gitjujutesting.StubCall{{ 193 FuncName: "ContainerConfig", 194 }}) 195 c.Assert(lxc.Id(), gc.Equals, instance.Id("juju-machine-1-lxc-0")) 196 c.Assert(s.lxcContainerDir(lxc), jc.IsDirectory) 197 s.assertInstances(c, lxc) 198 s.assertDefaultNetworkConfig(c, lxc) 199 s.assertDefaultStorageConfig(c, lxc) 200 } 201 202 func (s *lxcBrokerSuite) TestMaintainInstance(c *gc.C) { 203 machineId := "1/lxc/0" 204 s.SetFeatureFlags(feature.AddressAllocation) 205 lxc := s.startInstance(c, machineId, nil) 206 s.api.ResetCalls() 207 208 s.maintainInstance(c, machineId, nil) 209 s.api.CheckCalls(c, []gitjujutesting.StubCall{{ 210 FuncName: "GetContainerInterfaceInfo", 211 Args: []interface{}{names.NewMachineTag("1-lxc-0")}, 212 }}) 213 c.Assert(lxc.Id(), gc.Equals, instance.Id("juju-machine-1-lxc-0")) 214 c.Assert(s.lxcContainerDir(lxc), jc.IsDirectory) 215 s.assertInstances(c, lxc) 216 s.assertDefaultNetworkConfig(c, lxc) 217 s.assertDefaultStorageConfig(c, lxc) 218 } 219 220 func (s *lxcBrokerSuite) TestMaintainInstanceAddressAllocationDisabled(c *gc.C) { 221 machineId := "1/lxc/0" 222 lxc := s.startInstance(c, machineId, nil) 223 s.api.ResetCalls() 224 225 s.maintainInstance(c, machineId, nil) 226 s.api.CheckCalls(c, []gitjujutesting.StubCall{}) 227 c.Assert(lxc.Id(), gc.Equals, instance.Id("juju-machine-1-lxc-0")) 228 c.Assert(s.lxcContainerDir(lxc), jc.IsDirectory) 229 s.assertInstances(c, lxc) 230 s.assertDefaultNetworkConfig(c, lxc) 231 s.assertDefaultStorageConfig(c, lxc) 232 } 233 234 func (s *lxcBrokerSuite) TestStartInstanceWithStorage(c *gc.C) { 235 s.api.fakeContainerConfig.AllowLXCLoopMounts = true 236 s.SetFeatureFlags(feature.AddressAllocation) 237 238 machineId := "1/lxc/0" 239 lxc := s.startInstance(c, machineId, []storage.VolumeParams{{Provider: provider.LoopProviderType}}) 240 s.api.CheckCalls(c, []gitjujutesting.StubCall{{ 241 FuncName: "PrepareContainerInterfaceInfo", 242 Args: []interface{}{names.NewMachineTag("1-lxc-0")}, 243 }, { 244 FuncName: "ContainerConfig", 245 }}) 246 c.Assert(lxc.Id(), gc.Equals, instance.Id("juju-machine-1-lxc-0")) 247 c.Assert(s.lxcContainerDir(lxc), jc.IsDirectory) 248 s.assertInstances(c, lxc) 249 // Check storage config. 250 config := filepath.Join(s.LxcDir, string(lxc.Id()), "config") 251 AssertFileContents(c, jc.Contains, config, "lxc.aa_profile = lxc-container-default-with-mounting") 252 } 253 254 func (s *lxcBrokerSuite) TestStartInstanceLoopMountsDisallowed(c *gc.C) { 255 s.api.fakeContainerConfig.AllowLXCLoopMounts = false 256 s.SetFeatureFlags(feature.AddressAllocation) 257 machineId := "1/lxc/0" 258 lxc := s.startInstance(c, machineId, []storage.VolumeParams{{Provider: provider.LoopProviderType}}) 259 s.api.CheckCalls(c, []gitjujutesting.StubCall{{ 260 FuncName: "PrepareContainerInterfaceInfo", 261 Args: []interface{}{names.NewMachineTag("1-lxc-0")}, 262 }, { 263 FuncName: "ContainerConfig", 264 }}) 265 c.Assert(lxc.Id(), gc.Equals, instance.Id("juju-machine-1-lxc-0")) 266 c.Assert(s.lxcContainerDir(lxc), jc.IsDirectory) 267 s.assertInstances(c, lxc) 268 s.assertDefaultStorageConfig(c, lxc) 269 } 270 271 func (s *lxcBrokerSuite) TestStartInstanceHostArch(c *gc.C) { 272 instanceConfig := s.instanceConfig(c, "1/lxc/0") 273 274 // Patch the host's arch, so the LXC broker will filter tools. 275 s.PatchValue(&arch.HostArch, func() string { return arch.PPC64EL }) 276 possibleTools := coretools.List{&coretools.Tools{ 277 Version: version.MustParseBinary("2.3.4-quantal-amd64"), 278 URL: "http://tools.testing.invalid/2.3.4-quantal-amd64.tgz", 279 }, { 280 Version: version.MustParseBinary("2.3.4-quantal-ppc64el"), 281 URL: "http://tools.testing.invalid/2.3.4-quantal-ppc64el.tgz", 282 }} 283 _, err := s.broker.StartInstance(environs.StartInstanceParams{ 284 Constraints: constraints.Value{}, 285 Tools: possibleTools, 286 InstanceConfig: instanceConfig, 287 }) 288 c.Assert(err, jc.ErrorIsNil) 289 c.Assert(instanceConfig.Tools.Version.Arch, gc.Equals, arch.PPC64EL) 290 } 291 292 func (s *lxcBrokerSuite) TestStartInstanceToolsArchNotFound(c *gc.C) { 293 instanceConfig := s.instanceConfig(c, "1/lxc/0") 294 295 // Patch the host's arch, so the LXC broker will filter tools. 296 s.PatchValue(&arch.HostArch, func() string { return arch.PPC64EL }) 297 possibleTools := coretools.List{&coretools.Tools{ 298 Version: version.MustParseBinary("2.3.4-quantal-amd64"), 299 URL: "http://tools.testing.invalid/2.3.4-quantal-amd64.tgz", 300 }} 301 _, err := s.broker.StartInstance(environs.StartInstanceParams{ 302 Constraints: constraints.Value{}, 303 Tools: possibleTools, 304 InstanceConfig: instanceConfig, 305 }) 306 c.Assert(err, gc.ErrorMatches, "need tools for arch ppc64el, only found \\[amd64\\]") 307 } 308 309 func (s *lxcBrokerSuite) TestStartInstanceWithBridgeEnviron(c *gc.C) { 310 s.agentConfig.SetValue(agent.LxcBridge, "br0") 311 machineId := "1/lxc/0" 312 lxc := s.startInstance(c, machineId, nil) 313 s.api.CheckCalls(c, []gitjujutesting.StubCall{{ 314 FuncName: "ContainerConfig", 315 }}) 316 c.Assert(lxc.Id(), gc.Equals, instance.Id("juju-machine-1-lxc-0")) 317 c.Assert(s.lxcContainerDir(lxc), jc.IsDirectory) 318 s.assertInstances(c, lxc) 319 // Uses default network config 320 lxc_conf := filepath.Join(s.ContainerDir, string(lxc.Id()), "lxc.conf") 321 expect := []string{ 322 "lxc.network.type = veth", 323 "lxc.network.link = br0", 324 } 325 AssertFileContains(c, lxc_conf, expect...) 326 } 327 328 func (s *lxcBrokerSuite) TestStartInstancePopulatesNetworkInfo(c *gc.C) { 329 s.SetFeatureFlags(feature.AddressAllocation) 330 s.PatchValue(provisioner.InterfaceAddrs, func(i *net.Interface) ([]net.Addr, error) { 331 return []net.Addr{&fakeAddr{"0.1.2.1/24"}}, nil 332 }) 333 fakeResolvConf := filepath.Join(c.MkDir(), "resolv.conf") 334 err := ioutil.WriteFile(fakeResolvConf, []byte("nameserver ns1.dummy\n"), 0644) 335 c.Assert(err, jc.ErrorIsNil) 336 s.PatchValue(provisioner.ResolvConf, fakeResolvConf) 337 338 instanceConfig := s.instanceConfig(c, "42") 339 possibleTools := coretools.List{&coretools.Tools{ 340 Version: version.MustParseBinary("2.3.4-quantal-amd64"), 341 URL: "http://tools.testing.invalid/2.3.4-quantal-amd64.tgz", 342 }} 343 result, err := s.broker.StartInstance(environs.StartInstanceParams{ 344 Constraints: constraints.Value{}, 345 Tools: possibleTools, 346 InstanceConfig: instanceConfig, 347 }) 348 c.Assert(err, jc.ErrorIsNil) 349 c.Assert(result.NetworkInfo, gc.HasLen, 1) 350 iface := result.NetworkInfo[0] 351 c.Assert(err, jc.ErrorIsNil) 352 c.Assert(iface, jc.DeepEquals, network.InterfaceInfo{ 353 DeviceIndex: 0, 354 CIDR: "0.1.2.0/24", 355 ConfigType: network.ConfigStatic, 356 InterfaceName: "eth0", // generated from the device index. 357 MACAddress: "aa:bb:cc:dd:ee:ff", 358 DNSServers: network.NewAddresses("ns1.dummy"), 359 Address: network.NewAddress("0.1.2.3"), 360 GatewayAddress: network.NewAddress("0.1.2.1"), 361 NetworkName: network.DefaultPrivate, 362 ProviderId: network.DefaultProviderId, 363 }) 364 } 365 366 func (s *lxcBrokerSuite) TestStopInstance(c *gc.C) { 367 lxc0 := s.startInstance(c, "1/lxc/0", nil) 368 lxc1 := s.startInstance(c, "1/lxc/1", nil) 369 lxc2 := s.startInstance(c, "1/lxc/2", nil) 370 371 s.assertInstances(c, lxc0, lxc1, lxc2) 372 err := s.broker.StopInstances(lxc0.Id()) 373 c.Assert(err, jc.ErrorIsNil) 374 s.assertInstances(c, lxc1, lxc2) 375 c.Assert(s.lxcContainerDir(lxc0), jc.DoesNotExist) 376 c.Assert(s.lxcRemovedContainerDir(lxc0), jc.IsDirectory) 377 378 err = s.broker.StopInstances(lxc1.Id(), lxc2.Id()) 379 c.Assert(err, jc.ErrorIsNil) 380 s.assertInstances(c) 381 } 382 383 func (s *lxcBrokerSuite) TestAllInstances(c *gc.C) { 384 lxc0 := s.startInstance(c, "1/lxc/0", nil) 385 lxc1 := s.startInstance(c, "1/lxc/1", nil) 386 s.assertInstances(c, lxc0, lxc1) 387 388 err := s.broker.StopInstances(lxc1.Id()) 389 c.Assert(err, jc.ErrorIsNil) 390 lxc2 := s.startInstance(c, "1/lxc/2", nil) 391 s.assertInstances(c, lxc0, lxc2) 392 } 393 394 func (s *lxcBrokerSuite) assertInstances(c *gc.C, inst ...instance.Instance) { 395 results, err := s.broker.AllInstances() 396 c.Assert(err, jc.ErrorIsNil) 397 instancetest.MatchInstances(c, results, inst...) 398 } 399 400 func (s *lxcBrokerSuite) lxcContainerDir(inst instance.Instance) string { 401 return filepath.Join(s.ContainerDir, string(inst.Id())) 402 } 403 404 func (s *lxcBrokerSuite) lxcRemovedContainerDir(inst instance.Instance) string { 405 return filepath.Join(s.RemovedDir, string(inst.Id())) 406 } 407 408 func (s *lxcBrokerSuite) TestLocalDNSServers(c *gc.C) { 409 fakeConf := filepath.Join(c.MkDir(), "resolv.conf") 410 s.PatchValue(provisioner.ResolvConf, fakeConf) 411 412 // If config is missing, that's OK. 413 dnses, dnsSearch, err := provisioner.LocalDNSServers() 414 c.Assert(err, jc.ErrorIsNil) 415 c.Assert(dnses, gc.HasLen, 0) 416 c.Assert(dnsSearch, gc.Equals, "") 417 418 // Enter some data in fakeConf. 419 data := ` 420 anything else is ignored 421 # comments are ignored 422 nameserver 0.1.2.3 # that's parsed 423 search foo.baz # comment ignored 424 # nameserver 42.42.42.42 - ignored as well 425 nameserver 8.8.8.8 426 nameserver example.com # comment after is ok 427 ` 428 err = ioutil.WriteFile(fakeConf, []byte(data), 0644) 429 c.Assert(err, jc.ErrorIsNil) 430 431 dnses, dnsSearch, err = provisioner.LocalDNSServers() 432 c.Assert(err, jc.ErrorIsNil) 433 c.Assert(dnses, jc.DeepEquals, network.NewAddresses( 434 "0.1.2.3", "8.8.8.8", "example.com", 435 )) 436 c.Assert(dnsSearch, gc.Equals, "foo.baz") 437 } 438 439 func (s *lxcBrokerSuite) TestMustParseTemplate(c *gc.C) { 440 f := func() { provisioner.MustParseTemplate("", "{{invalid}") } 441 c.Assert(f, gc.PanicMatches, `template: :1: function "invalid" not defined`) 442 443 tmpl := provisioner.MustParseTemplate("name", "X={{.X}}") 444 c.Assert(tmpl, gc.NotNil) 445 c.Assert(tmpl.Name(), gc.Equals, "name") 446 447 var buf bytes.Buffer 448 err := tmpl.Execute(&buf, struct{ X string }{"42"}) 449 c.Assert(err, jc.ErrorIsNil) 450 c.Assert(buf.String(), gc.Equals, "X=42") 451 } 452 453 func (s *lxcBrokerSuite) TestRunTemplateCommand(c *gc.C) { 454 for i, test := range []struct { 455 source string 456 exitNonZeroOK bool 457 data interface{} 458 exitCode int 459 expectErr string 460 }{{ 461 source: "echo {{.Name}}", 462 exitNonZeroOK: false, 463 data: struct{ Name string }{"foo"}, 464 exitCode: 0, 465 }, { 466 source: "exit {{.Code}}", 467 exitNonZeroOK: false, 468 data: struct{ Code int }{123}, 469 exitCode: 123, 470 expectErr: `command "exit 123" failed with exit code 123`, 471 }, { 472 source: "exit {{.Code}}", 473 exitNonZeroOK: true, 474 data: struct{ Code int }{56}, 475 exitCode: 56, 476 }, { 477 source: "exit 42", 478 exitNonZeroOK: true, 479 exitCode: 42, 480 }, { 481 source: "some-invalid-command", 482 exitNonZeroOK: false, 483 exitCode: 127, // returned by bash. 484 expectErr: `command "some-invalid-command" failed with exit code 127`, 485 }} { 486 c.Logf("test %d: %q -> %d", i, test.source, test.exitCode) 487 t, err := template.New(fmt.Sprintf("test %d", i)).Parse(test.source) 488 if !c.Check(err, jc.ErrorIsNil, gc.Commentf("parsing %q", test.source)) { 489 continue 490 } 491 exitCode, err := provisioner.RunTemplateCommand(t, test.exitNonZeroOK, test.data) 492 if test.expectErr != "" { 493 c.Check(err, gc.ErrorMatches, test.expectErr) 494 } else { 495 c.Check(err, jc.ErrorIsNil) 496 } 497 c.Check(exitCode, gc.Equals, test.exitCode) 498 } 499 } 500 501 func (s *lxcBrokerSuite) TestSetupRoutesAndIPTablesInvalidArgs(c *gc.C) { 502 // Isolate the test from the host machine. 503 gitjujutesting.PatchExecutableThrowError(c, s, "iptables", 42) 504 gitjujutesting.PatchExecutableThrowError(c, s, "ip", 123) 505 506 // Check that all the arguments are verified to be non-empty. 507 expectStartupErr := "primaryNIC, primaryAddr, bridgeName, and ifaceInfo must be all set" 508 emptyIfaceInfo := []network.InterfaceInfo{} 509 for i, test := range []struct { 510 about string 511 primaryNIC string 512 primaryAddr network.Address 513 bridgeName string 514 ifaceInfo []network.InterfaceInfo 515 expectErr string 516 }{{ 517 about: "all empty", 518 primaryNIC: "", 519 primaryAddr: network.Address{}, 520 bridgeName: "", 521 ifaceInfo: nil, 522 expectErr: expectStartupErr, 523 }, { 524 about: "all but primaryNIC empty", 525 primaryNIC: "nic", 526 primaryAddr: network.Address{}, 527 bridgeName: "", 528 ifaceInfo: nil, 529 expectErr: expectStartupErr, 530 }, { 531 about: "all but primaryAddr empty", 532 primaryNIC: "", 533 primaryAddr: network.NewAddress("0.1.2.1"), 534 bridgeName: "", 535 ifaceInfo: nil, 536 expectErr: expectStartupErr, 537 }, { 538 about: "all but bridgeName empty", 539 primaryNIC: "", 540 primaryAddr: network.Address{}, 541 bridgeName: "bridge", 542 ifaceInfo: nil, 543 expectErr: expectStartupErr, 544 }, { 545 about: "all but primaryNIC and bridgeName empty", 546 primaryNIC: "nic", 547 primaryAddr: network.Address{}, 548 bridgeName: "bridge", 549 ifaceInfo: nil, 550 expectErr: expectStartupErr, 551 }, { 552 about: "all but primaryNIC and primaryAddr empty", 553 primaryNIC: "nic", 554 primaryAddr: network.NewAddress("0.1.2.1"), 555 bridgeName: "", 556 ifaceInfo: nil, 557 expectErr: expectStartupErr, 558 }, { 559 about: "all but primaryAddr and bridgeName empty", 560 primaryNIC: "", 561 primaryAddr: network.NewAddress("0.1.2.1"), 562 bridgeName: "bridge", 563 ifaceInfo: nil, 564 expectErr: expectStartupErr, 565 }, { 566 about: "all set except ifaceInfo", 567 primaryNIC: "nic", 568 primaryAddr: network.NewAddress("0.1.2.1"), 569 bridgeName: "bridge", 570 ifaceInfo: nil, 571 expectErr: expectStartupErr, 572 }, { 573 about: "all empty (ifaceInfo set but empty)", 574 primaryNIC: "", 575 primaryAddr: network.Address{}, 576 bridgeName: "", 577 ifaceInfo: emptyIfaceInfo, 578 expectErr: expectStartupErr, 579 }, { 580 about: "all but primaryNIC empty (ifaceInfo set but empty)", 581 primaryNIC: "nic", 582 primaryAddr: network.Address{}, 583 bridgeName: "", 584 ifaceInfo: emptyIfaceInfo, 585 expectErr: expectStartupErr, 586 }, { 587 about: "all but primaryAddr empty (ifaceInfo set but empty)", 588 primaryNIC: "", 589 primaryAddr: network.NewAddress("0.1.2.1"), 590 bridgeName: "", 591 ifaceInfo: emptyIfaceInfo, 592 expectErr: expectStartupErr, 593 }, { 594 about: "all but bridgeName empty (ifaceInfo set but empty)", 595 primaryNIC: "", 596 primaryAddr: network.Address{}, 597 bridgeName: "bridge", 598 ifaceInfo: emptyIfaceInfo, 599 expectErr: expectStartupErr, 600 }, { 601 about: "just primaryAddr is empty and ifaceInfo set but empty", 602 primaryNIC: "nic", 603 primaryAddr: network.Address{}, 604 bridgeName: "bridge", 605 ifaceInfo: emptyIfaceInfo, 606 expectErr: expectStartupErr, 607 }, { 608 about: "just bridgeName is empty and ifaceInfo set but empty", 609 primaryNIC: "nic", 610 primaryAddr: network.NewAddress("0.1.2.1"), 611 bridgeName: "", 612 ifaceInfo: emptyIfaceInfo, 613 expectErr: expectStartupErr, 614 }, { 615 about: "just primaryNIC is empty and ifaceInfo set but empty", 616 primaryNIC: "", 617 primaryAddr: network.NewAddress("0.1.2.1"), 618 bridgeName: "bridge", 619 ifaceInfo: emptyIfaceInfo, 620 expectErr: expectStartupErr, 621 }, { 622 about: "all set except ifaceInfo, which is set but empty", 623 primaryNIC: "nic", 624 primaryAddr: network.NewAddress("0.1.2.1"), 625 bridgeName: "bridge", 626 ifaceInfo: emptyIfaceInfo, 627 expectErr: expectStartupErr, 628 }, { 629 about: "all set, but ifaceInfo has empty Address", 630 primaryNIC: "nic", 631 primaryAddr: network.NewAddress("0.1.2.1"), 632 bridgeName: "bridge", 633 // No Address set. 634 ifaceInfo: []network.InterfaceInfo{{DeviceIndex: 0}}, 635 expectErr: `container IP "" must be set`, 636 }} { 637 c.Logf("test %d: %s", i, test.about) 638 err := provisioner.SetupRoutesAndIPTables( 639 test.primaryNIC, 640 test.primaryAddr, 641 test.bridgeName, 642 test.ifaceInfo, 643 false, // TODO(dimitern): Untested. 644 ) 645 c.Check(err, gc.ErrorMatches, test.expectErr) 646 } 647 } 648 649 func (s *lxcBrokerSuite) TestSetupRoutesAndIPTablesIPTablesCheckError(c *gc.C) { 650 // Isolate the test from the host machine. 651 gitjujutesting.PatchExecutableThrowError(c, s, "iptables", 42) 652 gitjujutesting.PatchExecutableThrowError(c, s, "ip", 123) 653 654 ifaceInfo := []network.InterfaceInfo{{ 655 Address: network.NewAddress("0.1.2.3"), 656 }} 657 658 addr := network.NewAddress("0.1.2.1") 659 err := provisioner.SetupRoutesAndIPTables("nic", addr, "bridge", ifaceInfo, false) 660 c.Assert(err, gc.ErrorMatches, "iptables failed with unexpected exit code 42") 661 } 662 663 func (s *lxcBrokerSuite) TestSetupRoutesAndIPTablesIPTablesAddError(c *gc.C) { 664 // Isolate the test from the host machine. Patch iptables with a 665 // script which returns code=1 for the check but fails when adding 666 // the rule. 667 script := `if [[ "$3" == "-C" ]]; then exit 1; else exit 42; fi` 668 gitjujutesting.PatchExecutable(c, s, "iptables", script) 669 gitjujutesting.PatchExecutableThrowError(c, s, "ip", 123) 670 671 fakeptablesRules := map[string]provisioner.IptablesRule{ 672 "IPTablesSNAT": { 673 "nat", 674 "POSTROUTING", 675 "{{.HostIF}} {{.HostIP}}", 676 }, 677 } 678 s.PatchValue(provisioner.IptablesRules, fakeptablesRules) 679 680 ifaceInfo := []network.InterfaceInfo{{ 681 Address: network.NewAddress("0.1.2.3"), 682 }} 683 684 addr := network.NewAddress("0.1.2.1") 685 err := provisioner.SetupRoutesAndIPTables("nic", addr, "bridge", ifaceInfo, false) 686 c.Assert(err, gc.ErrorMatches, `command "iptables -t nat -I .*" failed with exit code 42`) 687 } 688 689 func (s *lxcBrokerSuite) TestSetupRoutesAndIPTablesIPRouteError(c *gc.C) { 690 // Isolate the test from the host machine. 691 // Returning code=0 from iptables means we won't add a rule. 692 gitjujutesting.PatchExecutableThrowError(c, s, "iptables", 0) 693 gitjujutesting.PatchExecutableThrowError(c, s, "ip", 123) 694 695 ifaceInfo := []network.InterfaceInfo{{ 696 Address: network.NewAddress("0.1.2.3"), 697 }} 698 699 addr := network.NewAddress("0.1.2.1") 700 err := provisioner.SetupRoutesAndIPTables("nic", addr, "bridge", ifaceInfo, false) 701 c.Assert(err, gc.ErrorMatches, 702 `command "ip route add 0.1.2.3 dev bridge" failed with exit code 123`, 703 ) 704 } 705 706 func (s *lxcBrokerSuite) TestSetupRoutesAndIPTablesAddsRuleIfMissing(c *gc.C) { 707 // Isolate the test from the host machine. Because PatchExecutable 708 // does not allow us to assert on subsequent executions of the 709 // same binary, we need to replace the iptables commands with 710 // separate ones. The check returns code=1 to trigger calling 711 // add. 712 fakeptablesRules := map[string]provisioner.IptablesRule{ 713 "IPTablesSNAT": { 714 "nat", 715 "POSTROUTING", 716 "{{.HostIF}} {{.HostIP}}", 717 }, 718 } 719 s.PatchValue(provisioner.IptablesRules, fakeptablesRules) 720 721 gitjujutesting.PatchExecutableAsEchoArgs(c, s, "iptables", 1, 0) 722 gitjujutesting.PatchExecutableAsEchoArgs(c, s, "ip") 723 724 ifaceInfo := []network.InterfaceInfo{{ 725 Address: network.NewAddress("0.1.2.3"), 726 }} 727 728 addr := network.NewAddress("0.1.2.1") 729 err := provisioner.SetupRoutesAndIPTables("nic", addr, "bridge", ifaceInfo, false) 730 c.Assert(err, jc.ErrorIsNil) 731 732 // Now verify the expected commands - since check returns 1, add 733 // will be called before ip route add. 734 735 gitjujutesting.AssertEchoArgs(c, "iptables", "-t", "nat", "-C", "POSTROUTING", "nic", "0.1.2.1") 736 gitjujutesting.AssertEchoArgs(c, "iptables", "-t", "nat", "-I", "POSTROUTING", "1", "nic", "0.1.2.1") 737 gitjujutesting.AssertEchoArgs(c, "ip", "route", "add", "0.1.2.3", "dev", "bridge") 738 } 739 740 func (s *lxcBrokerSuite) TestDiscoverPrimaryNICNetInterfacesError(c *gc.C) { 741 s.PatchValue(provisioner.NetInterfaces, func() ([]net.Interface, error) { 742 return nil, errors.New("boom!") 743 }) 744 745 nic, addr, err := provisioner.DiscoverPrimaryNIC() 746 c.Assert(err, gc.ErrorMatches, "cannot get network interfaces: boom!") 747 c.Assert(nic, gc.Equals, "") 748 c.Assert(addr, jc.DeepEquals, network.Address{}) 749 } 750 751 func (s *lxcBrokerSuite) TestDiscoverPrimaryNICInterfaceAddrsError(c *gc.C) { 752 s.PatchValue(provisioner.NetInterfaces, func() ([]net.Interface, error) { 753 return []net.Interface{{ 754 Index: 0, 755 Name: "fake", 756 Flags: net.FlagUp, 757 }}, nil 758 }) 759 s.PatchValue(provisioner.InterfaceAddrs, func(i *net.Interface) ([]net.Addr, error) { 760 return nil, errors.New("boom!") 761 }) 762 763 nic, addr, err := provisioner.DiscoverPrimaryNIC() 764 c.Assert(err, gc.ErrorMatches, `cannot get "fake" addresses: boom!`) 765 c.Assert(nic, gc.Equals, "") 766 c.Assert(addr, jc.DeepEquals, network.Address{}) 767 } 768 769 func (s *lxcBrokerSuite) TestDiscoverPrimaryNICInvalidAddr(c *gc.C) { 770 s.PatchValue(provisioner.NetInterfaces, func() ([]net.Interface, error) { 771 return []net.Interface{{ 772 Index: 0, 773 Name: "fake", 774 Flags: net.FlagUp, 775 }}, nil 776 }) 777 s.PatchValue(provisioner.InterfaceAddrs, func(i *net.Interface) ([]net.Addr, error) { 778 return []net.Addr{&fakeAddr{}}, nil 779 }) 780 781 nic, addr, err := provisioner.DiscoverPrimaryNIC() 782 c.Assert(err, gc.ErrorMatches, `cannot parse address "fakeAddr": invalid CIDR address: fakeAddr`) 783 c.Assert(nic, gc.Equals, "") 784 c.Assert(addr, jc.DeepEquals, network.Address{}) 785 } 786 787 func (s *lxcBrokerSuite) TestDiscoverPrimaryNICInterfaceNotFound(c *gc.C) { 788 s.PatchValue(provisioner.NetInterfaces, func() ([]net.Interface, error) { 789 return nil, nil 790 }) 791 792 nic, addr, err := provisioner.DiscoverPrimaryNIC() 793 c.Assert(err, gc.ErrorMatches, "cannot detect the primary network interface") 794 c.Assert(nic, gc.Equals, "") 795 c.Assert(addr, jc.DeepEquals, network.Address{}) 796 } 797 798 type fakeAddr struct{ value string } 799 800 func (f *fakeAddr) Network() string { return "net" } 801 func (f *fakeAddr) String() string { 802 if f.value != "" { 803 return f.value 804 } 805 return "fakeAddr" 806 } 807 808 var _ net.Addr = (*fakeAddr)(nil) 809 810 func (s *lxcBrokerSuite) TestDiscoverPrimaryNICSuccess(c *gc.C) { 811 s.PatchValue(provisioner.NetInterfaces, func() ([]net.Interface, error) { 812 return []net.Interface{{ 813 Index: 0, 814 Name: "lo", 815 Flags: net.FlagUp | net.FlagLoopback, // up but loopback - ignored. 816 }, { 817 Index: 1, 818 Name: "if0", 819 Flags: net.FlagPointToPoint, // not up - ignored. 820 }, { 821 Index: 2, 822 Name: "if1", 823 Flags: net.FlagUp, // up but no addresses - ignored. 824 }, { 825 Index: 3, 826 Name: "if2", 827 Flags: net.FlagUp, // up and has addresses - returned. 828 }}, nil 829 }) 830 s.PatchValue(provisioner.InterfaceAddrs, func(i *net.Interface) ([]net.Addr, error) { 831 // We should be called only for the last two NICs. The first 832 // one (if1) won't have addresses, only the last one (if2). 833 c.Assert(i, gc.NotNil) 834 c.Assert(i.Name, gc.Matches, "if[12]") 835 if i.Name == "if2" { 836 return []net.Addr{&fakeAddr{"0.1.2.3/24"}}, nil 837 } 838 // For if1 we return no addresses. 839 return nil, nil 840 }) 841 842 nic, addr, err := provisioner.DiscoverPrimaryNIC() 843 c.Assert(err, jc.ErrorIsNil) 844 c.Assert(nic, gc.Equals, "if2") 845 c.Assert(addr, jc.DeepEquals, network.NewAddress("0.1.2.3")) 846 } 847 848 func (s *lxcBrokerSuite) TestConfigureContainerNetwork(c *gc.C) { 849 // All the pieces used by this func are separately tested, we just 850 // test the integration between them. 851 s.PatchValue(provisioner.NetInterfaces, func() ([]net.Interface, error) { 852 return []net.Interface{{ 853 Index: 0, 854 Name: "fake0", 855 Flags: net.FlagUp, 856 }}, nil 857 }) 858 s.PatchValue(provisioner.InterfaceAddrs, func(i *net.Interface) ([]net.Addr, error) { 859 return []net.Addr{&fakeAddr{"0.1.2.1/24"}}, nil 860 }) 861 fakeResolvConf := filepath.Join(c.MkDir(), "resolv.conf") 862 err := ioutil.WriteFile(fakeResolvConf, []byte("nameserver ns1.dummy\n"), 0644) 863 c.Assert(err, jc.ErrorIsNil) 864 s.PatchValue(provisioner.ResolvConf, fakeResolvConf) 865 866 // When ifaceInfo is not empty it shouldn't do anything and both 867 // the error and the result are nil. 868 ifaceInfo := []network.InterfaceInfo{{DeviceIndex: 0}} 869 // First call as if we are configuring the container for the first time 870 result, err := provisioner.ConfigureContainerNetwork("42", "bridge", s.api, ifaceInfo, true, false) 871 c.Assert(err, jc.ErrorIsNil) 872 c.Assert(result, gc.IsNil) 873 s.api.CheckCalls(c, []gitjujutesting.StubCall{}) 874 875 // Next call as if the container has already been configured. 876 s.api.ResetCalls() 877 result, err = provisioner.ConfigureContainerNetwork("42", "bridge", s.api, ifaceInfo, false, false) 878 c.Assert(err, jc.ErrorIsNil) 879 c.Assert(result, gc.IsNil) 880 s.api.CheckCalls(c, []gitjujutesting.StubCall{}) 881 882 // Call as if the container already has a network configuration, but doesn't. 883 s.api.ResetCalls() 884 s.api.SetErrors(errors.NotProvisionedf("machine-42 has no network provisioning info")) 885 ifaceInfo = []network.InterfaceInfo{} 886 result, err = provisioner.ConfigureContainerNetwork("42", "bridge", s.api, ifaceInfo, false, false) 887 c.Assert(err, gc.ErrorMatches, "machine-42 has no network provisioning info not provisioned") 888 c.Assert(result, jc.DeepEquals, []network.InterfaceInfo{}) 889 s.api.CheckCalls(c, []gitjujutesting.StubCall{{ 890 FuncName: "GetContainerInterfaceInfo", 891 Args: []interface{}{names.NewMachineTag("42")}, 892 }}) 893 894 // When it's not empty, result should be populated as expected. 895 s.api.ResetCalls() 896 result, err = provisioner.ConfigureContainerNetwork("42", "bridge", s.api, ifaceInfo, false, false) 897 898 c.Assert(err, jc.ErrorIsNil) 899 c.Assert(result, gc.HasLen, 1) 900 c.Assert(err, jc.ErrorIsNil) 901 c.Assert(result, jc.DeepEquals, []network.InterfaceInfo{{ 902 DeviceIndex: 0, 903 CIDR: "0.1.2.0/24", 904 ConfigType: network.ConfigStatic, 905 InterfaceName: "eth0", // generated from the device index. 906 MACAddress: "aa:bb:cc:dd:ee:ff", 907 DNSServers: network.NewAddresses("ns1.dummy"), 908 Address: network.NewAddress("0.1.2.3"), 909 GatewayAddress: network.NewAddress("0.1.2.1"), 910 NetworkName: network.DefaultPrivate, 911 ProviderId: network.DefaultProviderId, 912 }}) 913 s.api.CheckCalls(c, []gitjujutesting.StubCall{{ 914 FuncName: "GetContainerInterfaceInfo", 915 Args: []interface{}{names.NewMachineTag("42")}, 916 }}) 917 918 s.api.ResetCalls() 919 result, err = provisioner.ConfigureContainerNetwork("42", "bridge", s.api, ifaceInfo, false, false) 920 c.Assert(result, gc.HasLen, 1) 921 c.Assert(err, jc.ErrorIsNil) 922 c.Assert(result, jc.DeepEquals, []network.InterfaceInfo{{ 923 DeviceIndex: 0, 924 CIDR: "0.1.2.0/24", 925 ConfigType: network.ConfigStatic, 926 InterfaceName: "eth0", // generated from the device index. 927 MACAddress: "aa:bb:cc:dd:ee:ff", 928 DNSServers: network.NewAddresses("ns1.dummy"), 929 Address: network.NewAddress("0.1.2.3"), 930 GatewayAddress: network.NewAddress("0.1.2.1"), 931 NetworkName: network.DefaultPrivate, 932 ProviderId: network.DefaultProviderId, 933 }}) 934 935 s.api.CheckCalls(c, []gitjujutesting.StubCall{{ 936 FuncName: "GetContainerInterfaceInfo", 937 Args: []interface{}{names.NewMachineTag("42")}, 938 }}) 939 } 940 941 type lxcProvisionerSuite struct { 942 CommonProvisionerSuite 943 lxcSuite 944 events chan mock.Event 945 } 946 947 var _ = gc.Suite(&lxcProvisionerSuite{}) 948 949 func (s *lxcProvisionerSuite) SetUpSuite(c *gc.C) { 950 if runtime.GOOS == "windows" { 951 c.Skip("Skipping lxc tests on windows") 952 } 953 s.CommonProvisionerSuite.SetUpSuite(c) 954 s.lxcSuite.SetUpSuite(c) 955 } 956 957 func (s *lxcProvisionerSuite) TearDownSuite(c *gc.C) { 958 s.lxcSuite.TearDownSuite(c) 959 s.CommonProvisionerSuite.TearDownSuite(c) 960 } 961 962 func (s *lxcProvisionerSuite) SetUpTest(c *gc.C) { 963 s.CommonProvisionerSuite.SetUpTest(c) 964 s.lxcSuite.SetUpTest(c) 965 966 s.events = make(chan mock.Event, 25) 967 s.ContainerFactory.AddListener(s.events) 968 } 969 970 func (s *lxcProvisionerSuite) expectStarted(c *gc.C, machine *state.Machine) string { 971 // This check in particular leads to tests just hanging 972 // indefinitely quite often on i386. 973 coretesting.SkipIfI386(c, "lp:1425569") 974 975 var event mock.Event 976 s.State.StartSync() 977 select { 978 case event = <-s.events: 979 c.Assert(event.Action, gc.Equals, mock.Created) 980 argsSet := set.NewStrings(event.TemplateArgs...) 981 c.Assert(argsSet.Contains("imageURL"), jc.IsTrue) 982 case <-time.After(coretesting.LongWait): 983 c.Fatalf("timeout while waiting the mock container to get created") 984 } 985 986 select { 987 case event = <-s.events: 988 c.Assert(event.Action, gc.Equals, mock.Started) 989 err := machine.Refresh() 990 c.Assert(err, jc.ErrorIsNil) 991 case <-time.After(coretesting.LongWait): 992 c.Fatalf("timeout while waiting the mock container to start") 993 } 994 995 s.waitInstanceId(c, machine, instance.Id(event.InstanceId)) 996 return event.InstanceId 997 } 998 999 func (s *lxcProvisionerSuite) expectStopped(c *gc.C, instId string) { 1000 // This check in particular leads to tests just hanging 1001 // indefinitely quite often on i386. 1002 coretesting.SkipIfI386(c, "lp:1425569") 1003 1004 s.State.StartSync() 1005 select { 1006 case event := <-s.events: 1007 c.Assert(event.Action, gc.Equals, mock.Stopped) 1008 case <-time.After(coretesting.LongWait): 1009 c.Fatalf("timeout while waiting the mock container to stop") 1010 } 1011 1012 select { 1013 case event := <-s.events: 1014 c.Assert(event.Action, gc.Equals, mock.Destroyed) 1015 c.Assert(event.InstanceId, gc.Equals, instId) 1016 case <-time.After(coretesting.LongWait): 1017 c.Fatalf("timeout while waiting the mock container to get destroyed") 1018 } 1019 } 1020 1021 func (s *lxcProvisionerSuite) expectNoEvents(c *gc.C) { 1022 select { 1023 case event := <-s.events: 1024 c.Fatalf("unexpected event %#v", event) 1025 case <-time.After(coretesting.ShortWait): 1026 return 1027 } 1028 } 1029 1030 func (s *lxcProvisionerSuite) TearDownTest(c *gc.C) { 1031 close(s.events) 1032 s.lxcSuite.TearDownTest(c) 1033 s.CommonProvisionerSuite.TearDownTest(c) 1034 } 1035 1036 func (s *lxcProvisionerSuite) newLxcProvisioner(c *gc.C) provisioner.Provisioner { 1037 parentMachineTag := names.NewMachineTag("0") 1038 agentConfig := s.AgentConfigForTag(c, parentMachineTag) 1039 managerConfig := container.ManagerConfig{ 1040 container.ConfigName: "juju", 1041 "log-dir": c.MkDir(), 1042 "use-clone": "false", 1043 } 1044 broker, err := provisioner.NewLxcBroker(s.provisioner, agentConfig, managerConfig, &containertesting.MockURLGetter{}, false, 0) 1045 c.Assert(err, jc.ErrorIsNil) 1046 toolsFinder := (*provisioner.GetToolsFinder)(s.provisioner) 1047 return provisioner.NewContainerProvisioner(instance.LXC, s.provisioner, agentConfig, broker, toolsFinder) 1048 } 1049 1050 func (s *lxcProvisionerSuite) TestProvisionerStartStop(c *gc.C) { 1051 p := s.newLxcProvisioner(c) 1052 c.Assert(p.Stop(), gc.IsNil) 1053 } 1054 1055 func (s *lxcProvisionerSuite) TestDoesNotStartEnvironMachines(c *gc.C) { 1056 p := s.newLxcProvisioner(c) 1057 defer stop(c, p) 1058 1059 // Check that an instance is not provisioned when the machine is created. 1060 _, err := s.State.AddMachine(coretesting.FakeDefaultSeries, state.JobHostUnits) 1061 c.Assert(err, jc.ErrorIsNil) 1062 1063 s.expectNoEvents(c) 1064 } 1065 1066 func (s *lxcProvisionerSuite) TestDoesNotHaveRetryWatcher(c *gc.C) { 1067 p := s.newLxcProvisioner(c) 1068 defer stop(c, p) 1069 1070 w, err := provisioner.GetRetryWatcher(p) 1071 c.Assert(w, gc.IsNil) 1072 c.Assert(err, jc.Satisfies, errors.IsNotImplemented) 1073 } 1074 1075 func (s *lxcProvisionerSuite) addContainer(c *gc.C) *state.Machine { 1076 template := state.MachineTemplate{ 1077 Series: coretesting.FakeDefaultSeries, 1078 Jobs: []state.MachineJob{state.JobHostUnits}, 1079 } 1080 container, err := s.State.AddMachineInsideMachine(template, "0", instance.LXC) 1081 c.Assert(err, jc.ErrorIsNil) 1082 return container 1083 } 1084 1085 func (s *lxcProvisionerSuite) TestContainerStartedAndStopped(c *gc.C) { 1086 coretesting.SkipIfI386(c, "lp:1425569") 1087 1088 p := s.newLxcProvisioner(c) 1089 defer stop(c, p) 1090 1091 container := s.addContainer(c) 1092 name := "juju-" + container.Tag().String() 1093 containertesting.EnsureLXCRootFSEtcNetwork(c, name) 1094 instId := s.expectStarted(c, container) 1095 1096 // ...and removed, along with the machine, when the machine is Dead. 1097 c.Assert(container.EnsureDead(), gc.IsNil) 1098 s.expectStopped(c, instId) 1099 s.waitRemoved(c, container) 1100 } 1101 1102 func (s *lxcProvisionerSuite) TestLXCProvisionerObservesConfigChanges(c *gc.C) { 1103 p := s.newLxcProvisioner(c) 1104 defer stop(c, p) 1105 s.assertProvisionerObservesConfigChanges(c, p) 1106 } 1107 1108 type fakeAPI struct { 1109 *gitjujutesting.Stub 1110 1111 fakeContainerConfig params.ContainerConfig 1112 fakeInterfaceInfo network.InterfaceInfo 1113 } 1114 1115 var _ provisioner.APICalls = (*fakeAPI)(nil) 1116 1117 var fakeInterfaceInfo network.InterfaceInfo = network.InterfaceInfo{ 1118 DeviceIndex: 0, 1119 MACAddress: "aa:bb:cc:dd:ee:ff", 1120 CIDR: "0.1.2.0/24", 1121 InterfaceName: "dummy0", 1122 Address: network.NewAddress("0.1.2.3"), 1123 GatewayAddress: network.NewAddress("0.1.2.1"), 1124 } 1125 1126 var fakeContainerConfig = params.ContainerConfig{ 1127 UpdateBehavior: ¶ms.UpdateBehavior{true, true}, 1128 ProviderType: "fake", 1129 AuthorizedKeys: coretesting.FakeAuthKeys, 1130 SSLHostnameVerification: true, 1131 } 1132 1133 func NewFakeAPI() *fakeAPI { 1134 return &fakeAPI{ 1135 Stub: &gitjujutesting.Stub{}, 1136 fakeContainerConfig: fakeContainerConfig, 1137 fakeInterfaceInfo: fakeInterfaceInfo, 1138 } 1139 } 1140 1141 func (f *fakeAPI) ContainerConfig() (params.ContainerConfig, error) { 1142 f.MethodCall(f, "ContainerConfig") 1143 if err := f.NextErr(); err != nil { 1144 return params.ContainerConfig{}, err 1145 } 1146 return f.fakeContainerConfig, nil 1147 } 1148 1149 func (f *fakeAPI) PrepareContainerInterfaceInfo(tag names.MachineTag) ([]network.InterfaceInfo, error) { 1150 f.MethodCall(f, "PrepareContainerInterfaceInfo", tag) 1151 if err := f.NextErr(); err != nil { 1152 return nil, err 1153 } 1154 return []network.InterfaceInfo{f.fakeInterfaceInfo}, nil 1155 } 1156 1157 func (f *fakeAPI) GetContainerInterfaceInfo(tag names.MachineTag) ([]network.InterfaceInfo, error) { 1158 f.MethodCall(f, "GetContainerInterfaceInfo", tag) 1159 if err := f.NextErr(); err != nil { 1160 return nil, err 1161 } 1162 return []network.InterfaceInfo{f.fakeInterfaceInfo}, nil 1163 }