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  }