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