diff --git a/cliconfig.go b/cliconfig.go new file mode 100644 index 0000000000..76b00cb6cf --- /dev/null +++ b/cliconfig.go @@ -0,0 +1,9 @@ +package main + +// ConfigFile returns the default path to the configuration file. On +// Unix-like systems this is the ".terraformrc" file in the home directory. +// On Windows, this is the "terraform.rc" file in the application data +// directory. +func ConfigFile() (string, error) { + return configFile() +} diff --git a/cliconfig_unix.go b/cliconfig_unix.go new file mode 100644 index 0000000000..ade2c0fdba --- /dev/null +++ b/cliconfig_unix.go @@ -0,0 +1,45 @@ +// +build darwin freebsd linux netbsd openbsd + +package main + +import ( + "bytes" + "errors" + "log" + "os" + "os/exec" + "path/filepath" + "strings" +) + +func configFile() (string, error) { + dir, err := configDir() + if err != nil { + return "", err + } + + return filepath.Join(dir, ".terraformrc"), nil +} + +func configDir() (string, error) { + // First prefer the HOME environmental variable + if home := os.Getenv("HOME"); home != "" { + log.Printf("Detected home directory from env var: %s", home) + return home, nil + } + + // If that fails, try the shell + var stdout bytes.Buffer + cmd := exec.Command("sh", "-c", "eval echo ~$USER") + cmd.Stdout = &stdout + if err := cmd.Run(); err != nil { + return "", err + } + + result := strings.TrimSpace(stdout.String()) + if result == "" { + return "", errors.New("blank output") + } + + return result, nil +} diff --git a/cliconfig_windows.go b/cliconfig_windows.go new file mode 100644 index 0000000000..770c01978f --- /dev/null +++ b/cliconfig_windows.go @@ -0,0 +1,37 @@ +// +build windows + +package main + +import ( + "path/filepath" + "syscall" + "unsafe" +) + +var ( + shell = syscall.MustLoadDLL("Shell32.dll") + getFolderPath = shell.MustFindProc("SHGetFolderPathW") +) + +const CSIDL_APPDATA = 26 + +func configFile() (string, error) { + dir, err := configDir() + if err != nil { + return "", err + } + + return filepath.Join(dir, "terraform.rc"), nil +} + +func configDir() (string, error) { + b := make([]uint16, syscall.MAX_PATH) + + // See: http://msdn.microsoft.com/en-us/library/windows/desktop/bb762181(v=vs.85).aspx + r, _, err := getFolderPath.Call(0, CSIDL_APPDATA, 0, 0, uintptr(unsafe.Pointer(&b[0]))) + if uint32(r) != 0 { + return "", err + } + + return syscall.UTF16ToString(b), nil +} diff --git a/main.go b/main.go index 2fa5b1760d..3e13dc16a6 100644 --- a/main.go +++ b/main.go @@ -107,6 +107,24 @@ func wrappedMain() int { HelpWriter: os.Stdout, } + clicfgFile, err := cliConfigFile() + if err != nil { + fmt.Fprintf(os.Stderr, "Error loading user configuration: \n\n%s\n", err) + return 1 + } + + usrcfg, err := LoadConfig(clicfgFile) + if err != nil { + fmt.Fprintf(os.Stderr, "Error loading user configuration: \n\n%s\n", err) + return 1 + } + + config = *config.Merge(usrcfg) + + // Initialize the TFConfig settings for the commands... + ContextOpts.Providers = config.ProviderFactories() + ContextOpts.Provisioners = config.ProvisionerFactories() + exitCode, err := cli.Run() if err != nil { fmt.Fprintf(os.Stderr, "Error executing CLI: %s\n", err.Error()) @@ -116,6 +134,38 @@ func wrappedMain() int { return exitCode } +func cliConfigFile() (string, error) { + mustExist := true + configFilePath := os.Getenv("TERRAFORM_CONFIG") + if configFilePath == "" { + var err error + configFilePath, err = configFile() + mustExist = false + + if err != nil { + log.Printf("Error detecting default CLI config file path: %s", err) + } + } + + log.Printf("Attempting to open CLI config file: %s", configFilePath) + f, err := os.Open(configFilePath) + if err != nil { + if !os.IsNotExist(err) { + return "", err + } + + if mustExist { + return "", err + } + + log.Println("File doesn't exist, but doesn't need to. Ignoring.") + return "", nil + } + defer f.Close() + + return configFilePath, nil +} + // copyOutput uses output prefixes to determine whether data on stdout // should go to stdout or stderr. This is due to panicwrap using stderr // as the log and error channel.