github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/cmd/juju/environment/jenv.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 "fmt" 8 "os" 9 "path/filepath" 10 "strings" 11 12 "github.com/juju/cmd" 13 "github.com/juju/errors" 14 "github.com/juju/names" 15 "gopkg.in/yaml.v1" 16 17 "github.com/juju/juju/cmd/envcmd" 18 "github.com/juju/juju/environs/configstore" 19 "github.com/juju/juju/juju/osenv" 20 ) 21 22 // JenvCommand imports the given Juju generated jenv file into the local 23 // JUJU_HOME environments directory. 24 type JenvCommand struct { 25 cmd.CommandBase 26 jenvFile cmd.FileVar 27 envName string 28 } 29 30 const jenvHelpDoc = ` 31 Copy the provided Juju generated jenv file to the proper location inside your 32 local JUJU_HOME. Also switch to using the resulting environment as the default 33 one. This way it is possible to import and use jenv files generated by other 34 Juju commands such as "juju user add". 35 36 Example: 37 38 juju environment jenv my-env.jenv (my-env.jenv is the path to the jenv file) 39 juju environment jenv my-env.jenv ec2 (copy and rename the environment) 40 ` 41 42 func (c *JenvCommand) Info() *cmd.Info { 43 return &cmd.Info{ 44 Name: "jenv", 45 Args: "<path/to/file.jenv> [<environment name>]", 46 Purpose: "import previously generated Juju environment files", 47 Doc: strings.TrimSpace(jenvHelpDoc), 48 } 49 } 50 51 func (c *JenvCommand) Init(args []string) error { 52 if len(args) == 0 { 53 return errors.New("no jenv file provided") 54 } 55 56 // Store the path to the jenv file. 57 if err := c.jenvFile.Set(args[0]); err != nil { 58 return errors.Annotate(err, "invalid jenv path") 59 } 60 args = args[1:] 61 62 if len(args) > 0 { 63 // Store and validate the provided environment name. 64 c.envName, args = args[0], args[1:] 65 if !names.IsValidUser(c.envName) { 66 return errors.Errorf("invalid environment name %q", c.envName) 67 } 68 } else { 69 // Retrieve the environment name from the jenv file name. 70 base := filepath.Base(c.jenvFile.Path) 71 c.envName = base[:len(base)-len(filepath.Ext(base))] 72 } 73 74 // No other arguments are expected. 75 return cmd.CheckEmpty(args) 76 } 77 78 func (c *JenvCommand) Run(ctx *cmd.Context) error { 79 // Read data from the provided jenv file. 80 data, err := c.jenvFile.Read(ctx) 81 if err != nil { 82 if os.IsNotExist(errors.Cause(err)) { 83 return errors.NotFoundf("jenv file %q", c.jenvFile.Path) 84 } 85 return errors.Annotatef(err, "cannot read the provided jenv file %q", c.jenvFile.Path) 86 } 87 88 // Open the config store. 89 store, err := configstore.Default() 90 if err != nil { 91 return errors.Annotate(err, "cannot get config store") 92 } 93 94 // Create and update the new environment info object. 95 info := store.CreateInfo(c.envName) 96 if err := updateEnvironmentInfo(info, data); err != nil { 97 return errors.Annotatef(err, "invalid jenv file %q", c.jenvFile.Path) 98 } 99 100 // Write the environment info to JUJU_HOME. 101 if err := info.Write(); err != nil { 102 if errors.Cause(err) == configstore.ErrEnvironInfoAlreadyExists { 103 descriptiveErr := errors.Errorf("an environment named %q already exists: "+ 104 "you can provide a second parameter to rename the environment", 105 c.envName) 106 return errors.Wrap(err, descriptiveErr) 107 } 108 return errors.Annotate(err, "cannot write the jenv file") 109 } 110 111 // Switch to the new environment. 112 oldEnvName, err := switchEnvironment(c.envName) 113 if err != nil { 114 return errors.Annotatef(err, "cannot switch to the new environment %q", c.envName) 115 } 116 if oldEnvName == "" { 117 fmt.Fprintf(ctx.Stdout, "-> %s\n", c.envName) 118 } else { 119 fmt.Fprintf(ctx.Stdout, "%s -> %s\n", oldEnvName, c.envName) 120 } 121 return nil 122 } 123 124 // updateEnvironmentInfo updates the given environment info with the values 125 // stored in the provided YAML encoded data. 126 func updateEnvironmentInfo(info configstore.EnvironInfo, data []byte) error { 127 var values configstore.EnvironInfoData 128 if err := yaml.Unmarshal(data, &values); err != nil { 129 return errors.Annotate(err, "cannot unmarshal jenv data") 130 } 131 132 // Ensure the required values are present. 133 if missing := getMissingEnvironmentInfoFields(values); len(missing) != 0 { 134 return errors.Errorf("missing required fields in jenv data: %s", strings.Join(missing, ", ")) 135 } 136 137 // Update the environment info. 138 info.SetAPICredentials(configstore.APICredentials{ 139 User: values.User, 140 Password: values.Password, 141 }) 142 info.SetAPIEndpoint(configstore.APIEndpoint{ 143 Addresses: values.StateServers, 144 Hostnames: values.ServerHostnames, 145 CACert: values.CACert, 146 EnvironUUID: values.EnvironUUID, 147 }) 148 info.SetBootstrapConfig(values.Config) 149 return nil 150 } 151 152 // getMissingEnvironmentInfoFields returns a list of field names missing in the 153 // given environment info values. The only fields taken into consideration here 154 // are the ones explicitly set by the "juju user add" command. 155 func getMissingEnvironmentInfoFields(values configstore.EnvironInfoData) (missing []string) { 156 if values.User == "" { 157 missing = append(missing, "User") 158 } 159 if values.Password == "" { 160 missing = append(missing, "Password") 161 } 162 if values.EnvironUUID == "" { 163 missing = append(missing, "EnvironUUID") 164 } 165 if len(values.StateServers) == 0 { 166 missing = append(missing, "StateServers") 167 } 168 if values.CACert == "" { 169 missing = append(missing, "CACert") 170 } 171 return missing 172 } 173 174 // switchEnvironment changes the default environment to the given name and 175 // return, if set, the current default environment name. 176 func switchEnvironment(envName string) (string, error) { 177 if defaultEnv := os.Getenv(osenv.JujuEnvEnvKey); defaultEnv != "" { 178 return "", errors.Errorf("cannot switch when %s is overriding the environment (set to %q)", osenv.JujuEnvEnvKey, defaultEnv) 179 } 180 currentEnv, err := envcmd.GetDefaultEnvironment() 181 if err != nil { 182 return "", errors.Annotate(err, "cannot get the default environment") 183 } 184 if err := envcmd.WriteCurrentEnvironment(envName); err != nil { 185 return "", errors.Trace(err) 186 } 187 return currentEnv, nil 188 }