github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/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  }
    45  
    46  type lxdConfig interface {
    47  	ServerStatus() (*lxdshared.ServerState, error)
    48  	SetConfig(k, v string) error
    49  }
    50  
    51  type lxdInstances interface {
    52  	Instances(string, ...string) ([]lxdclient.Instance, error)
    53  	AddInstance(lxdclient.InstanceSpec) (*lxdclient.Instance, error)
    54  	RemoveInstances(string, ...string) error
    55  	Addresses(string) ([]network.Address, error)
    56  }
    57  
    58  type lxdProfiles interface {
    59  	CreateProfile(string, map[string]string) error
    60  	HasProfile(string) (bool, error)
    61  }
    62  
    63  type lxdImages interface {
    64  	EnsureImageExists(series string, sources []lxdclient.Remote, copyProgressHandler func(string)) error
    65  }
    66  
    67  func newRawProvider(spec environs.CloudSpec) (*rawProvider, error) {
    68  	client, err := newClient(spec, ioutil.ReadFile, utils.RunCommand)
    69  	if err != nil {
    70  		return nil, errors.Annotate(err, "creating LXD client")
    71  	}
    72  
    73  	raw := &rawProvider{
    74  		lxdCerts:     client,
    75  		lxdConfig:    client,
    76  		lxdInstances: client,
    77  		lxdProfiles:  client,
    78  		lxdImages:    client,
    79  		Firewaller:   common.NewFirewaller(),
    80  	}
    81  	return raw, nil
    82  }
    83  
    84  type readFileFunc func(string) ([]byte, error)
    85  type runCommandFunc func(string, ...string) (string, error)
    86  
    87  func newClient(
    88  	spec environs.CloudSpec,
    89  	readFile readFileFunc,
    90  	runCommand runCommandFunc,
    91  ) (*lxdclient.Client, error) {
    92  	if spec.Endpoint != "" {
    93  		// We don't handle connecting to non-local lxd at present.
    94  		return nil, errors.NotValidf("endpoint %q", spec.Endpoint)
    95  	}
    96  
    97  	config, err := getRemoteConfig(readFile, runCommand)
    98  	if errors.IsNotFound(err) {
    99  		config = &lxdclient.Config{Remote: lxdclient.Local}
   100  	} else if err != nil {
   101  		return nil, errors.Trace(err)
   102  	}
   103  
   104  	client, err := lxdclient.Connect(*config)
   105  	if err != nil {
   106  		return nil, errors.Trace(err)
   107  	}
   108  	return client, nil
   109  }
   110  
   111  // getRemoteConfig returns a lxdclient.Config using a TCP-based remote
   112  // if called from within an instance started by the LXD provider. Otherwise,
   113  // it returns an errors satisfying errors.IsNotFound.
   114  func getRemoteConfig(readFile readFileFunc, runCommand runCommandFunc) (*lxdclient.Config, error) {
   115  	readFileOrig := readFile
   116  	readFile = func(path string) ([]byte, error) {
   117  		data, err := readFileOrig(path)
   118  		if err != nil {
   119  			if os.IsNotExist(err) {
   120  				err = errors.NotFoundf("%s", path)
   121  			}
   122  			return nil, err
   123  		}
   124  		return data, nil
   125  	}
   126  	clientCert, err := readFile(clientCertPath)
   127  	if err != nil {
   128  		return nil, errors.Annotate(err, "reading client certificate")
   129  	}
   130  	clientKey, err := readFile(clientKeyPath)
   131  	if err != nil {
   132  		return nil, errors.Annotate(err, "reading client key")
   133  	}
   134  	serverCert, err := readFile(serverCertPath)
   135  	if err != nil {
   136  		return nil, errors.Annotate(err, "reading server certificate")
   137  	}
   138  	cert := lxdclient.NewCert(clientCert, clientKey)
   139  	hostAddress, err := getDefaultGateway(runCommand)
   140  	if err != nil {
   141  		return nil, errors.Annotate(err, "getting gateway address")
   142  	}
   143  	return &lxdclient.Config{
   144  		lxdclient.Remote{
   145  			Name:          "remote",
   146  			Host:          hostAddress,
   147  			Protocol:      lxdclient.LXDProtocol,
   148  			Cert:          &cert,
   149  			ServerPEMCert: string(serverCert),
   150  		},
   151  	}, nil
   152  }
   153  
   154  func getDefaultGateway(runCommand runCommandFunc) (string, error) {
   155  	out, err := runCommand("ip", "route", "list", "match", "0/0")
   156  	if err != nil {
   157  		return "", errors.Trace(err)
   158  	}
   159  	if !strings.HasPrefix(string(out), "default via") {
   160  		return "", errors.Errorf(`unexpected output from "ip route": %s`, out)
   161  	}
   162  	fields := strings.Fields(string(out))
   163  	return fields[2], nil
   164  }