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