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  }