func (s *certSuite) TestFingerprint(c *gc.C) { certPEM := []byte(testCertPEM) cert := lxdclient.NewCert(certPEM, nil) fingerprint, err := cert.Fingerprint() c.Assert(err, jc.ErrorIsNil) c.Check(fingerprint, gc.Equals, testCertFingerprint) }
func (s *certSuite) TestWriteKeyPEM(c *gc.C) { cert := lxdclient.NewCert(s.certPEM, s.keyPEM) var pemfile bytes.Buffer err := cert.WriteKeyPEM(&pemfile) c.Assert(err, jc.ErrorIsNil) c.Check(pemfile.String(), gc.Equals, string(s.keyPEM)) }
func (s *certSuite) TestX509Okay(c *gc.C) { certPEM := []byte(testCertPEM) cert := lxdclient.NewCert(certPEM, nil) x509Cert, err := cert.X509() c.Assert(err, jc.ErrorIsNil) block, _ := pem.Decode(certPEM) c.Assert(block, gc.NotNil) c.Check(string(x509Cert.Raw), gc.Equals, string(block.Bytes)) }
// getRemoteConfig returns a lxdclient.Config using a TCP-based remote // if called from within an instance started by the LXD provider. Otherwise, // it returns an errors satisfying errors.IsNotFound. func getRemoteConfig(readFile readFileFunc, runCommand runCommandFunc) (*lxdclient.Config, error) { readFileOrig := readFile readFile = func(path string) ([]byte, error) { data, err := readFileOrig(path) if err != nil { if os.IsNotExist(err) { err = errors.NotFoundf("%s", path) } return nil, err } return data, nil } clientCert, err := readFile(clientCertPath) if err != nil { return nil, errors.Annotate(err, "reading client certificate") } clientKey, err := readFile(clientKeyPath) if err != nil { return nil, errors.Annotate(err, "reading client key") } serverCert, err := readFile(serverCertPath) if err != nil { return nil, errors.Annotate(err, "reading server certificate") } cert := lxdclient.NewCert(clientCert, clientKey) hostAddress, err := getDefaultGateway(runCommand) if err != nil { return nil, errors.Annotate(err, "getting gateway address") } return &lxdclient.Config{ lxdclient.Remote{ Name: "remote", Host: hostAddress, Protocol: lxdclient.LXDProtocol, Cert: &cert, ServerPEMCert: string(serverCert), }, }, nil }
// clientConfig builds a LXD Config based on the env config and returns it. func (c *environConfig) clientConfig() (lxdclient.Config, error) { remote := lxdclient.Remote{ Name: "juju-remote", Host: c.remoteURL(), ServerPEMCert: c.serverPEMCert(), } if c.clientCert() != "" { certPEM := []byte(c.clientCert()) keyPEM := []byte(c.clientKey()) cert := lxdclient.NewCert(certPEM, keyPEM) cert.Name = fmt.Sprintf("juju cert for env %q", c.Name()) remote.Cert = &cert } cfg := lxdclient.Config{ Namespace: c.namespace(), Remote: remote, } cfg, err := cfg.WithDefaults() if err != nil { return cfg, errors.Trace(err) } return cfg, nil }
func (s *certSuite) TestValidateMissingKeyPEM(c *gc.C) { cert := lxdclient.NewCert(s.certPEM, nil) err := cert.Validate() c.Check(err, jc.Satisfies, errors.IsNotValid) }
func (s *certSuite) TestValidateOkay(c *gc.C) { cert := lxdclient.NewCert(s.certPEM, s.keyPEM) err := cert.Validate() c.Check(err, jc.ErrorIsNil) }
func (s *certSuite) TestNewCert(c *gc.C) { cert := lxdclient.NewCert(s.certPEM, s.keyPEM) checkCert(c, cert, s.certPEM, s.keyPEM) }
func (s *certSuite) TestX509BadPEM(c *gc.C) { cert := lxdclient.NewCert(s.certPEM, s.keyPEM) _, err := cert.X509() c.Check(err, gc.ErrorMatches, `invalid cert PEM \(\d+ bytes\)`) }
// 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 }