package command import ( "io/ioutil" "os" "path/filepath" "reflect" "testing" "github.com/hashicorp/terraform/terraform" "github.com/mitchellh/cli" ) func TestPlan(t *testing.T) { cwd, err := os.Getwd() if err != nil { t.Fatalf("err: %s", err) } if err := os.Chdir(testFixturePath("plan")); err != nil { t.Fatalf("err: %s", err) } defer os.Chdir(cwd) p := testProvider() ui := new(cli.MockUi) c := &PlanCommand{ Meta: Meta{ ContextOpts: testCtxConfig(p), Ui: ui, }, } args := []string{} if code := c.Run(args); code != 0 { t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) } } func TestPlan_destroy(t *testing.T) { originalState := &terraform.State{ Resources: map[string]*terraform.ResourceState{ "test_instance.foo": &terraform.ResourceState{ ID: "bar", Type: "test_instance", }, }, } outPath := testTempFile(t) statePath := testStateFile(t, originalState) p := testProvider() ui := new(cli.MockUi) c := &PlanCommand{ Meta: Meta{ ContextOpts: testCtxConfig(p), Ui: ui, }, } args := []string{ "-destroy", "-out", outPath, "-state", statePath, testFixturePath("plan"), } if code := c.Run(args); code != 0 { t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) } if !p.RefreshCalled { t.Fatal("refresh should be called") } plan := testReadPlan(t, outPath) for _, r := range plan.Diff.Resources { if !r.Destroy { t.Fatalf("bad: %#v", r) } } f, err := os.Open(statePath + DefaultBackupExtention) if err != nil { t.Fatalf("err: %s", err) } backupState, err := terraform.ReadState(f) f.Close() if err != nil { t.Fatalf("err: %s", err) } if !reflect.DeepEqual(backupState, originalState) { t.Fatalf("bad: %#v", backupState) } } func TestPlan_noState(t *testing.T) { p := testProvider() ui := new(cli.MockUi) c := &PlanCommand{ Meta: Meta{ ContextOpts: testCtxConfig(p), Ui: ui, }, } args := []string{ testFixturePath("plan"), } if code := c.Run(args); code != 0 { t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) } // Verify that refresh was called if p.RefreshCalled { t.Fatal("refresh should not be called") } // Verify that the provider was called with the existing state expectedState := &terraform.ResourceState{ Type: "test_instance", } if !reflect.DeepEqual(p.DiffState, expectedState) { t.Fatalf("bad: %#v", p.DiffState) } } func TestPlan_outPath(t *testing.T) { tf, err := ioutil.TempFile("", "tf") if err != nil { t.Fatalf("err: %s", err) } outPath := tf.Name() os.Remove(tf.Name()) p := testProvider() ui := new(cli.MockUi) c := &PlanCommand{ Meta: Meta{ ContextOpts: testCtxConfig(p), Ui: ui, }, } p.DiffReturn = &terraform.ResourceDiff{ Destroy: true, } args := []string{ "-out", outPath, testFixturePath("plan"), } if code := c.Run(args); code != 0 { t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) } f, err := os.Open(outPath) if err != nil { t.Fatalf("err: %s", err) } defer f.Close() if _, err := terraform.ReadPlan(f); err != nil { t.Fatalf("err: %s", err) } } func TestPlan_refresh(t *testing.T) { p := testProvider() ui := new(cli.MockUi) c := &PlanCommand{ Meta: Meta{ ContextOpts: testCtxConfig(p), Ui: ui, }, } args := []string{ "-refresh=false", testFixturePath("plan"), } if code := c.Run(args); code != 0 { t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) } if p.RefreshCalled { t.Fatal("refresh should not be called") } } func TestPlan_state(t *testing.T) { // Write out some prior state tf, err := ioutil.TempFile("", "tf") if err != nil { t.Fatalf("err: %s", err) } statePath := tf.Name() defer os.Remove(tf.Name()) originalState := &terraform.State{ Resources: map[string]*terraform.ResourceState{ "test_instance.foo": &terraform.ResourceState{ ID: "bar", Type: "test_instance", }, }, } err = terraform.WriteState(originalState, tf) tf.Close() if err != nil { t.Fatalf("err: %s", err) } p := testProvider() ui := new(cli.MockUi) c := &PlanCommand{ Meta: Meta{ ContextOpts: testCtxConfig(p), Ui: ui, }, } args := []string{ "-state", statePath, testFixturePath("plan"), } if code := c.Run(args); code != 0 { t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) } // Verify that the provider was called with the existing state expectedState := originalState.Resources["test_instance.foo"] if !reflect.DeepEqual(p.DiffState, expectedState) { t.Fatalf("bad: %#v", p.DiffState) } } func TestPlan_stateDefault(t *testing.T) { originalState := &terraform.State{ Resources: map[string]*terraform.ResourceState{ "test_instance.foo": &terraform.ResourceState{ ID: "bar", Type: "test_instance", }, }, } // Write the state file in a temporary directory with the // default filename. td, err := ioutil.TempDir("", "tf") if err != nil { t.Fatalf("err: %s", err) } statePath := filepath.Join(td, DefaultStateFilename) f, err := os.Create(statePath) if err != nil { t.Fatalf("err: %s", err) } err = terraform.WriteState(originalState, f) f.Close() if err != nil { t.Fatalf("err: %s", err) } // Change to that directory cwd, err := os.Getwd() if err != nil { t.Fatalf("err: %s", err) } if err := os.Chdir(filepath.Dir(statePath)); err != nil { t.Fatalf("err: %s", err) } defer os.Chdir(cwd) p := testProvider() ui := new(cli.MockUi) c := &PlanCommand{ Meta: Meta{ ContextOpts: testCtxConfig(p), Ui: ui, }, } args := []string{ testFixturePath("plan"), } if code := c.Run(args); code != 0 { t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) } // Verify that the provider was called with the existing state expectedState := originalState.Resources["test_instance.foo"] if !reflect.DeepEqual(p.DiffState, expectedState) { t.Fatalf("bad: %#v", p.DiffState) } } func TestPlan_vars(t *testing.T) { p := testProvider() ui := new(cli.MockUi) c := &PlanCommand{ Meta: Meta{ ContextOpts: testCtxConfig(p), Ui: ui, }, } actual := "" p.DiffFn = func( s *terraform.ResourceState, c *terraform.ResourceConfig) (*terraform.ResourceDiff, error) { if v, ok := c.Config["value"]; ok { actual = v.(string) } return nil, nil } args := []string{ "-var", "foo=bar", testFixturePath("plan-vars"), } if code := c.Run(args); code != 0 { t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) } if actual != "bar" { t.Fatal("didn't work") } } func TestPlan_varFile(t *testing.T) { varFilePath := testTempFile(t) if err := ioutil.WriteFile(varFilePath, []byte(planVarFile), 0644); err != nil { t.Fatalf("err: %s", err) } p := testProvider() ui := new(cli.MockUi) c := &PlanCommand{ Meta: Meta{ ContextOpts: testCtxConfig(p), Ui: ui, }, } actual := "" p.DiffFn = func( s *terraform.ResourceState, c *terraform.ResourceConfig) (*terraform.ResourceDiff, error) { if v, ok := c.Config["value"]; ok { actual = v.(string) } return nil, nil } args := []string{ "-var-file", varFilePath, testFixturePath("plan-vars"), } if code := c.Run(args); code != 0 { t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) } if actual != "bar" { t.Fatal("didn't work") } } func TestPlan_varFileDefault(t *testing.T) { varFileDir := testTempDir(t) varFilePath := filepath.Join(varFileDir, "terraform.tfvars") if err := ioutil.WriteFile(varFilePath, []byte(planVarFile), 0644); err != nil { t.Fatalf("err: %s", err) } cwd, err := os.Getwd() if err != nil { t.Fatalf("err: %s", err) } if err := os.Chdir(varFileDir); err != nil { t.Fatalf("err: %s", err) } defer os.Chdir(cwd) p := testProvider() ui := new(cli.MockUi) c := &PlanCommand{ Meta: Meta{ ContextOpts: testCtxConfig(p), Ui: ui, }, } actual := "" p.DiffFn = func( s *terraform.ResourceState, c *terraform.ResourceConfig) (*terraform.ResourceDiff, error) { if v, ok := c.Config["value"]; ok { actual = v.(string) } return nil, nil } args := []string{ testFixturePath("plan-vars"), } if code := c.Run(args); code != 0 { t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) } if actual != "bar" { t.Fatal("didn't work") } } func TestPlan_backup(t *testing.T) { // Write out some prior state tf, err := ioutil.TempFile("", "tf") if err != nil { t.Fatalf("err: %s", err) } statePath := tf.Name() defer os.Remove(tf.Name()) // Write out some prior state backupf, err := ioutil.TempFile("", "tf") if err != nil { t.Fatalf("err: %s", err) } backupPath := backupf.Name() backupf.Close() os.Remove(backupPath) defer os.Remove(backupPath) originalState := &terraform.State{ Resources: map[string]*terraform.ResourceState{ "test_instance.foo": &terraform.ResourceState{ ID: "bar", Type: "test_instance", }, }, } err = terraform.WriteState(originalState, tf) tf.Close() if err != nil { t.Fatalf("err: %s", err) } p := testProvider() ui := new(cli.MockUi) c := &PlanCommand{ Meta: Meta{ ContextOpts: testCtxConfig(p), Ui: ui, }, } args := []string{ "-state", statePath, "-backup", backupPath, testFixturePath("plan"), } if code := c.Run(args); code != 0 { t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) } // Verify that the provider was called with the existing state expectedState := originalState.Resources["test_instance.foo"] if !reflect.DeepEqual(p.DiffState, expectedState) { t.Fatalf("bad: %#v", p.DiffState) } // Verify the backup exist f, err := os.Open(backupPath) if err != nil { t.Fatalf("err: %s", err) } backupState, err := terraform.ReadState(f) f.Close() if err != nil { t.Fatalf("err: %s", err) } if !reflect.DeepEqual(backupState, originalState) { t.Fatalf("bad: %#v", backupState) } } func TestPlan_disableBackup(t *testing.T) { // Write out some prior state tf, err := ioutil.TempFile("", "tf") if err != nil { t.Fatalf("err: %s", err) } statePath := tf.Name() defer os.Remove(tf.Name()) originalState := &terraform.State{ Resources: map[string]*terraform.ResourceState{ "test_instance.foo": &terraform.ResourceState{ ID: "bar", Type: "test_instance", }, }, } err = terraform.WriteState(originalState, tf) tf.Close() if err != nil { t.Fatalf("err: %s", err) } p := testProvider() ui := new(cli.MockUi) c := &PlanCommand{ Meta: Meta{ ContextOpts: testCtxConfig(p), Ui: ui, }, } args := []string{ "-state", statePath, "-backup", "-", testFixturePath("plan"), } if code := c.Run(args); code != 0 { t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) } // Verify that the provider was called with the existing state expectedState := originalState.Resources["test_instance.foo"] if !reflect.DeepEqual(p.DiffState, expectedState) { t.Fatalf("bad: %#v", p.DiffState) } // Ensure there is no backup _, err = os.Stat(statePath + DefaultBackupExtention) if err == nil || !os.IsNotExist(err) { t.Fatalf("backup should not exist") } } const planVarFile = ` foo = "bar" `