github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/provider/lxd/environ_raw.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  // +build go1.3
     5  
     6  package lxd
     7  
     8  import (
     9  	"io/ioutil"
    10  	"os"
    11  	"path"
    12  	"strings"
    13  
    14  	"github.com/juju/errors"
    15  	"github.com/juju/utils"
    16  	"github.com/juju/utils/series"
    17  	lxdshared "github.com/lxc/lxd/shared"
    18  
    19  	"github.com/juju/juju/environs"
    20  	jujupaths "github.com/juju/juju/juju/paths"
    21  	"github.com/juju/juju/network"
    22  	"github.com/juju/juju/provider/common"
    23  	"github.com/juju/juju/tools/lxdclient"
    24  )
    25  
    26  var (
    27  	jujuConfDir    = jujupaths.MustSucceed(jujupaths.ConfDir(series.LatestLts()))
    28  	clientCertPath = path.Join(jujuConfDir, "lxd-client.crt")
    29  	clientKeyPath  = path.Join(jujuConfDir, "lxd-client.key")
    30  	serverCertPath = path.Join(jujuConfDir, "lxd-server.crt")
    31  )
    32  
    33  type rawProvider struct {
    34  	lxdCerts
    35  	lxdConfig
    36  	lxdInstances
    37  	lxdProfiles
    38  	lxdImages
    39  	common.Firewaller
    40  }
    41  
    42  type lxdCerts interface {
    43  	AddCert(lxdclient.Cert) error
    44  	RemoveCertByFingerprint(string) error
    45  }
    46  
    47  type lxdConfig interface {
    48  	ServerStatus() (*lxdshared.ServerState, error)
    49  	SetConfig(k, v string) error
    50  }
    51  
    52  type lxdInstances interface {
    53  	Instances(string, ...string) ([]lxdclient.Instance, error)
    54  	AddInstance(lxdclient.InstanceSpec) (*lxdclient.Instance, error)
    55  	RemoveInstances(string, ...string) error
    56  	Addresses(string) ([]network.Address, error)
    57  }
    58  
    59  type lxdProfiles interface {
    60  	CreateProfile(string, map[string]string) error
    61  	HasProfile(string) (bool, error)
    62  }
    63  
    64  type lxdImages interface {
    65  	EnsureImageExists(series string, sources []lxdclient.Remote, copyProgressHandler func(string)) error
    66  }
    67  
    68  func newRawProvider(spec environs.CloudSpec) (*rawProvider, error) {
    69  	client, err := newClient(spec, ioutil.ReadFile, utils.RunCommand)
    70  	if err != nil {
    71  		return nil, errors.Annotate(err, "creating LXD client")
    72  	}
    73  
    74  	raw := &rawProvider{
    75  		lxdCerts:     client,
    76  		lxdConfig:    client,
    77  		lxdInstances: client,
    78  		lxdProfiles:  client,
    79  		lxdImages:    client,
    80  		Firewaller:   common.NewFirewaller(),
    81  	}
    82  	return raw, nil
    83  }
    84  
    85  type readFileFunc func(string) ([]byte, error)
    86  type runCommandFunc func(string, ...string) (string, error)
    87  
    88  func newClient(
    89  	spec environs.CloudSpec,
    90  	readFile readFileFunc,
    91  	runCommand runCommandFunc,
    92  ) (*lxdclient.Client, error) {
    93  	if spec.Endpoint != "" {
    94  		// We don't handle connecting to non-local lxd at present.
    95  		return nil, errors.NotValidf("endpoint %q", spec.Endpoint)
    96  	}
    97  
    98  	config, err := getRemoteConfig(readFile, runCommand)
    99  	if errors.IsNotFound(err) {
   100  		config = &lxdclient.Config{Remote: lxdclient.Local}
   101  	} else if err != nil {
   102  		return nil, errors.Trace(err)
   103  	}
   104  
   105  	client, err := lxdclient.Connect(*config, true)
   106  	if err != nil {
   107  		return nil, errors.Trace(err)
   108  	}
   109  	return client, nil
   110  }
   111  
   112  // getRemoteConfig returns a lxdclient.Config using a TCP-based remote
   113  // if called from within an instance started by the LXD provider. Otherwise,
   114  // it returns an errors satisfying errors.IsNotFound.
   115  func getRemoteConfig(readFile readFileFunc, runCommand runCommandFunc) (*lxdclient.Config, error) {
   116  	readFileOrig := readFile
   117  	readFile = func(path string) ([]byte, error) {
   118  		data, err := readFileOrig(path)
   119  		if err != nil {
   120  			if os.IsNotExist(err) {
   121  				err = errors.NotFoundf("%s", path)
   122  			}
   123  			return nil, err
   124  		}
   125  		return data, nil
   126  	}
   127  	clientCert, err := readFile(clientCertPath)
   128  	if err != nil {
   129  		return nil, errors.Annotate(err, "reading client certificate")
   130  	}
   131  	clientKey, err := readFile(clientKeyPath)
   132  	if err != nil {
   133  		return nil, errors.Annotate(err, "reading client key")
   134  	}
   135  	serverCert, err := readFile(serverCertPath)
   136  	if err != nil {
   137  		return nil, errors.Annotate(err, "reading server certificate")
   138  	}
   139  	cert := lxdclient.NewCert(clientCert, clientKey)
   140  	hostAddress, err := getDefaultGateway(runCommand)
   141  	if err != nil {
   142  		return nil, errors.Annotate(err, "getting gateway address")
   143  	}
   144  	return &lxdclient.Config{
   145  		lxdclient.Remote{
   146  			Name:          "remote",
   147  			Host:          hostAddress,
   148  			Protocol:      lxdclient.LXDProtocol,
   149  			Cert:          &cert,
   150  			ServerPEMCert: string(serverCert),
   151  		},
   152  	}, nil
   153  }
   154  
   155  func getDefaultGateway(runCommand runCommandFunc) (string, error) {
   156  	out, err := runCommand("ip", "route", "list", "match", "0/0")
   157  	if err != nil {
   158  		return "", errors.Trace(err)
   159  	}
   160  	if !strings.HasPrefix(string(out), "default via") {
   161  		return "", errors.Errorf(`unexpected output from "ip route": %s`, out)
   162  	}
   163  	fields := strings.Fields(string(out))
   164  	return fields[2], nil
   165  }