github.com/tam7t/terraform@v0.7.0-rc2.0.20160705125922-be2469a05c5e/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_vars(t *testing.T) {
   399  	p := testProvider()
   400  	ui := new(cli.MockUi)
   401  	c := &PlanCommand{
   402  		Meta: Meta{
   403  			ContextOpts: testCtxConfig(p),
   404  			Ui:          ui,
   405  		},
   406  	}
   407  
   408  	actual := ""
   409  	p.DiffFn = func(
   410  		info *terraform.InstanceInfo,
   411  		s *terraform.InstanceState,
   412  		c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
   413  		if v, ok := c.Config["value"]; ok {
   414  			actual = v.(string)
   415  		}
   416  
   417  		return nil, nil
   418  	}
   419  
   420  	args := []string{
   421  		"-var", "foo=bar",
   422  		testFixturePath("plan-vars"),
   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 actual != "bar" {
   429  		t.Fatal("didn't work")
   430  	}
   431  }
   432  
   433  func TestPlan_varsUnset(t *testing.T) {
   434  	// Disable test mode so input would be asked
   435  	test = false
   436  	defer func() { test = true }()
   437  
   438  	defaultInputReader = bytes.NewBufferString("bar\n")
   439  
   440  	p := testProvider()
   441  	ui := new(cli.MockUi)
   442  	c := &PlanCommand{
   443  		Meta: Meta{
   444  			ContextOpts: testCtxConfig(p),
   445  			Ui:          ui,
   446  		},
   447  	}
   448  
   449  	args := []string{
   450  		testFixturePath("plan-vars"),
   451  	}
   452  	if code := c.Run(args); code != 0 {
   453  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   454  	}
   455  }
   456  
   457  func TestPlan_varFile(t *testing.T) {
   458  	varFilePath := testTempFile(t)
   459  	if err := ioutil.WriteFile(varFilePath, []byte(planVarFile), 0644); err != nil {
   460  		t.Fatalf("err: %s", err)
   461  	}
   462  
   463  	p := testProvider()
   464  	ui := new(cli.MockUi)
   465  	c := &PlanCommand{
   466  		Meta: Meta{
   467  			ContextOpts: testCtxConfig(p),
   468  			Ui:          ui,
   469  		},
   470  	}
   471  
   472  	actual := ""
   473  	p.DiffFn = func(
   474  		info *terraform.InstanceInfo,
   475  		s *terraform.InstanceState,
   476  		c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
   477  		if v, ok := c.Config["value"]; ok {
   478  			actual = v.(string)
   479  		}
   480  
   481  		return nil, nil
   482  	}
   483  
   484  	args := []string{
   485  		"-var-file", varFilePath,
   486  		testFixturePath("plan-vars"),
   487  	}
   488  	if code := c.Run(args); code != 0 {
   489  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   490  	}
   491  
   492  	if actual != "bar" {
   493  		t.Fatal("didn't work")
   494  	}
   495  }
   496  
   497  func TestPlan_varFileDefault(t *testing.T) {
   498  	varFileDir := testTempDir(t)
   499  	varFilePath := filepath.Join(varFileDir, "terraform.tfvars")
   500  	if err := ioutil.WriteFile(varFilePath, []byte(planVarFile), 0644); err != nil {
   501  		t.Fatalf("err: %s", err)
   502  	}
   503  
   504  	cwd, err := os.Getwd()
   505  	if err != nil {
   506  		t.Fatalf("err: %s", err)
   507  	}
   508  	if err := os.Chdir(varFileDir); err != nil {
   509  		t.Fatalf("err: %s", err)
   510  	}
   511  	defer os.Chdir(cwd)
   512  
   513  	p := testProvider()
   514  	ui := new(cli.MockUi)
   515  	c := &PlanCommand{
   516  		Meta: Meta{
   517  			ContextOpts: testCtxConfig(p),
   518  			Ui:          ui,
   519  		},
   520  	}
   521  
   522  	actual := ""
   523  	p.DiffFn = func(
   524  		info *terraform.InstanceInfo,
   525  		s *terraform.InstanceState,
   526  		c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
   527  		if v, ok := c.Config["value"]; ok {
   528  			actual = v.(string)
   529  		}
   530  
   531  		return nil, nil
   532  	}
   533  
   534  	args := []string{
   535  		testFixturePath("plan-vars"),
   536  	}
   537  	if code := c.Run(args); code != 0 {
   538  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   539  	}
   540  
   541  	if actual != "bar" {
   542  		t.Fatal("didn't work")
   543  	}
   544  }
   545  
   546  func TestPlan_detailedExitcode(t *testing.T) {
   547  	cwd, err := os.Getwd()
   548  	if err != nil {
   549  		t.Fatalf("err: %s", err)
   550  	}
   551  	if err := os.Chdir(testFixturePath("plan")); err != nil {
   552  		t.Fatalf("err: %s", err)
   553  	}
   554  	defer os.Chdir(cwd)
   555  
   556  	p := testProvider()
   557  	ui := new(cli.MockUi)
   558  	c := &PlanCommand{
   559  		Meta: Meta{
   560  			ContextOpts: testCtxConfig(p),
   561  			Ui:          ui,
   562  		},
   563  	}
   564  
   565  	args := []string{"-detailed-exitcode"}
   566  	if code := c.Run(args); code != 2 {
   567  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   568  	}
   569  }
   570  
   571  func TestPlan_detailedExitcode_emptyDiff(t *testing.T) {
   572  	cwd, err := os.Getwd()
   573  	if err != nil {
   574  		t.Fatalf("err: %s", err)
   575  	}
   576  	if err := os.Chdir(testFixturePath("plan-emptydiff")); err != nil {
   577  		t.Fatalf("err: %s", err)
   578  	}
   579  	defer os.Chdir(cwd)
   580  
   581  	p := testProvider()
   582  	ui := new(cli.MockUi)
   583  	c := &PlanCommand{
   584  		Meta: Meta{
   585  			ContextOpts: testCtxConfig(p),
   586  			Ui:          ui,
   587  		},
   588  	}
   589  
   590  	args := []string{"-detailed-exitcode"}
   591  	if code := c.Run(args); code != 0 {
   592  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   593  	}
   594  }
   595  
   596  const planVarFile = `
   597  foo = "bar"
   598  `
   599  
   600  const testPlanNoStateStr = `
   601  <not created>
   602  `
   603  
   604  const testPlanStateStr = `
   605  ID = bar
   606  Tainted = false
   607  `
   608  
   609  const testPlanStateDefaultStr = `
   610  ID = bar
   611  Tainted = false
   612  `