forked from canonical/lxd
-
Notifications
You must be signed in to change notification settings - Fork 0
/
config.go
175 lines (146 loc) · 4.6 KB
/
config.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
package lxd
import (
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"gopkg.in/yaml.v2"
"github.com/lxc/lxd/shared"
)
// Config holds settings to be used by a client or daemon.
type Config struct {
// DefaultRemote holds the remote daemon name from the Remotes map
// that the client should communicate with by default.
// If empty it defaults to "local".
DefaultRemote string `yaml:"default-remote"`
// Remotes defines a map of remote daemon names to the details for
// communication with the named daemon.
// The implicit "local" remote is always available and communicates
// with the local daemon over a unix socket.
Remotes map[string]RemoteConfig `yaml:"remotes"`
// Command line aliases for `lxc`
Aliases map[string]string `yaml:"aliases"`
// This is the path to the config directory, so the client can find
// previously stored server certs, give good error messages, and save
// new server certs, etc.
//
// We don't need to store it, because of course once we've loaded this
// structure we already know where it is :)
ConfigDir string `yaml:"-"`
}
// RemoteConfig holds details for communication with a remote daemon.
type RemoteConfig struct {
Addr string `yaml:"addr"`
Public bool `yaml:"public"`
Protocol string `yaml:"protocol,omitempty"`
Static bool `yaml:"-"`
}
var LocalRemote = RemoteConfig{
Addr: "unix://",
Static: true,
Public: false}
var ImagesRemote = RemoteConfig{
Addr: "https://images.linuxcontainers.org",
Public: true,
Protocol: "simplestreams"}
var UbuntuRemote = RemoteConfig{
Addr: "https://cloud-images.ubuntu.com/releases",
Static: true,
Public: true,
Protocol: "simplestreams"}
var UbuntuDailyRemote = RemoteConfig{
Addr: "https://cloud-images.ubuntu.com/daily",
Static: true,
Public: true,
Protocol: "simplestreams"}
var StaticRemotes = map[string]RemoteConfig{
"local": LocalRemote,
"ubuntu": UbuntuRemote,
"ubuntu-daily": UbuntuDailyRemote}
var DefaultRemotes = map[string]RemoteConfig{
"images": ImagesRemote,
"local": LocalRemote,
"ubuntu": UbuntuRemote,
"ubuntu-daily": UbuntuDailyRemote}
var DefaultConfig = Config{
Remotes: DefaultRemotes,
DefaultRemote: "local"}
// LoadConfig reads the configuration from the config path; if the path does
// not exist, it returns a default configuration.
func LoadConfig(path string) (*Config, error) {
data, err := ioutil.ReadFile(path)
if os.IsNotExist(err) {
// A missing file is equivalent to the default configuration.
withPath := DefaultConfig
withPath.ConfigDir = filepath.Dir(path)
return &withPath, nil
}
if err != nil {
return nil, fmt.Errorf("cannot read config file: %v", err)
}
var c Config
err = yaml.Unmarshal(data, &c)
if err != nil {
return nil, fmt.Errorf("cannot parse configuration: %v", err)
}
if c.Remotes == nil {
c.Remotes = make(map[string]RemoteConfig)
}
c.ConfigDir = filepath.Dir(path)
for k, v := range StaticRemotes {
c.Remotes[k] = v
}
// NOTE: Remove this once we only see a small fraction of non-simplestreams users
// Upgrade users to the "simplestreams" protocol
images, ok := c.Remotes["images"]
if ok && images.Protocol != ImagesRemote.Protocol && images.Addr == ImagesRemote.Addr {
c.Remotes["images"] = ImagesRemote
SaveConfig(&c, path)
}
return &c, nil
}
// SaveConfig writes the provided configuration to the config file.
func SaveConfig(c *Config, fname string) error {
for k := range StaticRemotes {
delete(c.Remotes, k)
}
// Ignore errors on these two calls. Create will report any problems.
os.Remove(fname + ".new")
os.Mkdir(filepath.Dir(fname), 0700)
f, err := os.Create(fname + ".new")
if err != nil {
return fmt.Errorf("cannot create config file: %v", err)
}
// If there are any errors, do not leave it around.
defer f.Close()
defer os.Remove(fname + ".new")
data, err := yaml.Marshal(c)
_, err = f.Write(data)
if err != nil {
return fmt.Errorf("cannot write configuration: %v", err)
}
f.Close()
err = shared.FileMove(fname+".new", fname)
if err != nil {
return fmt.Errorf("cannot rename temporary config file: %v", err)
}
return nil
}
func (c *Config) ParseRemoteAndContainer(raw string) (string, string) {
result := strings.SplitN(raw, ":", 2)
if len(result) == 1 {
return c.DefaultRemote, result[0]
}
return result[0], result[1]
}
func (c *Config) ParseRemote(raw string) string {
return strings.SplitN(raw, ":", 2)[0]
}
func (c *Config) ConfigPath(file string) string {
return path.Join(c.ConfigDir, file)
}
func (c *Config) ServerCertPath(name string) string {
return path.Join(c.ConfigDir, "servercerts", fmt.Sprintf("%s.crt", name))
}