github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/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  	"github.com/juju/utils"
    11  
    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  
    19  // Verify that we conform to the interface.
    20  var _ environs.EnvironProvider = (*manualProvider)(nil)
    21  
    22  var errNoBootstrapHost = errors.New("bootstrap-host must be specified")
    23  
    24  var initUbuntuUser = manual.InitUbuntuUser
    25  
    26  func ensureBootstrapUbuntuUser(ctx environs.BootstrapContext, cfg *environConfig) error {
    27  	err := initUbuntuUser(cfg.bootstrapHost(), cfg.bootstrapUser(), cfg.AuthorizedKeys(), ctx.GetStdin(), ctx.GetStdout())
    28  	if err != nil {
    29  		logger.Errorf("initializing ubuntu user: %v", err)
    30  		return err
    31  	}
    32  	logger.Infof("initialized ubuntu user")
    33  	return nil
    34  }
    35  
    36  // RestrictedConfigAttributes is specified in the EnvironProvider interface.
    37  func (p manualProvider) RestrictedConfigAttributes() []string {
    38  	return []string{"bootstrap-host", "bootstrap-user"}
    39  }
    40  
    41  // PrepareForCreateEnvironment is specified in the EnvironProvider interface.
    42  func (p manualProvider) PrepareForCreateEnvironment(cfg *config.Config) (*config.Config, error) {
    43  	// Not even sure if this will ever make sense.
    44  	return nil, errors.NotImplementedf("PrepareForCreateEnvironment")
    45  }
    46  
    47  func (p manualProvider) PrepareForBootstrap(ctx environs.BootstrapContext, cfg *config.Config) (environs.Environ, error) {
    48  	if _, ok := cfg.UnknownAttrs()["storage-auth-key"]; !ok {
    49  		uuid, err := utils.NewUUID()
    50  		if err != nil {
    51  			return nil, err
    52  		}
    53  		cfg, err = cfg.Apply(map[string]interface{}{
    54  			"storage-auth-key": uuid.String(),
    55  		})
    56  		if err != nil {
    57  			return nil, err
    58  		}
    59  	}
    60  	if use, ok := cfg.UnknownAttrs()["use-sshstorage"].(bool); ok && !use {
    61  		return nil, fmt.Errorf("use-sshstorage must not be specified")
    62  	}
    63  	envConfig, err := p.validate(cfg, nil)
    64  	if err != nil {
    65  		return nil, err
    66  	}
    67  	cfg, err = cfg.Apply(envConfig.attrs)
    68  	if err != nil {
    69  		return nil, err
    70  	}
    71  	envConfig = newEnvironConfig(cfg, envConfig.attrs)
    72  	if err := ensureBootstrapUbuntuUser(ctx, envConfig); err != nil {
    73  		return nil, err
    74  	}
    75  	return p.open(envConfig)
    76  }
    77  
    78  func (p manualProvider) Open(cfg *config.Config) (environs.Environ, error) {
    79  	_, err := p.validate(cfg, nil)
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  	// validate adds missing manual-specific config attributes
    84  	// with their defaults in the result; we don't wnat that in
    85  	// Open.
    86  	envConfig := newEnvironConfig(cfg, cfg.UnknownAttrs())
    87  	return p.open(envConfig)
    88  }
    89  
    90  func (p manualProvider) open(cfg *environConfig) (environs.Environ, error) {
    91  	env := &manualEnviron{cfg: cfg}
    92  	// Need to call SetConfig to initialise storage.
    93  	if err := env.SetConfig(cfg.Config); err != nil {
    94  		return nil, err
    95  	}
    96  	return env, nil
    97  }
    98  
    99  func checkImmutableString(cfg, old *environConfig, key string) error {
   100  	if old.attrs[key] != cfg.attrs[key] {
   101  		return fmt.Errorf("cannot change %s from %q to %q", key, old.attrs[key], cfg.attrs[key])
   102  	}
   103  	return nil
   104  }
   105  
   106  func (p manualProvider) validate(cfg, old *config.Config) (*environConfig, error) {
   107  	// Check for valid changes for the base config values.
   108  	if err := config.Validate(cfg, old); err != nil {
   109  		return nil, err
   110  	}
   111  	validated, err := cfg.ValidateUnknownAttrs(configFields, configDefaults)
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  	envConfig := newEnvironConfig(cfg, validated)
   116  	if envConfig.bootstrapHost() == "" {
   117  		return nil, errNoBootstrapHost
   118  	}
   119  	// Check various immutable attributes.
   120  	if old != nil {
   121  		oldEnvConfig, err := p.validate(old, nil)
   122  		if err != nil {
   123  			return nil, err
   124  		}
   125  		for _, key := range [...]string{
   126  			"bootstrap-user",
   127  			"bootstrap-host",
   128  			"storage-listen-ip",
   129  		} {
   130  			if err = checkImmutableString(envConfig, oldEnvConfig, key); err != nil {
   131  				return nil, err
   132  			}
   133  		}
   134  		oldPort, newPort := oldEnvConfig.storagePort(), envConfig.storagePort()
   135  		if oldPort != newPort {
   136  			return nil, fmt.Errorf("cannot change storage-port from %q to %q", oldPort, newPort)
   137  		}
   138  		oldUseSSHStorage, newUseSSHStorage := oldEnvConfig.useSSHStorage(), envConfig.useSSHStorage()
   139  		if oldUseSSHStorage != newUseSSHStorage && newUseSSHStorage == true {
   140  			return nil, fmt.Errorf("cannot change use-sshstorage from %v to %v", oldUseSSHStorage, newUseSSHStorage)
   141  		}
   142  	}
   143  
   144  	// If the user hasn't already specified a value, set it to the
   145  	// given value.
   146  	defineIfNot := func(keyName string, value interface{}) {
   147  		if _, defined := cfg.AllAttrs()[keyName]; !defined {
   148  			logger.Infof("%s was not defined. Defaulting to %v.", keyName, value)
   149  			envConfig.attrs[keyName] = value
   150  		}
   151  	}
   152  
   153  	// If the user hasn't specified a value, refresh the
   154  	// available updates, but don't upgrade.
   155  	defineIfNot("enable-os-refresh-update", true)
   156  	defineIfNot("enable-os-upgrade", false)
   157  
   158  	return envConfig, nil
   159  }
   160  
   161  func (p manualProvider) Validate(cfg, old *config.Config) (valid *config.Config, err error) {
   162  	envConfig, err := p.validate(cfg, old)
   163  	if err != nil {
   164  		return nil, err
   165  	}
   166  	return cfg.Apply(envConfig.attrs)
   167  }
   168  
   169  func (_ manualProvider) BoilerplateConfig() string {
   170  	return `
   171  manual:
   172      type: manual
   173      # bootstrap-host holds the host name of the machine where the
   174      # bootstrap machine agent will be started.
   175      bootstrap-host: somehost.example.com
   176  
   177      # bootstrap-user specifies the user to authenticate as when
   178      # connecting to the bootstrap machine. It defaults to
   179      # the current user.
   180      # bootstrap-user: joebloggs
   181  
   182      # storage-listen-ip specifies the IP address that the
   183      # bootstrap machine's Juju storage server will listen
   184      # on. By default, storage will be served on all
   185      # network interfaces.
   186      # storage-listen-ip:
   187  
   188      # storage-port specifes the TCP port that the
   189      # bootstrap machine's Juju storage server will listen
   190      # on. It defaults to ` + fmt.Sprint(defaultStoragePort) + `
   191      # storage-port: ` + fmt.Sprint(defaultStoragePort) + `
   192  
   193      # Whether or not to refresh the list of available updates for an
   194      # OS. The default option of true is recommended for use in
   195      # production systems.
   196      #
   197      # enable-os-refresh-update: true
   198  
   199      # Whether or not to perform OS upgrades when machines are
   200      # provisioned. The default option of false is set so that Juju
   201      # does not subsume any other way the system might be
   202      # maintained.
   203      #
   204      # enable-os-upgrade: false
   205  
   206  `[1:]
   207  }
   208  
   209  func (p manualProvider) SecretAttrs(cfg *config.Config) (map[string]string, error) {
   210  	envConfig, err := p.validate(cfg, nil)
   211  	if err != nil {
   212  		return nil, err
   213  	}
   214  	attrs := make(map[string]string)
   215  	attrs["storage-auth-key"] = envConfig.storageAuthKey()
   216  	return attrs, nil
   217  }