github.com/bpineau/terraform@v0.8.0-rc1.0.20161126184705-a8886012d185/command/apply_test.go (about)

     1  package command
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"net"
     8  	"net/http"
     9  	"net/url"
    10  	"os"
    11  	"path/filepath"
    12  	"reflect"
    13  	"strings"
    14  	"sync"
    15  	"testing"
    16  	"time"
    17  
    18  	"github.com/hashicorp/terraform/terraform"
    19  	"github.com/mitchellh/cli"
    20  )
    21  
    22  func TestApply(t *testing.T) {
    23  	statePath := testTempFile(t)
    24  
    25  	p := testProvider()
    26  	ui := new(cli.MockUi)
    27  	c := &ApplyCommand{
    28  		Meta: Meta{
    29  			ContextOpts: testCtxConfig(p),
    30  			Ui:          ui,
    31  		},
    32  	}
    33  
    34  	args := []string{
    35  		"-state", statePath,
    36  		testFixturePath("apply"),
    37  	}
    38  	if code := c.Run(args); code != 0 {
    39  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
    40  	}
    41  
    42  	if _, err := os.Stat(statePath); err != nil {
    43  		t.Fatalf("err: %s", err)
    44  	}
    45  
    46  	f, err := os.Open(statePath)
    47  	if err != nil {
    48  		t.Fatalf("err: %s", err)
    49  	}
    50  	defer f.Close()
    51  
    52  	state, err := terraform.ReadState(f)
    53  	if err != nil {
    54  		t.Fatalf("err: %s", err)
    55  	}
    56  	if state == nil {
    57  		t.Fatal("state should not be nil")
    58  	}
    59  }
    60  
    61  // high water mark counter
    62  type hwm struct {
    63  	sync.Mutex
    64  	val int
    65  	max int
    66  }
    67  
    68  func (t *hwm) Inc() {
    69  	t.Lock()
    70  	defer t.Unlock()
    71  	t.val++
    72  	if t.val > t.max {
    73  		t.max = t.val
    74  	}
    75  }
    76  
    77  func (t *hwm) Dec() {
    78  	t.Lock()
    79  	defer t.Unlock()
    80  	t.val--
    81  }
    82  
    83  func (t *hwm) Max() int {
    84  	t.Lock()
    85  	defer t.Unlock()
    86  	return t.max
    87  }
    88  
    89  func TestApply_parallelism(t *testing.T) {
    90  	provider := testProvider()
    91  	statePath := testTempFile(t)
    92  
    93  	par := 4
    94  
    95  	// This blocks all the appy functions. We close it when we exit so
    96  	// they end quickly after this test finishes.
    97  	block := make(chan struct{})
    98  	// signal how many goroutines have started
    99  	started := make(chan int, 100)
   100  
   101  	runCount := &hwm{}
   102  
   103  	provider.ApplyFn = func(
   104  		i *terraform.InstanceInfo,
   105  		s *terraform.InstanceState,
   106  		d *terraform.InstanceDiff) (*terraform.InstanceState, error) {
   107  		// Increment so we're counting parallelism
   108  		started <- 1
   109  		runCount.Inc()
   110  		defer runCount.Dec()
   111  		// Block here to stage up our max number of parallel instances
   112  		<-block
   113  
   114  		return nil, nil
   115  	}
   116  
   117  	ui := new(cli.MockUi)
   118  	c := &ApplyCommand{
   119  		Meta: Meta{
   120  			ContextOpts: testCtxConfig(provider),
   121  			Ui:          ui,
   122  		},
   123  	}
   124  
   125  	args := []string{
   126  		"-state", statePath,
   127  		fmt.Sprintf("-parallelism=%d", par),
   128  		testFixturePath("parallelism"),
   129  	}
   130  
   131  	// Run in a goroutine. We can get any errors from the ui.OutputWriter
   132  	doneCh := make(chan int, 1)
   133  	go func() {
   134  		doneCh <- c.Run(args)
   135  	}()
   136  
   137  	timeout := time.After(5 * time.Second)
   138  
   139  	// ensure things are running
   140  	for i := 0; i < par; i++ {
   141  		select {
   142  		case <-timeout:
   143  			t.Fatal("timeout waiting for all goroutines to start")
   144  		case <-started:
   145  		}
   146  	}
   147  
   148  	// a little extra sleep, since we can't ensure all goroutines from the walk have
   149  	// really started
   150  	time.Sleep(100 * time.Millisecond)
   151  	close(block)
   152  
   153  	select {
   154  	case res := <-doneCh:
   155  		if res != 0 {
   156  			t.Fatal(ui.OutputWriter.String())
   157  		}
   158  	case <-timeout:
   159  		t.Fatal("timeout waiting from Run()")
   160  	}
   161  
   162  	// The total in flight should equal the parallelism
   163  	if runCount.Max() != par {
   164  		t.Fatalf("Expected parallelism: %d, got: %d", par, runCount.Max())
   165  	}
   166  }
   167  
   168  func TestApply_configInvalid(t *testing.T) {
   169  	p := testProvider()
   170  	ui := new(cli.MockUi)
   171  	c := &ApplyCommand{
   172  		Meta: Meta{
   173  			ContextOpts: testCtxConfig(p),
   174  			Ui:          ui,
   175  		},
   176  	}
   177  
   178  	args := []string{
   179  		"-state", testTempFile(t),
   180  		testFixturePath("apply-config-invalid"),
   181  	}
   182  	if code := c.Run(args); code != 1 {
   183  		t.Fatalf("bad: \n%s", ui.OutputWriter.String())
   184  	}
   185  }
   186  
   187  func TestApply_defaultState(t *testing.T) {
   188  	td, err := ioutil.TempDir("", "tf")
   189  	if err != nil {
   190  		t.Fatalf("err: %s", err)
   191  	}
   192  	statePath := filepath.Join(td, DefaultStateFilename)
   193  
   194  	// Change to the temporary directory
   195  	cwd, err := os.Getwd()
   196  	if err != nil {
   197  		t.Fatalf("err: %s", err)
   198  	}
   199  	if err := os.Chdir(filepath.Dir(statePath)); err != nil {
   200  		t.Fatalf("err: %s", err)
   201  	}
   202  	defer os.Chdir(cwd)
   203  
   204  	p := testProvider()
   205  	ui := new(cli.MockUi)
   206  	c := &ApplyCommand{
   207  		Meta: Meta{
   208  			ContextOpts: testCtxConfig(p),
   209  			Ui:          ui,
   210  		},
   211  	}
   212  
   213  	args := []string{
   214  		testFixturePath("apply"),
   215  	}
   216  	if code := c.Run(args); code != 0 {
   217  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   218  	}
   219  
   220  	if _, err := os.Stat(statePath); err != nil {
   221  		t.Fatalf("err: %s", err)
   222  	}
   223  
   224  	f, err := os.Open(statePath)
   225  	if err != nil {
   226  		t.Fatalf("err: %s", err)
   227  	}
   228  	defer f.Close()
   229  
   230  	state, err := terraform.ReadState(f)
   231  	if err != nil {
   232  		t.Fatalf("err: %s", err)
   233  	}
   234  	if state == nil {
   235  		t.Fatal("state should not be nil")
   236  	}
   237  }
   238  
   239  func TestApply_error(t *testing.T) {
   240  	statePath := testTempFile(t)
   241  
   242  	p := testProvider()
   243  	ui := new(cli.MockUi)
   244  	c := &ApplyCommand{
   245  		Meta: Meta{
   246  			ContextOpts: testCtxConfig(p),
   247  			Ui:          ui,
   248  		},
   249  	}
   250  
   251  	var lock sync.Mutex
   252  	errored := false
   253  	p.ApplyFn = func(
   254  		info *terraform.InstanceInfo,
   255  		s *terraform.InstanceState,
   256  		d *terraform.InstanceDiff) (*terraform.InstanceState, error) {
   257  		lock.Lock()
   258  		defer lock.Unlock()
   259  
   260  		if !errored {
   261  			errored = true
   262  			return nil, fmt.Errorf("error")
   263  		}
   264  
   265  		return &terraform.InstanceState{ID: "foo"}, nil
   266  	}
   267  	p.DiffFn = func(
   268  		*terraform.InstanceInfo,
   269  		*terraform.InstanceState,
   270  		*terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
   271  		return &terraform.InstanceDiff{
   272  			Attributes: map[string]*terraform.ResourceAttrDiff{
   273  				"ami": &terraform.ResourceAttrDiff{
   274  					New: "bar",
   275  				},
   276  			},
   277  		}, nil
   278  	}
   279  
   280  	args := []string{
   281  		"-state", statePath,
   282  		testFixturePath("apply-error"),
   283  	}
   284  	if code := c.Run(args); code != 1 {
   285  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   286  	}
   287  
   288  	if _, err := os.Stat(statePath); err != nil {
   289  		t.Fatalf("err: %s", err)
   290  	}
   291  
   292  	f, err := os.Open(statePath)
   293  	if err != nil {
   294  		t.Fatalf("err: %s", err)
   295  	}
   296  	defer f.Close()
   297  
   298  	state, err := terraform.ReadState(f)
   299  	if err != nil {
   300  		t.Fatalf("err: %s", err)
   301  	}
   302  	if state == nil {
   303  		t.Fatal("state should not be nil")
   304  	}
   305  	if len(state.RootModule().Resources) == 0 {
   306  		t.Fatal("no resources in state")
   307  	}
   308  }
   309  
   310  func TestApply_init(t *testing.T) {
   311  	// Change to the temporary directory
   312  	cwd, err := os.Getwd()
   313  	if err != nil {
   314  		t.Fatalf("err: %s", err)
   315  	}
   316  	dir := tempDir(t)
   317  	if err := os.MkdirAll(dir, 0755); err != nil {
   318  		t.Fatalf("err: %s", err)
   319  	}
   320  	if err := os.Chdir(dir); err != nil {
   321  		t.Fatalf("err: %s", err)
   322  	}
   323  	defer os.Chdir(cwd)
   324  
   325  	// Create the test fixtures
   326  	statePath := testTempFile(t)
   327  	ln := testHttpServer(t)
   328  	defer ln.Close()
   329  
   330  	// Initialize the command
   331  	p := testProvider()
   332  	ui := new(cli.MockUi)
   333  	c := &ApplyCommand{
   334  		Meta: Meta{
   335  			ContextOpts: testCtxConfig(p),
   336  			Ui:          ui,
   337  		},
   338  	}
   339  
   340  	// Build the URL to the init
   341  	var u url.URL
   342  	u.Scheme = "http"
   343  	u.Host = ln.Addr().String()
   344  	u.Path = "/header"
   345  
   346  	args := []string{
   347  		"-state", statePath,
   348  		u.String(),
   349  	}
   350  	if code := c.Run(args); code != 0 {
   351  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   352  	}
   353  
   354  	if _, err := os.Stat("hello.tf"); err != nil {
   355  		t.Fatalf("err: %s", err)
   356  	}
   357  
   358  	if _, err := os.Stat(statePath); err != nil {
   359  		t.Fatalf("err: %s", err)
   360  	}
   361  
   362  	f, err := os.Open(statePath)
   363  	if err != nil {
   364  		t.Fatalf("err: %s", err)
   365  	}
   366  	defer f.Close()
   367  
   368  	state, err := terraform.ReadState(f)
   369  	if err != nil {
   370  		t.Fatalf("err: %s", err)
   371  	}
   372  	if state == nil {
   373  		t.Fatal("state should not be nil")
   374  	}
   375  }
   376  
   377  func TestApply_input(t *testing.T) {
   378  	// Disable test mode so input would be asked
   379  	test = false
   380  	defer func() { test = true }()
   381  
   382  	// Set some default reader/writers for the inputs
   383  	defaultInputReader = bytes.NewBufferString("foo\n")
   384  	defaultInputWriter = new(bytes.Buffer)
   385  
   386  	statePath := testTempFile(t)
   387  
   388  	p := testProvider()
   389  	ui := new(cli.MockUi)
   390  	c := &ApplyCommand{
   391  		Meta: Meta{
   392  			ContextOpts: testCtxConfig(p),
   393  			Ui:          ui,
   394  		},
   395  	}
   396  
   397  	args := []string{
   398  		"-state", statePath,
   399  		testFixturePath("apply-input"),
   400  	}
   401  	if code := c.Run(args); code != 0 {
   402  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   403  	}
   404  
   405  	if !p.InputCalled {
   406  		t.Fatal("input should be called")
   407  	}
   408  }
   409  
   410  // When only a partial set of the variables are set, Terraform
   411  // should still ask for the unset ones by default (with -input=true)
   412  func TestApply_inputPartial(t *testing.T) {
   413  	// Disable test mode so input would be asked
   414  	test = false
   415  	defer func() { test = true }()
   416  
   417  	// Set some default reader/writers for the inputs
   418  	defaultInputReader = bytes.NewBufferString("one\ntwo\n")
   419  	defaultInputWriter = new(bytes.Buffer)
   420  
   421  	statePath := testTempFile(t)
   422  
   423  	p := testProvider()
   424  	ui := new(cli.MockUi)
   425  	c := &ApplyCommand{
   426  		Meta: Meta{
   427  			ContextOpts: testCtxConfig(p),
   428  			Ui:          ui,
   429  		},
   430  	}
   431  
   432  	args := []string{
   433  		"-state", statePath,
   434  		"-var", "foo=foovalue",
   435  		testFixturePath("apply-input-partial"),
   436  	}
   437  	if code := c.Run(args); code != 0 {
   438  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   439  	}
   440  
   441  	expected := strings.TrimSpace(`
   442  <no state>
   443  Outputs:
   444  
   445  bar = one
   446  foo = foovalue
   447  	`)
   448  	testStateOutput(t, statePath, expected)
   449  }
   450  
   451  func TestApply_noArgs(t *testing.T) {
   452  	cwd, err := os.Getwd()
   453  	if err != nil {
   454  		t.Fatalf("err: %s", err)
   455  	}
   456  	if err := os.Chdir(testFixturePath("plan")); err != nil {
   457  		t.Fatalf("err: %s", err)
   458  	}
   459  	defer os.Chdir(cwd)
   460  
   461  	statePath := testTempFile(t)
   462  
   463  	p := testProvider()
   464  	ui := new(cli.MockUi)
   465  	c := &ApplyCommand{
   466  		Meta: Meta{
   467  			ContextOpts: testCtxConfig(p),
   468  			Ui:          ui,
   469  		},
   470  	}
   471  
   472  	args := []string{
   473  		"-state", statePath,
   474  	}
   475  	if code := c.Run(args); code != 0 {
   476  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   477  	}
   478  
   479  	if _, err := os.Stat(statePath); err != nil {
   480  		t.Fatalf("err: %s", err)
   481  	}
   482  
   483  	f, err := os.Open(statePath)
   484  	if err != nil {
   485  		t.Fatalf("err: %s", err)
   486  	}
   487  	defer f.Close()
   488  
   489  	state, err := terraform.ReadState(f)
   490  	if err != nil {
   491  		t.Fatalf("err: %s", err)
   492  	}
   493  	if state == nil {
   494  		t.Fatal("state should not be nil")
   495  	}
   496  }
   497  
   498  func TestApply_plan(t *testing.T) {
   499  	// Disable test mode so input would be asked
   500  	test = false
   501  	defer func() { test = true }()
   502  
   503  	// Set some default reader/writers for the inputs
   504  	defaultInputReader = new(bytes.Buffer)
   505  	defaultInputWriter = new(bytes.Buffer)
   506  
   507  	planPath := testPlanFile(t, &terraform.Plan{
   508  		Module: testModule(t, "apply"),
   509  	})
   510  	statePath := testTempFile(t)
   511  
   512  	p := testProvider()
   513  	ui := new(cli.MockUi)
   514  	c := &ApplyCommand{
   515  		Meta: Meta{
   516  			ContextOpts: testCtxConfig(p),
   517  			Ui:          ui,
   518  		},
   519  	}
   520  
   521  	args := []string{
   522  		"-state", statePath,
   523  		planPath,
   524  	}
   525  	if code := c.Run(args); code != 0 {
   526  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   527  	}
   528  
   529  	if p.InputCalled {
   530  		t.Fatalf("input should not be called for plans")
   531  	}
   532  
   533  	if _, err := os.Stat(statePath); err != nil {
   534  		t.Fatalf("err: %s", err)
   535  	}
   536  
   537  	f, err := os.Open(statePath)
   538  	if err != nil {
   539  		t.Fatalf("err: %s", err)
   540  	}
   541  	defer f.Close()
   542  
   543  	state, err := terraform.ReadState(f)
   544  	if err != nil {
   545  		t.Fatalf("err: %s", err)
   546  	}
   547  	if state == nil {
   548  		t.Fatal("state should not be nil")
   549  	}
   550  }
   551  
   552  func TestApply_plan_backup(t *testing.T) {
   553  	planPath := testPlanFile(t, testPlan(t))
   554  	statePath := testTempFile(t)
   555  	backupPath := testTempFile(t)
   556  
   557  	p := testProvider()
   558  	ui := new(cli.MockUi)
   559  	c := &ApplyCommand{
   560  		Meta: Meta{
   561  			ContextOpts: testCtxConfig(p),
   562  			Ui:          ui,
   563  		},
   564  	}
   565  
   566  	args := []string{
   567  		"-state", statePath,
   568  		"-backup", backupPath,
   569  		planPath,
   570  	}
   571  	if code := c.Run(args); code != 0 {
   572  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   573  	}
   574  
   575  	{
   576  		// Should have a backup file
   577  		f, err := os.Open(backupPath)
   578  		if err != nil {
   579  			t.Fatalf("err: %s", err)
   580  		}
   581  
   582  		_, err = terraform.ReadState(f)
   583  		f.Close()
   584  		if err != nil {
   585  			t.Fatalf("err: %s", err)
   586  		}
   587  	}
   588  }
   589  
   590  func TestApply_plan_noBackup(t *testing.T) {
   591  	planPath := testPlanFile(t, testPlan(t))
   592  	statePath := testTempFile(t)
   593  
   594  	p := testProvider()
   595  	ui := new(cli.MockUi)
   596  	c := &ApplyCommand{
   597  		Meta: Meta{
   598  			ContextOpts: testCtxConfig(p),
   599  			Ui:          ui,
   600  		},
   601  	}
   602  
   603  	args := []string{
   604  		"-state", statePath,
   605  		"-backup", "-",
   606  		planPath,
   607  	}
   608  	if code := c.Run(args); code != 0 {
   609  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   610  	}
   611  
   612  	// Ensure there is no backup
   613  	_, err := os.Stat(statePath + DefaultBackupExtension)
   614  	if err == nil || !os.IsNotExist(err) {
   615  		t.Fatalf("backup should not exist")
   616  	}
   617  
   618  	// Ensure there is no literal "-"
   619  	_, err = os.Stat("-")
   620  	if err == nil || !os.IsNotExist(err) {
   621  		t.Fatalf("backup should not exist")
   622  	}
   623  }
   624  
   625  func TestApply_plan_remoteState(t *testing.T) {
   626  	// Disable test mode so input would be asked
   627  	test = false
   628  	defer func() { test = true }()
   629  	tmp, cwd := testCwd(t)
   630  	defer testFixCwd(t, tmp, cwd)
   631  	remoteStatePath := filepath.Join(tmp, DefaultDataDir, DefaultStateFilename)
   632  	if err := os.MkdirAll(filepath.Dir(remoteStatePath), 0755); err != nil {
   633  		t.Fatalf("err: %s", err)
   634  	}
   635  
   636  	// Set some default reader/writers for the inputs
   637  	defaultInputReader = new(bytes.Buffer)
   638  	defaultInputWriter = new(bytes.Buffer)
   639  
   640  	// Create a remote state
   641  	state := testState()
   642  	conf, srv := testRemoteState(t, state, 200)
   643  	defer srv.Close()
   644  	state.Remote = conf
   645  
   646  	planPath := testPlanFile(t, &terraform.Plan{
   647  		Module: testModule(t, "apply"),
   648  		State:  state,
   649  	})
   650  
   651  	p := testProvider()
   652  	ui := new(cli.MockUi)
   653  	c := &ApplyCommand{
   654  		Meta: Meta{
   655  			ContextOpts: testCtxConfig(p),
   656  			Ui:          ui,
   657  		},
   658  	}
   659  
   660  	args := []string{
   661  		planPath,
   662  	}
   663  	if code := c.Run(args); code != 0 {
   664  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   665  	}
   666  
   667  	if p.InputCalled {
   668  		t.Fatalf("input should not be called for plans")
   669  	}
   670  
   671  	// State file should be not be installed
   672  	if _, err := os.Stat(filepath.Join(tmp, DefaultStateFilename)); err == nil {
   673  		t.Fatalf("State path should not exist")
   674  	}
   675  
   676  	// Check for remote state
   677  	if _, err := os.Stat(remoteStatePath); err != nil {
   678  		t.Fatalf("missing remote state: %s", err)
   679  	}
   680  }
   681  
   682  func TestApply_planWithVarFile(t *testing.T) {
   683  	varFileDir := testTempDir(t)
   684  	varFilePath := filepath.Join(varFileDir, "terraform.tfvars")
   685  	if err := ioutil.WriteFile(varFilePath, []byte(applyVarFile), 0644); err != nil {
   686  		t.Fatalf("err: %s", err)
   687  	}
   688  
   689  	planPath := testPlanFile(t, &terraform.Plan{
   690  		Module: testModule(t, "apply"),
   691  	})
   692  	statePath := testTempFile(t)
   693  
   694  	cwd, err := os.Getwd()
   695  	if err != nil {
   696  		t.Fatalf("err: %s", err)
   697  	}
   698  	if err := os.Chdir(varFileDir); err != nil {
   699  		t.Fatalf("err: %s", err)
   700  	}
   701  	defer os.Chdir(cwd)
   702  
   703  	p := testProvider()
   704  	ui := new(cli.MockUi)
   705  	c := &ApplyCommand{
   706  		Meta: Meta{
   707  			ContextOpts: testCtxConfig(p),
   708  			Ui:          ui,
   709  		},
   710  	}
   711  
   712  	args := []string{
   713  		"-state", statePath,
   714  		planPath,
   715  	}
   716  	if code := c.Run(args); code != 0 {
   717  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   718  	}
   719  
   720  	if _, err := os.Stat(statePath); err != nil {
   721  		t.Fatalf("err: %s", err)
   722  	}
   723  
   724  	f, err := os.Open(statePath)
   725  	if err != nil {
   726  		t.Fatalf("err: %s", err)
   727  	}
   728  	defer f.Close()
   729  
   730  	state, err := terraform.ReadState(f)
   731  	if err != nil {
   732  		t.Fatalf("err: %s", err)
   733  	}
   734  	if state == nil {
   735  		t.Fatal("state should not be nil")
   736  	}
   737  }
   738  
   739  func TestApply_planVars(t *testing.T) {
   740  	planPath := testPlanFile(t, &terraform.Plan{
   741  		Module: testModule(t, "apply"),
   742  	})
   743  	statePath := testTempFile(t)
   744  
   745  	p := testProvider()
   746  	ui := new(cli.MockUi)
   747  	c := &ApplyCommand{
   748  		Meta: Meta{
   749  			ContextOpts: testCtxConfig(p),
   750  			Ui:          ui,
   751  		},
   752  	}
   753  
   754  	args := []string{
   755  		"-state", statePath,
   756  		"-var", "foo=bar",
   757  		planPath,
   758  	}
   759  	if code := c.Run(args); code == 0 {
   760  		t.Fatal("should've failed")
   761  	}
   762  }
   763  
   764  func TestApply_refresh(t *testing.T) {
   765  	originalState := &terraform.State{
   766  		Modules: []*terraform.ModuleState{
   767  			&terraform.ModuleState{
   768  				Path: []string{"root"},
   769  				Resources: map[string]*terraform.ResourceState{
   770  					"test_instance.foo": &terraform.ResourceState{
   771  						Type: "test_instance",
   772  						Primary: &terraform.InstanceState{
   773  							ID: "bar",
   774  						},
   775  					},
   776  				},
   777  			},
   778  		},
   779  	}
   780  
   781  	statePath := testStateFile(t, originalState)
   782  
   783  	p := testProvider()
   784  	ui := new(cli.MockUi)
   785  	c := &ApplyCommand{
   786  		Meta: Meta{
   787  			ContextOpts: testCtxConfig(p),
   788  			Ui:          ui,
   789  		},
   790  	}
   791  
   792  	args := []string{
   793  		"-state", statePath,
   794  		testFixturePath("apply"),
   795  	}
   796  	if code := c.Run(args); code != 0 {
   797  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   798  	}
   799  
   800  	if !p.RefreshCalled {
   801  		t.Fatal("should call refresh")
   802  	}
   803  
   804  	if _, err := os.Stat(statePath); err != nil {
   805  		t.Fatalf("err: %s", err)
   806  	}
   807  
   808  	f, err := os.Open(statePath)
   809  	if err != nil {
   810  		t.Fatalf("err: %s", err)
   811  	}
   812  	defer f.Close()
   813  
   814  	state, err := terraform.ReadState(f)
   815  	if err != nil {
   816  		t.Fatalf("err: %s", err)
   817  	}
   818  	if state == nil {
   819  		t.Fatal("state should not be nil")
   820  	}
   821  
   822  	// Should have a backup file
   823  	f, err = os.Open(statePath + DefaultBackupExtension)
   824  	if err != nil {
   825  		t.Fatalf("err: %s", err)
   826  	}
   827  
   828  	backupState, err := terraform.ReadState(f)
   829  	f.Close()
   830  	if err != nil {
   831  		t.Fatalf("err: %s", err)
   832  	}
   833  
   834  	actualStr := strings.TrimSpace(backupState.String())
   835  	expectedStr := strings.TrimSpace(originalState.String())
   836  	if actualStr != expectedStr {
   837  		t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr)
   838  	}
   839  }
   840  
   841  func TestApply_shutdown(t *testing.T) {
   842  	stopped := false
   843  	stopCh := make(chan struct{})
   844  	stopReplyCh := make(chan struct{})
   845  
   846  	statePath := testTempFile(t)
   847  
   848  	p := testProvider()
   849  	shutdownCh := make(chan struct{})
   850  	ui := new(cli.MockUi)
   851  	c := &ApplyCommand{
   852  		Meta: Meta{
   853  			ContextOpts: testCtxConfig(p),
   854  			Ui:          ui,
   855  		},
   856  
   857  		ShutdownCh: shutdownCh,
   858  	}
   859  
   860  	p.DiffFn = func(
   861  		*terraform.InstanceInfo,
   862  		*terraform.InstanceState,
   863  		*terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
   864  		return &terraform.InstanceDiff{
   865  			Attributes: map[string]*terraform.ResourceAttrDiff{
   866  				"ami": &terraform.ResourceAttrDiff{
   867  					New: "bar",
   868  				},
   869  			},
   870  		}, nil
   871  	}
   872  	p.ApplyFn = func(
   873  		*terraform.InstanceInfo,
   874  		*terraform.InstanceState,
   875  		*terraform.InstanceDiff) (*terraform.InstanceState, error) {
   876  		if !stopped {
   877  			stopped = true
   878  			close(stopCh)
   879  			<-stopReplyCh
   880  		}
   881  
   882  		return &terraform.InstanceState{
   883  			ID: "foo",
   884  			Attributes: map[string]string{
   885  				"ami": "2",
   886  			},
   887  		}, nil
   888  	}
   889  
   890  	go func() {
   891  		<-stopCh
   892  		shutdownCh <- struct{}{}
   893  
   894  		// This is really dirty, but we have no other way to assure that
   895  		// tf.Stop() has been called. This doesn't assure it either, but
   896  		// it makes it much more certain.
   897  		time.Sleep(50 * time.Millisecond)
   898  
   899  		close(stopReplyCh)
   900  	}()
   901  
   902  	args := []string{
   903  		"-state", statePath,
   904  		testFixturePath("apply-shutdown"),
   905  	}
   906  	if code := c.Run(args); code != 0 {
   907  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   908  	}
   909  
   910  	if _, err := os.Stat(statePath); err != nil {
   911  		t.Fatalf("err: %s", err)
   912  	}
   913  
   914  	f, err := os.Open(statePath)
   915  	if err != nil {
   916  		t.Fatalf("err: %s", err)
   917  	}
   918  	defer f.Close()
   919  
   920  	state, err := terraform.ReadState(f)
   921  	if err != nil {
   922  		t.Fatalf("err: %s", err)
   923  	}
   924  	if state == nil {
   925  		t.Fatal("state should not be nil")
   926  	}
   927  
   928  	if len(state.RootModule().Resources) != 1 {
   929  		t.Fatalf("bad: %d", len(state.RootModule().Resources))
   930  	}
   931  }
   932  
   933  func TestApply_state(t *testing.T) {
   934  	originalState := &terraform.State{
   935  		Modules: []*terraform.ModuleState{
   936  			&terraform.ModuleState{
   937  				Path: []string{"root"},
   938  				Resources: map[string]*terraform.ResourceState{
   939  					"test_instance.foo": &terraform.ResourceState{
   940  						Type: "test_instance",
   941  						Primary: &terraform.InstanceState{
   942  							ID: "bar",
   943  						},
   944  					},
   945  				},
   946  			},
   947  		},
   948  	}
   949  
   950  	statePath := testStateFile(t, originalState)
   951  
   952  	p := testProvider()
   953  	p.DiffReturn = &terraform.InstanceDiff{
   954  		Attributes: map[string]*terraform.ResourceAttrDiff{
   955  			"ami": &terraform.ResourceAttrDiff{
   956  				New: "bar",
   957  			},
   958  		},
   959  	}
   960  
   961  	ui := new(cli.MockUi)
   962  	c := &ApplyCommand{
   963  		Meta: Meta{
   964  			ContextOpts: testCtxConfig(p),
   965  			Ui:          ui,
   966  		},
   967  	}
   968  
   969  	// Run the apply command pointing to our existing state
   970  	args := []string{
   971  		"-state", statePath,
   972  		testFixturePath("apply"),
   973  	}
   974  	if code := c.Run(args); code != 0 {
   975  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   976  	}
   977  
   978  	// Verify that the provider was called with the existing state
   979  	actual := strings.TrimSpace(p.DiffState.String())
   980  	expected := strings.TrimSpace(testApplyStateDiffStr)
   981  	if actual != expected {
   982  		t.Fatalf("bad:\n\n%s", actual)
   983  	}
   984  
   985  	actual = strings.TrimSpace(p.ApplyState.String())
   986  	expected = strings.TrimSpace(testApplyStateStr)
   987  	if actual != expected {
   988  		t.Fatalf("bad:\n\n%s", actual)
   989  	}
   990  
   991  	// Verify a new state exists
   992  	if _, err := os.Stat(statePath); err != nil {
   993  		t.Fatalf("err: %s", err)
   994  	}
   995  
   996  	f, err := os.Open(statePath)
   997  	if err != nil {
   998  		t.Fatalf("err: %s", err)
   999  	}
  1000  	defer f.Close()
  1001  
  1002  	state, err := terraform.ReadState(f)
  1003  	if err != nil {
  1004  		t.Fatalf("err: %s", err)
  1005  	}
  1006  	if state == nil {
  1007  		t.Fatal("state should not be nil")
  1008  	}
  1009  
  1010  	// Should have a backup file
  1011  	f, err = os.Open(statePath + DefaultBackupExtension)
  1012  	if err != nil {
  1013  		t.Fatalf("err: %s", err)
  1014  	}
  1015  
  1016  	backupState, err := terraform.ReadState(f)
  1017  	f.Close()
  1018  	if err != nil {
  1019  		t.Fatalf("err: %s", err)
  1020  	}
  1021  
  1022  	// nil out the ConnInfo since that should not be restored
  1023  	originalState.RootModule().Resources["test_instance.foo"].Primary.Ephemeral.ConnInfo = nil
  1024  
  1025  	actualStr := strings.TrimSpace(backupState.String())
  1026  	expectedStr := strings.TrimSpace(originalState.String())
  1027  	if actualStr != expectedStr {
  1028  		t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr)
  1029  	}
  1030  }
  1031  
  1032  func TestApply_stateNoExist(t *testing.T) {
  1033  	p := testProvider()
  1034  	ui := new(cli.MockUi)
  1035  	c := &ApplyCommand{
  1036  		Meta: Meta{
  1037  			ContextOpts: testCtxConfig(p),
  1038  			Ui:          ui,
  1039  		},
  1040  	}
  1041  
  1042  	args := []string{
  1043  		"idontexist.tfstate",
  1044  		testFixturePath("apply"),
  1045  	}
  1046  	if code := c.Run(args); code != 1 {
  1047  		t.Fatalf("bad: \n%s", ui.OutputWriter.String())
  1048  	}
  1049  }
  1050  
  1051  func TestApply_sensitiveOutput(t *testing.T) {
  1052  	p := testProvider()
  1053  	ui := new(cli.MockUi)
  1054  	c := &ApplyCommand{
  1055  		Meta: Meta{
  1056  			ContextOpts: testCtxConfig(p),
  1057  			Ui:          ui,
  1058  		},
  1059  	}
  1060  
  1061  	statePath := testTempFile(t)
  1062  
  1063  	args := []string{
  1064  		"-state", statePath,
  1065  		testFixturePath("apply-sensitive-output"),
  1066  	}
  1067  
  1068  	if code := c.Run(args); code != 0 {
  1069  		t.Fatalf("bad: \n%s", ui.OutputWriter.String())
  1070  	}
  1071  
  1072  	output := ui.OutputWriter.String()
  1073  	if !strings.Contains(output, "notsensitive = Hello world") {
  1074  		t.Fatalf("bad: output should contain 'notsensitive' output\n%s", output)
  1075  	}
  1076  	if !strings.Contains(output, "sensitive = <sensitive>") {
  1077  		t.Fatalf("bad: output should contain 'sensitive' output\n%s", output)
  1078  	}
  1079  }
  1080  
  1081  func TestApply_stateFuture(t *testing.T) {
  1082  	originalState := testState()
  1083  	originalState.TFVersion = "99.99.99"
  1084  	statePath := testStateFile(t, originalState)
  1085  
  1086  	p := testProvider()
  1087  	ui := new(cli.MockUi)
  1088  	c := &ApplyCommand{
  1089  		Meta: Meta{
  1090  			ContextOpts: testCtxConfig(p),
  1091  			Ui:          ui,
  1092  		},
  1093  	}
  1094  
  1095  	args := []string{
  1096  		"-state", statePath,
  1097  		testFixturePath("apply"),
  1098  	}
  1099  	if code := c.Run(args); code == 0 {
  1100  		t.Fatal("should fail")
  1101  	}
  1102  
  1103  	f, err := os.Open(statePath)
  1104  	if err != nil {
  1105  		t.Fatalf("err: %s", err)
  1106  	}
  1107  
  1108  	newState, err := terraform.ReadState(f)
  1109  	f.Close()
  1110  	if err != nil {
  1111  		t.Fatalf("err: %s", err)
  1112  	}
  1113  
  1114  	if !newState.Equal(originalState) {
  1115  		t.Fatalf("bad: %#v", newState)
  1116  	}
  1117  	if newState.TFVersion != originalState.TFVersion {
  1118  		t.Fatalf("bad: %#v", newState)
  1119  	}
  1120  }
  1121  
  1122  func TestApply_statePast(t *testing.T) {
  1123  	originalState := testState()
  1124  	originalState.TFVersion = "0.1.0"
  1125  	statePath := testStateFile(t, originalState)
  1126  
  1127  	p := testProvider()
  1128  	ui := new(cli.MockUi)
  1129  	c := &ApplyCommand{
  1130  		Meta: Meta{
  1131  			ContextOpts: testCtxConfig(p),
  1132  			Ui:          ui,
  1133  		},
  1134  	}
  1135  
  1136  	args := []string{
  1137  		"-state", statePath,
  1138  		testFixturePath("apply"),
  1139  	}
  1140  	if code := c.Run(args); code != 0 {
  1141  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
  1142  	}
  1143  }
  1144  
  1145  func TestApply_vars(t *testing.T) {
  1146  	statePath := testTempFile(t)
  1147  
  1148  	p := testProvider()
  1149  	ui := new(cli.MockUi)
  1150  	c := &ApplyCommand{
  1151  		Meta: Meta{
  1152  			ContextOpts: testCtxConfig(p),
  1153  			Ui:          ui,
  1154  		},
  1155  	}
  1156  
  1157  	actual := ""
  1158  	p.DiffFn = func(
  1159  		info *terraform.InstanceInfo,
  1160  		s *terraform.InstanceState,
  1161  		c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
  1162  		if v, ok := c.Config["value"]; ok {
  1163  			actual = v.(string)
  1164  		}
  1165  
  1166  		return &terraform.InstanceDiff{}, nil
  1167  	}
  1168  
  1169  	args := []string{
  1170  		"-var", "foo=bar",
  1171  		"-state", statePath,
  1172  		testFixturePath("apply-vars"),
  1173  	}
  1174  	if code := c.Run(args); code != 0 {
  1175  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
  1176  	}
  1177  
  1178  	if actual != "bar" {
  1179  		t.Fatal("didn't work")
  1180  	}
  1181  }
  1182  
  1183  func TestApply_varFile(t *testing.T) {
  1184  	varFilePath := testTempFile(t)
  1185  	if err := ioutil.WriteFile(varFilePath, []byte(applyVarFile), 0644); err != nil {
  1186  		t.Fatalf("err: %s", err)
  1187  	}
  1188  
  1189  	statePath := testTempFile(t)
  1190  
  1191  	p := testProvider()
  1192  	ui := new(cli.MockUi)
  1193  	c := &ApplyCommand{
  1194  		Meta: Meta{
  1195  			ContextOpts: testCtxConfig(p),
  1196  			Ui:          ui,
  1197  		},
  1198  	}
  1199  
  1200  	actual := ""
  1201  	p.DiffFn = func(
  1202  		info *terraform.InstanceInfo,
  1203  		s *terraform.InstanceState,
  1204  		c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
  1205  		if v, ok := c.Config["value"]; ok {
  1206  			actual = v.(string)
  1207  		}
  1208  
  1209  		return &terraform.InstanceDiff{}, nil
  1210  	}
  1211  
  1212  	args := []string{
  1213  		"-var-file", varFilePath,
  1214  		"-state", statePath,
  1215  		testFixturePath("apply-vars"),
  1216  	}
  1217  	if code := c.Run(args); code != 0 {
  1218  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
  1219  	}
  1220  
  1221  	if actual != "bar" {
  1222  		t.Fatal("didn't work")
  1223  	}
  1224  }
  1225  
  1226  func TestApply_varFileDefault(t *testing.T) {
  1227  	varFileDir := testTempDir(t)
  1228  	varFilePath := filepath.Join(varFileDir, "terraform.tfvars")
  1229  	if err := ioutil.WriteFile(varFilePath, []byte(applyVarFile), 0644); err != nil {
  1230  		t.Fatalf("err: %s", err)
  1231  	}
  1232  
  1233  	statePath := testTempFile(t)
  1234  
  1235  	cwd, err := os.Getwd()
  1236  	if err != nil {
  1237  		t.Fatalf("err: %s", err)
  1238  	}
  1239  	if err := os.Chdir(varFileDir); err != nil {
  1240  		t.Fatalf("err: %s", err)
  1241  	}
  1242  	defer os.Chdir(cwd)
  1243  
  1244  	p := testProvider()
  1245  	ui := new(cli.MockUi)
  1246  	c := &ApplyCommand{
  1247  		Meta: Meta{
  1248  			ContextOpts: testCtxConfig(p),
  1249  			Ui:          ui,
  1250  		},
  1251  	}
  1252  
  1253  	actual := ""
  1254  	p.DiffFn = func(
  1255  		info *terraform.InstanceInfo,
  1256  		s *terraform.InstanceState,
  1257  		c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
  1258  		if v, ok := c.Config["value"]; ok {
  1259  			actual = v.(string)
  1260  		}
  1261  
  1262  		return &terraform.InstanceDiff{}, nil
  1263  	}
  1264  
  1265  	args := []string{
  1266  		"-state", statePath,
  1267  		testFixturePath("apply-vars"),
  1268  	}
  1269  	if code := c.Run(args); code != 0 {
  1270  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
  1271  	}
  1272  
  1273  	if actual != "bar" {
  1274  		t.Fatal("didn't work")
  1275  	}
  1276  }
  1277  
  1278  func TestApply_varFileDefaultJSON(t *testing.T) {
  1279  	varFileDir := testTempDir(t)
  1280  	varFilePath := filepath.Join(varFileDir, "terraform.tfvars.json")
  1281  	if err := ioutil.WriteFile(varFilePath, []byte(applyVarFileJSON), 0644); err != nil {
  1282  		t.Fatalf("err: %s", err)
  1283  	}
  1284  
  1285  	statePath := testTempFile(t)
  1286  
  1287  	cwd, err := os.Getwd()
  1288  	if err != nil {
  1289  		t.Fatalf("err: %s", err)
  1290  	}
  1291  	if err := os.Chdir(varFileDir); err != nil {
  1292  		t.Fatalf("err: %s", err)
  1293  	}
  1294  	defer os.Chdir(cwd)
  1295  
  1296  	p := testProvider()
  1297  	ui := new(cli.MockUi)
  1298  	c := &ApplyCommand{
  1299  		Meta: Meta{
  1300  			ContextOpts: testCtxConfig(p),
  1301  			Ui:          ui,
  1302  		},
  1303  	}
  1304  
  1305  	actual := ""
  1306  	p.DiffFn = func(
  1307  		info *terraform.InstanceInfo,
  1308  		s *terraform.InstanceState,
  1309  		c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
  1310  		if v, ok := c.Config["value"]; ok {
  1311  			actual = v.(string)
  1312  		}
  1313  
  1314  		return &terraform.InstanceDiff{}, nil
  1315  	}
  1316  
  1317  	args := []string{
  1318  		"-state", statePath,
  1319  		testFixturePath("apply-vars"),
  1320  	}
  1321  	if code := c.Run(args); code != 0 {
  1322  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
  1323  	}
  1324  
  1325  	if actual != "bar" {
  1326  		t.Fatal("didn't work")
  1327  	}
  1328  }
  1329  
  1330  func TestApply_backup(t *testing.T) {
  1331  	originalState := &terraform.State{
  1332  		Modules: []*terraform.ModuleState{
  1333  			&terraform.ModuleState{
  1334  				Path: []string{"root"},
  1335  				Resources: map[string]*terraform.ResourceState{
  1336  					"test_instance.foo": &terraform.ResourceState{
  1337  						Type: "test_instance",
  1338  						Primary: &terraform.InstanceState{
  1339  							ID: "bar",
  1340  						},
  1341  					},
  1342  				},
  1343  			},
  1344  		},
  1345  	}
  1346  	originalState.Init()
  1347  
  1348  	statePath := testStateFile(t, originalState)
  1349  	backupPath := testTempFile(t)
  1350  
  1351  	p := testProvider()
  1352  	p.DiffReturn = &terraform.InstanceDiff{
  1353  		Attributes: map[string]*terraform.ResourceAttrDiff{
  1354  			"ami": &terraform.ResourceAttrDiff{
  1355  				New: "bar",
  1356  			},
  1357  		},
  1358  	}
  1359  
  1360  	ui := new(cli.MockUi)
  1361  	c := &ApplyCommand{
  1362  		Meta: Meta{
  1363  			ContextOpts: testCtxConfig(p),
  1364  			Ui:          ui,
  1365  		},
  1366  	}
  1367  
  1368  	// Run the apply command pointing to our existing state
  1369  	args := []string{
  1370  		"-state", statePath,
  1371  		"-backup", backupPath,
  1372  		testFixturePath("apply"),
  1373  	}
  1374  	if code := c.Run(args); code != 0 {
  1375  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
  1376  	}
  1377  
  1378  	// Verify a new state exists
  1379  	if _, err := os.Stat(statePath); err != nil {
  1380  		t.Fatalf("err: %s", err)
  1381  	}
  1382  
  1383  	f, err := os.Open(statePath)
  1384  	if err != nil {
  1385  		t.Fatalf("err: %s", err)
  1386  	}
  1387  	defer f.Close()
  1388  
  1389  	state, err := terraform.ReadState(f)
  1390  	if err != nil {
  1391  		t.Fatalf("err: %s", err)
  1392  	}
  1393  	if state == nil {
  1394  		t.Fatal("state should not be nil")
  1395  	}
  1396  
  1397  	// Should have a backup file
  1398  	f, err = os.Open(backupPath)
  1399  	if err != nil {
  1400  		t.Fatalf("err: %s", err)
  1401  	}
  1402  
  1403  	backupState, err := terraform.ReadState(f)
  1404  	f.Close()
  1405  	if err != nil {
  1406  		t.Fatalf("err: %s", err)
  1407  	}
  1408  
  1409  	actual := backupState.RootModule().Resources["test_instance.foo"]
  1410  	expected := originalState.RootModule().Resources["test_instance.foo"]
  1411  	if !reflect.DeepEqual(actual, expected) {
  1412  		t.Fatalf("bad: %#v %#v", actual, expected)
  1413  	}
  1414  }
  1415  
  1416  func TestApply_disableBackup(t *testing.T) {
  1417  	originalState := testState()
  1418  	statePath := testStateFile(t, originalState)
  1419  
  1420  	p := testProvider()
  1421  	p.DiffReturn = &terraform.InstanceDiff{
  1422  		Attributes: map[string]*terraform.ResourceAttrDiff{
  1423  			"ami": &terraform.ResourceAttrDiff{
  1424  				New: "bar",
  1425  			},
  1426  		},
  1427  	}
  1428  
  1429  	ui := new(cli.MockUi)
  1430  	c := &ApplyCommand{
  1431  		Meta: Meta{
  1432  			ContextOpts: testCtxConfig(p),
  1433  			Ui:          ui,
  1434  		},
  1435  	}
  1436  
  1437  	// Run the apply command pointing to our existing state
  1438  	args := []string{
  1439  		"-state", statePath,
  1440  		"-backup", "-",
  1441  		testFixturePath("apply"),
  1442  	}
  1443  	if code := c.Run(args); code != 0 {
  1444  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
  1445  	}
  1446  
  1447  	// Verify that the provider was called with the existing state
  1448  	actual := strings.TrimSpace(p.DiffState.String())
  1449  	expected := strings.TrimSpace(testApplyDisableBackupStr)
  1450  	if actual != expected {
  1451  		t.Fatalf("bad:\n\n%s", actual)
  1452  	}
  1453  
  1454  	actual = strings.TrimSpace(p.ApplyState.String())
  1455  	expected = strings.TrimSpace(testApplyDisableBackupStateStr)
  1456  	if actual != expected {
  1457  		t.Fatalf("bad:\n\n%s", actual)
  1458  	}
  1459  
  1460  	// Verify a new state exists
  1461  	if _, err := os.Stat(statePath); err != nil {
  1462  		t.Fatalf("err: %s", err)
  1463  	}
  1464  
  1465  	f, err := os.Open(statePath)
  1466  	if err != nil {
  1467  		t.Fatalf("err: %s", err)
  1468  	}
  1469  	defer f.Close()
  1470  
  1471  	state, err := terraform.ReadState(f)
  1472  	if err != nil {
  1473  		t.Fatalf("err: %s", err)
  1474  	}
  1475  	if state == nil {
  1476  		t.Fatal("state should not be nil")
  1477  	}
  1478  
  1479  	// Ensure there is no backup
  1480  	_, err = os.Stat(statePath + DefaultBackupExtension)
  1481  	if err == nil || !os.IsNotExist(err) {
  1482  		t.Fatalf("backup should not exist")
  1483  	}
  1484  
  1485  	// Ensure there is no literal "-"
  1486  	_, err = os.Stat("-")
  1487  	if err == nil || !os.IsNotExist(err) {
  1488  		t.Fatalf("backup should not exist")
  1489  	}
  1490  }
  1491  
  1492  // -state-out wasn't taking effect when a plan is supplied. GH-7264
  1493  func TestApply_stateOutWithPlan(t *testing.T) {
  1494  	p := testProvider()
  1495  	ui := new(cli.MockUi)
  1496  
  1497  	tmpDir := testTempDir(t)
  1498  	defer os.RemoveAll(tmpDir)
  1499  
  1500  	statePath := filepath.Join(tmpDir, "state.tfstate")
  1501  	planPath := filepath.Join(tmpDir, "terraform.tfplan")
  1502  
  1503  	args := []string{
  1504  		"-state", statePath,
  1505  		"-out", planPath,
  1506  		testFixturePath("plan"),
  1507  	}
  1508  
  1509  	// Run plan first to get a current plan file
  1510  	pc := &PlanCommand{
  1511  		Meta: Meta{
  1512  			ContextOpts: testCtxConfig(p),
  1513  			Ui:          ui,
  1514  		},
  1515  	}
  1516  	if code := pc.Run(args); code != 0 {
  1517  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
  1518  	}
  1519  
  1520  	// now run apply with the generated plan
  1521  	stateOutPath := filepath.Join(tmpDir, "state-new.tfstate")
  1522  
  1523  	args = []string{
  1524  		"-state", statePath,
  1525  		"-state-out", stateOutPath,
  1526  		planPath,
  1527  	}
  1528  
  1529  	ac := &ApplyCommand{
  1530  		Meta: Meta{
  1531  			ContextOpts: testCtxConfig(p),
  1532  			Ui:          ui,
  1533  		},
  1534  	}
  1535  	if code := ac.Run(args); code != 0 {
  1536  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
  1537  	}
  1538  
  1539  	// now make sure we wrote out our new state
  1540  	if _, err := os.Stat(stateOutPath); err != nil {
  1541  		t.Fatalf("missing new state file: %s", err)
  1542  	}
  1543  }
  1544  
  1545  func testHttpServer(t *testing.T) net.Listener {
  1546  	ln, err := net.Listen("tcp", "127.0.0.1:0")
  1547  	if err != nil {
  1548  		t.Fatalf("err: %s", err)
  1549  	}
  1550  
  1551  	mux := http.NewServeMux()
  1552  	mux.HandleFunc("/header", testHttpHandlerHeader)
  1553  
  1554  	var server http.Server
  1555  	server.Handler = mux
  1556  	go server.Serve(ln)
  1557  
  1558  	return ln
  1559  }
  1560  
  1561  func testHttpHandlerHeader(w http.ResponseWriter, r *http.Request) {
  1562  	var url url.URL
  1563  	url.Scheme = "file"
  1564  	url.Path = filepath.ToSlash(testFixturePath("init"))
  1565  
  1566  	w.Header().Add("X-Terraform-Get", url.String())
  1567  	w.WriteHeader(200)
  1568  }
  1569  
  1570  const applyVarFile = `
  1571  foo = "bar"
  1572  `
  1573  
  1574  const applyVarFileJSON = `
  1575  { "foo": "bar" }
  1576  `
  1577  
  1578  const testApplyDisableBackupStr = `
  1579  ID = bar
  1580  Tainted = false
  1581  `
  1582  
  1583  const testApplyDisableBackupStateStr = `
  1584  ID = bar
  1585  Tainted = false
  1586  `
  1587  
  1588  const testApplyStateStr = `
  1589  ID = bar
  1590  Tainted = false
  1591  `
  1592  
  1593  const testApplyStateDiffStr = `
  1594  ID = bar
  1595  Tainted = false
  1596  `