예제 #1
0
파일: live_test.go 프로젝트: makyo/juju
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
}
예제 #2
0
파일: lxc_test.go 프로젝트: kapilt/juju
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))
	}
}
예제 #3
0
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")
}
예제 #4
0
// 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

}
예제 #5
0
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)
}
예제 #6
0
파일: lxc_test.go 프로젝트: makyo/juju
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)
}
예제 #7
0
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)
}
예제 #8
0
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
}
예제 #9
0
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)
}
예제 #10
0
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)
}
예제 #11
0
파일: common.go 프로젝트: bac/juju
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)
}
예제 #12
0
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")
}
예제 #13
0
파일: kvm_test.go 프로젝트: klyachin/juju
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
}
예제 #14
0
파일: common.go 프로젝트: kapilt/juju
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
}
예제 #15
0
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")
}
예제 #16
0
파일: lxc_test.go 프로젝트: makyo/juju
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
}
예제 #17
0
파일: kvm-broker.go 프로젝트: kapilt/juju
// 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
}
예제 #18
0
파일: common.go 프로젝트: klyachin/juju
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
}
예제 #19
0
파일: lxc-broker.go 프로젝트: rogpeppe/juju
// 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
}
예제 #20
0
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)
}
예제 #21
0
파일: lxd_test.go 프로젝트: makyo/juju
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)
	}
}
예제 #22
0
파일: live_test.go 프로젝트: klyachin/juju
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
}
예제 #23
0
파일: lxc-broker.go 프로젝트: makyo/juju
// 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
}
예제 #24
0
// 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
}
예제 #25
0
파일: environ.go 프로젝트: imoapps/juju
	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 {
예제 #26
0
// 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
}
예제 #27
0
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
}
예제 #28
0
파일: lxc.go 프로젝트: claudiu-coblis/juju
// 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)
}
예제 #29
0
파일: lxc_test.go 프로젝트: makyo/juju
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()
	}
}
예제 #30
0
파일: lxc_test.go 프로젝트: makyo/juju
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)
}