github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/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  	"errors"
     8  	"fmt"
     9  
    10  	"launchpad.net/juju-core/environs"
    11  	"launchpad.net/juju-core/environs/config"
    12  	"launchpad.net/juju-core/environs/manual"
    13  	"launchpad.net/juju-core/utils"
    14  )
    15  
    16  type manualProvider struct{}
    17  
    18  func init() {
    19  	p := manualProvider{}
    20  	environs.RegisterProvider("manual", p, "null")
    21  }
    22  
    23  var errNoBootstrapHost = errors.New("bootstrap-host must be specified")
    24  
    25  var initUbuntuUser = manual.InitUbuntuUser
    26  
    27  func ensureBootstrapUbuntuUser(ctx environs.BootstrapContext, cfg *environConfig) error {
    28  	err := initUbuntuUser(cfg.bootstrapHost(), cfg.bootstrapUser(), cfg.AuthorizedKeys(), ctx.GetStdin(), ctx.GetStdout())
    29  	if err != nil {
    30  		logger.Errorf("initializing ubuntu user: %v", err)
    31  		return err
    32  	}
    33  	logger.Infof("initialized ubuntu user")
    34  	return nil
    35  }
    36  
    37  func (p manualProvider) Prepare(ctx environs.BootstrapContext, cfg *config.Config) (environs.Environ, error) {
    38  	if _, ok := cfg.UnknownAttrs()["storage-auth-key"]; !ok {
    39  		uuid, err := utils.NewUUID()
    40  		if err != nil {
    41  			return nil, err
    42  		}
    43  		cfg, err = cfg.Apply(map[string]interface{}{
    44  			"storage-auth-key": uuid.String(),
    45  		})
    46  		if err != nil {
    47  			return nil, err
    48  		}
    49  	}
    50  	if use, ok := cfg.UnknownAttrs()["use-sshstorage"].(bool); ok && !use {
    51  		return nil, fmt.Errorf("use-sshstorage must not be specified")
    52  	}
    53  	envConfig, err := p.validate(cfg, nil)
    54  	if err != nil {
    55  		return nil, err
    56  	}
    57  	if err := ensureBootstrapUbuntuUser(ctx, envConfig); err != nil {
    58  		return nil, err
    59  	}
    60  	return p.open(envConfig)
    61  }
    62  
    63  func (p manualProvider) Open(cfg *config.Config) (environs.Environ, error) {
    64  	envConfig, err := p.validate(cfg, nil)
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  	return p.open(envConfig)
    69  }
    70  
    71  func (p manualProvider) open(cfg *environConfig) (environs.Environ, error) {
    72  	env := &manualEnviron{cfg: cfg}
    73  	// Need to call SetConfig to initialise storage.
    74  	if err := env.SetConfig(cfg.Config); err != nil {
    75  		return nil, err
    76  	}
    77  	return env, nil
    78  }
    79  
    80  func checkImmutableString(cfg, old *environConfig, key string) error {
    81  	if old.attrs[key] != cfg.attrs[key] {
    82  		return fmt.Errorf("cannot change %s from %q to %q", key, old.attrs[key], cfg.attrs[key])
    83  	}
    84  	return nil
    85  }
    86  
    87  func (p manualProvider) validate(cfg, old *config.Config) (*environConfig, error) {
    88  	// Check for valid changes for the base config values.
    89  	if err := config.Validate(cfg, old); err != nil {
    90  		return nil, err
    91  	}
    92  	validated, err := cfg.ValidateUnknownAttrs(configFields, configDefaults)
    93  	if err != nil {
    94  		return nil, err
    95  	}
    96  	envConfig := newEnvironConfig(cfg, validated)
    97  	if envConfig.bootstrapHost() == "" {
    98  		return nil, errNoBootstrapHost
    99  	}
   100  	// Check various immutable attributes.
   101  	if old != nil {
   102  		oldEnvConfig, err := p.validate(old, nil)
   103  		if err != nil {
   104  			return nil, err
   105  		}
   106  		for _, key := range [...]string{
   107  			"bootstrap-user",
   108  			"bootstrap-host",
   109  			"storage-listen-ip",
   110  		} {
   111  			if err = checkImmutableString(envConfig, oldEnvConfig, key); err != nil {
   112  				return nil, err
   113  			}
   114  		}
   115  		oldPort, newPort := oldEnvConfig.storagePort(), envConfig.storagePort()
   116  		if oldPort != newPort {
   117  			return nil, fmt.Errorf("cannot change storage-port from %q to %q", oldPort, newPort)
   118  		}
   119  		oldUseSSHStorage, newUseSSHStorage := oldEnvConfig.useSSHStorage(), envConfig.useSSHStorage()
   120  		if oldUseSSHStorage != newUseSSHStorage && newUseSSHStorage == true {
   121  			return nil, fmt.Errorf("cannot change use-sshstorage from %v to %v", oldUseSSHStorage, newUseSSHStorage)
   122  		}
   123  	}
   124  	return envConfig, nil
   125  }
   126  
   127  func (p manualProvider) Validate(cfg, old *config.Config) (valid *config.Config, err error) {
   128  	envConfig, err := p.validate(cfg, old)
   129  	if err != nil {
   130  		return nil, err
   131  	}
   132  	return cfg.Apply(envConfig.attrs)
   133  }
   134  
   135  func (_ manualProvider) BoilerplateConfig() string {
   136  	return `
   137  manual:
   138      type: manual
   139      # bootstrap-host holds the host name of the machine where the
   140      # bootstrap machine agent will be started.
   141      bootstrap-host: somehost.example.com
   142      
   143      # bootstrap-user specifies the user to authenticate as when
   144      # connecting to the bootstrap machine. If defaults to
   145      # the current user.
   146      # bootstrap-user: joebloggs
   147      
   148      # storage-listen-ip specifies the IP address that the
   149      # bootstrap machine's Juju storage server will listen
   150      # on. By default, storage will be served on all
   151      # network interfaces.
   152      # storage-listen-ip:
   153      
   154      # storage-port specifes the TCP port that the
   155      # bootstrap machine's Juju storage server will listen
   156      # on. It defaults to ` + fmt.Sprint(defaultStoragePort) + `
   157      # storage-port: ` + fmt.Sprint(defaultStoragePort) + `
   158  
   159  
   160  `[1:]
   161  }
   162  
   163  func (p manualProvider) SecretAttrs(cfg *config.Config) (map[string]string, error) {
   164  	envConfig, err := p.validate(cfg, nil)
   165  	if err != nil {
   166  		return nil, err
   167  	}
   168  	attrs := make(map[string]string)
   169  	attrs["storage-auth-key"] = envConfig.storageAuthKey()
   170  	return attrs, nil
   171  }
   172  
   173  func (_ manualProvider) PublicAddress() (string, error) {
   174  	// TODO(axw) 2013-09-10 bug #1222643
   175  	//
   176  	// eth0 may not be the desired interface for traffic to route
   177  	// through. We should somehow make this configurable, and
   178  	// possibly also record the IP resolved during manual bootstrap.
   179  	return utils.GetAddressForInterface("eth0")
   180  }
   181  
   182  func (p manualProvider) PrivateAddress() (string, error) {
   183  	return p.PublicAddress()
   184  }