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?