// newRawInstance is where the new physical instance is actually // provisioned, relative to the provided args and spec. Info for that // low-level instance is returned. func (env *environ) newRawInstance(args environs.StartInstanceParams) (*lxdclient.Instance, error) { hostname, err := env.namespace.Hostname(args.InstanceConfig.MachineId) if err != nil { return nil, errors.Trace(err) } // Note: other providers have the ImageMetadata already read for them // and passed in as args.ImageMetadata. However, lxd provider doesn't // use datatype: image-ids, it uses datatype: image-download, and we // don't have a registered cloud/region. imageSources, err := env.getImageSources() if err != nil { return nil, errors.Trace(err) } series := args.InstanceConfig.Series // TODO(jam): We should get this information from EnsureImageExists, or // something given to us from 'raw', not assume it ourselves. image := "ubuntu-" + series // TODO: support args.Constraints.Arch, we'll want to map from // Keep track of StatusCallback output so we may clean up later. // This is implemented here, close to where the StatusCallback calls // are made, instead of at a higher level in the package, so as not to // assume that all providers will have the same need to be implemented // in the same way. longestMsg := 0 statusCallback := func(currentStatus status.Status, msg string) { if args.StatusCallback != nil { args.StatusCallback(currentStatus, msg, nil) } if len(msg) > longestMsg { longestMsg = len(msg) } } cleanupCallback := func() { if args.CleanupCallback != nil { args.CleanupCallback(strings.Repeat(" ", longestMsg)) } } defer cleanupCallback() imageCallback := func(copyProgress string) { statusCallback(status.Allocating, copyProgress) } if err := env.raw.EnsureImageExists(series, imageSources, imageCallback); err != nil { return nil, errors.Trace(err) } cleanupCallback() // Clean out any long line of completed download status cloudcfg, err := cloudinit.New(series) if err != nil { return nil, errors.Trace(err) } var certificateFingerprint string if args.InstanceConfig.Controller != nil { // For controller machines, generate a certificate pair and write // them to the instance's disk in a well-defined location, along // with the server's certificate. certPEM, keyPEM, err := lxdshared.GenerateMemCert(true) if err != nil { return nil, errors.Trace(err) } cert := lxdclient.NewCert(certPEM, keyPEM) cert.Name = hostname // We record the certificate's fingerprint in metadata, so we can // remove the certificate along with the instance. certificateFingerprint, err = cert.Fingerprint() if err != nil { return nil, errors.Trace(err) } if err := env.raw.AddCert(cert); err != nil { return nil, errors.Annotatef(err, "adding certificate %q", cert.Name) } serverState, err := env.raw.ServerStatus() if err != nil { return nil, errors.Annotate(err, "getting server status") } cloudcfg.AddRunTextFile(clientCertPath, string(certPEM), 0600) cloudcfg.AddRunTextFile(clientKeyPath, string(keyPEM), 0600) cloudcfg.AddRunTextFile(serverCertPath, serverState.Environment.Certificate, 0600) } cloudcfg.SetAttr("hostname", hostname) cloudcfg.SetAttr("manage_etc_hosts", true) metadata, err := getMetadata(cloudcfg, args) if err != nil { return nil, errors.Trace(err) } if certificateFingerprint != "" { metadata[metadataKeyCertificateFingerprint] = certificateFingerprint } // TODO(ericsnow) Use the env ID for the network name (instead of default)? // TODO(ericsnow) Make the network name configurable? // TODO(ericsnow) Support multiple networks? // TODO(ericsnow) Use a different net interface name? Configurable? instSpec := lxdclient.InstanceSpec{ Name: hostname, Image: image, //Type: spec.InstanceType.Name, //Disks: getDisks(spec, args.Constraints), //NetworkInterfaces: []string{"ExternalNAT"}, Metadata: metadata, Profiles: []string{ //TODO(wwitzel3) allow the user to specify lxc profiles to apply. This allows the // user to setup any custom devices order config settings for their environment. // Also we must ensure that a device with the parent: lxcbr0 exists in at least // one of the profiles. "default", env.profileName(), }, // Network is omitted (left empty). } logger.Infof("starting instance %q (image %q)...", instSpec.Name, instSpec.Image) statusCallback(status.Allocating, "preparing image") inst, err := env.raw.AddInstance(instSpec) if err != nil { return nil, errors.Trace(err) } statusCallback(status.Running, "container started") return inst, nil }
} /* The "daily" stream. This consists of images that are built from the daily * package builds. These images have not been independently tested, but in * theory "should" be good, since they're build from packages from the released * archive. */ var CloudImagesDailyRemote = Remote{ Name: "cloud-images.ubuntu.com", Host: "https://cloud-images.ubuntu.com/daily", Protocol: SimplestreamsProtocol, Cert: nil, ServerPEMCert: "", } var generateCertificate = func() ([]byte, []byte, error) { return lxdshared.GenerateMemCert(true) } var DefaultImageSources = []Remote{CloudImagesRemote, CloudImagesDailyRemote} // Remote describes a LXD "remote" server for a client. In // particular it holds the information needed for the client // to connect to the remote. type Remote struct { // Name is a label for this remote. Name string // Host identifies the host to which the client should connect. // An empty string is interpreted as: // "localhost over a unix socket (unencrypted)". Host string // Protocol indicates whether this Remote is accessed via the normal