2014-06-19 06:36:44 +02:00
|
|
|
package command
|
|
|
|
|
|
|
|
import (
|
2016-08-01 23:16:22 +02:00
|
|
|
"flag"
|
2016-11-30 20:36:54 +01:00
|
|
|
"io"
|
2014-06-27 23:43:23 +02:00
|
|
|
"io/ioutil"
|
2016-08-01 23:16:22 +02:00
|
|
|
"log"
|
2014-06-27 23:43:23 +02:00
|
|
|
"os"
|
2014-06-19 06:36:44 +02:00
|
|
|
"path/filepath"
|
2015-02-26 19:29:23 +01:00
|
|
|
"strings"
|
2014-06-27 23:43:23 +02:00
|
|
|
"testing"
|
2014-06-19 06:36:44 +02:00
|
|
|
|
2015-10-15 22:48:58 +02:00
|
|
|
"github.com/hashicorp/go-getter"
|
2014-09-25 00:48:46 +02:00
|
|
|
"github.com/hashicorp/terraform/config/module"
|
2016-08-01 23:16:22 +02:00
|
|
|
"github.com/hashicorp/terraform/helper/logging"
|
2014-06-19 06:36:44 +02:00
|
|
|
"github.com/hashicorp/terraform/terraform"
|
|
|
|
)
|
|
|
|
|
|
|
|
// This is the directory where our test fixtures are.
|
2014-07-12 06:03:56 +02:00
|
|
|
var fixtureDir = "./test-fixtures"
|
|
|
|
|
|
|
|
func init() {
|
2014-09-29 20:24:16 +02:00
|
|
|
test = true
|
|
|
|
|
2014-07-12 06:03:56 +02:00
|
|
|
// Expand the fixture dir on init because we change the working
|
|
|
|
// directory in some tests.
|
|
|
|
var err error
|
|
|
|
fixtureDir, err = filepath.Abs(fixtureDir)
|
|
|
|
if err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
}
|
2014-06-19 06:36:44 +02:00
|
|
|
|
2016-08-01 23:16:22 +02:00
|
|
|
func TestMain(m *testing.M) {
|
|
|
|
flag.Parse()
|
|
|
|
if testing.Verbose() {
|
|
|
|
// if we're verbose, use the logging requested by TF_LOG
|
|
|
|
logging.SetOutput()
|
|
|
|
} else {
|
|
|
|
// otherwise silence all logs
|
|
|
|
log.SetOutput(ioutil.Discard)
|
|
|
|
}
|
|
|
|
|
|
|
|
os.Exit(m.Run())
|
|
|
|
}
|
|
|
|
|
2014-09-25 00:48:46 +02:00
|
|
|
func tempDir(t *testing.T) string {
|
|
|
|
dir, err := ioutil.TempDir("", "tf")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
if err := os.RemoveAll(dir); err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return dir
|
|
|
|
}
|
|
|
|
|
2014-06-19 06:36:44 +02:00
|
|
|
func testFixturePath(name string) string {
|
2014-07-12 05:38:03 +02:00
|
|
|
return filepath.Join(fixtureDir, name)
|
2014-06-19 06:36:44 +02:00
|
|
|
}
|
|
|
|
|
2014-07-03 20:46:40 +02:00
|
|
|
func testCtxConfig(p terraform.ResourceProvider) *terraform.ContextOpts {
|
|
|
|
return &terraform.ContextOpts{
|
2014-06-19 06:36:44 +02:00
|
|
|
Providers: map[string]terraform.ResourceProviderFactory{
|
|
|
|
"test": func() (terraform.ResourceProvider, error) {
|
|
|
|
return p, nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-08 09:01:21 +02:00
|
|
|
func testCtxConfigWithShell(p terraform.ResourceProvider, pr terraform.ResourceProvisioner) *terraform.ContextOpts {
|
|
|
|
return &terraform.ContextOpts{
|
|
|
|
Providers: map[string]terraform.ResourceProviderFactory{
|
|
|
|
"test": func() (terraform.ResourceProvider, error) {
|
|
|
|
return p, nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Provisioners: map[string]terraform.ResourceProvisionerFactory{
|
|
|
|
"shell": func() (terraform.ResourceProvisioner, error) {
|
|
|
|
return pr, nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-25 00:48:46 +02:00
|
|
|
func testModule(t *testing.T, name string) *module.Tree {
|
|
|
|
mod, err := module.NewTreeModule("", filepath.Join(fixtureDir, name))
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
2015-10-15 22:48:58 +02:00
|
|
|
s := &getter.FolderStorage{StorageDir: tempDir(t)}
|
2014-09-25 00:48:46 +02:00
|
|
|
if err := mod.Load(s, module.GetModeGet); err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return mod
|
|
|
|
}
|
|
|
|
|
2016-10-29 02:51:05 +02:00
|
|
|
// testPlan returns a non-nil noop plan.
|
|
|
|
func testPlan(t *testing.T) *terraform.Plan {
|
|
|
|
state := terraform.NewState()
|
|
|
|
state.RootModule().Outputs["foo"] = &terraform.OutputState{
|
|
|
|
Type: "string",
|
|
|
|
Value: "foo",
|
|
|
|
}
|
|
|
|
|
|
|
|
return &terraform.Plan{
|
|
|
|
Module: testModule(t, "apply"),
|
|
|
|
State: state,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-27 23:43:23 +02:00
|
|
|
func testPlanFile(t *testing.T, plan *terraform.Plan) string {
|
|
|
|
path := testTempFile(t)
|
|
|
|
|
|
|
|
f, err := os.Create(path)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
if err := terraform.WritePlan(plan, f); err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return path
|
|
|
|
}
|
|
|
|
|
2014-07-01 18:12:05 +02:00
|
|
|
func testReadPlan(t *testing.T, path string) *terraform.Plan {
|
|
|
|
f, err := os.Open(path)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
p, err := terraform.ReadPlan(f)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return p
|
|
|
|
}
|
|
|
|
|
2014-09-17 20:15:07 +02:00
|
|
|
// testState returns a test State structure that we use for a lot of tests.
|
|
|
|
func testState() *terraform.State {
|
2016-08-10 21:47:25 +02:00
|
|
|
state := &terraform.State{
|
2016-05-12 02:05:02 +02:00
|
|
|
Version: 2,
|
2014-09-17 20:15:07 +02:00
|
|
|
Modules: []*terraform.ModuleState{
|
|
|
|
&terraform.ModuleState{
|
|
|
|
Path: []string{"root"},
|
|
|
|
Resources: map[string]*terraform.ResourceState{
|
|
|
|
"test_instance.foo": &terraform.ResourceState{
|
|
|
|
Type: "test_instance",
|
|
|
|
Primary: &terraform.InstanceState{
|
|
|
|
ID: "bar",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-06-22 16:06:41 +02:00
|
|
|
Outputs: map[string]*terraform.OutputState{},
|
2014-09-17 20:15:07 +02:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
2016-08-10 21:47:25 +02:00
|
|
|
state.Init()
|
|
|
|
return state
|
2014-09-17 20:15:07 +02:00
|
|
|
}
|
|
|
|
|
2014-06-27 23:43:23 +02:00
|
|
|
func testStateFile(t *testing.T, s *terraform.State) string {
|
|
|
|
path := testTempFile(t)
|
|
|
|
|
|
|
|
f, err := os.Create(path)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
if err := terraform.WriteState(s, f); err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return path
|
|
|
|
}
|
|
|
|
|
2015-02-26 19:29:23 +01:00
|
|
|
// testStateFileDefault writes the state out to the default statefile
|
|
|
|
// in the cwd. Use `testCwd` to change into a temp cwd.
|
|
|
|
func testStateFileDefault(t *testing.T, s *terraform.State) string {
|
|
|
|
f, err := os.Create(DefaultStateFilename)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
if err := terraform.WriteState(s, f); err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return DefaultStateFilename
|
|
|
|
}
|
|
|
|
|
2015-03-05 23:55:15 +01:00
|
|
|
// testStateFileRemote writes the state out to the remote statefile
|
|
|
|
// in the cwd. Use `testCwd` to change into a temp cwd.
|
|
|
|
func testStateFileRemote(t *testing.T, s *terraform.State) string {
|
|
|
|
path := filepath.Join(DefaultDataDir, DefaultStateFilename)
|
|
|
|
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
f, err := os.Create(path)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
if err := terraform.WriteState(s, f); err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return path
|
|
|
|
}
|
|
|
|
|
2015-02-26 19:29:23 +01:00
|
|
|
// testStateOutput tests that the state at the given path contains
|
|
|
|
// the expected state string.
|
|
|
|
func testStateOutput(t *testing.T, path string, expected string) {
|
|
|
|
f, err := os.Open(path)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
newState, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
actual := strings.TrimSpace(newState.String())
|
|
|
|
expected = strings.TrimSpace(expected)
|
|
|
|
if actual != expected {
|
2016-03-08 21:37:34 +01:00
|
|
|
t.Fatalf("expected:\n%s\nactual:\n%s", expected, actual)
|
2015-02-26 19:29:23 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-19 06:36:44 +02:00
|
|
|
func testProvider() *terraform.MockResourceProvider {
|
|
|
|
p := new(terraform.MockResourceProvider)
|
2014-09-18 01:33:24 +02:00
|
|
|
p.DiffReturn = &terraform.InstanceDiff{}
|
2014-06-20 21:51:42 +02:00
|
|
|
p.RefreshFn = func(
|
2014-09-17 20:15:07 +02:00
|
|
|
info *terraform.InstanceInfo,
|
|
|
|
s *terraform.InstanceState) (*terraform.InstanceState, error) {
|
2014-06-20 21:51:42 +02:00
|
|
|
return s, nil
|
|
|
|
}
|
2014-06-19 06:36:44 +02:00
|
|
|
p.ResourcesReturn = []terraform.ResourceType{
|
|
|
|
terraform.ResourceType{
|
|
|
|
Name: "test_instance",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
return p
|
|
|
|
}
|
2014-06-27 23:43:23 +02:00
|
|
|
|
|
|
|
func testTempFile(t *testing.T) string {
|
2016-05-11 02:03:58 +02:00
|
|
|
return filepath.Join(testTempDir(t), "state.tfstate")
|
2014-06-27 23:43:23 +02:00
|
|
|
}
|
2014-08-05 18:32:01 +02:00
|
|
|
|
|
|
|
func testTempDir(t *testing.T) string {
|
|
|
|
d, err := ioutil.TempDir("", "tf")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return d
|
|
|
|
}
|
2014-10-08 21:08:35 +02:00
|
|
|
|
2016-08-24 07:11:21 +02:00
|
|
|
// testRename renames the path to new and returns a function to defer to
|
|
|
|
// revert the rename.
|
|
|
|
func testRename(t *testing.T, base, path, new string) func() {
|
|
|
|
if base != "" {
|
|
|
|
path = filepath.Join(base, path)
|
|
|
|
new = filepath.Join(base, new)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := os.Rename(path, new); err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return func() {
|
|
|
|
// Just re-rename and ignore the return value
|
|
|
|
testRename(t, "", new, path)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// testChdir changes the directory and returns a function to defer to
|
|
|
|
// revert the old cwd.
|
|
|
|
func testChdir(t *testing.T, new string) func() {
|
|
|
|
old, err := os.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := os.Chdir(new); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return func() {
|
|
|
|
// Re-run the function ignoring the defer result
|
|
|
|
testChdir(t, old)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-26 19:29:23 +01:00
|
|
|
// testCwd is used to change the current working directory
|
2014-10-08 21:08:35 +02:00
|
|
|
// into a test directory that should be remoted after
|
|
|
|
func testCwd(t *testing.T) (string, string) {
|
2015-02-22 19:49:31 +01:00
|
|
|
tmp, err := ioutil.TempDir("", "tf")
|
2014-10-08 21:08:35 +02:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2015-02-22 19:49:31 +01:00
|
|
|
|
2014-10-08 21:08:35 +02:00
|
|
|
cwd, err := os.Getwd()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2015-02-22 19:49:31 +01:00
|
|
|
|
2014-10-09 23:45:08 +02:00
|
|
|
if err := os.Chdir(tmp); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2015-02-22 19:49:31 +01:00
|
|
|
|
2014-10-08 21:08:35 +02:00
|
|
|
return tmp, cwd
|
|
|
|
}
|
|
|
|
|
2014-10-09 23:45:08 +02:00
|
|
|
// testFixCwd is used to as a defer to testDir
|
|
|
|
func testFixCwd(t *testing.T, tmp, cwd string) {
|
|
|
|
if err := os.Chdir(cwd); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2015-02-22 19:49:31 +01:00
|
|
|
|
2014-10-09 23:45:08 +02:00
|
|
|
if err := os.RemoveAll(tmp); err != nil {
|
|
|
|
t.Fatalf("err: %v", err)
|
|
|
|
}
|
2014-10-08 21:08:35 +02:00
|
|
|
}
|
2016-11-30 20:36:54 +01:00
|
|
|
|
|
|
|
// testStdinPipe changes os.Stdin to be a pipe that sends the data from
|
|
|
|
// the reader before closing the pipe.
|
|
|
|
//
|
|
|
|
// The returned function should be deferred to properly clean up and restore
|
|
|
|
// the original stdin.
|
|
|
|
func testStdinPipe(t *testing.T, src io.Reader) func() {
|
|
|
|
r, w, err := os.Pipe()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Modify stdin to point to our new pipe
|
|
|
|
old := os.Stdin
|
|
|
|
os.Stdin = r
|
|
|
|
|
|
|
|
// Copy the data from the reader to the pipe
|
|
|
|
go func() {
|
|
|
|
defer w.Close()
|
|
|
|
io.Copy(w, src)
|
|
|
|
}()
|
|
|
|
|
|
|
|
return func() {
|
|
|
|
// Close our read end
|
|
|
|
r.Close()
|
|
|
|
|
|
|
|
// Reset stdin
|
|
|
|
os.Stdin = old
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Modify os.Stdout to write to the given buffer. Note that this is generally
|
|
|
|
// not useful since the commands are configured to write to a cli.Ui, not
|
|
|
|
// Stdout directly. Commands like `console` though use the raw stdout.
|
|
|
|
func testStdoutCapture(t *testing.T, dst io.Writer) func() {
|
|
|
|
r, w, err := os.Pipe()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Modify stdout
|
|
|
|
old := os.Stdout
|
|
|
|
os.Stdout = w
|
|
|
|
|
|
|
|
// Copy
|
|
|
|
doneCh := make(chan struct{})
|
|
|
|
go func() {
|
|
|
|
defer close(doneCh)
|
|
|
|
defer r.Close()
|
|
|
|
io.Copy(dst, r)
|
|
|
|
}()
|
|
|
|
|
|
|
|
return func() {
|
|
|
|
// Close the writer end of the pipe
|
|
|
|
w.Sync()
|
|
|
|
w.Close()
|
|
|
|
|
|
|
|
// Reset stdout
|
|
|
|
os.Stdout = old
|
|
|
|
|
|
|
|
// Wait for the data copy to complete to avoid a race reading data
|
|
|
|
<-doneCh
|
|
|
|
}
|
|
|
|
}
|