github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/environs/open.go (about) 1 // Copyright 2011, 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package environs 5 6 import ( 7 "fmt" 8 "time" 9 10 "github.com/juju/errors" 11 "github.com/juju/utils" 12 13 "github.com/juju/juju/cert" 14 "github.com/juju/juju/environs/config" 15 "github.com/juju/juju/environs/configstore" 16 ) 17 18 var ( 19 InvalidEnvironmentError = fmt.Errorf( 20 "environment is not a juju-core environment") 21 ) 22 23 // ConfigSource represents where some configuration data 24 // has come from. 25 // TODO(rog) remove this when we don't have to support 26 // old environments with no configstore info. See lp#1235217 27 type ConfigSource int 28 29 const ( 30 ConfigFromNowhere ConfigSource = iota 31 ConfigFromInfo 32 ConfigFromEnvirons 33 ) 34 35 // EmptyConfig indicates the .jenv file is empty. 36 type EmptyConfig struct { 37 error 38 } 39 40 // IsEmptyConfig reports whether err is a EmptyConfig. 41 func IsEmptyConfig(err error) bool { 42 _, ok := err.(EmptyConfig) 43 return ok 44 } 45 46 // ConfigForName returns the configuration for the environment with 47 // the given name from the default environments file. If the name is 48 // blank, the default environment will be used. If the configuration 49 // is not found, an errors.NotFoundError is returned. If the given 50 // store contains an entry for the environment and it has associated 51 // bootstrap config, that configuration will be returned. 52 // ConfigForName also returns where the configuration was sourced from 53 // (this is also valid even when there is an error. 54 func ConfigForName(name string, store configstore.Storage) (*config.Config, ConfigSource, error) { 55 envs, err := ReadEnvirons("") 56 if err != nil { 57 return nil, ConfigFromNowhere, err 58 } 59 if name == "" { 60 name = envs.Default 61 } 62 63 info, err := store.ReadInfo(name) 64 if err == nil { 65 if len(info.BootstrapConfig()) == 0 { 66 return nil, ConfigFromNowhere, EmptyConfig{fmt.Errorf("environment has no bootstrap configuration data")} 67 } 68 logger.Debugf("ConfigForName found bootstrap config %#v", info.BootstrapConfig()) 69 cfg, err := config.New(config.NoDefaults, info.BootstrapConfig()) 70 return cfg, ConfigFromInfo, err 71 } else if !errors.IsNotFound(err) { 72 return nil, ConfigFromInfo, fmt.Errorf("cannot read environment info for %q: %v", name, err) 73 } 74 75 cfg, err := envs.Config(name) 76 return cfg, ConfigFromEnvirons, err 77 } 78 79 // maybeNotBootstrapped takes an error and source, returned by 80 // ConfigForName and returns ErrNotBootstrapped if it looks like the 81 // environment is not bootstrapped, or err as-is otherwise. 82 func maybeNotBootstrapped(err error, source ConfigSource) error { 83 if err != nil && source == ConfigFromEnvirons { 84 return ErrNotBootstrapped 85 } 86 return err 87 } 88 89 // NewFromName opens the environment with the given 90 // name from the default environments file. If the 91 // name is blank, the default environment will be used. 92 // If the given store contains an entry for the environment 93 // and it has associated bootstrap config, that configuration 94 // will be returned. 95 func NewFromName(name string, store configstore.Storage) (Environ, error) { 96 // If we get an error when reading from a legacy 97 // environments.yaml entry, we pretend it didn't exist 98 // because the error is likely to be because 99 // configuration attributes don't exist which 100 // will be filled in by Prepare. 101 cfg, source, err := ConfigForName(name, store) 102 if err := maybeNotBootstrapped(err, source); err != nil { 103 return nil, err 104 } 105 if err != nil { 106 return nil, err 107 } 108 109 env, err := New(cfg) 110 if err := maybeNotBootstrapped(err, source); err != nil { 111 return nil, err 112 } 113 return env, err 114 } 115 116 // PrepareFromName is the same as NewFromName except 117 // that the environment is is prepared as well as opened, 118 // and environment information is created using the 119 // given store. If the environment is already prepared, 120 // it behaves like NewFromName. 121 var PrepareFromName = prepareFromNameProductionFunc 122 123 func prepareFromNameProductionFunc(name string, ctx BootstrapContext, store configstore.Storage) (Environ, error) { 124 cfg, _, err := ConfigForName(name, store) 125 if err != nil { 126 return nil, err 127 } 128 return Prepare(cfg, ctx, store) 129 } 130 131 // NewFromAttrs returns a new environment based on the provided configuration 132 // attributes. 133 // TODO(rog) remove this function - it's almost always wrong to use it. 134 func NewFromAttrs(attrs map[string]interface{}) (Environ, error) { 135 cfg, err := config.New(config.NoDefaults, attrs) 136 if err != nil { 137 return nil, errors.Trace(err) 138 } 139 return New(cfg) 140 } 141 142 // New returns a new environment based on the provided configuration. 143 func New(config *config.Config) (Environ, error) { 144 p, err := Provider(config.Type()) 145 if err != nil { 146 return nil, errors.Trace(err) 147 } 148 return p.Open(config) 149 } 150 151 // Prepare prepares a new environment based on the provided configuration. 152 // If the environment is already prepared, it behaves like New. 153 func Prepare(cfg *config.Config, ctx BootstrapContext, store configstore.Storage) (Environ, error) { 154 155 if p, err := Provider(cfg.Type()); err != nil { 156 return nil, errors.Trace(err) 157 } else if info, err := store.ReadInfo(cfg.Name()); errors.IsNotFound(errors.Cause(err)) { 158 info = store.CreateInfo(cfg.Name()) 159 if env, err := prepare(ctx, cfg, info, p); err == nil { 160 return env, decorateAndWriteInfo(info, env.Config()) 161 } else { 162 if err := info.Destroy(); err != nil { 163 logger.Warningf("cannot destroy newly created environment info: %v", err) 164 } 165 return nil, errors.Trace(err) 166 } 167 } else if err != nil { 168 return nil, errors.Annotatef(err, "error reading environment info %q", cfg.Name()) 169 } else if !info.Initialized() { 170 return nil, 171 errors.Errorf( 172 "found uninitialized environment info for %q; environment preparation probably in progress or interrupted", 173 cfg.Name(), 174 ) 175 } else if len(info.BootstrapConfig()) == 0 { 176 return nil, errors.New("found environment info but no bootstrap config") 177 } else { 178 cfg, err = config.New(config.NoDefaults, info.BootstrapConfig()) 179 if err != nil { 180 return nil, errors.Annotate(err, "cannot parse bootstrap config") 181 } 182 return New(cfg) 183 } 184 } 185 186 // decorateAndWriteInfo decorates the info struct with information 187 // from the given cfg, and the writes that out to the filesystem. 188 func decorateAndWriteInfo(info configstore.EnvironInfo, cfg *config.Config) error { 189 190 // Sanity check our config. 191 var endpoint configstore.APIEndpoint 192 if cert, ok := cfg.CACert(); !ok { 193 return errors.Errorf("CACert is not set") 194 } else if uuid, ok := cfg.UUID(); !ok { 195 return errors.Errorf("UUID is not set") 196 } else if adminSecret := cfg.AdminSecret(); adminSecret == "" { 197 return errors.Errorf("admin-secret is not set") 198 } else { 199 endpoint = configstore.APIEndpoint{ 200 CACert: cert, 201 EnvironUUID: uuid, 202 } 203 } 204 205 creds := configstore.APICredentials{ 206 User: configstore.DefaultAdminUsername, 207 Password: cfg.AdminSecret(), 208 } 209 info.SetAPICredentials(creds) 210 info.SetAPIEndpoint(endpoint) 211 info.SetBootstrapConfig(cfg.AllAttrs()) 212 213 if err := info.Write(); err != nil { 214 return errors.Annotatef(err, "cannot create environment info %q", cfg.Name()) 215 } 216 217 return nil 218 } 219 220 func prepare(ctx BootstrapContext, cfg *config.Config, info configstore.EnvironInfo, p EnvironProvider) (Environ, error) { 221 cfg, err := ensureAdminSecret(cfg) 222 if err != nil { 223 return nil, errors.Annotate(err, "cannot generate admin-secret") 224 } 225 cfg, err = ensureCertificate(cfg) 226 if err != nil { 227 return nil, errors.Annotate(err, "cannot ensure CA certificate") 228 } 229 cfg, err = ensureUUID(cfg) 230 if err != nil { 231 return nil, errors.Annotate(err, "cannot ensure uuid") 232 } 233 234 return p.PrepareForBootstrap(ctx, cfg) 235 } 236 237 // ensureAdminSecret returns a config with a non-empty admin-secret. 238 func ensureAdminSecret(cfg *config.Config) (*config.Config, error) { 239 if cfg.AdminSecret() != "" { 240 return cfg, nil 241 } 242 return cfg.Apply(map[string]interface{}{ 243 "admin-secret": randomKey(), 244 }) 245 } 246 247 // ensureCertificate generates a new CA certificate and 248 // attaches it to the given environment configuration, 249 // unless the configuration already has one. 250 func ensureCertificate(cfg *config.Config) (*config.Config, error) { 251 _, hasCACert := cfg.CACert() 252 _, hasCAKey := cfg.CAPrivateKey() 253 if hasCACert && hasCAKey { 254 return cfg, nil 255 } 256 if hasCACert && !hasCAKey { 257 return nil, fmt.Errorf("environment configuration with a certificate but no CA private key") 258 } 259 260 caCert, caKey, err := cert.NewCA(cfg.Name(), time.Now().UTC().AddDate(10, 0, 0)) 261 if err != nil { 262 return nil, err 263 } 264 return cfg.Apply(map[string]interface{}{ 265 "ca-cert": string(caCert), 266 "ca-private-key": string(caKey), 267 }) 268 } 269 270 // ensureUUID generates a new uuid and attaches it to 271 // the given environment configuration, unless the 272 // configuration already has one. 273 func ensureUUID(cfg *config.Config) (*config.Config, error) { 274 _, hasUUID := cfg.UUID() 275 if hasUUID { 276 return cfg, nil 277 } 278 uuid, err := utils.NewUUID() 279 if err != nil { 280 return nil, errors.Trace(err) 281 } 282 return cfg.Apply(map[string]interface{}{ 283 "uuid": uuid.String(), 284 }) 285 } 286 287 // Destroy destroys the environment and, if successful, 288 // its associated configuration data from the given store. 289 func Destroy(env Environ, store configstore.Storage) error { 290 name := env.Config().Name() 291 if err := env.Destroy(); err != nil { 292 return err 293 } 294 return DestroyInfo(name, store) 295 } 296 297 // DestroyInfo destroys the configuration data for the named 298 // environment from the given store. 299 func DestroyInfo(envName string, store configstore.Storage) error { 300 info, err := store.ReadInfo(envName) 301 if err != nil { 302 if errors.IsNotFound(err) { 303 return nil 304 } 305 return err 306 } 307 if err := info.Destroy(); err != nil { 308 return errors.Annotate(err, "cannot destroy environment configuration information") 309 } 310 return nil 311 }