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 }