Example #1
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")
}
Example #2
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")
}
Example #3
0
// TestCloudInitUserDataNoNetworkConfig tests that no network-interfaces, or
// related data, appear in user-data when no networkConfig is passed to
// CloudInitUserData.
func (s *UserDataSuite) TestCloudInitUserDataNoNetworkConfig(c *gc.C) {
	instanceConfig, err := containertesting.MockMachineConfig("1/lxd/0")
	c.Assert(err, jc.ErrorIsNil)
	data, err := containerinit.CloudInitUserData(instanceConfig, nil)
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(data, gc.NotNil)

	linesToMatch := CloudInitDataExcludingOutputSection(string(data))

	c.Assert(strings.Join(linesToMatch, "\n"), gc.Equals, "#cloud-config")
}
Example #4
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")
}
Example #5
0
File: lxd.go Project: bac/juju
func (manager *containerManager) CreateContainer(
	instanceConfig *instancecfg.InstanceConfig,
	cons constraints.Value,
	series string,
	networkConfig *container.NetworkConfig,
	storageConfig *container.StorageConfig,
	callback container.StatusCallback,
) (inst instance.Instance, _ *instance.HardwareCharacteristics, err error) {

	defer func() {
		if err != nil {
			callback(status.ProvisioningError, fmt.Sprintf("Creating container: %v", err), nil)
		}
	}()

	if manager.client == nil {
		manager.client, err = ConnectLocal()
		if err != nil {
			err = errors.Annotatef(err, "failed to connect to local LXD")
			return
		}
	}

	err = manager.client.EnsureImageExists(series,
		lxdclient.DefaultImageSources,
		func(progress string) {
			callback(status.Provisioning, progress, nil)
		})
	if err != nil {
		err = errors.Annotatef(err, "failed to ensure LXD image")
		return
	}

	name, err := manager.namespace.Hostname(instanceConfig.MachineId)
	if err != nil {
		return nil, nil, errors.Trace(err)
	}

	// Do not pass networkConfig, as we want to directly inject our own ENI
	// rather than using cloud-init.
	userData, err := containerinit.CloudInitUserData(instanceConfig, nil)
	if err != nil {
		return
	}

	metadata := map[string]string{
		lxdclient.UserdataKey: string(userData),
		// An extra piece of info to let people figure out where this
		// thing came from.
		"user.juju-model": manager.modelUUID,

		// Make sure these come back up on host reboot.
		"boot.autostart": "true",
	}

	nics, err := networkDevices(networkConfig)
	if err != nil {
		return
	}

	// TODO(macgreagoir) This might be dead code. Do we always get
	// len(nics) > 0?
	profiles := []string{}

	if len(nics) == 0 {
		logger.Infof("instance %q configured with %q profile", name, lxdDefaultProfileName)
		profiles = append(profiles, lxdDefaultProfileName)
	} else {
		logger.Infof("instance %q configured with %v network devices", name, nics)
	}

	// Push the required /etc/network/interfaces file to the container.
	// By pushing this file (which happens after LXD init, and before LXD
	// start) we ensure that we get Juju's version of ENI, as opposed to
	// the default LXD version, which may assume it can do DHCP over eth0.
	// Especially on a multi-nic host, it is possible for MAAS to provide
	// DHCP on a different space to that which the container eth0 interface
	// will be bridged, or not provide DHCP at all.
	eni, err := containerinit.GenerateNetworkConfig(networkConfig)
	if err != nil {
		err = errors.Annotatef(err, "failed to generate /etc/network/interfaces content")
		return
	}

	spec := lxdclient.InstanceSpec{
		Name:     name,
		Image:    manager.client.ImageNameForSeries(series),
		Metadata: metadata,
		Devices:  nics,
		Profiles: profiles,
		Files: lxdclient.Files{
			lxdclient.File{
				Content: []byte(eni),
				Path:    "/etc/network/interfaces",
				GID:     0,
				UID:     0,
				Mode:    0644,
			},
		},
	}

	logger.Infof("starting instance %q (image %q)...", spec.Name, spec.Image)
	callback(status.Provisioning, "Starting container", nil)
	_, err = manager.client.AddInstance(spec)
	if err != nil {
		return
	}

	callback(status.Running, "Container started", nil)
	inst = &lxdInstance{name, manager.client}
	return
}
Example #6
0
File: lxd.go Project: makyo/juju
func (manager *containerManager) CreateContainer(
	instanceConfig *instancecfg.InstanceConfig,
	series string,
	networkConfig *container.NetworkConfig,
	storageConfig *container.StorageConfig,
	callback container.StatusCallback,
) (inst instance.Instance, _ *instance.HardwareCharacteristics, err error) {

	defer func() {
		if err != nil {
			manager.deleteNetworkProfile()
			callback(status.StatusProvisioningError, fmt.Sprintf("Creating container: %v", err), nil)
		}
	}()

	if manager.client == nil {
		manager.client, err = ConnectLocal(manager.name)
		if err != nil {
			err = errors.Annotatef(err, "failed to connect to local LXD")
			return
		}
	}

	err = manager.client.EnsureImageExists(series,
		lxdclient.DefaultImageSources,
		func(progress string) {
			callback(status.StatusProvisioning, progress, nil)
		})
	if err != nil {
		err = errors.Annotatef(err, "failed to ensure LXD image")
		return
	}

	name := names.NewMachineTag(instanceConfig.MachineId).String()
	if manager.name != "" {
		name = fmt.Sprintf("%s-%s", manager.name, name)
	}

	userData, err := containerinit.CloudInitUserData(instanceConfig, networkConfig)
	if err != nil {
		return
	}

	metadata := map[string]string{
		lxdclient.UserdataKey: string(userData),
		// An extra piece of info to let people figure out where this
		// thing came from.
		"user.juju-environment": manager.name,

		// Make sure these come back up on host reboot.
		"boot.autostart": "true",
	}

	networkProfile := fmt.Sprintf("%s-network", name)

	if len(networkConfig.Interfaces) > 0 || networkConfig.Device != "" {
		if err = createNetworkProfile(manager.client, networkProfile); err != nil {
			return
		}

		manager.networkProfile = networkProfile
		if len(networkConfig.Interfaces) > 0 {
			err = networkProfileAddMultipleInterfaces(manager.client, networkProfile, networkConfig.Interfaces)
		} else {
			err = networkProfileAddSingleInterface(manager.client, networkProfile, networkConfig.Device, networkConfig.MTU)
		}
		if err != nil {
			return
		}
	} else {
		networkProfile = "default"
	}

	spec := lxdclient.InstanceSpec{
		Name:     name,
		Image:    manager.client.ImageNameForSeries(series),
		Metadata: metadata,
		Profiles: []string{
			networkProfile,
		},
	}

	logger.Infof("starting instance %q (image %q)...", spec.Name, spec.Image)
	callback(status.StatusProvisioning, "Starting container", nil)
	_, err = manager.client.AddInstance(spec)
	if err != nil {
		manager.client.ProfileDelete(networkProfile)
		return
	}

	callback(status.StatusRunning, "Container started", nil)
	inst = &lxdInstance{name, manager.client}
	return
}
Example #7
0
File: lxd.go Project: kat-co/juju
func (manager *containerManager) CreateContainer(
	instanceConfig *instancecfg.InstanceConfig,
	cons constraints.Value,
	series string,
	networkConfig *container.NetworkConfig,
	storageConfig *container.StorageConfig,
	callback container.StatusCallback,
) (inst instance.Instance, _ *instance.HardwareCharacteristics, err error) {

	defer func() {
		if err != nil {
			callback(status.ProvisioningError, fmt.Sprintf("Creating container: %v", err), nil)
		}
	}()

	if manager.client == nil {
		manager.client, err = ConnectLocal()
		if err != nil {
			err = errors.Annotatef(err, "failed to connect to local LXD")
			return
		}
	}

	err = manager.client.EnsureImageExists(series,
		lxdclient.DefaultImageSources,
		func(progress string) {
			callback(status.Provisioning, progress, nil)
		})
	if err != nil {
		err = errors.Annotatef(err, "failed to ensure LXD image")
		return
	}

	name, err := manager.namespace.Hostname(instanceConfig.MachineId)
	if err != nil {
		return nil, nil, errors.Trace(err)
	}

	userData, err := containerinit.CloudInitUserData(instanceConfig, networkConfig)
	if err != nil {
		return
	}

	metadata := map[string]string{
		lxdclient.UserdataKey: string(userData),
		// An extra piece of info to let people figure out where this
		// thing came from.
		"user.juju-model": manager.modelUUID,

		// Make sure these come back up on host reboot.
		"boot.autostart": "true",
	}

	nics, err := networkDevices(networkConfig)
	if err != nil {
		return
	}

	profiles := []string{}

	if len(nics) == 0 {
		logger.Infof("instance %q configured with %q profile", name, lxdDefaultProfileName)
		profiles = append(profiles, lxdDefaultProfileName)
	} else {
		logger.Infof("instance %q configured with %v network devices", name, nics)
	}

	spec := lxdclient.InstanceSpec{
		Name:     name,
		Image:    manager.client.ImageNameForSeries(series),
		Metadata: metadata,
		Devices:  nics,
		Profiles: profiles,
	}

	logger.Infof("starting instance %q (image %q)...", spec.Name, spec.Image)
	callback(status.Provisioning, "Starting container", nil)
	_, err = manager.client.AddInstance(spec)
	if err != nil {
		return
	}

	callback(status.Running, "Container started", nil)
	inst = &lxdInstance{name, manager.client}
	return
}