github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/provider/manual/provider.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package manual
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/juju/errors"
    10  
    11  	"github.com/juju/juju/cloud"
    12  	"github.com/juju/juju/environs"
    13  	"github.com/juju/juju/environs/config"
    14  	"github.com/juju/juju/environs/manual"
    15  )
    16  
    17  type manualProvider struct {
    18  	environProviderCredentials
    19  }
    20  
    21  // Verify that we conform to the interface.
    22  var _ environs.EnvironProvider = (*manualProvider)(nil)
    23  
    24  var errNoBootstrapHost = errors.New("bootstrap-host must be specified")
    25  
    26  var initUbuntuUser = manual.InitUbuntuUser
    27  
    28  func ensureBootstrapUbuntuUser(ctx environs.BootstrapContext, cfg *environConfig) error {
    29  	err := initUbuntuUser(cfg.bootstrapHost(), cfg.bootstrapUser(), cfg.AuthorizedKeys(), ctx.GetStdin(), ctx.GetStdout())
    30  	if err != nil {
    31  		logger.Errorf("initializing ubuntu user: %v", err)
    32  		return err
    33  	}
    34  	logger.Infof("initialized ubuntu user")
    35  	return nil
    36  }
    37  
    38  // RestrictedConfigAttributes is specified in the EnvironProvider interface.
    39  func (p manualProvider) RestrictedConfigAttributes() []string {
    40  	return []string{"bootstrap-host", "bootstrap-user"}
    41  }
    42  
    43  // DetectRegions is specified in the environs.CloudRegionDetector interface.
    44  func (p manualProvider) DetectRegions() ([]cloud.Region, error) {
    45  	return nil, errors.NotFoundf("regions")
    46  }
    47  
    48  // PrepareForCreateEnvironment is specified in the EnvironProvider interface.
    49  func (p manualProvider) PrepareForCreateEnvironment(cfg *config.Config) (*config.Config, error) {
    50  	return cfg, nil
    51  }
    52  
    53  // BootstrapConfig is specified in the EnvironProvider interface.
    54  func (p manualProvider) BootstrapConfig(args environs.BootstrapConfigParams) (*config.Config, error) {
    55  	var bootstrapHost string
    56  	switch {
    57  	case args.CloudEndpoint != "":
    58  		// If an endpoint is specified, then we expect that the user
    59  		// has specified in their clouds.yaml a region with the
    60  		// bootstrap host as the endpoint.
    61  		bootstrapHost = args.CloudEndpoint
    62  	case args.CloudRegion != "":
    63  		// If only a region is specified, then we expect that the user
    64  		// has run "juju bootstrap manual/<host>", and treat the region
    65  		// name as the name of the bootstrap machine.
    66  		bootstrapHost = args.CloudRegion
    67  	default:
    68  		return nil, errors.Errorf(
    69  			"missing address of host to bootstrap: " +
    70  				`please specify "juju bootstrap manual/<host>"`,
    71  		)
    72  	}
    73  	cfg, err := args.Config.Apply(map[string]interface{}{
    74  		"bootstrap-host": bootstrapHost,
    75  	})
    76  	if err != nil {
    77  		return nil, errors.Trace(err)
    78  	}
    79  	envConfig, err := p.validate(cfg, nil)
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  	return cfg.Apply(envConfig.attrs)
    84  }
    85  
    86  // PrepareForBootstrap is specified in the EnvironProvider interface.
    87  func (p manualProvider) PrepareForBootstrap(ctx environs.BootstrapContext, cfg *config.Config) (environs.Environ, error) {
    88  	if _, err := p.validate(cfg, nil); err != nil {
    89  		return nil, err
    90  	}
    91  	envConfig := newModelConfig(cfg, cfg.UnknownAttrs())
    92  	if err := ensureBootstrapUbuntuUser(ctx, envConfig); err != nil {
    93  		return nil, err
    94  	}
    95  	return p.open(envConfig)
    96  }
    97  
    98  func (p manualProvider) Open(cfg *config.Config) (environs.Environ, error) {
    99  	_, err := p.validate(cfg, nil)
   100  	if err != nil {
   101  		return nil, err
   102  	}
   103  	// validate adds missing manual-specific config attributes
   104  	// with their defaults in the result; we don't wnat that in
   105  	// Open.
   106  	envConfig := newModelConfig(cfg, cfg.UnknownAttrs())
   107  	return p.open(envConfig)
   108  }
   109  
   110  func (p manualProvider) open(cfg *environConfig) (environs.Environ, error) {
   111  	env := &manualEnviron{cfg: cfg}
   112  	// Need to call SetConfig to initialise storage.
   113  	if err := env.SetConfig(cfg.Config); err != nil {
   114  		return nil, err
   115  	}
   116  	return env, nil
   117  }
   118  
   119  func checkImmutableString(cfg, old *environConfig, key string) error {
   120  	if old.attrs[key] != cfg.attrs[key] {
   121  		return fmt.Errorf("cannot change %s from %q to %q", key, old.attrs[key], cfg.attrs[key])
   122  	}
   123  	return nil
   124  }
   125  
   126  func (p manualProvider) validate(cfg, old *config.Config) (*environConfig, error) {
   127  	// Check for valid changes for the base config values.
   128  	if err := config.Validate(cfg, old); err != nil {
   129  		return nil, err
   130  	}
   131  	validated, err := cfg.ValidateUnknownAttrs(configFields, configDefaults)
   132  	if err != nil {
   133  		return nil, err
   134  	}
   135  	envConfig := newModelConfig(cfg, validated)
   136  	if envConfig.bootstrapHost() == "" {
   137  		return nil, errNoBootstrapHost
   138  	}
   139  	// Check various immutable attributes.
   140  	if old != nil {
   141  		oldEnvConfig, err := p.validate(old, nil)
   142  		if err != nil {
   143  			return nil, err
   144  		}
   145  		for _, key := range [...]string{
   146  			"bootstrap-user",
   147  			"bootstrap-host",
   148  		} {
   149  			if err = checkImmutableString(envConfig, oldEnvConfig, key); err != nil {
   150  				return nil, err
   151  			}
   152  		}
   153  	}
   154  
   155  	// If the user hasn't already specified a value, set it to the
   156  	// given value.
   157  	defineIfNot := func(keyName string, value interface{}) {
   158  		if _, defined := cfg.AllAttrs()[keyName]; !defined {
   159  			logger.Infof("%s was not defined. Defaulting to %v.", keyName, value)
   160  			envConfig.attrs[keyName] = value
   161  		}
   162  	}
   163  
   164  	// If the user hasn't specified a value, refresh the
   165  	// available updates, but don't upgrade.
   166  	defineIfNot("enable-os-refresh-update", true)
   167  	defineIfNot("enable-os-upgrade", false)
   168  
   169  	return envConfig, nil
   170  }
   171  
   172  func (p manualProvider) Validate(cfg, old *config.Config) (valid *config.Config, err error) {
   173  	envConfig, err := p.validate(cfg, old)
   174  	if err != nil {
   175  		return nil, err
   176  	}
   177  	return cfg.Apply(envConfig.attrs)
   178  }
   179  
   180  func (p manualProvider) SecretAttrs(cfg *config.Config) (map[string]string, error) {
   181  	attrs := make(map[string]string)
   182  	return attrs, nil
   183  }