func createContainer(c *gc.C, manager container.Manager, machineId string) instance.Instance { machineNonce := "fake-nonce" stateInfo := jujutesting.FakeStateInfo(machineId) apiInfo := jujutesting.FakeAPIInfo(machineId) instanceConfig, err := instancecfg.NewInstanceConfig(machineId, machineNonce, imagemetadata.ReleasedStream, "quantal", "", true, stateInfo, apiInfo) c.Assert(err, jc.ErrorIsNil) network := container.BridgeNetworkConfig("virbr0", 0, nil) err = instanceConfig.SetTools(tools.List{ &tools.Tools{ Version: version.MustParseBinary("2.3.4-foo-bar"), URL: "http://tools.testing.invalid/2.3.4-foo-bar.tgz", }, }) c.Assert(err, jc.ErrorIsNil) environConfig := dummyConfig(c) err = instancecfg.FinishInstanceConfig(instanceConfig, environConfig) c.Assert(err, jc.ErrorIsNil) callback := func(settableStatus status.Status, info string, data map[string]interface{}) error { return nil } inst, hardware, err := manager.CreateContainer(instanceConfig, "precise", network, nil, callback) c.Assert(err, jc.ErrorIsNil) c.Assert(hardware, gc.NotNil) expected := fmt.Sprintf("arch=%s cpu-cores=1 mem=512M root-disk=8192M", arch.HostArch()) c.Assert(hardware.String(), gc.Equals, expected) return inst }
func (*NetworkSuite) TestGenerateNetworkConfig(c *gc.C) { for _, test := range []struct { config *container.NetworkConfig net string link string }{{ config: nil, net: "veth", link: "lxcbr0", }, { config: lxc.DefaultNetworkConfig(), net: "veth", link: "lxcbr0", }, { config: container.BridgeNetworkConfig("foo"), net: "veth", link: "foo", }, { config: container.PhysicalNetworkConfig("foo"), net: "phys", link: "foo", }} { config := lxc.GenerateNetworkConfig(test.config) c.Assert(config, jc.Contains, fmt.Sprintf("lxc.network.type = %s\n", test.net)) c.Assert(config, jc.Contains, fmt.Sprintf("lxc.network.link = %s\n", test.link)) } }
func (s *UserDataSuite) TestCloudInitUserDataFallbackConfigWithContainerHostname(c *gc.C) { instanceConfig, err := containertesting.MockMachineConfig("1/lxd/0") instanceConfig.MachineContainerHostname = "lxdhostname" c.Assert(err, jc.ErrorIsNil) networkConfig := container.BridgeNetworkConfig("foo", 0, nil) data, err := containerinit.CloudInitUserData(instanceConfig, networkConfig) c.Assert(err, jc.ErrorIsNil) c.Assert(data, gc.NotNil) linesToMatch := CloudInitDataExcludingOutputSection(string(data)) expected := fmt.Sprintf(s.expectedFallbackUserData, s.networkInterfacesFile, s.systemNetworkInterfacesFile) var expectedLinesToMatch []string for _, line := range strings.Split(expected, "\n") { if strings.HasPrefix(line, "runcmd:") { break } expectedLinesToMatch = append(expectedLinesToMatch, line) } expectedLinesToMatch = append(expectedLinesToMatch, "hostname: lxdhostname") expectedLinesToMatch = append(expectedLinesToMatch, "manage_etc_hosts: true") c.Assert(strings.Join(linesToMatch, "\n")+"\n", gc.Equals, strings.Join(expectedLinesToMatch, "\n")+"\n") }
// CreateContainerTest tries to create a container and returns any errors encountered along the // way func CreateContainerTest(c *gc.C, manager container.Manager, machineId string) (instance.Instance, error) { instanceConfig, err := MockMachineConfig(machineId) if err != nil { return nil, errors.Trace(err) } envConfig, err := config.New(config.NoDefaults, dummy.SampleConfig()) if err != nil { return nil, errors.Trace(err) } instanceConfig.Config = envConfig network := container.BridgeNetworkConfig("nic42", 0, nil) storage := &container.StorageConfig{} callback := func(settableStatus status.Status, info string, data map[string]interface{}) error { return nil } inst, hardware, err := manager.CreateContainer(instanceConfig, "quantal", network, storage, callback) if err != nil { return nil, errors.Trace(err) } if hardware == nil { return nil, errors.New("nil hardware characteristics") } if hardware.String() == "" { return nil, errors.New("empty hardware characteristics") } return inst, nil }
func (s *UserDataSuite) TestNewCloudInitConfigWithNetworksNoConfig(c *gc.C) { netConfig := container.BridgeNetworkConfig("foo", 0, nil) cloudConf, err := containerinit.NewCloudInitConfigWithNetworks("quantal", netConfig) c.Assert(err, jc.ErrorIsNil) expected := "#cloud-config\n{}\n" assertUserData(c, cloudConf, expected) }
func (s *LxcSuite) TestCreateContainerWithBlockStorage(c *gc.C) { err := os.Remove(s.RestartDir) c.Assert(err, jc.ErrorIsNil) manager := s.makeManager(c, "test") machineConfig, err := containertesting.MockMachineConfig("1/lxc/0") c.Assert(err, jc.ErrorIsNil) storageConfig := &container.StorageConfig{AllowMount: true} networkConfig := container.BridgeNetworkConfig("nic42", 4321, nil) instance := containertesting.CreateContainerWithMachineAndNetworkAndStorageConfig(c, manager, machineConfig, networkConfig, storageConfig) name := string(instance.Id()) autostartLink := lxc.RestartSymlink(name) config, err := ioutil.ReadFile(lxc.ContainerConfigFilename(name)) c.Assert(err, jc.ErrorIsNil) expected := fmt.Sprintf(` # network config # interface "eth0" lxc.network.type = veth lxc.network.link = nic42 lxc.network.flags = up lxc.network.mtu = 4321 lxc.start.auto = 1 lxc.mount.entry = %s var/log/juju none defaults,bind 0 0 lxc.aa_profile = lxc-container-default-with-mounting lxc.cgroup.devices.allow = b 7:* rwm lxc.cgroup.devices.allow = c 10:237 rwm `, s.logDir) c.Assert(string(config), gc.Equals, expected) c.Assert(autostartLink, jc.DoesNotExist) }
func (s *UserDataSuite) TestGenerateNetworkConfig(c *gc.C) { data, err := containerinit.GenerateNetworkConfig(nil) c.Assert(err, gc.ErrorMatches, "missing container network config") c.Assert(data, gc.Equals, "") netConfig := container.BridgeNetworkConfig("foo", 0, nil) data, err = containerinit.GenerateNetworkConfig(netConfig) c.Assert(err, jc.ErrorIsNil) c.Assert(data, gc.Equals, s.expectedFallbackConfig) // Test with all interface types. netConfig = container.BridgeNetworkConfig("foo", 0, s.fakeInterfaces) data, err = containerinit.GenerateNetworkConfig(netConfig) c.Assert(err, jc.ErrorIsNil) c.Assert(data, gc.Equals, s.expectedSampleConfig) }
func (s *LxcSuite) createTemplate(c *gc.C) golxc.Container { name := "juju-series-template" ch := s.ensureTemplateStopped(name) defer func() { <-ch }() network := container.BridgeNetworkConfig("nic42") authorizedKeys := "authorized keys list" aptProxy := proxy.Settings{} template, err := lxc.EnsureCloneTemplate( "ext4", "series", network, authorizedKeys, aptProxy) c.Assert(err, gc.IsNil) c.Assert(template.Name(), gc.Equals, name) s.AssertEvent(c, <-s.events, mock.Created, name) s.AssertEvent(c, <-s.events, mock.Started, name) s.AssertEvent(c, <-s.events, mock.Stopped, name) autostartLink := lxc.RestartSymlink(name) config, err := ioutil.ReadFile(lxc.ContainerConfigFilename(name)) c.Assert(err, gc.IsNil) expected := ` lxc.network.type = veth lxc.network.link = nic42 lxc.network.flags = up ` // NOTE: no autostart, no mounting the log dir c.Assert(string(config), gc.Equals, expected) c.Assert(autostartLink, jc.DoesNotExist) return template }
func (s *UserDataSuite) TestGenerateNetworkConfig(c *gc.C) { // No config or no interfaces - no error, but also noting to generate. data, err := containerinit.GenerateNetworkConfig(nil) c.Assert(err, jc.ErrorIsNil) c.Assert(data, gc.HasLen, 0) netConfig := container.BridgeNetworkConfig("foo", 0, nil) data, err = containerinit.GenerateNetworkConfig(netConfig) c.Assert(err, jc.ErrorIsNil) c.Assert(data, gc.HasLen, 0) // Test with all interface types. netConfig = container.BridgeNetworkConfig("foo", 0, s.fakeInterfaces) data, err = containerinit.GenerateNetworkConfig(netConfig) c.Assert(err, jc.ErrorIsNil) c.Assert(data, gc.Equals, s.expectedNetConfig) }
func (s *UserDataSuite) TestNewCloudInitConfigWithNetworksFallbackConfig(c *gc.C) { netConfig := container.BridgeNetworkConfig("foo", 0, nil) cloudConf, err := containerinit.NewCloudInitConfigWithNetworks("quantal", netConfig) c.Assert(err, jc.ErrorIsNil) c.Assert(cloudConf, gc.NotNil) expected := fmt.Sprintf(s.expectedFallbackUserData, s.networkInterfacesFile, s.systemNetworkInterfacesFile) assertUserData(c, cloudConf, expected) }
func CreateContainerWithMachineConfig( c *gc.C, manager container.Manager, instanceConfig *instancecfg.InstanceConfig, ) instance.Instance { networkConfig := container.BridgeNetworkConfig("nic42", 0, nil) storageConfig := &container.StorageConfig{} return CreateContainerWithMachineAndNetworkAndStorageConfig(c, manager, instanceConfig, networkConfig, storageConfig) }
func (s *UserDataSuite) TestCloudInitUserData(c *gc.C) { instanceConfig, err := containertesting.MockMachineConfig("1/lxc/0") c.Assert(err, jc.ErrorIsNil) networkConfig := container.BridgeNetworkConfig("foo", 0, nil) data, err := containerinit.CloudInitUserData(instanceConfig, networkConfig) c.Assert(err, jc.ErrorIsNil) // No need to test the exact contents here, as they are already // tested separately. c.Assert(string(data), jc.HasPrefix, "#cloud-config\n") }
func (s *KVMSuite) createRunningContainer(c *gc.C, name string) kvm.Container { kvmContainer := s.ContainerFactory.New(name) network := container.BridgeNetworkConfig("testbr0") c.Assert(kvmContainer.Start(kvm.StartParams{ Series: "quantal", Arch: version.Current.Arch, UserDataFile: "userdata.txt", Network: network}), gc.IsNil) return kvmContainer }
func CreateContainerWithMachineConfig( c *gc.C, manager container.Manager, machineConfig *cloudinit.MachineConfig, ) instance.Instance { network := container.BridgeNetworkConfig("nic42") inst, hardware, err := manager.CreateContainer(machineConfig, "quantal", network) c.Assert(err, gc.IsNil) c.Assert(hardware, gc.NotNil) c.Assert(hardware.String(), gc.Not(gc.Equals), "") return inst }
func (s *UserDataSuite) TestCloudInitUserDataFallbackConfig(c *gc.C) { instanceConfig, err := containertesting.MockMachineConfig("1/lxd/0") c.Assert(err, jc.ErrorIsNil) networkConfig := container.BridgeNetworkConfig("foo", 0, nil) data, err := containerinit.CloudInitUserData(instanceConfig, networkConfig) c.Assert(err, jc.ErrorIsNil) c.Assert(data, gc.NotNil) // Extract the "#cloud-config" header and all lines between // from the "bootcmd" section up to (but not including) the // "output" sections to match against expected. But we cannot // possibly handle all the /other/ output that may be added by // CloudInitUserData() in the future, so we also truncate at // the first runcmd which now happens to include the runcmd's // added for raising the network interfaces captured in // expectedFallbackUserData. However, the other tests above do // check for that output. var linesToMatch []string seenBootcmd := false for _, line := range strings.Split(string(data), "\n") { if strings.HasPrefix(line, "#cloud-config") { linesToMatch = append(linesToMatch, line) continue } if strings.HasPrefix(line, "bootcmd:") { seenBootcmd = true } if strings.HasPrefix(line, "output:") && seenBootcmd { break } if seenBootcmd { linesToMatch = append(linesToMatch, line) } } expected := fmt.Sprintf(s.expectedFallbackUserData, s.networkInterfacesFile, s.systemNetworkInterfacesFile) var expectedLinesToMatch []string for _, line := range strings.Split(expected, "\n") { if strings.HasPrefix(line, "runcmd:") { break } expectedLinesToMatch = append(expectedLinesToMatch, line) } c.Assert(strings.Join(linesToMatch, "\n")+"\n", gc.Equals, strings.Join(expectedLinesToMatch, "\n")+"\n") }
func (s *LxcSuite) createTemplate(c *gc.C) golxc.Container { name := "juju-quantal-lxc-template" ch := s.ensureTemplateStopped(name) defer func() { <-ch }() network := container.BridgeNetworkConfig("nic42", 4321, nil) authorizedKeys := "authorized keys list" aptProxy := proxy.Settings{} aptMirror := "http://my.archive.ubuntu.com/ubuntu" callback := func(containerStatus status.Status, info string, data map[string]interface{}) error { return nil } template, err := lxc.EnsureCloneTemplate( "ext4", "quantal", network, authorizedKeys, aptProxy, aptMirror, true, true, &containertesting.MockURLGetter{}, false, callback, ) c.Assert(err, jc.ErrorIsNil) c.Assert(template.Name(), gc.Equals, name) createEvent := <-s.events c.Assert(createEvent.Action, gc.Equals, mock.Created) c.Assert(createEvent.InstanceId, gc.Equals, name) argsSet := set.NewStrings(createEvent.TemplateArgs...) c.Assert(argsSet.Contains("imageURL"), jc.IsTrue) s.AssertEvent(c, <-s.events, mock.Started, name) s.AssertEvent(c, <-s.events, mock.Stopped, name) autostartLink := lxc.RestartSymlink(name) config, err := ioutil.ReadFile(lxc.ContainerConfigFilename(name)) c.Assert(err, jc.ErrorIsNil) expected := ` # network config # interface "eth0" lxc.network.type = veth lxc.network.link = nic42 lxc.network.flags = up lxc.network.mtu = 4321 ` // NOTE: no autostart, no mounting the log dir c.Assert(string(config), gc.Equals, expected) c.Assert(autostartLink, jc.DoesNotExist) return template }
// StartInstance is specified in the Broker interface. func (broker *kvmBroker) StartInstance(args environs.StartInstanceParams) (instance.Instance, *instance.HardwareCharacteristics, []network.Info, error) { if args.MachineConfig.HasNetworks() { return nil, nil, nil, fmt.Errorf("starting kvm containers with networks is not supported yet.") } // TODO: refactor common code out of the container brokers. machineId := args.MachineConfig.MachineId kvmLogger.Infof("starting kvm container for machineId: %s", machineId) // TODO: Default to using the host network until we can configure. Yes, // this is using the LxcBridge value, we should put it in the api call for // container config. bridgeDevice := broker.agentConfig.Value(agent.LxcBridge) if bridgeDevice == "" { bridgeDevice = kvm.DefaultKvmBridge } network := container.BridgeNetworkConfig(bridgeDevice) series := args.Tools.OneSeries() args.MachineConfig.MachineContainerType = instance.KVM args.MachineConfig.Tools = args.Tools[0] config, err := broker.api.ContainerConfig() if err != nil { kvmLogger.Errorf("failed to get container config: %v", err) return nil, nil, nil, err } if err := environs.PopulateMachineConfig( args.MachineConfig, config.ProviderType, config.AuthorizedKeys, config.SSLHostnameVerification, config.Proxy, config.AptProxy, config.PreferIPv6, config.EnableOSRefreshUpdate, config.EnableOSUpgrade, ); err != nil { kvmLogger.Errorf("failed to populate machine config: %v", err) return nil, nil, nil, err } inst, hardware, err := broker.manager.CreateContainer(args.MachineConfig, series, network) if err != nil { kvmLogger.Errorf("failed to start container: %v", err) return nil, nil, nil, err } kvmLogger.Infof("started kvm container for machineId: %s, %s, %s", machineId, inst.Id(), hardware.String()) return inst, hardware, nil, nil }
func CreateContainer(c *gc.C, manager container.Manager, machineId string) instance.Instance { stateInfo := jujutesting.FakeStateInfo(machineId) apiInfo := jujutesting.FakeAPIInfo(machineId) machineConfig := environs.NewMachineConfig(machineId, "fake-nonce", nil, stateInfo, apiInfo) machineConfig.Tools = &tools.Tools{ Version: version.MustParseBinary("2.3.4-foo-bar"), URL: "http://tools.testing.invalid/2.3.4-foo-bar.tgz", } series := "series" network := container.BridgeNetworkConfig("nic42") inst, hardware, err := manager.CreateContainer(machineConfig, series, network) c.Assert(err, gc.IsNil) c.Assert(hardware, gc.NotNil) c.Assert(hardware.String(), gc.Not(gc.Equals), "") return inst }
// StartInstance is specified in the Broker interface. func (broker *lxcBroker) StartInstance(args environs.StartInstanceParams) (instance.Instance, *instance.HardwareCharacteristics, []network.Info, error) { if args.MachineConfig.HasNetworks() { return nil, nil, nil, fmt.Errorf("starting lxc containers with networks is not supported yet.") } // TODO: refactor common code out of the container brokers. machineId := args.MachineConfig.MachineId lxcLogger.Infof("starting lxc container for machineId: %s", machineId) // Default to using the host network until we can configure. bridgeDevice := broker.agentConfig.Value(agent.LxcBridge) if bridgeDevice == "" { bridgeDevice = lxc.DefaultLxcBridge } network := container.BridgeNetworkConfig(bridgeDevice) series := args.Tools.OneSeries() args.MachineConfig.MachineContainerType = instance.LXC args.MachineConfig.Tools = args.Tools[0] config, err := broker.api.ContainerConfig() if err != nil { lxcLogger.Errorf("failed to get container config: %v", err) return nil, nil, nil, err } if err := environs.PopulateMachineConfig( args.MachineConfig, config.ProviderType, config.AuthorizedKeys, config.SSLHostnameVerification, config.Proxy, config.AptProxy, ); err != nil { lxcLogger.Errorf("failed to populate machine config: %v", err) return nil, nil, nil, err } inst, hardware, err := broker.manager.CreateContainer(args.MachineConfig, series, network) if err != nil { lxcLogger.Errorf("failed to start container: %v", err) return nil, nil, nil, err } lxcLogger.Infof("started lxc container for machineId: %s, %s, %s", machineId, inst.Id(), hardware.String()) return inst, hardware, nil, nil }
func (s *UserDataSuite) TestNewCloudInitConfigWithNetworks(c *gc.C) { netConfig := container.BridgeNetworkConfig("foo", 0, s.fakeInterfaces) cloudConf, err := containerinit.NewCloudInitConfigWithNetworks("quantal", netConfig) c.Assert(err, jc.ErrorIsNil) // We need to indent expectNetConfig to make it valid YAML, // dropping the last new line and using unindented blank lines. lines := strings.Split(s.expectedNetConfig, "\n") indentedNetConfig := strings.Join(lines[:len(lines)-1], "\n ") indentedNetConfig = strings.Replace(indentedNetConfig, "\n \n", "\n\n", -1) expected := ` #cloud-config bootcmd: - install -D -m 644 /dev/null '`[1:] + s.networkInterfacesFile + `' - |- printf '%s\n' '` + indentedNetConfig + ` ' > '` + s.networkInterfacesFile + `' ` assertUserData(c, cloudConf, expected) }
func (t *LxdSuite) TestNotAllContainersAreDeleted(c *gc.C) { c.Skip("Test skipped because it talks directly to LXD agent.") lxdClient, err := lxd.ConnectLocal("") c.Assert(err, jc.ErrorIsNil) /* create a container to make sure isn't deleted */ instanceSpec := lxdclient.InstanceSpec{ Name: "juju-lxd-tests", Image: "ubuntu-xenial", } _, err = lxdClient.AddInstance(instanceSpec) c.Assert(err, jc.ErrorIsNil) defer lxdClient.RemoveInstances("", "juju-lxd-tests") instanceConfig, err := containertesting.MockMachineConfig("1/lxd/0") c.Assert(err, jc.ErrorIsNil) envConfig, err := config.New(config.NoDefaults, dummy.SampleConfig()) c.Assert(err, jc.ErrorIsNil) instanceConfig.Config = envConfig storageConfig := &container.StorageConfig{} networkConfig := container.BridgeNetworkConfig("nic42", 4321, nil) manager := t.makeManager(c, "manager") callback := func(settableStatus status.Status, info string, data map[string]interface{}) error { return nil } _, _, err = manager.CreateContainer( instanceConfig, "xenial", networkConfig, storageConfig, callback, ) c.Assert(err, jc.ErrorIsNil) instances, err := manager.ListContainers() c.Assert(err, jc.ErrorIsNil) for _, inst := range instances { err = manager.DestroyContainer(inst.Id()) c.Assert(err, jc.ErrorIsNil) } }
func createContainer(c *gc.C, manager container.Manager, machineId string) instance.Instance { machineNonce := "fake-nonce" stateInfo := jujutesting.FakeStateInfo(machineId) apiInfo := jujutesting.FakeAPIInfo(machineId) machineConfig := environs.NewMachineConfig(machineId, machineNonce, nil, stateInfo, apiInfo) network := container.BridgeNetworkConfig("virbr0") machineConfig.Tools = &tools.Tools{ Version: version.MustParseBinary("2.3.4-foo-bar"), URL: "http://tools.testing.invalid/2.3.4-foo-bar.tgz", } environConfig := dummyConfig(c) err := environs.FinishMachineConfig(machineConfig, environConfig, constraints.Value{}) c.Assert(err, gc.IsNil) inst, hardware, err := manager.CreateContainer(machineConfig, "precise", network) c.Assert(err, gc.IsNil) c.Assert(hardware, gc.NotNil) expected := fmt.Sprintf("arch=%s cpu-cores=1 mem=512M root-disk=8192M", version.Current.Arch) c.Assert(hardware.String(), gc.Equals, expected) return inst }
// StartInstance is specified in the Broker interface. func (broker *lxcBroker) StartInstance(args environs.StartInstanceParams) (*environs.StartInstanceResult, error) { // TODO: refactor common code out of the container brokers. machineId := args.InstanceConfig.MachineId lxcLogger.Infof("starting lxc container for machineId: %s", machineId) // Default to using the host network until we can configure. bridgeDevice := broker.agentConfig.Value(agent.LxcBridge) if bridgeDevice == "" { bridgeDevice = container.DefaultLxcBridge } config, err := broker.api.ContainerConfig() if err != nil { lxcLogger.Errorf("failed to get container config: %v", err) return nil, err } preparedInfo, err := prepareOrGetContainerInterfaceInfo( broker.api, machineId, bridgeDevice, true, // allocate if possible, do not maintain existing. broker.enableNAT, args.NetworkInfo, lxcLogger, config.ProviderType, ) if err != nil { // It's not fatal (yet) if we couldn't pre-allocate addresses for the // container. logger.Warningf("failed to prepare container %q network config: %v", machineId, err) } else { args.NetworkInfo = preparedInfo } network := container.BridgeNetworkConfig(bridgeDevice, broker.defaultMTU, args.NetworkInfo) // The provisioner worker will provide all tools it knows about // (after applying explicitly specified constraints), which may // include tools for architectures other than the host's. We // must constrain to the host's architecture for LXC. archTools, err := matchHostArchTools(args.Tools) if err != nil { return nil, errors.Trace(err) } series := archTools.OneSeries() args.InstanceConfig.MachineContainerType = instance.LXC if err := args.InstanceConfig.SetTools(archTools); err != nil { return nil, errors.Trace(err) } storageConfig := &container.StorageConfig{ AllowMount: config.AllowLXCLoopMounts, } if err := instancecfg.PopulateInstanceConfig( args.InstanceConfig, config.ProviderType, config.AuthorizedKeys, config.SSLHostnameVerification, config.Proxy, config.AptProxy, config.AptMirror, config.PreferIPv6, config.EnableOSRefreshUpdate, config.EnableOSUpgrade, ); err != nil { lxcLogger.Errorf("failed to populate machine config: %v", err) return nil, err } inst, hardware, err := broker.manager.CreateContainer(args.InstanceConfig, series, network, storageConfig, args.StatusCallback) if err != nil { lxcLogger.Errorf("failed to start container: %v", err) return nil, err } lxcLogger.Infof("started lxc container for machineId: %s, %s, %s", machineId, inst.Id(), hardware.String()) return &environs.StartInstanceResult{ Instance: inst, Hardware: hardware, NetworkInfo: network.Interfaces, }, nil }
// StartInstance is specified in the Broker interface. func (broker *kvmBroker) StartInstance(args environs.StartInstanceParams) (*environs.StartInstanceResult, error) { if args.InstanceConfig.HasNetworks() { return nil, errors.New("starting kvm containers with networks is not supported yet") } // TODO: refactor common code out of the container brokers. machineId := args.InstanceConfig.MachineId kvmLogger.Infof("starting kvm container for machineId: %s", machineId) // TODO: Default to using the host network until we can configure. Yes, // this is using the LxcBridge value, we should put it in the api call for // container config. bridgeDevice := broker.agentConfig.Value(agent.LxcBridge) if bridgeDevice == "" { bridgeDevice = kvm.DefaultKvmBridge } preparedInfo, err := prepareOrGetContainerInterfaceInfo( broker.api, machineId, bridgeDevice, true, // allocate if possible, do not maintain existing. broker.enableNAT, args.NetworkInfo, kvmLogger, ) if err != nil { // It's not fatal (yet) if we couldn't pre-allocate addresses for the // container. logger.Warningf("failed to prepare container %q network config: %v", machineId, err) } else { args.NetworkInfo = preparedInfo } // Unlike with LXC, we don't override the default MTU to use. network := container.BridgeNetworkConfig(bridgeDevice, 0, args.NetworkInfo) series := args.Tools.OneSeries() args.InstanceConfig.MachineContainerType = instance.KVM args.InstanceConfig.Tools = args.Tools[0] config, err := broker.api.ContainerConfig() if err != nil { kvmLogger.Errorf("failed to get container config: %v", err) return nil, err } if err := instancecfg.PopulateInstanceConfig( args.InstanceConfig, config.ProviderType, config.AuthorizedKeys, config.SSLHostnameVerification, config.Proxy, config.AptProxy, config.AptMirror, config.PreferIPv6, config.EnableOSRefreshUpdate, config.EnableOSUpgrade, ); err != nil { kvmLogger.Errorf("failed to populate machine config: %v", err) return nil, err } storageConfig := &container.StorageConfig{ AllowMount: true, } inst, hardware, err := broker.manager.CreateContainer(args.InstanceConfig, series, network, storageConfig, args.StatusCallback) if err != nil { kvmLogger.Errorf("failed to start container: %v", err) return nil, err } kvmLogger.Infof("started kvm container for machineId: %s, %s, %s", machineId, inst.Id(), hardware.String()) return &environs.StartInstanceResult{ Instance: inst, Hardware: hardware, NetworkInfo: network.Interfaces, }, nil }
args.InstanceConfig.Constraints = args.Constraints args.InstanceConfig.AgentEnvironment[agent.Namespace] = env.config.namespace() inst, hardware, err := createContainer(env, args) if err != nil { return nil, err } return &environs.StartInstanceResult{ Instance: inst, Hardware: hardware, }, nil } // Override for testing. var createContainer = func(env *localEnviron, args environs.StartInstanceParams) (instance.Instance, *instance.HardwareCharacteristics, error) { series := args.Tools.OneSeries() network := container.BridgeNetworkConfig(env.config.networkBridge(), 0, args.NetworkInfo) allowLoopMounts, _ := env.config.AllowLXCLoopMounts() isLXC := env.config.container() == instance.LXC storage := &container.StorageConfig{ AllowMount: !isLXC || allowLoopMounts, } inst, hardware, err := env.containerManager.CreateContainer(args.InstanceConfig, series, network, storage) if err != nil { return nil, nil, err } return inst, hardware, nil } // StopInstances is specified in the InstanceBroker interface. func (env *localEnviron) StopInstances(ids ...instance.Id) error { for _, id := range ids {
// StartInstance is specified in the Broker interface. func (broker *kvmBroker) StartInstance(args environs.StartInstanceParams) (*environs.StartInstanceResult, error) { if args.InstanceConfig.HasNetworks() { return nil, errors.New("starting kvm containers with networks is not supported yet") } // TODO: refactor common code out of the container brokers. machineId := args.InstanceConfig.MachineId kvmLogger.Infof("starting kvm container for machineId: %s", machineId) // TODO: Default to using the host network until we can configure. Yes, // this is using the LxcBridge value, we should put it in the api call for // container config. bridgeDevice := broker.agentConfig.Value(agent.LxcBridge) if bridgeDevice == "" { bridgeDevice = kvm.DefaultKvmBridge } if !environs.AddressAllocationEnabled() { logger.Debugf( "address allocation feature flag not enabled; using DHCP for container %q", machineId, ) } else { logger.Debugf("trying to allocate static IP for container %q", machineId) allocatedInfo, err := configureContainerNetwork( machineId, bridgeDevice, broker.api, args.NetworkInfo, true, // allocate a new address. broker.enableNAT, ) if err != nil { // It's fine, just ignore it. The effect will be that the // container won't have a static address configured. logger.Infof("not allocating static IP for container %q: %v", machineId, err) } else { args.NetworkInfo = allocatedInfo } } // Unlike with LXC, we don't override the default MTU to use. network := container.BridgeNetworkConfig(bridgeDevice, 0, args.NetworkInfo) series := args.Tools.OneSeries() args.InstanceConfig.MachineContainerType = instance.KVM args.InstanceConfig.Tools = args.Tools[0] config, err := broker.api.ContainerConfig() if err != nil { kvmLogger.Errorf("failed to get container config: %v", err) return nil, err } if err := instancecfg.PopulateInstanceConfig( args.InstanceConfig, config.ProviderType, config.AuthorizedKeys, config.SSLHostnameVerification, config.Proxy, config.AptProxy, config.AptMirror, config.PreferIPv6, config.EnableOSRefreshUpdate, config.EnableOSUpgrade, ); err != nil { kvmLogger.Errorf("failed to populate machine config: %v", err) return nil, err } storageConfig := &container.StorageConfig{ AllowMount: true, } inst, hardware, err := broker.manager.CreateContainer(args.InstanceConfig, series, network, storageConfig) if err != nil { kvmLogger.Errorf("failed to start container: %v", err) return nil, err } kvmLogger.Infof("started kvm container for machineId: %s, %s, %s", machineId, inst.Id(), hardware.String()) return &environs.StartInstanceResult{ Instance: inst, Hardware: hardware, NetworkInfo: network.Interfaces, }, nil }
func (broker *lxdBroker) StartInstance(args environs.StartInstanceParams) (*environs.StartInstanceResult, error) { if args.InstanceConfig.HasNetworks() { return nil, errors.New("starting lxd containers with networks is not supported yet") } machineId := args.InstanceConfig.MachineId bridgeDevice := broker.agentConfig.Value(agent.LxcBridge) if bridgeDevice == "" { var err error bridgeDevice, err = lxdclient.GetDefaultBridgeName() if err != nil { return nil, errors.Trace(err) } } preparedInfo, err := prepareOrGetContainerInterfaceInfo( broker.api, machineId, bridgeDevice, true, // allocate if possible, do not maintain existing. broker.enableNAT, args.NetworkInfo, lxdLogger, ) if err != nil { // It's not fatal (yet) if we couldn't pre-allocate addresses for the // container. logger.Warningf("failed to prepare container %q network config: %v", machineId, err) } else { args.NetworkInfo = preparedInfo } network := container.BridgeNetworkConfig(bridgeDevice, 0, args.NetworkInfo) series := args.Tools.OneSeries() args.InstanceConfig.MachineContainerType = instance.LXD args.InstanceConfig.Tools = args.Tools[0] config, err := broker.api.ContainerConfig() if err != nil { lxdLogger.Errorf("failed to get container config: %v", err) return nil, err } if err := instancecfg.PopulateInstanceConfig( args.InstanceConfig, config.ProviderType, config.AuthorizedKeys, config.SSLHostnameVerification, config.Proxy, config.AptProxy, config.AptMirror, config.PreferIPv6, config.EnableOSRefreshUpdate, config.EnableOSUpgrade, ); err != nil { lxdLogger.Errorf("failed to populate machine config: %v", err) return nil, err } storageConfig := &container.StorageConfig{} inst, hardware, err := broker.manager.CreateContainer(args.InstanceConfig, series, network, storageConfig, args.StatusCallback) if err != nil { return nil, err } return &environs.StartInstanceResult{ Instance: inst, Hardware: hardware, NetworkInfo: network.Interfaces, }, nil }
// DefaultNetworkConfig returns a valid NetworkConfig to use the // defaultLxcBridge that is created by the lxc package. func DefaultNetworkConfig() *container.NetworkConfig { return container.BridgeNetworkConfig(DefaultLxcBridge, 0, nil) }
func (*NetworkSuite) TestGenerateNetworkConfig(c *gc.C) { dhcpNIC := network.InterfaceInfo{ DeviceIndex: 0, MACAddress: "aa:bb:cc:dd:ee:f0", InterfaceName: "eth0", // The following is not part of the LXC config, but cause the // generated cloud-init user-data to change accordingly. ConfigType: network.ConfigDHCP, } staticNIC := network.InterfaceInfo{ DeviceIndex: 1, CIDR: "0.1.2.0/20", // used to infer the subnet mask. MACAddress: "aa:bb:cc:dd:ee:f1", InterfaceName: "eth1", Address: network.NewAddress("0.1.2.3"), GatewayAddress: network.NewAddress("0.1.2.1"), // The rest is passed to cloud-init. ConfigType: network.ConfigStatic, DNSServers: network.NewAddresses("ns1.invalid", "ns2.invalid"), } extraConfigNIC := network.InterfaceInfo{ DeviceIndex: 2, MACAddress: "aa:bb:cc:dd:ee:f2", InterfaceName: "eth2", VLANTag: 42, NoAutoStart: true, // The rest is passed to cloud-init. ConfigType: network.ConfigManual, DNSServers: network.NewAddresses("ns1.invalid", "ns2.invalid"), } // Test /24 is used by default when the CIDR is invalid or empty. staticNICNoCIDR, staticNICBadCIDR := staticNIC, staticNIC staticNICNoCIDR.CIDR = "" staticNICBadCIDR.CIDR = "bad" // Test when NoAutoStart is true gateway is not added, even if there. staticNICNoAutoWithGW := staticNIC staticNICNoAutoWithGW.NoAutoStart = true var lastTestLog string allNICs := []network.InterfaceInfo{dhcpNIC, staticNIC, extraConfigNIC} for i, test := range []struct { about string config *container.NetworkConfig nics []network.InterfaceInfo rendered []string logContains string logDoesNotContain string }{{ about: "empty config", config: nil, rendered: []string{ "lxc.network.type = veth", "lxc.network.link = lxcbr0", "lxc.network.flags = up", }, logContains: `WARNING juju.container.lxc network type missing, using the default "bridge" config`, logDoesNotContain: `INFO juju.container.lxc setting MTU to 0 for LXC network interfaces`, }, { about: "default config", config: lxc.DefaultNetworkConfig(), rendered: []string{ "lxc.network.type = veth", "lxc.network.link = lxcbr0", "lxc.network.flags = up", }, logDoesNotContain: `INFO juju.container.lxc setting MTU to 0 for LXC network interfaces`, }, { about: "bridge config with MTU 1500, device foo, no NICs", config: container.BridgeNetworkConfig("foo", 1500, nil), rendered: []string{ "lxc.network.type = veth", "lxc.network.link = foo", "lxc.network.flags = up", "lxc.network.mtu = 1500", }, }, { about: "phys config with MTU 9000, device foo, no NICs", config: container.PhysicalNetworkConfig("foo", 9000, nil), rendered: []string{ "lxc.network.type = phys", "lxc.network.link = foo", "lxc.network.flags = up", "lxc.network.mtu = 9000", }, }, { about: "bridge config with MTU 8000, device foo, all NICs", config: container.BridgeNetworkConfig("foo", 8000, allNICs), nics: allNICs, rendered: []string{ "lxc.network.type = veth", "lxc.network.link = foo", "lxc.network.flags = up", "lxc.network.name = eth0", "lxc.network.hwaddr = aa:bb:cc:dd:ee:f0", "lxc.network.type = veth", "lxc.network.link = foo", "lxc.network.flags = up", "lxc.network.name = eth1", "lxc.network.hwaddr = aa:bb:cc:dd:ee:f1", "lxc.network.ipv4 = 0.1.2.3/20", "lxc.network.type = veth", "lxc.network.link = foo", "lxc.network.name = eth2", "lxc.network.hwaddr = aa:bb:cc:dd:ee:f2", }, }, { about: "bridge config with MTU 0, device foo, staticNICNoCIDR", config: container.BridgeNetworkConfig("foo", 0, []network.InterfaceInfo{staticNICNoCIDR}), nics: []network.InterfaceInfo{staticNICNoCIDR}, rendered: []string{ "lxc.network.type = veth", "lxc.network.link = foo", "lxc.network.flags = up", "lxc.network.name = eth1", "lxc.network.hwaddr = aa:bb:cc:dd:ee:f1", }, logDoesNotContain: `INFO juju.container.lxc setting MTU to 0 for all LXC network interfaces`, }, { about: "bridge config with MTU 0, device foo, staticNICBadCIDR", config: container.BridgeNetworkConfig("foo", 0, []network.InterfaceInfo{staticNICBadCIDR}), nics: []network.InterfaceInfo{staticNICBadCIDR}, rendered: []string{ "lxc.network.type = veth", "lxc.network.link = foo", "lxc.network.flags = up", "lxc.network.name = eth1", "lxc.network.hwaddr = aa:bb:cc:dd:ee:f1", "lxc.network.ipv4 = invalid CIDR address: bad", }, }, { about: "bridge config with MTU 0, device foo, staticNICNoAutoWithGW", config: container.BridgeNetworkConfig("foo", 0, []network.InterfaceInfo{staticNICNoAutoWithGW}), nics: []network.InterfaceInfo{staticNICNoAutoWithGW}, rendered: []string{ "lxc.network.type = veth", "lxc.network.link = foo", "lxc.network.name = eth1", "lxc.network.hwaddr = aa:bb:cc:dd:ee:f1", "lxc.network.ipv4 = 0.1.2.3/20", }, }} { c.Logf("test #%d: %s", i, test.about) config := lxc.GenerateNetworkConfig(test.config) // Parse the config to drop comments and empty lines. This is // needed to ensure the order of all settings match what we // expect to get rendered, as the order matters. var configLines []string for _, line := range strings.Split(config, "\n") { line = strings.TrimSpace(line) if line == "" || strings.HasPrefix(line, "#") { continue } configLines = append(configLines, line) } currentLog := strings.TrimPrefix(c.GetTestLog(), lastTestLog) c.Check(configLines, jc.DeepEquals, test.rendered) if test.logContains != "" { c.Check(currentLog, jc.Contains, test.logContains) } if test.logDoesNotContain != "" { c.Check(currentLog, gc.Not(jc.Contains), test.logDoesNotContain) } // TODO(dimitern) In a follow-up, test the generated user-data // honors the other settings. lastTestLog = c.GetTestLog() } }
func (s *LxcSuite) TestUpdateContainerConfig(c *gc.C) { networkConfig := container.BridgeNetworkConfig("nic42", 4321, []network.InterfaceInfo{{ DeviceIndex: 0, CIDR: "0.1.2.0/20", InterfaceName: "eth0", MACAddress: "aa:bb:cc:dd:ee:f0", Address: network.NewAddress("0.1.2.3"), GatewayAddress: network.NewAddress("0.1.2.1"), }, { DeviceIndex: 1, InterfaceName: "eth1", }}) storageConfig := &container.StorageConfig{ AllowMount: true, } manager := s.makeManager(c, "test") instanceConfig, err := containertesting.MockMachineConfig("1/lxc/0") c.Assert(err, jc.ErrorIsNil) envConfig, err := config.New(config.NoDefaults, dummy.SampleConfig()) c.Assert(err, jc.ErrorIsNil) instanceConfig.Config = envConfig instance := containertesting.CreateContainerWithMachineAndNetworkAndStorageConfig( c, manager, instanceConfig, networkConfig, storageConfig, ) name := string(instance.Id()) // Append a few extra lines to the config. extraLines := []string{ " lxc.rootfs = /some/thing # else ", "", " # just comment", "lxc.network.vlan.id=42", "something else # ignore", "lxc.network.type=veth", "lxc.network.link = foo # comment", "lxc.network.hwaddr = bar", } configPath := lxc.ContainerConfigFilename(name) configFile, err := os.OpenFile(configPath, os.O_RDWR|os.O_APPEND, 0644) c.Assert(err, jc.ErrorIsNil) _, err = configFile.WriteString(strings.Join(extraLines, "\n") + "\n") c.Assert(err, jc.ErrorIsNil) err = configFile.Close() c.Assert(err, jc.ErrorIsNil) expectedConf := fmt.Sprintf(` # network config # interface "eth0" lxc.network.type = veth lxc.network.link = nic42 lxc.network.flags = up lxc.network.name = eth0 lxc.network.hwaddr = aa:bb:cc:dd:ee:f0 lxc.network.ipv4 = 0.1.2.3/20 lxc.network.ipv4.gateway = 0.1.2.1 # interface "eth1" lxc.network.type = veth lxc.network.link = nic42 lxc.network.flags = up lxc.network.name = eth1 lxc.mount.entry = %s var/log/juju none defaults,bind 0 0 lxc.aa_profile = lxc-container-default-with-mounting lxc.cgroup.devices.allow = b 7:* rwm lxc.cgroup.devices.allow = c 10:237 rwm `, s.logDir) + strings.Join(extraLines, "\n") + "\n" lxcConfContents, err := ioutil.ReadFile(configPath) c.Assert(err, jc.ErrorIsNil) c.Assert(string(lxcConfContents), gc.Equals, expectedConf) linesToReplace := []string{ "", // empty lines are ignored " lxc.network.type = bar # free drinks !! ", // formatting is sanitized. " # comments are ignored", "lxc.network.type=foo", // replace the second "type". "lxc.network.name = em0 # renamed now", // replace the first "name" "lxc.network.name = em1", // replace the second "name" "lxc.network.mtu = 1234", // replace only the first "mtu". "lxc.network.hwaddr = ff:ee:dd:cc:bb:aa", // replace the first "hwaddr". "lxc.network.hwaddr=deadbeef", // replace second "hwaddr". "lxc.network.hwaddr=nonsense", // no third "hwaddr", so append. "lxc.network.hwaddr = ", // no fourth "hwaddr" to remove - ignored. "lxc.network.link=", // remove only the first "link" "lxc.network.vlan.id=69", // replace. "lxc.missing = appended", // missing - appended. "lxc.network.type = phys", // replace the third "type". "lxc.mount.entry=", // delete existing "entry". "lxc.rootfs = /foo/bar", // replace first "rootfs". "lxc.rootfs = /bar/foo", // append new. } newConfig := strings.Join(linesToReplace, "\n") updatedConfig := ` # network config # interface "eth0" lxc.network.type = bar lxc.network.flags = up lxc.network.name = em0 lxc.network.hwaddr = ff:ee:dd:cc:bb:aa lxc.network.ipv4 = 0.1.2.3/20 lxc.network.ipv4.gateway = 0.1.2.1 # interface "eth1" lxc.network.type = foo lxc.network.link = nic42 lxc.network.flags = up lxc.network.name = em1 lxc.aa_profile = lxc-container-default-with-mounting lxc.cgroup.devices.allow = b 7:* rwm lxc.cgroup.devices.allow = c 10:237 rwm lxc.rootfs = /foo/bar # just comment lxc.network.vlan.id = 69 something else # ignore lxc.network.type = phys lxc.network.link = foo # comment lxc.network.hwaddr = deadbeef lxc.network.mtu = 1234 lxc.network.hwaddr = nonsense lxc.missing = appended lxc.rootfs = /bar/foo ` err = lxc.UpdateContainerConfig(name, newConfig) c.Assert(err, jc.ErrorIsNil) lxcConfContents, err = ioutil.ReadFile(configPath) c.Assert(err, jc.ErrorIsNil) c.Assert(string(lxcConfContents), gc.Equals, updatedConfig) // Now test the example in updateContainerConfig's doc string. oldConfig := ` lxc.foo = off lxc.bar=42 ` newConfig = ` lxc.bar= lxc.foo = bar lxc.foo = baz # xx ` updatedConfig = ` lxc.foo = bar lxc.foo = baz ` err = ioutil.WriteFile(configPath, []byte(oldConfig), 0644) c.Assert(err, jc.ErrorIsNil) err = lxc.UpdateContainerConfig(name, newConfig) c.Assert(err, jc.ErrorIsNil) lxcConfContents, err = ioutil.ReadFile(configPath) c.Assert(err, jc.ErrorIsNil) c.Assert(string(lxcConfContents), gc.Equals, updatedConfig) }