github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/provider/joyent/config.go (about) 1 // Copyright 2013 Joyent Inc. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package joyent 5 6 import ( 7 "fmt" 8 "io/ioutil" 9 "net/url" 10 "os" 11 "strings" 12 13 "github.com/juju/schema" 14 "github.com/juju/utils" 15 16 "github.com/juju/juju/environs/config" 17 ) 18 19 // boilerplateConfig will be shown in help output, so please keep it up to 20 // date when you change environment configuration below. 21 const boilerplateConfig = `joyent: 22 type: joyent 23 24 # SDC config 25 # Can be set via env variables, or specified here 26 # sdc-user: <secret> 27 # Can be set via env variables, or specified here 28 # sdc-key-id: <secret> 29 # url defaults to us-west-1 DC, override if required 30 # sdc-url: https://us-west-1.api.joyentcloud.com 31 32 # Manta config 33 # Can be set via env variables, or specified here 34 # manta-user: <secret> 35 # Can be set via env variables, or specified here 36 # manta-key-id: <secret> 37 # url defaults to us-east DC, override if required 38 # manta-url: https://us-east.manta.joyent.com 39 40 # Auth config 41 # private-key-path is the private key used to sign Joyent requests. 42 # Defaults to ~/.ssh/id_rsa, override if a different ssh key is used. 43 # Alternatively, you can supply "private-key" with the content of the private 44 # key instead supplying the path to a file. 45 # private-key-path: ~/.ssh/id_rsa 46 # algorithm defaults to rsa-sha256, override if required 47 # algorithm: rsa-sha256 48 49 # Whether or not to refresh the list of available updates for an 50 # OS. The default option of true is recommended for use in 51 # production systems, but disabling this can speed up local 52 # deployments for development or testing. 53 # 54 # enable-os-refresh-update: true 55 56 # Whether or not to perform OS upgrades when machines are 57 # provisioned. The default option of true is recommended for use 58 # in production systems, but disabling this can speed up local 59 # deployments for development or testing. 60 # 61 # enable-os-upgrade: true 62 63 ` 64 65 const ( 66 SdcAccount = "SDC_ACCOUNT" 67 SdcKeyId = "SDC_KEY_ID" 68 SdcUrl = "SDC_URL" 69 MantaUser = "MANTA_USER" 70 MantaKeyId = "MANTA_KEY_ID" 71 MantaUrl = "MANTA_URL" 72 MantaPrivateKeyFile = "MANTA_PRIVATE_KEY_FILE" 73 DefaultPrivateKey = "~/.ssh/id_rsa" 74 ) 75 76 var environmentVariables = map[string]string{ 77 "sdc-user": SdcAccount, 78 "sdc-key-id": SdcKeyId, 79 "sdc-url": SdcUrl, 80 "manta-user": MantaUser, 81 "manta-key-id": MantaKeyId, 82 "manta-url": MantaUrl, 83 "private-key-path": MantaPrivateKeyFile, 84 } 85 86 var configFields = schema.Fields{ 87 "sdc-user": schema.String(), 88 "sdc-key-id": schema.String(), 89 "sdc-url": schema.String(), 90 "manta-user": schema.String(), 91 "manta-key-id": schema.String(), 92 "manta-url": schema.String(), 93 "private-key-path": schema.String(), 94 "algorithm": schema.String(), 95 "control-dir": schema.String(), 96 "private-key": schema.String(), 97 } 98 99 var configDefaults = schema.Defaults{ 100 "sdc-url": "https://us-west-1.api.joyentcloud.com", 101 "manta-url": "https://us-east.manta.joyent.com", 102 "algorithm": "rsa-sha256", 103 "private-key-path": schema.Omit, 104 "sdc-user": schema.Omit, 105 "sdc-key-id": schema.Omit, 106 "manta-user": schema.Omit, 107 "manta-key-id": schema.Omit, 108 "private-key": schema.Omit, 109 } 110 111 var configSecretFields = []string{ 112 "sdc-user", 113 "sdc-key-id", 114 "manta-user", 115 "manta-key-id", 116 "private-key", 117 } 118 119 var configImmutableFields = []string{ 120 "sdc-url", 121 "manta-url", 122 "private-key-path", 123 "private-key", 124 "algorithm", 125 } 126 127 func prepareConfig(cfg *config.Config) (*config.Config, error) { 128 // Turn an incomplete config into a valid one, if possible. 129 attrs := cfg.UnknownAttrs() 130 131 if _, ok := attrs["control-dir"]; !ok { 132 uuid, err := utils.NewUUID() 133 if err != nil { 134 return nil, err 135 } 136 attrs["control-dir"] = fmt.Sprintf("%x", uuid.Raw()) 137 } 138 return cfg.Apply(attrs) 139 } 140 141 func validateConfig(cfg, old *config.Config) (*environConfig, error) { 142 // Check for valid changes for the base config values. 143 if err := config.Validate(cfg, old); err != nil { 144 return nil, err 145 } 146 147 newAttrs, err := cfg.ValidateUnknownAttrs(configFields, configDefaults) 148 if err != nil { 149 return nil, err 150 } 151 envConfig := &environConfig{cfg, newAttrs} 152 // If an old config was supplied, check any immutable fields have not changed. 153 if old != nil { 154 oldEnvConfig, err := validateConfig(old, nil) 155 if err != nil { 156 return nil, err 157 } 158 for _, field := range configImmutableFields { 159 if oldEnvConfig.attrs[field] != envConfig.attrs[field] { 160 return nil, fmt.Errorf( 161 "%s: cannot change from %v to %v", 162 field, oldEnvConfig.attrs[field], envConfig.attrs[field], 163 ) 164 } 165 } 166 } 167 168 // Read env variables to fill in any missing fields. 169 for field, envVar := range environmentVariables { 170 // If field is not set, get it from env variables 171 if fieldValue, ok := envConfig.attrs[field]; !ok || fieldValue == "" { 172 localEnvVariable := os.Getenv(envVar) 173 if localEnvVariable != "" { 174 envConfig.attrs[field] = localEnvVariable 175 } else { 176 if field != "private-key-path" { 177 return nil, fmt.Errorf("cannot get %s value from environment variable %s", field, envVar) 178 } 179 } 180 } 181 } 182 183 // Ensure private-key-path is set - if it's not in config or an env var, use a default value. 184 if v, ok := envConfig.attrs["private-key-path"]; !ok || v == "" { 185 v = os.Getenv(environmentVariables["private-key-path"]) 186 if v == "" { 187 v = DefaultPrivateKey 188 } 189 envConfig.attrs["private-key-path"] = v 190 } 191 // Now that we've ensured private-key-path is properly set, we go back and set 192 // up the private key - this is used to sign requests. 193 if fieldValue, ok := envConfig.attrs["private-key"]; !ok || fieldValue == "" { 194 keyFile, err := utils.NormalizePath(envConfig.attrs["private-key-path"].(string)) 195 if err != nil { 196 return nil, err 197 } 198 privateKey, err := ioutil.ReadFile(keyFile) 199 if err != nil { 200 return nil, err 201 } 202 envConfig.attrs["private-key"] = string(privateKey) 203 } 204 205 // Check for missing fields. 206 for field := range configFields { 207 if envConfig.attrs[field] == "" { 208 return nil, fmt.Errorf("%s: must not be empty", field) 209 } 210 } 211 return envConfig, nil 212 } 213 214 type environConfig struct { 215 *config.Config 216 attrs map[string]interface{} 217 } 218 219 func (ecfg *environConfig) GetAttrs() map[string]interface{} { 220 return ecfg.attrs 221 } 222 223 func (ecfg *environConfig) sdcUrl() string { 224 return ecfg.attrs["sdc-url"].(string) 225 } 226 227 func (ecfg *environConfig) sdcUser() string { 228 return ecfg.attrs["sdc-user"].(string) 229 } 230 231 func (ecfg *environConfig) sdcKeyId() string { 232 return ecfg.attrs["sdc-key-id"].(string) 233 } 234 235 func (ecfg *environConfig) mantaUrl() string { 236 return ecfg.attrs["manta-url"].(string) 237 } 238 239 func (ecfg *environConfig) mantaUser() string { 240 return ecfg.attrs["manta-user"].(string) 241 } 242 243 func (ecfg *environConfig) mantaKeyId() string { 244 return ecfg.attrs["manta-key-id"].(string) 245 } 246 247 func (ecfg *environConfig) privateKey() string { 248 if v, ok := ecfg.attrs["private-key"]; ok { 249 return v.(string) 250 } 251 return "" 252 } 253 254 func (ecfg *environConfig) algorithm() string { 255 return ecfg.attrs["algorithm"].(string) 256 } 257 258 func (c *environConfig) controlDir() string { 259 return c.attrs["control-dir"].(string) 260 } 261 262 func (c *environConfig) ControlDir() string { 263 return c.controlDir() 264 } 265 266 func (ecfg *environConfig) SdcUrl() string { 267 return ecfg.sdcUrl() 268 } 269 270 func (ecfg *environConfig) Region() string { 271 sdcUrl := ecfg.sdcUrl() 272 // Check if running against local services 273 if isLocalhost(sdcUrl) { 274 return "some-region" 275 } 276 return sdcUrl[strings.LastIndex(sdcUrl, "/")+1 : strings.Index(sdcUrl, ".")] 277 } 278 279 func isLocalhost(u string) bool { 280 parsedUrl, err := url.Parse(u) 281 if err != nil { 282 return false 283 } 284 if strings.HasPrefix(parsedUrl.Host, "localhost") || strings.HasPrefix(parsedUrl.Host, "127.0.0.") { 285 return true 286 } 287 288 return false 289 }