main: load CLI config files from ~/.terraform.d/*.tfrc

Now that we are looking into the CLI config file for service host
credentials, it's important to support multiple separate files so that
users can keep credentials separate from other settings and credentials
for different hosts separate from one another.

There is no restriction on which settings can appear in which locations.
This is up to the user to decide, depending on their security needs and
e.g. on whether certain files are generated vs. manually-edited.
This commit is contained in:
Martin Atkins 2017-10-19 17:28:44 -07:00
parent 11ba1d2a4c
commit 1feb26f196
2 changed files with 43 additions and 0 deletions

View File

@ -1,6 +1,7 @@
package main package main
import ( import (
"log"
"os" "os"
"os/signal" "os/signal"
@ -361,9 +362,11 @@ func credentialsSource(config *Config) auth.CredentialsSource {
} }
for helperType, helperConfig := range config.CredentialsHelpers { for helperType, helperConfig := range config.CredentialsHelpers {
log.Printf("[DEBUG] Searching for credentials helper named %q", helperType)
available := pluginDiscovery.FindPlugins("credentials", globalPluginDirs()) available := pluginDiscovery.FindPlugins("credentials", globalPluginDirs())
available = available.WithName(helperType) available = available.WithName(helperType)
if available.Count() == 0 { if available.Count() == 0 {
log.Printf("[ERROR] Unable to find credentials helper %q; ignoring", helperType)
break break
} }

View File

@ -6,6 +6,7 @@ import (
"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
"path/filepath"
"github.com/hashicorp/hcl" "github.com/hashicorp/hcl"
@ -79,6 +80,14 @@ func LoadConfig() (*Config, tfdiags.Diagnostics) {
} }
} }
if configDir, err := ConfigDir(); err == nil {
if info, err := os.Stat(configDir); err == nil && info.IsDir() {
dirConfig, dirDiags := loadConfigDir(configDir)
diags = diags.Append(dirDiags)
config = config.Merge(dirConfig)
}
}
if envConfig := EnvConfig(); envConfig != nil { if envConfig := EnvConfig(); envConfig != nil {
// envConfig takes precedence // envConfig takes precedence
config = envConfig.Merge(config) config = envConfig.Merge(config)
@ -94,6 +103,8 @@ func loadConfigFile(path string) (*Config, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics var diags tfdiags.Diagnostics
result := &Config{} result := &Config{}
log.Printf("Loading CLI configuration from %s", path)
// Read the HCL file and prepare for parsing // Read the HCL file and prepare for parsing
d, err := ioutil.ReadFile(path) d, err := ioutil.ReadFile(path)
if err != nil { if err != nil {
@ -129,6 +140,35 @@ func loadConfigFile(path string) (*Config, tfdiags.Diagnostics) {
return result, diags return result, diags
} }
func loadConfigDir(path string) (*Config, tfdiags.Diagnostics) {
var diags tfdiags.Diagnostics
result := &Config{}
entries, err := ioutil.ReadDir(path)
if err != nil {
diags = diags.Append(fmt.Errorf("Error reading %s: %s", path, err))
return result, diags
}
for _, entry := range entries {
name := entry.Name()
// Ignoring errors here because it is used only to indicate pattern
// syntax errors, and our patterns are hard-coded here.
hclMatched, _ := filepath.Match("*.tfrc", name)
jsonMatched, _ := filepath.Match("*.tfrc.json", name)
if !(hclMatched || jsonMatched) {
continue
}
filePath := filepath.Join(path, name)
fileConfig, fileDiags := loadConfigFile(filePath)
diags = diags.Append(fileDiags)
result = result.Merge(fileConfig)
}
return result, diags
}
// EnvConfig returns a Config populated from environment variables. // EnvConfig returns a Config populated from environment variables.
// //
// Any values specified in this config should override those set in the // Any values specified in this config should override those set in the