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