~rogpeppe/juju-core/azure

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
package environs

import (
	"errors"
	"fmt"
	"io/ioutil"
	"launchpad.net/goyaml"
	"os"
	"path/filepath"
)

// environ holds information about one environment.
type environ struct {
	kind   string      // the type of environment (e.g. ec2).
	config interface{} // the configuration data for passing to NewEnviron.
	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
}

// 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                 "default"
		Environments map[string]interface{} "environments"
	}
	raw.Environments = make(map[string]interface{}) // TODO fix bug in goyaml - it should make this automatically.
	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, x := range raw.Environments {
		attrs, ok := x.(map[interface{}]interface{})
		if !ok {
			return nil, fmt.Errorf("environment %q does not have attributes", name)
		}
		kind, _ := attrs["type"].(string)
		if kind == "" {
			return nil, fmt.Errorf("environment %q has no type", name)
		}

		p := providers[kind]
		if p == nil {
			// unknown provider type - skip entry but leave error message
			// in case the environment is used later.
			environs[name] = environ{
				kind: kind,
				err:  fmt.Errorf("environment %q has an unknown provider type: %q", name, kind),
			}
			continue
		}
		cfg, err := p.ConfigChecker().Coerce(attrs, nil)
		if err != nil {
			return nil, fmt.Errorf("error parsing environment %q: %v", name, err)
		}
		environs[name] = environ{
			kind:   kind,
			config: cfg,
		}
	}
	return &Environs{raw.Default, environs}, nil
}

// ReadEnvirons reads the juju environments.yaml file
// and returns the result of running ParseEnvironments
// on the file's contents.
// If environsFile is empty, $HOME/.juju/environments.yaml
// is used.
func ReadEnvirons(environsFile string) (*Environs, error) {
	if environsFile == "" {
		home := os.Getenv("HOME")
		if home == "" {
			return nil, errors.New("$HOME not set")
		}
		environsFile = filepath.Join(home, ".juju/environments.yaml")
	}
	data, err := ioutil.ReadFile(environsFile)
	if err != nil {
		return nil, err
	}
	e, err := ReadEnvironsBytes(data)
	if err != nil {
		return nil, fmt.Errorf("cannot parse %q: %v", environsFile, err)
	}
	return e, nil
}