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 }