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