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