~rogpeppe/juju-core/azure

« back to all changes in this revision

Viewing changes to environs/config.go

  • Committer: Roger Peppe
  • Date: 2011-12-15 18:54:31 UTC
  • mfrom: (19.5.4 go-juju-ec2-operations)
  • mto: This revision was merged to the branch mainline in revision 30.
  • Revision ID: roger.peppe@canonical.com-20111215185431-tjuxi6bmg1mswcwg
renameĀ environ->environs

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
// Copyright 2011, 2012, 2013 Canonical Ltd.
2
 
// Licensed under the AGPLv3, see LICENCE file for details.
3
 
 
4
1
package environs
5
2
 
6
3
import (
 
4
        "errors"
7
5
        "fmt"
8
6
        "io/ioutil"
 
7
        "launchpad.net/goyaml"
9
8
        "os"
10
9
        "path/filepath"
11
 
 
12
 
        "launchpad.net/goyaml"
13
 
        "launchpad.net/loggo"
14
 
 
15
 
        "launchpad.net/juju-core/environs/config"
16
10
)
17
11
 
18
 
var logger = loggo.GetLogger("juju.environs")
19
 
 
20
12
// environ holds information about one environment.
21
13
type environ struct {
22
 
        config *config.Config
23
 
        err    error // an error if the config data could not be parsed.
 
14
        kind   string      // the type of environment (e.g. ec2).
 
15
        config interface{} // the configuration data for passing to NewEnviron.
 
16
        err    error       // an error if the config data could not be parsed.
24
17
}
25
18
 
26
19
// Environs holds information about each named environment
54
47
        providers[name] = p
55
48
}
56
49
 
57
 
// Provider returns the previously registered provider with the given type.
58
 
func Provider(typ string) (EnvironProvider, error) {
59
 
        p, ok := providers[typ]
60
 
        if !ok {
61
 
                return nil, fmt.Errorf("no registered provider for %q", typ)
62
 
        }
63
 
        return p, nil
64
 
}
65
 
 
66
50
// ReadEnvironsBytes parses the contents of an environments.yaml file
67
51
// and returns its representation. An environment with an unknown type
68
52
// will only generate an error when New is called for that environment.
69
53
// Attributes for environments with known types are checked.
70
54
func ReadEnvironsBytes(data []byte) (*Environs, error) {
71
55
        var raw struct {
72
 
                Default      string
73
 
                Environments map[string]map[string]interface{}
 
56
                Default      string                 "default"
 
57
                Environments map[string]interface{} "environments"
74
58
        }
 
59
        raw.Environments = make(map[string]interface{}) // TODO fix bug in goyaml - it should make this automatically.
75
60
        err := goyaml.Unmarshal(data, &raw)
76
61
        if err != nil {
77
62
                return nil, err
92
77
        }
93
78
 
94
79
        environs := make(map[string]environ)
95
 
        for name, attrs := range raw.Environments {
 
80
        for name, x := range raw.Environments {
 
81
                attrs, ok := x.(map[interface{}]interface{})
 
82
                if !ok {
 
83
                        return nil, fmt.Errorf("environment %q does not have attributes", name)
 
84
                }
96
85
                kind, _ := attrs["type"].(string)
97
86
                if kind == "" {
98
 
                        environs[name] = environ{
99
 
                                err: fmt.Errorf("environment %q has no type", name),
100
 
                        }
101
 
                        continue
 
87
                        return nil, fmt.Errorf("environment %q has no type", name)
102
88
                }
 
89
 
103
90
                p := providers[kind]
104
91
                if p == nil {
 
92
                        // unknown provider type - skip entry but leave error message
 
93
                        // in case the environment is used later.
105
94
                        environs[name] = environ{
106
 
                                err: fmt.Errorf("environment %q has an unknown provider type %q", name, kind),
 
95
                                kind: kind,
 
96
                                err:  fmt.Errorf("environment %q has an unknown provider type: %q", name, kind),
107
97
                        }
108
98
                        continue
109
99
                }
110
 
                // store the name of the this environment in the config itself
111
 
                // so that providers can see it.
112
 
                attrs["name"] = name
113
 
                cfg, err := config.New(attrs)
 
100
                cfg, err := p.ConfigChecker().Coerce(attrs, nil)
114
101
                if err != nil {
115
 
                        environs[name] = environ{
116
 
                                err: fmt.Errorf("error parsing environment %q: %v", name, err),
117
 
                        }
118
 
                        continue
119
 
                }
120
 
                environs[name] = environ{config: cfg}
 
102
                        return nil, fmt.Errorf("error parsing environment %q: %v", name, err)
 
103
                }
 
104
                environs[name] = environ{
 
105
                        kind:   kind,
 
106
                        config: cfg,
 
107
                }
121
108
        }
122
109
        return &Environs{raw.Default, environs}, nil
123
110
}
124
111
 
125
 
func environsPath(path string) string {
126
 
        if path == "" {
127
 
                path = config.JujuHomePath("environments.yaml")
128
 
        }
129
 
        return path
130
 
}
131
 
 
132
112
// ReadEnvirons reads the juju environments.yaml file
133
113
// and returns the result of running ParseEnvironments
134
114
// on the file's contents.
135
 
// If path is empty, $HOME/.juju/environments.yaml is used.
136
 
func ReadEnvirons(path string) (*Environs, error) {
137
 
        environsFilepath := environsPath(path)
138
 
        data, err := ioutil.ReadFile(environsFilepath)
 
115
// If environsFile is empty, $HOME/.juju/environments.yaml
 
116
// is used.
 
117
func ReadEnvirons(environsFile string) (*Environs, error) {
 
118
        if environsFile == "" {
 
119
                home := os.Getenv("HOME")
 
120
                if home == "" {
 
121
                        return nil, errors.New("$HOME not set")
 
122
                }
 
123
                environsFile = filepath.Join(home, ".juju/environments.yaml")
 
124
        }
 
125
        data, err := ioutil.ReadFile(environsFile)
139
126
        if err != nil {
140
127
                return nil, err
141
128
        }
142
129
        e, err := ReadEnvironsBytes(data)
143
130
        if err != nil {
144
 
                return nil, fmt.Errorf("cannot parse %q: %v", environsFilepath, err)
 
131
                return nil, fmt.Errorf("cannot parse %q: %v", environsFile, err)
145
132
        }
146
133
        return e, nil
147
134
}
148
 
 
149
 
// WriteEnvirons creates a new juju environments.yaml file with the specified contents.
150
 
func WriteEnvirons(path string, fileContents string) (string, error) {
151
 
        environsFilepath := environsPath(path)
152
 
        environsDir := filepath.Dir(environsFilepath)
153
 
        var info os.FileInfo
154
 
        var err error
155
 
        if info, err = os.Lstat(environsDir); os.IsNotExist(err) {
156
 
                if err = os.MkdirAll(environsDir, 0700); err != nil {
157
 
                        return "", err
158
 
                }
159
 
        } else if err != nil {
160
 
                return "", err
161
 
        } else if info.Mode().Perm() != 0700 {
162
 
                logger.Warningf("permission of %q is %q", environsDir, info.Mode().Perm())
163
 
        }
164
 
        if err := ioutil.WriteFile(environsFilepath, []byte(fileContents), 0600); err != nil {
165
 
                return "", err
166
 
        }
167
 
        // WriteFile does not change permissions of existing files.
168
 
        if err := os.Chmod(environsFilepath, 0600); err != nil {
169
 
                return "", err
170
 
        }
171
 
        return environsFilepath, nil
172
 
}
173
 
 
174
 
// BootstrapConfig returns a copy of the supplied configuration with
175
 
// secret attributes removed. If the resulting config is not suitable
176
 
// for bootstrapping an environment, an error is returned.
177
 
func BootstrapConfig(cfg *config.Config) (*config.Config, error) {
178
 
        p, err := Provider(cfg.Type())
179
 
        if err != nil {
180
 
                return nil, err
181
 
        }
182
 
        secrets, err := p.SecretAttrs(cfg)
183
 
        if err != nil {
184
 
                return nil, err
185
 
        }
186
 
        m := cfg.AllAttrs()
187
 
        for k := range secrets {
188
 
                delete(m, k)
189
 
        }
190
 
 
191
 
        // We never want to push admin-secret or the root CA private key to the cloud.
192
 
        delete(m, "admin-secret")
193
 
        m["ca-private-key"] = ""
194
 
        if cfg, err = config.New(m); err != nil {
195
 
                return nil, err
196
 
        }
197
 
        if _, ok := cfg.AgentVersion(); !ok {
198
 
                return nil, fmt.Errorf("environment configuration has no agent-version")
199
 
        }
200
 
        return cfg, nil
201
 
}