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