github.com/r3labs/terraform@v0.8.4/command/plan_test.go (about)

     1  package command
     2  
     3  import (
     4  	"bytes"
     5  	"io/ioutil"
     6  	"os"
     7  	"path/filepath"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/hashicorp/terraform/terraform"
    12  	"github.com/mitchellh/cli"
    13  )
    14  
    15  func TestPlan(t *testing.T) {
    16  	cwd, err := os.Getwd()
    17  	if err != nil {
    18  		t.Fatalf("err: %s", err)
    19  	}
    20  	if err := os.Chdir(testFixturePath("plan")); err != nil {
    21  		t.Fatalf("err: %s", err)
    22  	}
    23  	defer os.Chdir(cwd)
    24  
    25  	p := testProvider()
    26  	ui := new(cli.MockUi)
    27  	c := &PlanCommand{
    28  		Meta: Meta{
    29  			ContextOpts: testCtxConfig(p),
    30  			Ui:          ui,
    31  		},
    32  	}
    33  
    34  	args := []string{}
    35  	if code := c.Run(args); code != 0 {
    36  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
    37  	}
    38  }
    39  
    40  func TestPlan_plan(t *testing.T) {
    41  	tmp, cwd := testCwd(t)
    42  	defer testFixCwd(t, tmp, cwd)
    43  
    44  	planPath := testPlanFile(t, &terraform.Plan{
    45  		Module: testModule(t, "apply"),
    46  	})
    47  
    48  	p := testProvider()
    49  	ui := new(cli.MockUi)
    50  	c := &PlanCommand{
    51  		Meta: Meta{
    52  			ContextOpts: testCtxConfig(p),
    53  			Ui:          ui,
    54  		},
    55  	}
    56  
    57  	args := []string{planPath}
    58  	if code := c.Run(args); code != 0 {
    59  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
    60  	}
    61  
    62  	if p.RefreshCalled {
    63  		t.Fatal("refresh should not be called")
    64  	}
    65  }
    66  
    67  func TestPlan_destroy(t *testing.T) {
    68  	originalState := &terraform.State{
    69  		Modules: []*terraform.ModuleState{
    70  			&terraform.ModuleState{
    71  				Path: []string{"root"},
    72  				Resources: map[string]*terraform.ResourceState{
    73  					"test_instance.foo": &terraform.ResourceState{
    74  						Type: "test_instance",
    75  						Primary: &terraform.InstanceState{
    76  							ID: "bar",
    77  						},
    78  					},
    79  				},
    80  			},
    81  		},
    82  	}
    83  
    84  	outPath := testTempFile(t)
    85  	statePath := testStateFile(t, originalState)
    86  
    87  	p := testProvider()
    88  	ui := new(cli.MockUi)
    89  	c := &PlanCommand{
    90  		Meta: Meta{
    91  			ContextOpts: testCtxConfig(p),
    92  			Ui:          ui,
    93  		},
    94  	}
    95  
    96  	args := []string{
    97  		"-destroy",
    98  		"-out", outPath,
    99  		"-state", statePath,
   100  		testFixturePath("plan"),
   101  	}
   102  	if code := c.Run(args); code != 0 {
   103  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   104  	}
   105  
   106  	if !p.RefreshCalled {
   107  		t.Fatal("refresh should be called")
   108  	}
   109  
   110  	plan := testReadPlan(t, outPath)
   111  	for _, m := range plan.Diff.Modules {
   112  		for _, r := range m.Resources {
   113  			if !r.Destroy {
   114  				t.Fatalf("bad: %#v", r)
   115  			}
   116  		}
   117  	}
   118  }
   119  
   120  func TestPlan_noState(t *testing.T) {
   121  	tmp, cwd := testCwd(t)
   122  	defer testFixCwd(t, tmp, cwd)
   123  
   124  	p := testProvider()
   125  	ui := new(cli.MockUi)
   126  	c := &PlanCommand{
   127  		Meta: Meta{
   128  			ContextOpts: testCtxConfig(p),
   129  			Ui:          ui,
   130  		},
   131  	}
   132  
   133  	args := []string{
   134  		testFixturePath("plan"),
   135  	}
   136  	if code := c.Run(args); code != 0 {
   137  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   138  	}
   139  
   140  	// Verify that refresh was called
   141  	if p.RefreshCalled {
   142  		t.Fatal("refresh should not be called")
   143  	}
   144  
   145  	// Verify that the provider was called with the existing state
   146  	actual := strings.TrimSpace(p.DiffState.String())
   147  	expected := strings.TrimSpace(testPlanNoStateStr)
   148  	if actual != expected {
   149  		t.Fatalf("bad:\n\n%s", actual)
   150  	}
   151  }
   152  
   153  func TestPlan_outPath(t *testing.T) {
   154  	tf, err := ioutil.TempFile("", "tf")
   155  	if err != nil {
   156  		t.Fatalf("err: %s", err)
   157  	}
   158  	outPath := tf.Name()
   159  	os.Remove(tf.Name())
   160  
   161  	p := testProvider()
   162  	ui := new(cli.MockUi)
   163  	c := &PlanCommand{
   164  		Meta: Meta{
   165  			ContextOpts: testCtxConfig(p),
   166  			Ui:          ui,
   167  		},
   168  	}
   169  
   170  	p.DiffReturn = &terraform.InstanceDiff{
   171  		Destroy: true,
   172  	}
   173  
   174  	args := []string{
   175  		"-out", outPath,
   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  	f, err := os.Open(outPath)
   183  	if err != nil {
   184  		t.Fatalf("err: %s", err)
   185  	}
   186  	defer f.Close()
   187  
   188  	if _, err := terraform.ReadPlan(f); err != nil {
   189  		t.Fatalf("err: %s", err)
   190  	}
   191  }
   192  
   193  func TestPlan_outPathNoChange(t *testing.T) {
   194  	originalState := &terraform.State{
   195  		Modules: []*terraform.ModuleState{
   196  			&terraform.ModuleState{
   197  				Path: []string{"root"},
   198  				Resources: map[string]*terraform.ResourceState{
   199  					"test_instance.foo": &terraform.ResourceState{
   200  						Type: "test_instance",
   201  						Primary: &terraform.InstanceState{
   202  							ID: "bar",
   203  						},
   204  					},
   205  				},
   206  			},
   207  		},
   208  	}
   209  	statePath := testStateFile(t, originalState)
   210  
   211  	tf, err := ioutil.TempFile("", "tf")
   212  	if err != nil {
   213  		t.Fatalf("err: %s", err)
   214  	}
   215  	outPath := tf.Name()
   216  	os.Remove(tf.Name())
   217  
   218  	p := testProvider()
   219  	ui := new(cli.MockUi)
   220  	c := &PlanCommand{
   221  		Meta: Meta{
   222  			ContextOpts: testCtxConfig(p),
   223  			Ui:          ui,
   224  		},
   225  	}
   226  
   227  	args := []string{
   228  		"-out", outPath,
   229  		"-state", statePath,
   230  		testFixturePath("plan"),
   231  	}
   232  	if code := c.Run(args); code != 0 {
   233  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   234  	}
   235  
   236  	plan := testReadPlan(t, outPath)
   237  	if !plan.Diff.Empty() {
   238  		t.Fatalf("Expected empty plan to be written to plan file, got: %s", plan)
   239  	}
   240  }
   241  
   242  func TestPlan_refresh(t *testing.T) {
   243  	p := testProvider()
   244  	ui := new(cli.MockUi)
   245  	c := &PlanCommand{
   246  		Meta: Meta{
   247  			ContextOpts: testCtxConfig(p),
   248  			Ui:          ui,
   249  		},
   250  	}
   251  
   252  	args := []string{
   253  		"-refresh=false",
   254  		testFixturePath("plan"),
   255  	}
   256  	if code := c.Run(args); code != 0 {
   257  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   258  	}
   259  
   260  	if p.RefreshCalled {
   261  		t.Fatal("refresh should not be called")
   262  	}
   263  }
   264  
   265  func TestPlan_state(t *testing.T) {
   266  	// Write out some prior state
   267  	tf, err := ioutil.TempFile("", "tf")
   268  	if err != nil {
   269  		t.Fatalf("err: %s", err)
   270  	}
   271  	statePath := tf.Name()
   272  	defer os.Remove(tf.Name())
   273  
   274  	originalState := testState()
   275  	err = terraform.WriteState(originalState, tf)
   276  	tf.Close()
   277  	if err != nil {
   278  		t.Fatalf("err: %s", err)
   279  	}
   280  
   281  	p := testProvider()
   282  	ui := new(cli.MockUi)
   283  	c := &PlanCommand{
   284  		Meta: Meta{
   285  			ContextOpts: testCtxConfig(p),
   286  			Ui:          ui,
   287  		},
   288  	}
   289  
   290  	args := []string{
   291  		"-state", statePath,
   292  		testFixturePath("plan"),
   293  	}
   294  	if code := c.Run(args); code != 0 {
   295  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   296  	}
   297  
   298  	// Verify that the provider was called with the existing state
   299  	actual := strings.TrimSpace(p.DiffState.String())
   300  	expected := strings.TrimSpace(testPlanStateStr)
   301  	if actual != expected {
   302  		t.Fatalf("bad:\n\n%s", actual)
   303  	}
   304  }
   305  
   306  func TestPlan_stateDefault(t *testing.T) {
   307  	originalState := testState()
   308  
   309  	// Write the state file in a temporary directory with the
   310  	// default filename.
   311  	td, err := ioutil.TempDir("", "tf")
   312  	if err != nil {
   313  		t.Fatalf("err: %s", err)
   314  	}
   315  	statePath := filepath.Join(td, DefaultStateFilename)
   316  
   317  	f, err := os.Create(statePath)
   318  	if err != nil {
   319  		t.Fatalf("err: %s", err)
   320  	}
   321  	err = terraform.WriteState(originalState, f)
   322  	f.Close()
   323  	if err != nil {
   324  		t.Fatalf("err: %s", err)
   325  	}
   326  
   327  	// Change to that directory
   328  	cwd, err := os.Getwd()
   329  	if err != nil {
   330  		t.Fatalf("err: %s", err)
   331  	}
   332  	if err := os.Chdir(filepath.Dir(statePath)); err != nil {
   333  		t.Fatalf("err: %s", err)
   334  	}
   335  	defer os.Chdir(cwd)
   336  
   337  	p := testProvider()
   338  	ui := new(cli.MockUi)
   339  	c := &PlanCommand{
   340  		Meta: Meta{
   341  			ContextOpts: testCtxConfig(p),
   342  			Ui:          ui,
   343  		},
   344  	}
   345  
   346  	args := []string{
   347  		testFixturePath("plan"),
   348  	}
   349  	if code := c.Run(args); code != 0 {
   350  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   351  	}
   352  
   353  	// Verify that the provider was called with the existing state
   354  	actual := strings.TrimSpace(p.DiffState.String())
   355  	expected := strings.TrimSpace(testPlanStateDefaultStr)
   356  	if actual != expected {
   357  		t.Fatalf("bad:\n\n%s", actual)
   358  	}
   359  }
   360  
   361  func TestPlan_stateFuture(t *testing.T) {
   362  	originalState := testState()
   363  	originalState.TFVersion = "99.99.99"
   364  	statePath := testStateFile(t, originalState)
   365  
   366  	p := testProvider()
   367  	ui := new(cli.MockUi)
   368  	c := &PlanCommand{
   369  		Meta: Meta{
   370  			ContextOpts: testCtxConfig(p),
   371  			Ui:          ui,
   372  		},
   373  	}
   374  
   375  	args := []string{
   376  		"-state", statePath,
   377  		testFixturePath("plan"),
   378  	}
   379  	if code := c.Run(args); code == 0 {
   380  		t.Fatal("should fail")
   381  	}
   382  
   383  	f, err := os.Open(statePath)
   384  	if err != nil {
   385  		t.Fatalf("err: %s", err)
   386  	}
   387  
   388  	newState, err := terraform.ReadState(f)
   389  	f.Close()
   390  	if err != nil {
   391  		t.Fatalf("err: %s", err)
   392  	}
   393  
   394  	if !newState.Equal(originalState) {
   395  		t.Fatalf("bad: %#v", newState)
   396  	}
   397  	if newState.TFVersion != originalState.TFVersion {
   398  		t.Fatalf("bad: %#v", newState)
   399  	}
   400  }
   401  
   402  func TestPlan_statePast(t *testing.T) {
   403  	originalState := testState()
   404  	originalState.TFVersion = "0.1.0"
   405  	statePath := testStateFile(t, originalState)
   406  
   407  	p := testProvider()
   408  	ui := new(cli.MockUi)
   409  	c := &PlanCommand{
   410  		Meta: Meta{
   411  			ContextOpts: testCtxConfig(p),
   412  			Ui:          ui,
   413  		},
   414  	}
   415  
   416  	args := []string{
   417  		"-state", statePath,
   418  		testFixturePath("plan"),
   419  	}
   420  	if code := c.Run(args); code != 0 {
   421  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   422  	}
   423  }
   424  
   425  func TestPlan_validate(t *testing.T) {
   426  	// This is triggered by not asking for input so we have to set this to false
   427  	test = false
   428  	defer func() { test = true }()
   429  
   430  	cwd, err := os.Getwd()
   431  	if err != nil {
   432  		t.Fatalf("err: %s", err)
   433  	}
   434  	if err := os.Chdir(testFixturePath("plan-invalid")); err != nil {
   435  		t.Fatalf("err: %s", err)
   436  	}
   437  	defer os.Chdir(cwd)
   438  
   439  	p := testProvider()
   440  	ui := new(cli.MockUi)
   441  	c := &PlanCommand{
   442  		Meta: Meta{
   443  			ContextOpts: testCtxConfig(p),
   444  			Ui:          ui,
   445  		},
   446  	}
   447  
   448  	args := []string{}
   449  	if code := c.Run(args); code != 1 {
   450  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   451  	}
   452  
   453  	actual := ui.ErrorWriter.String()
   454  	if !strings.Contains(actual, "can't reference") {
   455  		t.Fatalf("bad: %s", actual)
   456  	}
   457  }
   458  
   459  func TestPlan_vars(t *testing.T) {
   460  	p := testProvider()
   461  	ui := new(cli.MockUi)
   462  	c := &PlanCommand{
   463  		Meta: Meta{
   464  			ContextOpts: testCtxConfig(p),
   465  			Ui:          ui,
   466  		},
   467  	}
   468  
   469  	actual := ""
   470  	p.DiffFn = func(
   471  		info *terraform.InstanceInfo,
   472  		s *terraform.InstanceState,
   473  		c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
   474  		if v, ok := c.Config["value"]; ok {
   475  			actual = v.(string)
   476  		}
   477  
   478  		return nil, nil
   479  	}
   480  
   481  	args := []string{
   482  		"-var", "foo=bar",
   483  		testFixturePath("plan-vars"),
   484  	}
   485  	if code := c.Run(args); code != 0 {
   486  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   487  	}
   488  
   489  	if actual != "bar" {
   490  		t.Fatal("didn't work")
   491  	}
   492  }
   493  
   494  func TestPlan_varsUnset(t *testing.T) {
   495  	// Disable test mode so input would be asked
   496  	test = false
   497  	defer func() { test = true }()
   498  
   499  	defaultInputReader = bytes.NewBufferString("bar\n")
   500  
   501  	p := testProvider()
   502  	ui := new(cli.MockUi)
   503  	c := &PlanCommand{
   504  		Meta: Meta{
   505  			ContextOpts: testCtxConfig(p),
   506  			Ui:          ui,
   507  		},
   508  	}
   509  
   510  	args := []string{
   511  		testFixturePath("plan-vars"),
   512  	}
   513  	if code := c.Run(args); code != 0 {
   514  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   515  	}
   516  }
   517  
   518  func TestPlan_varFile(t *testing.T) {
   519  	varFilePath := testTempFile(t)
   520  	if err := ioutil.WriteFile(varFilePath, []byte(planVarFile), 0644); err != nil {
   521  		t.Fatalf("err: %s", err)
   522  	}
   523  
   524  	p := testProvider()
   525  	ui := new(cli.MockUi)
   526  	c := &PlanCommand{
   527  		Meta: Meta{
   528  			ContextOpts: testCtxConfig(p),
   529  			Ui:          ui,
   530  		},
   531  	}
   532  
   533  	actual := ""
   534  	p.DiffFn = func(
   535  		info *terraform.InstanceInfo,
   536  		s *terraform.InstanceState,
   537  		c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
   538  		if v, ok := c.Config["value"]; ok {
   539  			actual = v.(string)
   540  		}
   541  
   542  		return nil, nil
   543  	}
   544  
   545  	args := []string{
   546  		"-var-file", varFilePath,
   547  		testFixturePath("plan-vars"),
   548  	}
   549  	if code := c.Run(args); code != 0 {
   550  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   551  	}
   552  
   553  	if actual != "bar" {
   554  		t.Fatal("didn't work")
   555  	}
   556  }
   557  
   558  func TestPlan_varFileDefault(t *testing.T) {
   559  	varFileDir := testTempDir(t)
   560  	varFilePath := filepath.Join(varFileDir, "terraform.tfvars")
   561  	if err := ioutil.WriteFile(varFilePath, []byte(planVarFile), 0644); err != nil {
   562  		t.Fatalf("err: %s", err)
   563  	}
   564  
   565  	cwd, err := os.Getwd()
   566  	if err != nil {
   567  		t.Fatalf("err: %s", err)
   568  	}
   569  	if err := os.Chdir(varFileDir); err != nil {
   570  		t.Fatalf("err: %s", err)
   571  	}
   572  	defer os.Chdir(cwd)
   573  
   574  	p := testProvider()
   575  	ui := new(cli.MockUi)
   576  	c := &PlanCommand{
   577  		Meta: Meta{
   578  			ContextOpts: testCtxConfig(p),
   579  			Ui:          ui,
   580  		},
   581  	}
   582  
   583  	actual := ""
   584  	p.DiffFn = func(
   585  		info *terraform.InstanceInfo,
   586  		s *terraform.InstanceState,
   587  		c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
   588  		if v, ok := c.Config["value"]; ok {
   589  			actual = v.(string)
   590  		}
   591  
   592  		return nil, nil
   593  	}
   594  
   595  	args := []string{
   596  		testFixturePath("plan-vars"),
   597  	}
   598  	if code := c.Run(args); code != 0 {
   599  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   600  	}
   601  
   602  	if actual != "bar" {
   603  		t.Fatal("didn't work")
   604  	}
   605  }
   606  
   607  func TestPlan_detailedExitcode(t *testing.T) {
   608  	cwd, err := os.Getwd()
   609  	if err != nil {
   610  		t.Fatalf("err: %s", err)
   611  	}
   612  	if err := os.Chdir(testFixturePath("plan")); err != nil {
   613  		t.Fatalf("err: %s", err)
   614  	}
   615  	defer os.Chdir(cwd)
   616  
   617  	p := testProvider()
   618  	ui := new(cli.MockUi)
   619  	c := &PlanCommand{
   620  		Meta: Meta{
   621  			ContextOpts: testCtxConfig(p),
   622  			Ui:          ui,
   623  		},
   624  	}
   625  
   626  	args := []string{"-detailed-exitcode"}
   627  	if code := c.Run(args); code != 2 {
   628  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   629  	}
   630  }
   631  
   632  func TestPlan_detailedExitcode_emptyDiff(t *testing.T) {
   633  	cwd, err := os.Getwd()
   634  	if err != nil {
   635  		t.Fatalf("err: %s", err)
   636  	}
   637  	if err := os.Chdir(testFixturePath("plan-emptydiff")); err != nil {
   638  		t.Fatalf("err: %s", err)
   639  	}
   640  	defer os.Chdir(cwd)
   641  
   642  	p := testProvider()
   643  	ui := new(cli.MockUi)
   644  	c := &PlanCommand{
   645  		Meta: Meta{
   646  			ContextOpts: testCtxConfig(p),
   647  			Ui:          ui,
   648  		},
   649  	}
   650  
   651  	args := []string{"-detailed-exitcode"}
   652  	if code := c.Run(args); code != 0 {
   653  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   654  	}
   655  }
   656  
   657  const planVarFile = `
   658  foo = "bar"
   659  `
   660  
   661  const testPlanNoStateStr = `
   662  <not created>
   663  `
   664  
   665  const testPlanStateStr = `
   666  ID = bar
   667  Tainted = false
   668  `
   669  
   670  const testPlanStateDefaultStr = `
   671  ID = bar
   672  Tainted = false
   673  `