github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/cmd/juju/environment/create.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package environment 5 6 import ( 7 "strings" 8 9 "github.com/juju/cmd" 10 "github.com/juju/errors" 11 "github.com/juju/utils/keyvalues" 12 "gopkg.in/yaml.v1" 13 "launchpad.net/gnuflag" 14 15 "github.com/juju/juju/api/environmentmanager" 16 "github.com/juju/juju/apiserver/params" 17 "github.com/juju/juju/cmd/envcmd" 18 "github.com/juju/juju/environs" 19 "github.com/juju/juju/environs/config" 20 "github.com/juju/juju/environs/configstore" 21 ) 22 23 // CreateCommand calls the API to create a new environment. 24 type CreateCommand struct { 25 envcmd.EnvCommandBase 26 api CreateEnvironmentAPI 27 // These attributes are exported only for testing purposes. 28 Name string 29 // TODO: owner string 30 ConfigFile cmd.FileVar 31 ConfValues map[string]string 32 } 33 34 const createEnvHelpDoc = ` 35 This command will create another environment within the current Juju 36 Environment Server. The provider has to match, and the environment config must 37 specify all the required configuration values for the provider. In the cases 38 of ‘ec2’ and ‘openstack’, the same environment variables are checked for the 39 access and secret keys. 40 41 If configuration values are passed by both extra command line arguments and 42 the --config option, the command line args take priority. 43 ` 44 45 func (c *CreateCommand) Info() *cmd.Info { 46 return &cmd.Info{ 47 Name: "create", 48 Args: "<name> [key=[value] ...]", 49 Purpose: "create an environment within the Juju Environment Server", 50 Doc: strings.TrimSpace(createEnvHelpDoc), 51 } 52 } 53 54 func (c *CreateCommand) SetFlags(f *gnuflag.FlagSet) { 55 // TODO: support creating environments for someone else when we work 56 // out how to have the other user login and start using the environement. 57 // f.StringVar(&c.owner, "owner", "", "the owner of the new environment if not the current user") 58 f.Var(&c.ConfigFile, "config", "path to yaml-formatted file containing environment config values") 59 } 60 61 func (c *CreateCommand) Init(args []string) error { 62 if len(args) == 0 { 63 return errors.New("environment name is required") 64 } 65 c.Name, args = args[0], args[1:] 66 67 values, err := keyvalues.Parse(args, true) 68 if err != nil { 69 return err 70 } 71 c.ConfValues = values 72 return nil 73 } 74 75 type CreateEnvironmentAPI interface { 76 Close() error 77 ConfigSkeleton(provider, region string) (params.EnvironConfig, error) 78 CreateEnvironment(owner string, account, config map[string]interface{}) (params.Environment, error) 79 } 80 81 func (c *CreateCommand) getAPI() (CreateEnvironmentAPI, error) { 82 if c.api != nil { 83 return c.api, nil 84 } 85 root, err := c.NewAPIRoot() 86 if err != nil { 87 return nil, err 88 } 89 return environmentmanager.NewClient(root), nil 90 } 91 92 func (c *CreateCommand) Run(ctx *cmd.Context) (err error) { 93 client, err := c.getAPI() 94 if err != nil { 95 return err 96 } 97 defer client.Close() 98 99 // Create the configstore entry and write it to disk, as this will error 100 // if one with the same name already exists. 101 creds, err := c.ConnectionCredentials() 102 if err != nil { 103 return errors.Trace(err) 104 } 105 endpoint, err := c.ConnectionEndpoint(false) 106 if err != nil { 107 return errors.Trace(err) 108 } 109 110 store, err := configstore.Default() 111 if err != nil { 112 return errors.Trace(err) 113 } 114 info := store.CreateInfo(c.Name) 115 info.SetAPICredentials(creds) 116 endpoint.EnvironUUID = "" 117 if err := info.Write(); err != nil { 118 if errors.Cause(err) == configstore.ErrEnvironInfoAlreadyExists { 119 newErr := errors.AlreadyExistsf("environment %q", c.Name) 120 return errors.Wrap(err, newErr) 121 } 122 return errors.Trace(err) 123 } 124 defer func() { 125 if err != nil { 126 e := info.Destroy() 127 if e != nil { 128 logger.Errorf("could not remove environment file: %v", e) 129 } 130 } 131 }() 132 133 // TODO: support provider and region. 134 serverSkeleton, err := client.ConfigSkeleton("", "") 135 if err != nil { 136 return errors.Trace(err) 137 } 138 139 attrs, err := c.getConfigValues(ctx, serverSkeleton) 140 if err != nil { 141 return errors.Trace(err) 142 } 143 144 // We pass nil through for the account details until we implement that bit. 145 env, err := client.CreateEnvironment(creds.User, nil, attrs) 146 if err != nil { 147 // cleanup configstore 148 return errors.Trace(err) 149 } 150 151 // update the .jenv file with the environment uuid 152 endpoint.EnvironUUID = env.UUID 153 info.SetAPIEndpoint(endpoint) 154 if err := info.Write(); err != nil { 155 return errors.Trace(err) 156 } 157 158 return nil 159 } 160 161 func (c *CreateCommand) getConfigValues(ctx *cmd.Context, serverSkeleton params.EnvironConfig) (map[string]interface{}, error) { 162 // The reading of the config YAML is done in the Run 163 // method because the Read method requires the cmd Context 164 // for the current directory. 165 fileConfig := make(map[string]interface{}) 166 if c.ConfigFile.Path != "" { 167 configYAML, err := c.ConfigFile.Read(ctx) 168 if err != nil { 169 return nil, err 170 } 171 err = yaml.Unmarshal(configYAML, &fileConfig) 172 if err != nil { 173 return nil, err 174 } 175 } 176 177 configValues := make(map[string]interface{}) 178 for key, value := range serverSkeleton { 179 configValues[key] = value 180 } 181 for key, value := range fileConfig { 182 configValues[key] = value 183 } 184 for key, value := range c.ConfValues { 185 configValues[key] = value 186 } 187 configValues["name"] = c.Name 188 189 cfg, err := config.New(config.UseDefaults, configValues) 190 if err != nil { 191 return nil, errors.Trace(err) 192 } 193 194 provider, err := environs.Provider(cfg.Type()) 195 if err != nil { 196 return nil, errors.Trace(err) 197 } 198 199 cfg, err = provider.PrepareForCreateEnvironment(cfg) 200 if err != nil { 201 return nil, errors.Trace(err) 202 } 203 204 attrs := cfg.AllAttrs() 205 delete(attrs, "agent-version") 206 // TODO: allow version to be specified on the command line and add here. 207 208 return attrs, nil 209 }