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