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