Exemple #1
0
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)
}
Exemple #2
0
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))
}
Exemple #3
0
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))
}
Exemple #4
0
// 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
}
Exemple #5
0
// 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
}
Exemple #6
0
func (s *certSuite) TestValidateMissingKeyPEM(c *gc.C) {
	cert := lxdclient.NewCert(s.certPEM, nil)
	err := cert.Validate()

	c.Check(err, jc.Satisfies, errors.IsNotValid)
}
Exemple #7
0
func (s *certSuite) TestValidateOkay(c *gc.C) {
	cert := lxdclient.NewCert(s.certPEM, s.keyPEM)
	err := cert.Validate()

	c.Check(err, jc.ErrorIsNil)
}
Exemple #8
0
func (s *certSuite) TestNewCert(c *gc.C) {
	cert := lxdclient.NewCert(s.certPEM, s.keyPEM)

	checkCert(c, cert, s.certPEM, s.keyPEM)
}
Exemple #9
0
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\)`)
}
Exemple #10
0
// 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
}