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 }