~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/environs/bootstrap/prepare.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright 2011-2016 Canonical Ltd.
 
2
// Licensed under the AGPLv3, see LICENCE file for details.
 
3
 
 
4
package bootstrap
 
5
 
 
6
import (
 
7
        "github.com/juju/errors"
 
8
        "gopkg.in/juju/names.v2"
 
9
 
 
10
        "github.com/juju/juju/controller"
 
11
        "github.com/juju/juju/environs"
 
12
        "github.com/juju/juju/environs/config"
 
13
        "github.com/juju/juju/jujuclient"
 
14
)
 
15
 
 
16
// ControllerModelName is the name of the admin model in each controller.
 
17
const ControllerModelName = "controller"
 
18
 
 
19
// PrepareParams contains the parameters for preparing a controller Environ
 
20
// for bootstrapping.
 
21
type PrepareParams struct {
 
22
        // ModelConfig contains the base configuration for the controller model.
 
23
        //
 
24
        // This includes the model name, cloud type, any user-supplied
 
25
        // configuration, config inherited from controller, and any defaults.
 
26
        ModelConfig map[string]interface{}
 
27
 
 
28
        // ControllerConfig is the configuration of the controller being prepared.
 
29
        ControllerConfig controller.Config
 
30
 
 
31
        // ControllerName is the name of the controller being prepared.
 
32
        ControllerName string
 
33
 
 
34
        // Cloud is the specification of the cloud that the controller is
 
35
        // being prepared for.
 
36
        Cloud environs.CloudSpec
 
37
 
 
38
        // CredentialName is the name of the credential to use to bootstrap.
 
39
        // This will be empty for auto-detected credentials.
 
40
        CredentialName string
 
41
 
 
42
        // AdminSecret contains the password for the admin user.
 
43
        AdminSecret string
 
44
}
 
45
 
 
46
// Validate validates the PrepareParams.
 
47
func (p PrepareParams) Validate() error {
 
48
        if err := p.ControllerConfig.Validate(); err != nil {
 
49
                return errors.Annotate(err, "validating controller config")
 
50
        }
 
51
        if p.ControllerName == "" {
 
52
                return errors.NotValidf("empty controller name")
 
53
        }
 
54
        if p.Cloud.Name == "" {
 
55
                return errors.NotValidf("empty cloud name")
 
56
        }
 
57
        if p.AdminSecret == "" {
 
58
                return errors.NotValidf("empty admin-secret")
 
59
        }
 
60
        return nil
 
61
}
 
62
 
 
63
// Prepare prepares a new controller based on the provided configuration.
 
64
// It is an error to prepare a controller if there already exists an
 
65
// entry in the client store with the same name.
 
66
//
 
67
// Upon success, Prepare will update the ClientStore with the details of
 
68
// the controller, admin account, and admin model.
 
69
func Prepare(
 
70
        ctx environs.BootstrapContext,
 
71
        store jujuclient.ClientStore,
 
72
        args PrepareParams,
 
73
) (environs.Environ, error) {
 
74
 
 
75
        if err := args.Validate(); err != nil {
 
76
                return nil, errors.Trace(err)
 
77
        }
 
78
 
 
79
        _, err := store.ControllerByName(args.ControllerName)
 
80
        if err == nil {
 
81
                return nil, errors.AlreadyExistsf("controller %q", args.ControllerName)
 
82
        } else if !errors.IsNotFound(err) {
 
83
                return nil, errors.Annotatef(err, "error reading controller %q info", args.ControllerName)
 
84
        }
 
85
 
 
86
        cloudType, ok := args.ModelConfig["type"].(string)
 
87
        if !ok {
 
88
                return nil, errors.NotFoundf("cloud type in base configuration")
 
89
        }
 
90
 
 
91
        p, err := environs.Provider(cloudType)
 
92
        if err != nil {
 
93
                return nil, errors.Trace(err)
 
94
        }
 
95
 
 
96
        env, details, err := prepare(ctx, p, args)
 
97
        if err != nil {
 
98
                return nil, errors.Trace(err)
 
99
        }
 
100
 
 
101
        if err := decorateAndWriteInfo(
 
102
                store, details, args.ControllerName, env.Config().Name(),
 
103
        ); err != nil {
 
104
                return nil, errors.Annotatef(err, "cannot create controller %q info", args.ControllerName)
 
105
        }
 
106
        return env, nil
 
107
}
 
108
 
 
109
// decorateAndWriteInfo decorates the info struct with information
 
110
// from the given cfg, and the writes that out to the filesystem.
 
111
func decorateAndWriteInfo(
 
112
        store jujuclient.ClientStore,
 
113
        details prepareDetails,
 
114
        controllerName, modelName string,
 
115
) error {
 
116
        qualifiedModelName := jujuclient.JoinOwnerModelName(
 
117
                names.NewUserTag(details.AccountDetails.User),
 
118
                modelName,
 
119
        )
 
120
        if err := store.UpdateController(controllerName, details.ControllerDetails); err != nil {
 
121
                return errors.Trace(err)
 
122
        }
 
123
        if err := store.UpdateBootstrapConfig(controllerName, details.BootstrapConfig); err != nil {
 
124
                return errors.Trace(err)
 
125
        }
 
126
        if err := store.UpdateAccount(controllerName, details.AccountDetails); err != nil {
 
127
                return errors.Trace(err)
 
128
        }
 
129
        if err := store.UpdateModel(controllerName, qualifiedModelName, details.ModelDetails); err != nil {
 
130
                return errors.Trace(err)
 
131
        }
 
132
        if err := store.SetCurrentModel(controllerName, qualifiedModelName); err != nil {
 
133
                return errors.Trace(err)
 
134
        }
 
135
        return nil
 
136
}
 
137
 
 
138
func prepare(
 
139
        ctx environs.BootstrapContext,
 
140
        p environs.EnvironProvider,
 
141
        args PrepareParams,
 
142
) (environs.Environ, prepareDetails, error) {
 
143
        var details prepareDetails
 
144
 
 
145
        cfg, err := config.New(config.NoDefaults, args.ModelConfig)
 
146
        if err != nil {
 
147
                return nil, details, errors.Trace(err)
 
148
        }
 
149
 
 
150
        cfg, err = p.PrepareConfig(environs.PrepareConfigParams{
 
151
                args.ControllerConfig.ControllerUUID(), args.Cloud, cfg,
 
152
        })
 
153
        if err != nil {
 
154
                return nil, details, errors.Trace(err)
 
155
        }
 
156
        env, err := p.Open(environs.OpenParams{
 
157
                Cloud:  args.Cloud,
 
158
                Config: cfg,
 
159
        })
 
160
        if err != nil {
 
161
                return nil, details, errors.Trace(err)
 
162
        }
 
163
        if err := env.PrepareForBootstrap(ctx); err != nil {
 
164
                return nil, details, errors.Trace(err)
 
165
        }
 
166
 
 
167
        // We store the base configuration only; we don't want the
 
168
        // default attributes, generated secrets/certificates, or
 
169
        // UUIDs stored in the bootstrap config. Make a copy, so
 
170
        // we don't disturb the caller's config map.
 
171
        details.Config = make(map[string]interface{})
 
172
        for k, v := range args.ModelConfig {
 
173
                details.Config[k] = v
 
174
        }
 
175
        delete(details.Config, config.UUIDKey)
 
176
 
 
177
        // TODO(axw) change signature of CACert() to not return a bool.
 
178
        // It's no longer possible to have a controller config without
 
179
        // a CA certificate.
 
180
        caCert, ok := args.ControllerConfig.CACert()
 
181
        if !ok {
 
182
                return nil, details, errors.New("controller config is missing CA certificate")
 
183
        }
 
184
 
 
185
        // We want to store attributes describing how a controller has been configured.
 
186
        // These do not include the CACert or UUID since they will be replaced with new
 
187
        // values when/if we need to use this configuration.
 
188
        details.ControllerConfig = make(controller.Config)
 
189
        for k, v := range args.ControllerConfig {
 
190
                if k == controller.CACertKey || k == controller.ControllerUUIDKey {
 
191
                        continue
 
192
                }
 
193
                details.ControllerConfig[k] = v
 
194
        }
 
195
        for k, v := range args.ControllerConfig {
 
196
                if k == controller.CACertKey || k == controller.ControllerUUIDKey {
 
197
                        continue
 
198
                }
 
199
                details.ControllerConfig[k] = v
 
200
        }
 
201
        details.CACert = caCert
 
202
        details.ControllerUUID = args.ControllerConfig.ControllerUUID()
 
203
        details.User = environs.AdminUser
 
204
        details.Password = args.AdminSecret
 
205
        details.ModelUUID = cfg.UUID()
 
206
        details.ControllerDetails.Cloud = args.Cloud.Name
 
207
        details.ControllerDetails.CloudRegion = args.Cloud.Region
 
208
        details.BootstrapConfig.CloudType = args.Cloud.Type
 
209
        details.BootstrapConfig.Cloud = args.Cloud.Name
 
210
        details.BootstrapConfig.CloudRegion = args.Cloud.Region
 
211
        details.CloudEndpoint = args.Cloud.Endpoint
 
212
        details.CloudStorageEndpoint = args.Cloud.StorageEndpoint
 
213
        details.Credential = args.CredentialName
 
214
 
 
215
        return env, details, nil
 
216
}
 
217
 
 
218
type prepareDetails struct {
 
219
        jujuclient.ControllerDetails
 
220
        jujuclient.BootstrapConfig
 
221
        jujuclient.AccountDetails
 
222
        jujuclient.ModelDetails
 
223
}