mirror of https://github.com/hashicorp/terraform
We've been moving away from config fields expecting file paths that Terraform will load, instead prefering fields that expect file contents, leaning on `file()` to do loading from a path. This helps with consistency and also flexibility - since this makes it easier to shift sensitive files into environment variables. Here we add a little helper package to manage the transitional period for these fields where we support both behaviors. Also included is the first of several fields being shifted over - SSH private keys in provisioner connection config. We're moving to new field names so the behavior is more intuitive, so instead of `key_file` it's `private_key` now. Additional field shifts will be included in follow up PRs so they can be reviewed and discussed individually.pull/3846/head
parent
4252a3e557
commit
7ffa66d1a5
@ -0,0 +1,40 @@
|
||||
// Helpers for dealing with file paths and their contents
|
||||
package pathorcontents
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/mitchellh/go-homedir"
|
||||
)
|
||||
|
||||
// If the argument is a path, Read loads it and returns the contents,
|
||||
// otherwise the argument is assumed to be the desired contents and is simply
|
||||
// returned.
|
||||
//
|
||||
// The boolean second return value can be called `wasPath` - it indicates if a
|
||||
// path was detected and a file loaded.
|
||||
func Read(poc string) (string, bool, error) {
|
||||
if len(poc) == 0 {
|
||||
return poc, false, nil
|
||||
}
|
||||
|
||||
path := poc
|
||||
if path[0] == '~' {
|
||||
var err error
|
||||
path, err = homedir.Expand(path)
|
||||
if err != nil {
|
||||
return path, true, err
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := os.Stat(path); err == nil {
|
||||
contents, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return string(contents), true, err
|
||||
}
|
||||
return string(contents), true, nil
|
||||
}
|
||||
|
||||
return poc, false, nil
|
||||
}
|
||||
@ -0,0 +1,140 @@
|
||||
package pathorcontents
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/mitchellh/go-homedir"
|
||||
)
|
||||
|
||||
func TestRead_Path(t *testing.T) {
|
||||
isPath := true
|
||||
f, cleanup := testTempFile(t)
|
||||
defer cleanup()
|
||||
|
||||
if _, err := io.WriteString(f, "foobar"); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
f.Close()
|
||||
|
||||
contents, wasPath, err := Read(f.Name())
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if wasPath != isPath {
|
||||
t.Fatalf("expected wasPath: %t, got %t", isPath, wasPath)
|
||||
}
|
||||
if contents != "foobar" {
|
||||
t.Fatalf("expected contents %s, got %s", "foobar", contents)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRead_TildePath(t *testing.T) {
|
||||
isPath := true
|
||||
home, err := homedir.Dir()
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
f, cleanup := testTempFile(t, home)
|
||||
defer cleanup()
|
||||
|
||||
if _, err := io.WriteString(f, "foobar"); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
f.Close()
|
||||
|
||||
r := strings.NewReplacer(home, "~")
|
||||
homePath := r.Replace(f.Name())
|
||||
contents, wasPath, err := Read(homePath)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if wasPath != isPath {
|
||||
t.Fatalf("expected wasPath: %t, got %t", isPath, wasPath)
|
||||
}
|
||||
if contents != "foobar" {
|
||||
t.Fatalf("expected contents %s, got %s", "foobar", contents)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRead_PathNoPermission(t *testing.T) {
|
||||
isPath := true
|
||||
f, cleanup := testTempFile(t)
|
||||
defer cleanup()
|
||||
|
||||
if _, err := io.WriteString(f, "foobar"); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
f.Close()
|
||||
|
||||
if err := os.Chmod(f.Name(), 0); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
contents, wasPath, err := Read(f.Name())
|
||||
|
||||
if err == nil {
|
||||
t.Fatal("Expected error, got none!")
|
||||
}
|
||||
if wasPath != isPath {
|
||||
t.Fatalf("expected wasPath: %t, got %t", isPath, wasPath)
|
||||
}
|
||||
if contents != "" {
|
||||
t.Fatalf("expected contents %s, got %s", "", contents)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRead_Contents(t *testing.T) {
|
||||
isPath := false
|
||||
input := "hello"
|
||||
|
||||
contents, wasPath, err := Read(input)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if wasPath != isPath {
|
||||
t.Fatalf("expected wasPath: %t, got %t", isPath, wasPath)
|
||||
}
|
||||
if contents != input {
|
||||
t.Fatalf("expected contents %s, got %s", input, contents)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRead_TildeContents(t *testing.T) {
|
||||
isPath := false
|
||||
input := "~/hello/notafile"
|
||||
|
||||
contents, wasPath, err := Read(input)
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
if wasPath != isPath {
|
||||
t.Fatalf("expected wasPath: %t, got %t", isPath, wasPath)
|
||||
}
|
||||
if contents != input {
|
||||
t.Fatalf("expected contents %s, got %s", input, contents)
|
||||
}
|
||||
}
|
||||
|
||||
// Returns an open tempfile based at baseDir and a function to clean it up.
|
||||
func testTempFile(t *testing.T, baseDir ...string) (*os.File, func()) {
|
||||
base := ""
|
||||
if len(baseDir) == 1 {
|
||||
base = baseDir[0]
|
||||
}
|
||||
f, err := ioutil.TempFile(base, "tf")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
|
||||
return f, func() {
|
||||
os.Remove(f.Name())
|
||||
}
|
||||
}
|
||||
Loading…
Reference in new issue