github.com/richardmarshall/terraform@v0.9.5-0.20170429023105-15704cc6ee35/command/apply_test.go (about)

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