github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/tools/lxdclient/remote.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 lxdclient
     7  
     8  import (
     9  	"github.com/juju/errors"
    10  	"github.com/juju/utils"
    11  	lxdshared "github.com/lxc/lxd/shared"
    12  )
    13  
    14  const (
    15  	// remoteLocalName is a specific remote name in the default LXD config.
    16  	// See https://github.com/lxc/lxd/blob/master/config.go:defaultRemote.
    17  	remoteLocalName  = "local"
    18  	remoteIDForLocal = remoteLocalName
    19  )
    20  
    21  // Local is LXD's default "remote". Essentially it is an unencrypted,
    22  // unauthenticated connection to localhost over a unix socket.
    23  // However it does require users to be in the lxd group.
    24  var Local = Remote{
    25  	Name: remoteLocalName,
    26  	Host: "", // If Host is empty we will translate it into the local Unix socket
    27  	// No certificates are used when connecting to the Unix socket
    28  	Protocol:      LXDProtocol,
    29  	Cert:          nil,
    30  	ServerPEMCert: "",
    31  }
    32  
    33  type Protocol string
    34  
    35  const (
    36  	LXDProtocol           Protocol = "lxd"
    37  	SimplestreamsProtocol Protocol = "simplestreams"
    38  )
    39  
    40  var CloudImagesRemote = Remote{
    41  	Name:          "cloud-images.ubuntu.com",
    42  	Host:          "https://cloud-images.ubuntu.com/releases",
    43  	Protocol:      SimplestreamsProtocol,
    44  	Cert:          nil,
    45  	ServerPEMCert: "",
    46  }
    47  
    48  var generateCertificate = lxdshared.GenerateMemCert
    49  var DefaultImageSources = []Remote{CloudImagesRemote}
    50  
    51  // Remote describes a LXD "remote" server for a client. In
    52  // particular it holds the information needed for the client
    53  // to connect to the remote.
    54  type Remote struct {
    55  	// Name is a label for this remote.
    56  	Name string
    57  
    58  	// Host identifies the host to which the client should connect.
    59  	// An empty string is interpreted as:
    60  	//   "localhost over a unix socket (unencrypted)".
    61  	Host string
    62  
    63  	// Protocol indicates whether this Remote is accessed via the normal
    64  	// "LXD" protocol, or whether it is a Simplestreams source. The value
    65  	// is only useful for Remotes that are image sources
    66  	Protocol Protocol
    67  
    68  	// Cert holds the TLS certificate data for the client to use.
    69  	Cert *Cert
    70  
    71  	// ServerPEMCert is the certificate to be supplied as the acceptable
    72  	// server certificate when connecting to the remote.
    73  	ServerPEMCert string
    74  }
    75  
    76  // isLocal determines if the remote is the implicit "local" remote,
    77  // an unencrypted, unauthenticated unix socket to a locally running LXD.
    78  func (r Remote) isLocal() bool {
    79  	return r.Host == Local.Host
    80  }
    81  
    82  // ID identifies the remote to the raw LXD client code. For the
    83  // non-local case this is Remote.Name. For the local case it is the
    84  // remote name that LXD special-cases for the local unix socket.
    85  func (r Remote) ID() string {
    86  	if r.isLocal() {
    87  		return remoteIDForLocal
    88  	}
    89  	return r.Name
    90  }
    91  
    92  // WithDefaults updates a copy of the remote with default values
    93  // where needed.
    94  func (r Remote) WithDefaults() (Remote, error) {
    95  	// Note that r is a value receiver, so it is an implicit copy.
    96  
    97  	if r.isLocal() {
    98  		return r.withLocalDefaults(), nil
    99  	}
   100  
   101  	if r.Protocol == "" {
   102  		r.Protocol = LXDProtocol
   103  	}
   104  
   105  	if r.Cert == nil {
   106  		certPEM, keyPEM, err := generateCertificate()
   107  		if err != nil {
   108  			return r, errors.Trace(err)
   109  		}
   110  		cert := NewCert(certPEM, keyPEM)
   111  		r.Cert = &cert
   112  	}
   113  
   114  	cert, err := r.Cert.WithDefaults()
   115  	if err != nil {
   116  		return r, errors.Trace(err)
   117  	}
   118  	r.Cert = &cert
   119  
   120  	return r, nil
   121  }
   122  
   123  func (r Remote) withLocalDefaults() Remote {
   124  	if r.Name == "" {
   125  		r.Name = remoteLocalName
   126  	}
   127  	if r.Protocol == "" {
   128  		r.Protocol = LXDProtocol
   129  	}
   130  
   131  	// TODO(ericsnow) Set r.Cert to nil?
   132  
   133  	return r
   134  }
   135  
   136  // Validate checks the Remote fields for invalid values.
   137  func (r Remote) Validate() error {
   138  	if r.Name == "" {
   139  		return errors.NotValidf("remote missing name,")
   140  	}
   141  
   142  	if r.isLocal() {
   143  		if err := r.validateLocal(); err != nil {
   144  			return errors.Trace(err)
   145  		}
   146  		return nil
   147  	}
   148  
   149  	if r.Protocol == "" {
   150  		return errors.NotValidf("missing Protocol")
   151  	}
   152  	if r.Protocol != LXDProtocol && r.Protocol != SimplestreamsProtocol {
   153  		return errors.NotValidf("unknown Protocol %q", r.Protocol)
   154  	}
   155  
   156  	// r.Cert is allowed to be nil for Public remotes
   157  	if r.Cert != nil {
   158  		if err := r.Cert.Validate(); err != nil {
   159  			return errors.Trace(err)
   160  		}
   161  	}
   162  
   163  	return nil
   164  }
   165  
   166  func (r Remote) validateLocal() error {
   167  	if r.Cert != nil {
   168  		return errors.NotValidf("hostless remote with cert")
   169  	}
   170  	if r.Protocol != LXDProtocol {
   171  		return errors.NotValidf("localhost always talks LXD protocol not: %s", r.Protocol)
   172  	}
   173  
   174  	return nil
   175  }
   176  
   177  // UsingTCP converts the remote into a non-local version. For
   178  // non-local remotes this is a no-op.
   179  //
   180  // For a "local" remote (see Local), the remote is changed to a one
   181  // with the host set to the IP address of the local lxcbr0 bridge
   182  // interface. The remote is also set up for remote access, setting
   183  // the cert if not already set.
   184  func (r Remote) UsingTCP() (Remote, error) {
   185  	// Note that r is a value receiver, so it is an implicit copy.
   186  
   187  	if !r.isLocal() {
   188  		return r, nil
   189  	}
   190  
   191  	// TODO: jam 2016-02-25 This should be updated for systems that are
   192  	// 	 space aware, as we may not be just using the default LXC
   193  	// 	 bridge.
   194  	addr, err := utils.GetAddressForInterface(DefaultLXDBridge)
   195  	if err != nil {
   196  		return r, errors.Trace(err)
   197  	}
   198  	r.Host = addr
   199  
   200  	r, err = r.WithDefaults()
   201  	if err != nil {
   202  		return r, errors.Trace(err)
   203  	}
   204  
   205  	// TODO(ericsnow) Change r.Name if "local"? Prepend "juju-"?
   206  
   207  	return r, nil
   208  }
   209  
   210  // TODO(ericsnow) Add a "Connect(Config)" method that connects
   211  // to the remote and returns the corresponding Client.
   212  
   213  // TODO(ericsnow) Add a "Register" method to Client that adds the remote
   214  // to the client's remote?