github.com/meteor/terraform@v0.6.15-0.20210412225145-79ec4bc057c6/command/plan_test.go (about)

     1  package command
     2  
     3  import (
     4  	"bytes"
     5  	"io/ioutil"
     6  	"os"
     7  	"path/filepath"
     8  	"reflect"
     9  	"strings"
    10  	"testing"
    11  
    12  	"github.com/hashicorp/terraform/helper/copy"
    13  	"github.com/hashicorp/terraform/terraform"
    14  	"github.com/mitchellh/cli"
    15  )
    16  
    17  func TestPlan(t *testing.T) {
    18  	cwd, err := os.Getwd()
    19  	if err != nil {
    20  		t.Fatalf("err: %s", err)
    21  	}
    22  	if err := os.Chdir(testFixturePath("plan")); err != nil {
    23  		t.Fatalf("err: %s", err)
    24  	}
    25  	defer os.Chdir(cwd)
    26  
    27  	p := testProvider()
    28  	ui := new(cli.MockUi)
    29  	c := &PlanCommand{
    30  		Meta: Meta{
    31  			ContextOpts: testCtxConfig(p),
    32  			Ui:          ui,
    33  		},
    34  	}
    35  
    36  	args := []string{}
    37  	if code := c.Run(args); code != 0 {
    38  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
    39  	}
    40  }
    41  
    42  func TestPlan_lockedState(t *testing.T) {
    43  	cwd, err := os.Getwd()
    44  	if err != nil {
    45  		t.Fatalf("err: %s", err)
    46  	}
    47  
    48  	testPath := testFixturePath("plan")
    49  	unlock, err := testLockState("./testdata", filepath.Join(testPath, DefaultStateFilename))
    50  	if err != nil {
    51  		t.Fatal(err)
    52  	}
    53  	defer unlock()
    54  
    55  	if err := os.Chdir(testPath); err != nil {
    56  		t.Fatalf("err: %s", err)
    57  	}
    58  	defer os.Chdir(cwd)
    59  
    60  	p := testProvider()
    61  	ui := new(cli.MockUi)
    62  	c := &PlanCommand{
    63  		Meta: Meta{
    64  			ContextOpts: testCtxConfig(p),
    65  			Ui:          ui,
    66  		},
    67  	}
    68  
    69  	args := []string{}
    70  	if code := c.Run(args); code == 0 {
    71  		t.Fatal("expected error")
    72  	}
    73  
    74  	output := ui.ErrorWriter.String()
    75  	if !strings.Contains(output, "lock") {
    76  		t.Fatal("command output does not look like a lock error:", output)
    77  	}
    78  }
    79  
    80  func TestPlan_plan(t *testing.T) {
    81  	tmp, cwd := testCwd(t)
    82  	defer testFixCwd(t, tmp, cwd)
    83  
    84  	planPath := testPlanFile(t, &terraform.Plan{
    85  		Module: testModule(t, "apply"),
    86  	})
    87  
    88  	p := testProvider()
    89  	ui := new(cli.MockUi)
    90  	c := &PlanCommand{
    91  		Meta: Meta{
    92  			ContextOpts: testCtxConfig(p),
    93  			Ui:          ui,
    94  		},
    95  	}
    96  
    97  	args := []string{planPath}
    98  	if code := c.Run(args); code != 0 {
    99  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   100  	}
   101  
   102  	if p.RefreshCalled {
   103  		t.Fatal("refresh should not be called")
   104  	}
   105  }
   106  
   107  func TestPlan_destroy(t *testing.T) {
   108  	originalState := &terraform.State{
   109  		Modules: []*terraform.ModuleState{
   110  			&terraform.ModuleState{
   111  				Path: []string{"root"},
   112  				Resources: map[string]*terraform.ResourceState{
   113  					"test_instance.foo": &terraform.ResourceState{
   114  						Type: "test_instance",
   115  						Primary: &terraform.InstanceState{
   116  							ID: "bar",
   117  						},
   118  					},
   119  				},
   120  			},
   121  		},
   122  	}
   123  
   124  	outPath := testTempFile(t)
   125  	statePath := testStateFile(t, originalState)
   126  
   127  	p := testProvider()
   128  	ui := new(cli.MockUi)
   129  	c := &PlanCommand{
   130  		Meta: Meta{
   131  			ContextOpts: testCtxConfig(p),
   132  			Ui:          ui,
   133  		},
   134  	}
   135  
   136  	args := []string{
   137  		"-destroy",
   138  		"-out", outPath,
   139  		"-state", statePath,
   140  		testFixturePath("plan"),
   141  	}
   142  	if code := c.Run(args); code != 0 {
   143  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   144  	}
   145  
   146  	if !p.RefreshCalled {
   147  		t.Fatal("refresh should be called")
   148  	}
   149  
   150  	plan := testReadPlan(t, outPath)
   151  	for _, m := range plan.Diff.Modules {
   152  		for _, r := range m.Resources {
   153  			if !r.Destroy {
   154  				t.Fatalf("bad: %#v", r)
   155  			}
   156  		}
   157  	}
   158  }
   159  
   160  func TestPlan_noState(t *testing.T) {
   161  	tmp, cwd := testCwd(t)
   162  	defer testFixCwd(t, tmp, cwd)
   163  
   164  	p := testProvider()
   165  	ui := new(cli.MockUi)
   166  	c := &PlanCommand{
   167  		Meta: Meta{
   168  			ContextOpts: testCtxConfig(p),
   169  			Ui:          ui,
   170  		},
   171  	}
   172  
   173  	args := []string{
   174  		testFixturePath("plan"),
   175  	}
   176  	if code := c.Run(args); code != 0 {
   177  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   178  	}
   179  
   180  	// Verify that refresh was called
   181  	if p.RefreshCalled {
   182  		t.Fatal("refresh should not be called")
   183  	}
   184  
   185  	// Verify that the provider was called with the existing state
   186  	actual := strings.TrimSpace(p.DiffState.String())
   187  	expected := strings.TrimSpace(testPlanNoStateStr)
   188  	if actual != expected {
   189  		t.Fatalf("bad:\n\n%s", actual)
   190  	}
   191  }
   192  
   193  func TestPlan_outPath(t *testing.T) {
   194  	tmp, cwd := testCwd(t)
   195  	defer testFixCwd(t, tmp, cwd)
   196  
   197  	tf, err := ioutil.TempFile("", "tf")
   198  	if err != nil {
   199  		t.Fatalf("err: %s", err)
   200  	}
   201  	outPath := tf.Name()
   202  	os.Remove(tf.Name())
   203  
   204  	p := testProvider()
   205  	ui := new(cli.MockUi)
   206  	c := &PlanCommand{
   207  		Meta: Meta{
   208  			ContextOpts: testCtxConfig(p),
   209  			Ui:          ui,
   210  		},
   211  	}
   212  
   213  	p.DiffReturn = &terraform.InstanceDiff{
   214  		Destroy: true,
   215  	}
   216  
   217  	args := []string{
   218  		"-out", outPath,
   219  		testFixturePath("plan"),
   220  	}
   221  	if code := c.Run(args); code != 0 {
   222  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   223  	}
   224  
   225  	f, err := os.Open(outPath)
   226  	if err != nil {
   227  		t.Fatalf("err: %s", err)
   228  	}
   229  	defer f.Close()
   230  
   231  	if _, err := terraform.ReadPlan(f); err != nil {
   232  		t.Fatalf("err: %s", err)
   233  	}
   234  }
   235  
   236  func TestPlan_outPathNoChange(t *testing.T) {
   237  	originalState := &terraform.State{
   238  		Modules: []*terraform.ModuleState{
   239  			&terraform.ModuleState{
   240  				Path: []string{"root"},
   241  				Resources: map[string]*terraform.ResourceState{
   242  					"test_instance.foo": &terraform.ResourceState{
   243  						Type: "test_instance",
   244  						Primary: &terraform.InstanceState{
   245  							ID: "bar",
   246  						},
   247  					},
   248  				},
   249  			},
   250  		},
   251  	}
   252  	statePath := testStateFile(t, originalState)
   253  
   254  	tf, err := ioutil.TempFile("", "tf")
   255  	if err != nil {
   256  		t.Fatalf("err: %s", err)
   257  	}
   258  	outPath := tf.Name()
   259  	os.Remove(tf.Name())
   260  
   261  	p := testProvider()
   262  	ui := new(cli.MockUi)
   263  	c := &PlanCommand{
   264  		Meta: Meta{
   265  			ContextOpts: testCtxConfig(p),
   266  			Ui:          ui,
   267  		},
   268  	}
   269  
   270  	args := []string{
   271  		"-out", outPath,
   272  		"-state", statePath,
   273  		testFixturePath("plan"),
   274  	}
   275  	if code := c.Run(args); code != 0 {
   276  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   277  	}
   278  
   279  	plan := testReadPlan(t, outPath)
   280  	if !plan.Diff.Empty() {
   281  		t.Fatalf("Expected empty plan to be written to plan file, got: %s", plan)
   282  	}
   283  }
   284  
   285  // When using "-out" with a backend, the plan should encode the backend config
   286  func TestPlan_outBackend(t *testing.T) {
   287  	// Create a temporary working directory that is empty
   288  	td := tempDir(t)
   289  	copy.CopyDir(testFixturePath("plan-out-backend"), td)
   290  	defer os.RemoveAll(td)
   291  	defer testChdir(t, td)()
   292  
   293  	// Our state
   294  	originalState := &terraform.State{
   295  		Modules: []*terraform.ModuleState{
   296  			&terraform.ModuleState{
   297  				Path: []string{"root"},
   298  				Resources: map[string]*terraform.ResourceState{
   299  					"test_instance.foo": &terraform.ResourceState{
   300  						Type: "test_instance",
   301  						Primary: &terraform.InstanceState{
   302  							ID: "bar",
   303  						},
   304  					},
   305  				},
   306  			},
   307  		},
   308  	}
   309  	originalState.Init()
   310  
   311  	// Setup our backend state
   312  	dataState, srv := testBackendState(t, originalState, 200)
   313  	defer srv.Close()
   314  	testStateFileRemote(t, dataState)
   315  
   316  	outPath := "foo"
   317  	p := testProvider()
   318  	ui := new(cli.MockUi)
   319  	c := &PlanCommand{
   320  		Meta: Meta{
   321  			ContextOpts: testCtxConfig(p),
   322  			Ui:          ui,
   323  		},
   324  	}
   325  
   326  	args := []string{
   327  		"-out", outPath,
   328  	}
   329  	if code := c.Run(args); code != 0 {
   330  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   331  	}
   332  
   333  	plan := testReadPlan(t, outPath)
   334  	if !plan.Diff.Empty() {
   335  		t.Fatalf("Expected empty plan to be written to plan file, got: %s", plan)
   336  	}
   337  
   338  	if plan.Backend.Empty() {
   339  		t.Fatal("should have backend info")
   340  	}
   341  	if !reflect.DeepEqual(plan.Backend, dataState.Backend) {
   342  		t.Fatalf("bad: %#v", plan.Backend)
   343  	}
   344  }
   345  
   346  // When using "-out" with a legacy remote state, the plan should encode
   347  // the backend config
   348  func TestPlan_outBackendLegacy(t *testing.T) {
   349  	// Create a temporary working directory that is empty
   350  	td := tempDir(t)
   351  	copy.CopyDir(testFixturePath("plan-out-backend-legacy"), td)
   352  	defer os.RemoveAll(td)
   353  	defer testChdir(t, td)()
   354  
   355  	// Our state
   356  	originalState := &terraform.State{
   357  		Modules: []*terraform.ModuleState{
   358  			&terraform.ModuleState{
   359  				Path: []string{"root"},
   360  				Resources: map[string]*terraform.ResourceState{
   361  					"test_instance.foo": &terraform.ResourceState{
   362  						Type: "test_instance",
   363  						Primary: &terraform.InstanceState{
   364  							ID: "bar",
   365  						},
   366  					},
   367  				},
   368  			},
   369  		},
   370  	}
   371  	originalState.Init()
   372  
   373  	// Setup our legacy state
   374  	remoteState, srv := testRemoteState(t, originalState, 200)
   375  	defer srv.Close()
   376  	dataState := terraform.NewState()
   377  	dataState.Remote = remoteState
   378  	testStateFileRemote(t, dataState)
   379  
   380  	outPath := "foo"
   381  	p := testProvider()
   382  	ui := new(cli.MockUi)
   383  	c := &PlanCommand{
   384  		Meta: Meta{
   385  			ContextOpts: testCtxConfig(p),
   386  			Ui:          ui,
   387  		},
   388  	}
   389  
   390  	args := []string{
   391  		"-out", outPath,
   392  	}
   393  	if code := c.Run(args); code != 0 {
   394  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   395  	}
   396  
   397  	plan := testReadPlan(t, outPath)
   398  	if !plan.Diff.Empty() {
   399  		t.Fatalf("Expected empty plan to be written to plan file, got: %s", plan)
   400  	}
   401  
   402  	if plan.State.Remote.Empty() {
   403  		t.Fatal("should have remote info")
   404  	}
   405  }
   406  
   407  func TestPlan_refresh(t *testing.T) {
   408  	tmp, cwd := testCwd(t)
   409  	defer testFixCwd(t, tmp, cwd)
   410  
   411  	p := testProvider()
   412  	ui := new(cli.MockUi)
   413  	c := &PlanCommand{
   414  		Meta: Meta{
   415  			ContextOpts: testCtxConfig(p),
   416  			Ui:          ui,
   417  		},
   418  	}
   419  
   420  	args := []string{
   421  		"-refresh=false",
   422  		testFixturePath("plan"),
   423  	}
   424  	if code := c.Run(args); code != 0 {
   425  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   426  	}
   427  
   428  	if p.RefreshCalled {
   429  		t.Fatal("refresh should not be called")
   430  	}
   431  }
   432  
   433  func TestPlan_state(t *testing.T) {
   434  	// Write out some prior state
   435  	tf, err := ioutil.TempFile("", "tf")
   436  	if err != nil {
   437  		t.Fatalf("err: %s", err)
   438  	}
   439  	statePath := tf.Name()
   440  	defer os.Remove(tf.Name())
   441  
   442  	originalState := testState()
   443  	err = terraform.WriteState(originalState, tf)
   444  	tf.Close()
   445  	if err != nil {
   446  		t.Fatalf("err: %s", err)
   447  	}
   448  
   449  	p := testProvider()
   450  	ui := new(cli.MockUi)
   451  	c := &PlanCommand{
   452  		Meta: Meta{
   453  			ContextOpts: testCtxConfig(p),
   454  			Ui:          ui,
   455  		},
   456  	}
   457  
   458  	args := []string{
   459  		"-state", statePath,
   460  		testFixturePath("plan"),
   461  	}
   462  	if code := c.Run(args); code != 0 {
   463  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   464  	}
   465  
   466  	// Verify that the provider was called with the existing state
   467  	actual := strings.TrimSpace(p.DiffState.String())
   468  	expected := strings.TrimSpace(testPlanStateStr)
   469  	if actual != expected {
   470  		t.Fatalf("bad:\n\n%s", actual)
   471  	}
   472  }
   473  
   474  func TestPlan_stateDefault(t *testing.T) {
   475  	originalState := testState()
   476  
   477  	// Write the state file in a temporary directory with the
   478  	// default filename.
   479  	td, err := ioutil.TempDir("", "tf")
   480  	if err != nil {
   481  		t.Fatalf("err: %s", err)
   482  	}
   483  	statePath := filepath.Join(td, DefaultStateFilename)
   484  
   485  	f, err := os.Create(statePath)
   486  	if err != nil {
   487  		t.Fatalf("err: %s", err)
   488  	}
   489  	err = terraform.WriteState(originalState, f)
   490  	f.Close()
   491  	if err != nil {
   492  		t.Fatalf("err: %s", err)
   493  	}
   494  
   495  	// Change to that directory
   496  	cwd, err := os.Getwd()
   497  	if err != nil {
   498  		t.Fatalf("err: %s", err)
   499  	}
   500  	if err := os.Chdir(filepath.Dir(statePath)); err != nil {
   501  		t.Fatalf("err: %s", err)
   502  	}
   503  	defer os.Chdir(cwd)
   504  
   505  	p := testProvider()
   506  	ui := new(cli.MockUi)
   507  	c := &PlanCommand{
   508  		Meta: Meta{
   509  			ContextOpts: testCtxConfig(p),
   510  			Ui:          ui,
   511  		},
   512  	}
   513  
   514  	args := []string{
   515  		testFixturePath("plan"),
   516  	}
   517  	if code := c.Run(args); code != 0 {
   518  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   519  	}
   520  
   521  	// Verify that the provider was called with the existing state
   522  	actual := strings.TrimSpace(p.DiffState.String())
   523  	expected := strings.TrimSpace(testPlanStateDefaultStr)
   524  	if actual != expected {
   525  		t.Fatalf("bad:\n\n%s", actual)
   526  	}
   527  }
   528  
   529  func TestPlan_stateFuture(t *testing.T) {
   530  	originalState := testState()
   531  	originalState.TFVersion = "99.99.99"
   532  	statePath := testStateFile(t, originalState)
   533  
   534  	p := testProvider()
   535  	ui := new(cli.MockUi)
   536  	c := &PlanCommand{
   537  		Meta: Meta{
   538  			ContextOpts: testCtxConfig(p),
   539  			Ui:          ui,
   540  		},
   541  	}
   542  
   543  	args := []string{
   544  		"-state", statePath,
   545  		testFixturePath("plan"),
   546  	}
   547  	if code := c.Run(args); code == 0 {
   548  		t.Fatal("should fail")
   549  	}
   550  
   551  	f, err := os.Open(statePath)
   552  	if err != nil {
   553  		t.Fatalf("err: %s", err)
   554  	}
   555  
   556  	newState, err := terraform.ReadState(f)
   557  	f.Close()
   558  	if err != nil {
   559  		t.Fatalf("err: %s", err)
   560  	}
   561  
   562  	if !newState.Equal(originalState) {
   563  		t.Fatalf("bad: %#v", newState)
   564  	}
   565  	if newState.TFVersion != originalState.TFVersion {
   566  		t.Fatalf("bad: %#v", newState)
   567  	}
   568  }
   569  
   570  func TestPlan_statePast(t *testing.T) {
   571  	originalState := testState()
   572  	originalState.TFVersion = "0.1.0"
   573  	statePath := testStateFile(t, originalState)
   574  
   575  	p := testProvider()
   576  	ui := new(cli.MockUi)
   577  	c := &PlanCommand{
   578  		Meta: Meta{
   579  			ContextOpts: testCtxConfig(p),
   580  			Ui:          ui,
   581  		},
   582  	}
   583  
   584  	args := []string{
   585  		"-state", statePath,
   586  		testFixturePath("plan"),
   587  	}
   588  	if code := c.Run(args); code != 0 {
   589  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   590  	}
   591  }
   592  
   593  func TestPlan_validate(t *testing.T) {
   594  	// This is triggered by not asking for input so we have to set this to false
   595  	test = false
   596  	defer func() { test = true }()
   597  
   598  	cwd, err := os.Getwd()
   599  	if err != nil {
   600  		t.Fatalf("err: %s", err)
   601  	}
   602  	if err := os.Chdir(testFixturePath("plan-invalid")); err != nil {
   603  		t.Fatalf("err: %s", err)
   604  	}
   605  	defer os.Chdir(cwd)
   606  
   607  	p := testProvider()
   608  	ui := new(cli.MockUi)
   609  	c := &PlanCommand{
   610  		Meta: Meta{
   611  			ContextOpts: testCtxConfig(p),
   612  			Ui:          ui,
   613  		},
   614  	}
   615  
   616  	args := []string{}
   617  	if code := c.Run(args); code != 1 {
   618  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   619  	}
   620  
   621  	actual := ui.ErrorWriter.String()
   622  	if !strings.Contains(actual, "cannot be computed") {
   623  		t.Fatalf("bad: %s", actual)
   624  	}
   625  }
   626  
   627  func TestPlan_vars(t *testing.T) {
   628  	tmp, cwd := testCwd(t)
   629  	defer testFixCwd(t, tmp, cwd)
   630  
   631  	p := testProvider()
   632  	ui := new(cli.MockUi)
   633  	c := &PlanCommand{
   634  		Meta: Meta{
   635  			ContextOpts: testCtxConfig(p),
   636  			Ui:          ui,
   637  		},
   638  	}
   639  
   640  	actual := ""
   641  	p.DiffFn = func(
   642  		info *terraform.InstanceInfo,
   643  		s *terraform.InstanceState,
   644  		c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
   645  		if v, ok := c.Config["value"]; ok {
   646  			actual = v.(string)
   647  		}
   648  
   649  		return nil, nil
   650  	}
   651  
   652  	args := []string{
   653  		"-var", "foo=bar",
   654  		testFixturePath("plan-vars"),
   655  	}
   656  	if code := c.Run(args); code != 0 {
   657  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   658  	}
   659  
   660  	if actual != "bar" {
   661  		t.Fatal("didn't work")
   662  	}
   663  }
   664  
   665  func TestPlan_varsUnset(t *testing.T) {
   666  	tmp, cwd := testCwd(t)
   667  	defer testFixCwd(t, tmp, cwd)
   668  
   669  	// Disable test mode so input would be asked
   670  	test = false
   671  	defer func() { test = true }()
   672  
   673  	defaultInputReader = bytes.NewBufferString("bar\n")
   674  
   675  	p := testProvider()
   676  	ui := new(cli.MockUi)
   677  	c := &PlanCommand{
   678  		Meta: Meta{
   679  			ContextOpts: testCtxConfig(p),
   680  			Ui:          ui,
   681  		},
   682  	}
   683  
   684  	args := []string{
   685  		testFixturePath("plan-vars"),
   686  	}
   687  	if code := c.Run(args); code != 0 {
   688  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   689  	}
   690  }
   691  
   692  func TestPlan_varFile(t *testing.T) {
   693  	tmp, cwd := testCwd(t)
   694  	defer testFixCwd(t, tmp, cwd)
   695  
   696  	varFilePath := testTempFile(t)
   697  	if err := ioutil.WriteFile(varFilePath, []byte(planVarFile), 0644); err != nil {
   698  		t.Fatalf("err: %s", err)
   699  	}
   700  
   701  	p := testProvider()
   702  	ui := new(cli.MockUi)
   703  	c := &PlanCommand{
   704  		Meta: Meta{
   705  			ContextOpts: testCtxConfig(p),
   706  			Ui:          ui,
   707  		},
   708  	}
   709  
   710  	actual := ""
   711  	p.DiffFn = func(
   712  		info *terraform.InstanceInfo,
   713  		s *terraform.InstanceState,
   714  		c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
   715  		if v, ok := c.Config["value"]; ok {
   716  			actual = v.(string)
   717  		}
   718  
   719  		return nil, nil
   720  	}
   721  
   722  	args := []string{
   723  		"-var-file", varFilePath,
   724  		testFixturePath("plan-vars"),
   725  	}
   726  	if code := c.Run(args); code != 0 {
   727  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   728  	}
   729  
   730  	if actual != "bar" {
   731  		t.Fatal("didn't work")
   732  	}
   733  }
   734  
   735  func TestPlan_varFileDefault(t *testing.T) {
   736  	varFileDir := testTempDir(t)
   737  	varFilePath := filepath.Join(varFileDir, "terraform.tfvars")
   738  	if err := ioutil.WriteFile(varFilePath, []byte(planVarFile), 0644); err != nil {
   739  		t.Fatalf("err: %s", err)
   740  	}
   741  
   742  	cwd, err := os.Getwd()
   743  	if err != nil {
   744  		t.Fatalf("err: %s", err)
   745  	}
   746  	if err := os.Chdir(varFileDir); err != nil {
   747  		t.Fatalf("err: %s", err)
   748  	}
   749  	defer os.Chdir(cwd)
   750  
   751  	p := testProvider()
   752  	ui := new(cli.MockUi)
   753  	c := &PlanCommand{
   754  		Meta: Meta{
   755  			ContextOpts: testCtxConfig(p),
   756  			Ui:          ui,
   757  		},
   758  	}
   759  
   760  	actual := ""
   761  	p.DiffFn = func(
   762  		info *terraform.InstanceInfo,
   763  		s *terraform.InstanceState,
   764  		c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
   765  		if v, ok := c.Config["value"]; ok {
   766  			actual = v.(string)
   767  		}
   768  
   769  		return nil, nil
   770  	}
   771  
   772  	args := []string{
   773  		testFixturePath("plan-vars"),
   774  	}
   775  	if code := c.Run(args); code != 0 {
   776  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   777  	}
   778  
   779  	if actual != "bar" {
   780  		t.Fatal("didn't work")
   781  	}
   782  }
   783  
   784  func TestPlan_detailedExitcode(t *testing.T) {
   785  	cwd, err := os.Getwd()
   786  	if err != nil {
   787  		t.Fatalf("err: %s", err)
   788  	}
   789  	if err := os.Chdir(testFixturePath("plan")); err != nil {
   790  		t.Fatalf("err: %s", err)
   791  	}
   792  	defer os.Chdir(cwd)
   793  
   794  	p := testProvider()
   795  	ui := new(cli.MockUi)
   796  	c := &PlanCommand{
   797  		Meta: Meta{
   798  			ContextOpts: testCtxConfig(p),
   799  			Ui:          ui,
   800  		},
   801  	}
   802  
   803  	args := []string{"-detailed-exitcode"}
   804  	if code := c.Run(args); code != 2 {
   805  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   806  	}
   807  }
   808  
   809  func TestPlan_detailedExitcode_emptyDiff(t *testing.T) {
   810  	cwd, err := os.Getwd()
   811  	if err != nil {
   812  		t.Fatalf("err: %s", err)
   813  	}
   814  	if err := os.Chdir(testFixturePath("plan-emptydiff")); err != nil {
   815  		t.Fatalf("err: %s", err)
   816  	}
   817  	defer os.Chdir(cwd)
   818  
   819  	p := testProvider()
   820  	ui := new(cli.MockUi)
   821  	c := &PlanCommand{
   822  		Meta: Meta{
   823  			ContextOpts: testCtxConfig(p),
   824  			Ui:          ui,
   825  		},
   826  	}
   827  
   828  	args := []string{"-detailed-exitcode"}
   829  	if code := c.Run(args); code != 0 {
   830  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   831  	}
   832  }
   833  
   834  const planVarFile = `
   835  foo = "bar"
   836  `
   837  
   838  const testPlanNoStateStr = `
   839  <not created>
   840  `
   841  
   842  const testPlanStateStr = `
   843  ID = bar
   844  Tainted = false
   845  `
   846  
   847  const testPlanStateDefaultStr = `
   848  ID = bar
   849  Tainted = false
   850  `