1
// Copyright 2011-2016 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
7
"github.com/juju/errors"
8
"gopkg.in/juju/names.v2"
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"
16
// ControllerModelName is the name of the admin model in each controller.
17
const ControllerModelName = "controller"
19
// PrepareParams contains the parameters for preparing a controller Environ
21
type PrepareParams struct {
22
// ModelConfig contains the base configuration for the controller model.
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{}
28
// ControllerConfig is the configuration of the controller being prepared.
29
ControllerConfig controller.Config
31
// ControllerName is the name of the controller being prepared.
34
// Cloud is the specification of the cloud that the controller is
35
// being prepared for.
36
Cloud environs.CloudSpec
38
// CredentialName is the name of the credential to use to bootstrap.
39
// This will be empty for auto-detected credentials.
42
// AdminSecret contains the password for the admin user.
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")
51
if p.ControllerName == "" {
52
return errors.NotValidf("empty controller name")
54
if p.Cloud.Name == "" {
55
return errors.NotValidf("empty cloud name")
57
if p.AdminSecret == "" {
58
return errors.NotValidf("empty admin-secret")
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.
67
// Upon success, Prepare will update the ClientStore with the details of
68
// the controller, admin account, and admin model.
70
ctx environs.BootstrapContext,
71
store jujuclient.ClientStore,
73
) (environs.Environ, error) {
75
if err := args.Validate(); err != nil {
76
return nil, errors.Trace(err)
79
_, err := store.ControllerByName(args.ControllerName)
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)
86
cloudType, ok := args.ModelConfig["type"].(string)
88
return nil, errors.NotFoundf("cloud type in base configuration")
91
p, err := environs.Provider(cloudType)
93
return nil, errors.Trace(err)
96
env, details, err := prepare(ctx, p, args)
98
return nil, errors.Trace(err)
101
if err := decorateAndWriteInfo(
102
store, details, args.ControllerName, env.Config().Name(),
104
return nil, errors.Annotatef(err, "cannot create controller %q info", args.ControllerName)
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,
116
qualifiedModelName := jujuclient.JoinOwnerModelName(
117
names.NewUserTag(details.AccountDetails.User),
120
if err := store.UpdateController(controllerName, details.ControllerDetails); err != nil {
121
return errors.Trace(err)
123
if err := store.UpdateBootstrapConfig(controllerName, details.BootstrapConfig); err != nil {
124
return errors.Trace(err)
126
if err := store.UpdateAccount(controllerName, details.AccountDetails); err != nil {
127
return errors.Trace(err)
129
if err := store.UpdateModel(controllerName, qualifiedModelName, details.ModelDetails); err != nil {
130
return errors.Trace(err)
132
if err := store.SetCurrentModel(controllerName, qualifiedModelName); err != nil {
133
return errors.Trace(err)
139
ctx environs.BootstrapContext,
140
p environs.EnvironProvider,
142
) (environs.Environ, prepareDetails, error) {
143
var details prepareDetails
145
cfg, err := config.New(config.NoDefaults, args.ModelConfig)
147
return nil, details, errors.Trace(err)
150
cfg, err = p.PrepareConfig(environs.PrepareConfigParams{
151
args.ControllerConfig.ControllerUUID(), args.Cloud, cfg,
154
return nil, details, errors.Trace(err)
156
env, err := p.Open(environs.OpenParams{
161
return nil, details, errors.Trace(err)
163
if err := env.PrepareForBootstrap(ctx); err != nil {
164
return nil, details, errors.Trace(err)
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
175
delete(details.Config, config.UUIDKey)
177
// TODO(axw) change signature of CACert() to not return a bool.
178
// It's no longer possible to have a controller config without
180
caCert, ok := args.ControllerConfig.CACert()
182
return nil, details, errors.New("controller config is missing CA certificate")
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 {
193
details.ControllerConfig[k] = v
195
for k, v := range args.ControllerConfig {
196
if k == controller.CACertKey || k == controller.ControllerUUIDKey {
199
details.ControllerConfig[k] = v
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
215
return env, details, nil
218
type prepareDetails struct {
219
jujuclient.ControllerDetails
220
jujuclient.BootstrapConfig
221
jujuclient.AccountDetails
222
jujuclient.ModelDetails