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") }
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") }
// 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") }
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 (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 }
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 }
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 }