github.com/blacked/terraform@v0.6.2-0.20150806163846-669c4ad71586/command/plan_test.go (about)

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