This repository has been archived by the owner on Jun 15, 2019. It is now read-only.
/
config.go
201 lines (182 loc) · 5.5 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
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
// Copyright 2011, 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.
package environs
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"launchpad.net/goyaml"
"launchpad.net/loggo"
"launchpad.net/juju-core/environs/config"
)
var logger = loggo.GetLogger("juju.environs")
// environ holds information about one environment.
type environ struct {
config *config.Config
err error // an error if the config data could not be parsed.
}
// Environs holds information about each named environment
// in an environments.yaml file.
type Environs struct {
Default string // The name of the default environment.
environs map[string]environ
}
// Names returns the list of environment names.
func (e *Environs) Names() (names []string) {
for name := range e.environs {
names = append(names, name)
}
return
}
// providers maps from provider type to EnvironProvider for
// each registered provider type.
var providers = make(map[string]EnvironProvider)
// RegisterProvider registers a new environment provider. Name gives the name
// of the provider, and p the interface to that provider.
//
// RegisterProvider will panic if the same provider name is registered more than
// once.
func RegisterProvider(name string, p EnvironProvider) {
if providers[name] != nil {
panic(fmt.Errorf("juju: duplicate provider name %q", name))
}
providers[name] = p
}
// Provider returns the previously registered provider with the given type.
func Provider(typ string) (EnvironProvider, error) {
p, ok := providers[typ]
if !ok {
return nil, fmt.Errorf("no registered provider for %q", typ)
}
return p, nil
}
// ReadEnvironsBytes parses the contents of an environments.yaml file
// and returns its representation. An environment with an unknown type
// will only generate an error when New is called for that environment.
// Attributes for environments with known types are checked.
func ReadEnvironsBytes(data []byte) (*Environs, error) {
var raw struct {
Default string
Environments map[string]map[string]interface{}
}
err := goyaml.Unmarshal(data, &raw)
if err != nil {
return nil, err
}
if raw.Default != "" && raw.Environments[raw.Default] == nil {
return nil, fmt.Errorf("default environment %q does not exist", raw.Default)
}
if raw.Default == "" {
// If there's a single environment, then we get the default
// automatically.
if len(raw.Environments) == 1 {
for name := range raw.Environments {
raw.Default = name
break
}
}
}
environs := make(map[string]environ)
for name, attrs := range raw.Environments {
kind, _ := attrs["type"].(string)
if kind == "" {
environs[name] = environ{
err: fmt.Errorf("environment %q has no type", name),
}
continue
}
p := providers[kind]
if p == nil {
environs[name] = environ{
err: fmt.Errorf("environment %q has an unknown provider type %q", name, kind),
}
continue
}
// store the name of the this environment in the config itself
// so that providers can see it.
attrs["name"] = name
cfg, err := config.New(attrs)
if err != nil {
environs[name] = environ{
err: fmt.Errorf("error parsing environment %q: %v", name, err),
}
continue
}
environs[name] = environ{config: cfg}
}
return &Environs{raw.Default, environs}, nil
}
func environsPath(path string) string {
if path == "" {
path = config.JujuHomePath("environments.yaml")
}
return path
}
// ReadEnvirons reads the juju environments.yaml file
// and returns the result of running ParseEnvironments
// on the file's contents.
// If path is empty, $HOME/.juju/environments.yaml is used.
func ReadEnvirons(path string) (*Environs, error) {
environsFilepath := environsPath(path)
data, err := ioutil.ReadFile(environsFilepath)
if err != nil {
return nil, err
}
e, err := ReadEnvironsBytes(data)
if err != nil {
return nil, fmt.Errorf("cannot parse %q: %v", environsFilepath, err)
}
return e, nil
}
// WriteEnvirons creates a new juju environments.yaml file with the specified contents.
func WriteEnvirons(path string, fileContents string) (string, error) {
environsFilepath := environsPath(path)
environsDir := filepath.Dir(environsFilepath)
var info os.FileInfo
var err error
if info, err = os.Lstat(environsDir); os.IsNotExist(err) {
if err = os.MkdirAll(environsDir, 0700); err != nil {
return "", err
}
} else if err != nil {
return "", err
} else if info.Mode().Perm() != 0700 {
logger.Warningf("permission of %q is %q", environsDir, info.Mode().Perm())
}
if err := ioutil.WriteFile(environsFilepath, []byte(fileContents), 0600); err != nil {
return "", err
}
// WriteFile does not change permissions of existing files.
if err := os.Chmod(environsFilepath, 0600); err != nil {
return "", err
}
return environsFilepath, nil
}
// BootstrapConfig returns a copy of the supplied configuration with
// secret attributes removed. If the resulting config is not suitable
// for bootstrapping an environment, an error is returned.
func BootstrapConfig(cfg *config.Config) (*config.Config, error) {
p, err := Provider(cfg.Type())
if err != nil {
return nil, err
}
secrets, err := p.SecretAttrs(cfg)
if err != nil {
return nil, err
}
m := cfg.AllAttrs()
for k := range secrets {
delete(m, k)
}
// We never want to push admin-secret or the root CA private key to the cloud.
delete(m, "admin-secret")
m["ca-private-key"] = ""
if cfg, err = config.New(m); err != nil {
return nil, err
}
if _, ok := cfg.AgentVersion(); !ok {
return nil, fmt.Errorf("environment configuration has no agent-version")
}
return cfg, nil
}