~juju-qa/ubuntu/xenial/juju/xenial-2.0-beta3

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/cmd/juju/controller/createmodel.go

  • Committer: Martin Packman
  • Date: 2016-03-30 19:31:08 UTC
  • mfrom: (1.1.41)
  • Revision ID: martin.packman@canonical.com-20160330193108-h9iz3ak334uk0z5r
Merge new upstream source 2.0~beta3

Show diffs side-by-side

added added

removed removed

Lines of Context:
9
9
        "github.com/juju/cmd"
10
10
        "github.com/juju/errors"
11
11
        "github.com/juju/names"
12
 
        "github.com/juju/utils/keyvalues"
13
 
        "gopkg.in/yaml.v2"
14
12
        "launchpad.net/gnuflag"
15
13
 
16
14
        "github.com/juju/juju/apiserver/params"
 
15
        "github.com/juju/juju/cloud"
17
16
        "github.com/juju/juju/cmd/juju/common"
18
17
        "github.com/juju/juju/cmd/modelcmd"
19
 
        "github.com/juju/juju/environs/config"
20
18
        "github.com/juju/juju/jujuclient"
21
19
)
22
20
 
23
21
// NewCreateModelCommand returns a command to create an model.
24
22
func NewCreateModelCommand() cmd.Command {
25
 
        return modelcmd.WrapController(&createModelCommand{})
 
23
        return modelcmd.WrapController(&createModelCommand{
 
24
                credentialStore: jujuclient.NewFileCredentialStore(),
 
25
        })
26
26
}
27
27
 
28
28
// createModelCommand calls the API to create a new model.
29
29
type createModelCommand struct {
30
30
        modelcmd.ControllerCommandBase
31
 
        api CreateModelAPI
 
31
        api             CreateModelAPI
 
32
        credentialStore jujuclient.CredentialStore
32
33
 
33
 
        Name         string
34
 
        Owner        string
35
 
        ConfigFile   cmd.FileVar
36
 
        ConfValues   map[string]string
37
 
        configParser func(interface{}) (interface{}, error)
 
34
        Name           string
 
35
        Owner          string
 
36
        CredentialSpec string
 
37
        CloudName      string
 
38
        CloudType      string
 
39
        CredentialName string
 
40
        Config         common.ConfigFlag
38
41
}
39
42
 
40
43
const createModelHelpDoc = `
41
44
This command will create another model within the current Juju
42
45
Controller. The provider has to match, and the model config must
43
 
specify all the required configuration values for the provider. In the cases
44
 
of ‘ec2’ and ‘openstack’, the same model variables are checked for the
45
 
access and secret keys.
46
 
 
47
 
If configuration values are passed by both extra command line arguments and
48
 
the --config option, the command line args take priority.
 
46
specify all the required configuration values for the provider.
 
47
 
 
48
If configuration values are passed by both extra command line
 
49
arguments and the --config option, the command line args take
 
50
priority.
 
51
 
 
52
If creating a model in a controller for which you are not the
 
53
administrator, the cloud credentials and authorized ssh keys must
 
54
be specified. The credentials are specified using the argument
 
55
--credential <cloud>:<credential>. The authorized ssh keys are
 
56
specified using a --config argument, either authorized=keys=value
 
57
or via a config yaml file.
 
58
 
 
59
Any credentials used must be for a cloud with the same provider
 
60
type as the controller. Controller administrators do not have to
 
61
specify credentials or ssh keys; by default, the credentials and
 
62
keys used to bootstrap the controller are used if no others are
 
63
specified.
49
64
 
50
65
Examples:
51
66
 
52
67
    juju create-model new-model
53
68
 
54
 
    juju create-model new-model --config=aws-creds.yaml
 
69
    juju create-model new-model --config aws-creds.yaml --config image-stream=daily
 
70
    
 
71
    juju create-model new-model --credential aws:mysekrets --config authorized-keys="ssh-rsa ..."
55
72
 
56
73
See Also:
57
74
    juju help model share
60
77
func (c *createModelCommand) Info() *cmd.Info {
61
78
        return &cmd.Info{
62
79
                Name:    "create-model",
63
 
                Args:    "<name> [key=[value] ...]",
 
80
                Args:    "<name> [--config key=[value] ...] [--credential <cloud>:<credential>]",
64
81
                Purpose: "create an model within the Juju Model Server",
65
82
                Doc:     strings.TrimSpace(createModelHelpDoc),
66
83
        }
68
85
 
69
86
func (c *createModelCommand) SetFlags(f *gnuflag.FlagSet) {
70
87
        f.StringVar(&c.Owner, "owner", "", "the owner of the new model if not the current user")
71
 
        f.Var(&c.ConfigFile, "config", "path to yaml-formatted file containing model config values")
 
88
        f.StringVar(&c.CredentialSpec, "credential", "", "the name of the cloud and credentials the new model uses to create cloud resources")
 
89
        f.Var(&c.Config, "config", "specify a controller config file, or one or more controller configuration options (--config config.yaml [--config k=v ...])")
72
90
}
73
91
 
74
92
func (c *createModelCommand) Init(args []string) error {
77
95
        }
78
96
        c.Name, args = args[0], args[1:]
79
97
 
80
 
        values, err := keyvalues.Parse(args, true)
81
 
        if err != nil {
82
 
                return err
83
 
        }
84
 
        c.ConfValues = values
85
 
 
86
98
        if c.Owner != "" && !names.IsValidUser(c.Owner) {
87
99
                return errors.Errorf("%q is not a valid user", c.Owner)
88
100
        }
89
101
 
90
 
        if c.configParser == nil {
91
 
                c.configParser = common.ConformYAML
 
102
        if c.CredentialSpec != "" {
 
103
                parts := strings.Split(c.CredentialSpec, ":")
 
104
                if len(parts) < 2 {
 
105
                        return errors.Errorf("invalid cloud credential %s, expected <cloud>:<credential-name>", c.CredentialSpec)
 
106
                }
 
107
                c.CloudName = parts[0]
 
108
                if cloud, err := cloud.CloudByName(c.CloudName); err != nil {
 
109
                        return errors.Trace(err)
 
110
                } else {
 
111
                        c.CloudType = cloud.Type
 
112
                }
 
113
                c.CredentialName = parts[1]
92
114
        }
93
 
 
94
115
        return nil
95
116
}
96
117
 
133
154
                modelOwner = names.NewUserTag(c.Owner).Canonical()
134
155
        }
135
156
 
136
 
        serverSkeleton, err := client.ConfigSkeleton("", "")
 
157
        serverSkeleton, err := client.ConfigSkeleton(c.CloudType, "")
137
158
        if err != nil {
138
159
                return errors.Trace(err)
139
160
        }
143
164
                return errors.Trace(err)
144
165
        }
145
166
 
146
 
        // TODO we pass nil for the account details until we implement that bit.
147
 
        model, err := client.CreateModel(modelOwner, nil, attrs)
 
167
        accountDetails := map[string]interface{}{}
 
168
        if c.CredentialName != "" {
 
169
                cred, _, _, err := modelcmd.GetCredentials(
 
170
                        c.credentialStore, "", c.CredentialName, c.CloudName, c.CloudType,
 
171
                )
 
172
                if err != nil {
 
173
                        return errors.Trace(err)
 
174
                }
 
175
                for k, v := range cred.Attributes() {
 
176
                        accountDetails[k] = v
 
177
                }
 
178
        }
 
179
        model, err := client.CreateModel(modelOwner, accountDetails, attrs)
148
180
        if err != nil {
149
181
                return errors.Trace(err)
150
182
        }
168
200
}
169
201
 
170
202
func (c *createModelCommand) getConfigValues(ctx *cmd.Context, serverSkeleton params.ModelConfig) (map[string]interface{}, error) {
171
 
        // The reading of the config YAML is done in the Run
172
 
        // method because the Read method requires the cmd Context
173
 
        // for the current directory.
174
 
        fileConfig := make(map[string]interface{})
175
 
        if c.ConfigFile.Path != "" {
176
 
                configYAML, err := c.ConfigFile.Read(ctx)
177
 
                if err != nil {
178
 
                        return nil, errors.Annotate(err, "unable to read config file")
179
 
                }
180
 
 
181
 
                rawFileConfig := make(map[string]interface{})
182
 
                err = yaml.Unmarshal(configYAML, &rawFileConfig)
183
 
                if err != nil {
184
 
                        return nil, errors.Annotate(err, "unable to parse config file")
185
 
                }
186
 
 
187
 
                conformantConfig, err := c.configParser(rawFileConfig)
188
 
                if err != nil {
189
 
                        return nil, errors.Annotate(err, "unable to parse config file")
190
 
                }
191
 
                betterConfig, ok := conformantConfig.(map[string]interface{})
192
 
                if !ok {
193
 
                        return nil, errors.New("config must contain a YAML map with string keys")
194
 
                }
195
 
 
196
 
                fileConfig = betterConfig
197
 
        }
198
 
 
199
203
        configValues := make(map[string]interface{})
200
204
        for key, value := range serverSkeleton {
201
205
                configValues[key] = value
202
206
        }
203
 
        for key, value := range fileConfig {
204
 
                configValues[key] = value
 
207
        configAttr, err := c.Config.ReadAttrs(ctx)
 
208
        if err != nil {
 
209
                return nil, errors.Annotate(err, "unable to parse config")
205
210
        }
206
 
        for key, value := range c.ConfValues {
 
211
        for key, value := range configAttr {
207
212
                configValues[key] = value
208
213
        }
209
214
        configValues["name"] = c.Name
210
 
 
211
 
        // TODO: allow version to be specified on the command line and add here.
212
 
        cfg, err := config.New(config.UseDefaults, configValues)
 
215
        coercedValues, err := common.ConformYAML(configValues)
213
216
        if err != nil {
214
 
                return nil, errors.Trace(err)
 
217
                return nil, errors.Annotatef(err, "unable to parse config")
 
218
        }
 
219
        stringParams, ok := coercedValues.(map[string]interface{})
 
220
        if !ok {
 
221
                return nil, errors.New("params must contain a YAML map with string keys")
215
222
        }
216
223
 
217
 
        return cfg.AllAttrs(), nil
 
224
        return stringParams, nil
218
225
}