github.com/rogpeppe/juju@v0.0.0-20140613142852-6337964b789e/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 50 const ( 51 SdcAccount = "SDC_ACCOUNT" 52 SdcKeyId = "SDC_KEY_ID" 53 SdcUrl = "SDC_URL" 54 MantaUser = "MANTA_USER" 55 MantaKeyId = "MANTA_KEY_ID" 56 MantaUrl = "MANTA_URL" 57 MantaPrivateKeyFile = "MANTA_PRIVATE_KEY_FILE" 58 DefaultPrivateKey = "~/.ssh/id_rsa" 59 ) 60 61 var environmentVariables = map[string]string{ 62 "sdc-user": SdcAccount, 63 "sdc-key-id": SdcKeyId, 64 "sdc-url": SdcUrl, 65 "manta-user": MantaUser, 66 "manta-key-id": MantaKeyId, 67 "manta-url": MantaUrl, 68 "private-key-path": MantaPrivateKeyFile, 69 } 70 71 var configFields = schema.Fields{ 72 "sdc-user": schema.String(), 73 "sdc-key-id": schema.String(), 74 "sdc-url": schema.String(), 75 "manta-user": schema.String(), 76 "manta-key-id": schema.String(), 77 "manta-url": schema.String(), 78 "private-key-path": schema.String(), 79 "algorithm": schema.String(), 80 "control-dir": schema.String(), 81 "private-key": schema.String(), 82 } 83 84 var configDefaults = schema.Defaults{ 85 "sdc-url": "https://us-west-1.api.joyentcloud.com", 86 "manta-url": "https://us-east.manta.joyent.com", 87 "algorithm": "rsa-sha256", 88 "private-key-path": schema.Omit, 89 "sdc-user": schema.Omit, 90 "sdc-key-id": schema.Omit, 91 "manta-user": schema.Omit, 92 "manta-key-id": schema.Omit, 93 "private-key": schema.Omit, 94 } 95 96 var configSecretFields = []string{ 97 "sdc-user", 98 "sdc-key-id", 99 "manta-user", 100 "manta-key-id", 101 "private-key", 102 } 103 104 var configImmutableFields = []string{ 105 "sdc-url", 106 "manta-url", 107 "private-key-path", 108 "private-key", 109 "algorithm", 110 } 111 112 func prepareConfig(cfg *config.Config) (*config.Config, error) { 113 // Turn an incomplete config into a valid one, if possible. 114 attrs := cfg.UnknownAttrs() 115 116 if _, ok := attrs["control-dir"]; !ok { 117 uuid, err := utils.NewUUID() 118 if err != nil { 119 return nil, err 120 } 121 attrs["control-dir"] = fmt.Sprintf("%x", uuid.Raw()) 122 } 123 return cfg.Apply(attrs) 124 } 125 126 func validateConfig(cfg, old *config.Config) (*environConfig, error) { 127 // Check for valid changes for the base config values. 128 if err := config.Validate(cfg, old); err != nil { 129 return nil, err 130 } 131 132 newAttrs, err := cfg.ValidateUnknownAttrs(configFields, configDefaults) 133 if err != nil { 134 return nil, err 135 } 136 envConfig := &environConfig{cfg, newAttrs} 137 // If an old config was supplied, check any immutable fields have not changed. 138 if old != nil { 139 oldEnvConfig, err := validateConfig(old, nil) 140 if err != nil { 141 return nil, err 142 } 143 for _, field := range configImmutableFields { 144 if oldEnvConfig.attrs[field] != envConfig.attrs[field] { 145 return nil, fmt.Errorf( 146 "%s: cannot change from %v to %v", 147 field, oldEnvConfig.attrs[field], envConfig.attrs[field], 148 ) 149 } 150 } 151 } 152 153 // Read env variables to fill in any missing fields. 154 for field, envVar := range environmentVariables { 155 // If field is not set, get it from env variables 156 if fieldValue, ok := envConfig.attrs[field]; !ok || fieldValue == "" { 157 localEnvVariable := os.Getenv(envVar) 158 if localEnvVariable != "" { 159 envConfig.attrs[field] = localEnvVariable 160 } else { 161 if field != "private-key-path" { 162 return nil, fmt.Errorf("cannot get %s value from environment variable %s", field, envVar) 163 } 164 } 165 } 166 } 167 168 // Ensure private-key-path is set - if it's not in config or an env var, use a default value. 169 if v, ok := envConfig.attrs["private-key-path"]; !ok || v == "" { 170 v = os.Getenv(environmentVariables["private-key-path"]) 171 if v == "" { 172 v = DefaultPrivateKey 173 } 174 envConfig.attrs["private-key-path"] = v 175 } 176 // Now that we've ensured private-key-path is properly set, we go back and set 177 // up the private key - this is used to sign requests. 178 if fieldValue, ok := envConfig.attrs["private-key"]; !ok || fieldValue == "" { 179 keyFile, err := utils.NormalizePath(envConfig.attrs["private-key-path"].(string)) 180 if err != nil { 181 return nil, err 182 } 183 privateKey, err := ioutil.ReadFile(keyFile) 184 if err != nil { 185 return nil, err 186 } 187 envConfig.attrs["private-key"] = string(privateKey) 188 } 189 190 // Check for missing fields. 191 for field := range configFields { 192 if envConfig.attrs[field] == "" { 193 return nil, fmt.Errorf("%s: must not be empty", field) 194 } 195 } 196 return envConfig, nil 197 } 198 199 type environConfig struct { 200 *config.Config 201 attrs map[string]interface{} 202 } 203 204 func (ecfg *environConfig) GetAttrs() map[string]interface{} { 205 return ecfg.attrs 206 } 207 208 func (ecfg *environConfig) sdcUrl() string { 209 return ecfg.attrs["sdc-url"].(string) 210 } 211 212 func (ecfg *environConfig) sdcUser() string { 213 return ecfg.attrs["sdc-user"].(string) 214 } 215 216 func (ecfg *environConfig) sdcKeyId() string { 217 return ecfg.attrs["sdc-key-id"].(string) 218 } 219 220 func (ecfg *environConfig) mantaUrl() string { 221 return ecfg.attrs["manta-url"].(string) 222 } 223 224 func (ecfg *environConfig) mantaUser() string { 225 return ecfg.attrs["manta-user"].(string) 226 } 227 228 func (ecfg *environConfig) mantaKeyId() string { 229 return ecfg.attrs["manta-key-id"].(string) 230 } 231 232 func (ecfg *environConfig) privateKey() string { 233 if v, ok := ecfg.attrs["private-key"]; ok { 234 return v.(string) 235 } 236 return "" 237 } 238 239 func (ecfg *environConfig) algorithm() string { 240 return ecfg.attrs["algorithm"].(string) 241 } 242 243 func (c *environConfig) controlDir() string { 244 return c.attrs["control-dir"].(string) 245 } 246 247 func (c *environConfig) ControlDir() string { 248 return c.controlDir() 249 } 250 251 func (ecfg *environConfig) SdcUrl() string { 252 return ecfg.sdcUrl() 253 } 254 255 func (ecfg *environConfig) Region() string { 256 sdcUrl := ecfg.sdcUrl() 257 // Check if running against local services 258 if isLocalhost(sdcUrl) { 259 return "some-region" 260 } 261 return sdcUrl[strings.LastIndex(sdcUrl, "/")+1 : strings.Index(sdcUrl, ".")] 262 } 263 264 func isLocalhost(u string) bool { 265 parsedUrl, err := url.Parse(u) 266 if err != nil { 267 return false 268 } 269 if strings.HasPrefix(parsedUrl.Host, "localhost") || strings.HasPrefix(parsedUrl.Host, "127.0.0.") { 270 return true 271 } 272 273 return false 274 }