github.com/i0n/terraform@v0.4.3-0.20150506151324-010a39a58ec1/command/apply_test.go (about)

     1  package command
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"net"
     8  	"net/http"
     9  	"net/url"
    10  	"os"
    11  	"path/filepath"
    12  	"reflect"
    13  	"strings"
    14  	"sync"
    15  	"testing"
    16  	"time"
    17  
    18  	"github.com/hashicorp/terraform/terraform"
    19  	"github.com/mitchellh/cli"
    20  )
    21  
    22  func TestApply(t *testing.T) {
    23  	statePath := testTempFile(t)
    24  
    25  	p := testProvider()
    26  	ui := new(cli.MockUi)
    27  	c := &ApplyCommand{
    28  		Meta: Meta{
    29  			ContextOpts: testCtxConfig(p),
    30  			Ui:          ui,
    31  		},
    32  	}
    33  
    34  	args := []string{
    35  		"-state", statePath,
    36  		testFixturePath("apply"),
    37  	}
    38  	if code := c.Run(args); code != 0 {
    39  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
    40  	}
    41  
    42  	if _, err := os.Stat(statePath); err != nil {
    43  		t.Fatalf("err: %s", err)
    44  	}
    45  
    46  	f, err := os.Open(statePath)
    47  	if err != nil {
    48  		t.Fatalf("err: %s", err)
    49  	}
    50  	defer f.Close()
    51  
    52  	state, err := terraform.ReadState(f)
    53  	if err != nil {
    54  		t.Fatalf("err: %s", err)
    55  	}
    56  	if state == nil {
    57  		t.Fatal("state should not be nil")
    58  	}
    59  }
    60  
    61  func TestApply_configInvalid(t *testing.T) {
    62  	p := testProvider()
    63  	ui := new(cli.MockUi)
    64  	c := &ApplyCommand{
    65  		Meta: Meta{
    66  			ContextOpts: testCtxConfig(p),
    67  			Ui:          ui,
    68  		},
    69  	}
    70  
    71  	args := []string{
    72  		"-state", testTempFile(t),
    73  		testFixturePath("apply-config-invalid"),
    74  	}
    75  	if code := c.Run(args); code != 1 {
    76  		t.Fatalf("bad: \n%s", ui.OutputWriter.String())
    77  	}
    78  }
    79  
    80  func TestApply_defaultState(t *testing.T) {
    81  	td, err := ioutil.TempDir("", "tf")
    82  	if err != nil {
    83  		t.Fatalf("err: %s", err)
    84  	}
    85  	statePath := filepath.Join(td, DefaultStateFilename)
    86  
    87  	// Change to the temporary directory
    88  	cwd, err := os.Getwd()
    89  	if err != nil {
    90  		t.Fatalf("err: %s", err)
    91  	}
    92  	if err := os.Chdir(filepath.Dir(statePath)); err != nil {
    93  		t.Fatalf("err: %s", err)
    94  	}
    95  	defer os.Chdir(cwd)
    96  
    97  	p := testProvider()
    98  	ui := new(cli.MockUi)
    99  	c := &ApplyCommand{
   100  		Meta: Meta{
   101  			ContextOpts: testCtxConfig(p),
   102  			Ui:          ui,
   103  		},
   104  	}
   105  
   106  	args := []string{
   107  		testFixturePath("apply"),
   108  	}
   109  	if code := c.Run(args); code != 0 {
   110  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   111  	}
   112  
   113  	if _, err := os.Stat(statePath); err != nil {
   114  		t.Fatalf("err: %s", err)
   115  	}
   116  
   117  	f, err := os.Open(statePath)
   118  	if err != nil {
   119  		t.Fatalf("err: %s", err)
   120  	}
   121  	defer f.Close()
   122  
   123  	state, err := terraform.ReadState(f)
   124  	if err != nil {
   125  		t.Fatalf("err: %s", err)
   126  	}
   127  	if state == nil {
   128  		t.Fatal("state should not be nil")
   129  	}
   130  }
   131  
   132  func TestApply_error(t *testing.T) {
   133  	statePath := testTempFile(t)
   134  
   135  	p := testProvider()
   136  	ui := new(cli.MockUi)
   137  	c := &ApplyCommand{
   138  		Meta: Meta{
   139  			ContextOpts: testCtxConfig(p),
   140  			Ui:          ui,
   141  		},
   142  	}
   143  
   144  	var lock sync.Mutex
   145  	errored := false
   146  	p.ApplyFn = func(
   147  		info *terraform.InstanceInfo,
   148  		s *terraform.InstanceState,
   149  		d *terraform.InstanceDiff) (*terraform.InstanceState, error) {
   150  		lock.Lock()
   151  		defer lock.Unlock()
   152  
   153  		if !errored {
   154  			errored = true
   155  			return nil, fmt.Errorf("error")
   156  		}
   157  
   158  		return &terraform.InstanceState{ID: "foo"}, nil
   159  	}
   160  	p.DiffFn = func(
   161  		*terraform.InstanceInfo,
   162  		*terraform.InstanceState,
   163  		*terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
   164  		return &terraform.InstanceDiff{
   165  			Attributes: map[string]*terraform.ResourceAttrDiff{
   166  				"ami": &terraform.ResourceAttrDiff{
   167  					New: "bar",
   168  				},
   169  			},
   170  		}, nil
   171  	}
   172  
   173  	args := []string{
   174  		"-state", statePath,
   175  		testFixturePath("apply-error"),
   176  	}
   177  	if code := c.Run(args); code != 1 {
   178  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   179  	}
   180  
   181  	if _, err := os.Stat(statePath); err != nil {
   182  		t.Fatalf("err: %s", err)
   183  	}
   184  
   185  	f, err := os.Open(statePath)
   186  	if err != nil {
   187  		t.Fatalf("err: %s", err)
   188  	}
   189  	defer f.Close()
   190  
   191  	state, err := terraform.ReadState(f)
   192  	if err != nil {
   193  		t.Fatalf("err: %s", err)
   194  	}
   195  	if state == nil {
   196  		t.Fatal("state should not be nil")
   197  	}
   198  	if len(state.RootModule().Resources) == 0 {
   199  		t.Fatal("no resources in state")
   200  	}
   201  }
   202  
   203  func TestApply_init(t *testing.T) {
   204  	// Change to the temporary directory
   205  	cwd, err := os.Getwd()
   206  	if err != nil {
   207  		t.Fatalf("err: %s", err)
   208  	}
   209  	dir := tempDir(t)
   210  	if err := os.MkdirAll(dir, 0755); err != nil {
   211  		t.Fatalf("err: %s", err)
   212  	}
   213  	if err := os.Chdir(dir); err != nil {
   214  		t.Fatalf("err: %s", err)
   215  	}
   216  	defer os.Chdir(cwd)
   217  
   218  	// Create the test fixtures
   219  	statePath := testTempFile(t)
   220  	ln := testHttpServer(t)
   221  	defer ln.Close()
   222  
   223  	// Initialize the command
   224  	p := testProvider()
   225  	ui := new(cli.MockUi)
   226  	c := &ApplyCommand{
   227  		Meta: Meta{
   228  			ContextOpts: testCtxConfig(p),
   229  			Ui:          ui,
   230  		},
   231  	}
   232  
   233  	// Build the URL to the init
   234  	var u url.URL
   235  	u.Scheme = "http"
   236  	u.Host = ln.Addr().String()
   237  	u.Path = "/header"
   238  
   239  	args := []string{
   240  		"-state", statePath,
   241  		u.String(),
   242  	}
   243  	if code := c.Run(args); code != 0 {
   244  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   245  	}
   246  
   247  	if _, err := os.Stat("hello.tf"); err != nil {
   248  		t.Fatalf("err: %s", err)
   249  	}
   250  
   251  	if _, err := os.Stat(statePath); err != nil {
   252  		t.Fatalf("err: %s", err)
   253  	}
   254  
   255  	f, err := os.Open(statePath)
   256  	if err != nil {
   257  		t.Fatalf("err: %s", err)
   258  	}
   259  	defer f.Close()
   260  
   261  	state, err := terraform.ReadState(f)
   262  	if err != nil {
   263  		t.Fatalf("err: %s", err)
   264  	}
   265  	if state == nil {
   266  		t.Fatal("state should not be nil")
   267  	}
   268  }
   269  
   270  func TestApply_input(t *testing.T) {
   271  	// Disable test mode so input would be asked
   272  	test = false
   273  	defer func() { test = true }()
   274  
   275  	// Set some default reader/writers for the inputs
   276  	defaultInputReader = bytes.NewBufferString("foo\n")
   277  	defaultInputWriter = new(bytes.Buffer)
   278  
   279  	statePath := testTempFile(t)
   280  
   281  	p := testProvider()
   282  	ui := new(cli.MockUi)
   283  	c := &ApplyCommand{
   284  		Meta: Meta{
   285  			ContextOpts: testCtxConfig(p),
   286  			Ui:          ui,
   287  		},
   288  	}
   289  
   290  	args := []string{
   291  		"-state", statePath,
   292  		testFixturePath("apply-input"),
   293  	}
   294  	if code := c.Run(args); code != 0 {
   295  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   296  	}
   297  
   298  	if !p.InputCalled {
   299  		t.Fatal("input should be called")
   300  	}
   301  }
   302  
   303  func TestApply_noArgs(t *testing.T) {
   304  	cwd, err := os.Getwd()
   305  	if err != nil {
   306  		t.Fatalf("err: %s", err)
   307  	}
   308  	if err := os.Chdir(testFixturePath("plan")); err != nil {
   309  		t.Fatalf("err: %s", err)
   310  	}
   311  	defer os.Chdir(cwd)
   312  
   313  	statePath := testTempFile(t)
   314  
   315  	p := testProvider()
   316  	ui := new(cli.MockUi)
   317  	c := &ApplyCommand{
   318  		Meta: Meta{
   319  			ContextOpts: testCtxConfig(p),
   320  			Ui:          ui,
   321  		},
   322  	}
   323  
   324  	args := []string{
   325  		"-state", statePath,
   326  	}
   327  	if code := c.Run(args); code != 0 {
   328  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   329  	}
   330  
   331  	if _, err := os.Stat(statePath); err != nil {
   332  		t.Fatalf("err: %s", err)
   333  	}
   334  
   335  	f, err := os.Open(statePath)
   336  	if err != nil {
   337  		t.Fatalf("err: %s", err)
   338  	}
   339  	defer f.Close()
   340  
   341  	state, err := terraform.ReadState(f)
   342  	if err != nil {
   343  		t.Fatalf("err: %s", err)
   344  	}
   345  	if state == nil {
   346  		t.Fatal("state should not be nil")
   347  	}
   348  }
   349  
   350  func TestApply_plan(t *testing.T) {
   351  	// Disable test mode so input would be asked
   352  	test = false
   353  	defer func() { test = true }()
   354  
   355  	// Set some default reader/writers for the inputs
   356  	defaultInputReader = new(bytes.Buffer)
   357  	defaultInputWriter = new(bytes.Buffer)
   358  
   359  	planPath := testPlanFile(t, &terraform.Plan{
   360  		Module: testModule(t, "apply"),
   361  	})
   362  	statePath := testTempFile(t)
   363  
   364  	p := testProvider()
   365  	ui := new(cli.MockUi)
   366  	c := &ApplyCommand{
   367  		Meta: Meta{
   368  			ContextOpts: testCtxConfig(p),
   369  			Ui:          ui,
   370  		},
   371  	}
   372  
   373  	args := []string{
   374  		"-state", statePath,
   375  		planPath,
   376  	}
   377  	if code := c.Run(args); code != 0 {
   378  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   379  	}
   380  
   381  	if p.InputCalled {
   382  		t.Fatalf("input should not be called for plans")
   383  	}
   384  
   385  	if _, err := os.Stat(statePath); err != nil {
   386  		t.Fatalf("err: %s", err)
   387  	}
   388  
   389  	f, err := os.Open(statePath)
   390  	if err != nil {
   391  		t.Fatalf("err: %s", err)
   392  	}
   393  	defer f.Close()
   394  
   395  	state, err := terraform.ReadState(f)
   396  	if err != nil {
   397  		t.Fatalf("err: %s", err)
   398  	}
   399  	if state == nil {
   400  		t.Fatal("state should not be nil")
   401  	}
   402  }
   403  
   404  func TestApply_plan_remoteState(t *testing.T) {
   405  	// Disable test mode so input would be asked
   406  	test = false
   407  	defer func() { test = true }()
   408  	tmp, cwd := testCwd(t)
   409  	defer testFixCwd(t, tmp, cwd)
   410  	remoteStatePath := filepath.Join(tmp, DefaultDataDir, DefaultStateFilename)
   411  	if err := os.MkdirAll(filepath.Dir(remoteStatePath), 0755); err != nil {
   412  		t.Fatalf("err: %s", err)
   413  	}
   414  
   415  	// Set some default reader/writers for the inputs
   416  	defaultInputReader = new(bytes.Buffer)
   417  	defaultInputWriter = new(bytes.Buffer)
   418  
   419  	// Create a remote state
   420  	state := testState()
   421  	conf, srv := testRemoteState(t, state, 200)
   422  	defer srv.Close()
   423  	state.Remote = conf
   424  
   425  	planPath := testPlanFile(t, &terraform.Plan{
   426  		Module: testModule(t, "apply"),
   427  		State:  state,
   428  	})
   429  
   430  	p := testProvider()
   431  	ui := new(cli.MockUi)
   432  	c := &ApplyCommand{
   433  		Meta: Meta{
   434  			ContextOpts: testCtxConfig(p),
   435  			Ui:          ui,
   436  		},
   437  	}
   438  
   439  	args := []string{
   440  		planPath,
   441  	}
   442  	if code := c.Run(args); code != 0 {
   443  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   444  	}
   445  
   446  	if p.InputCalled {
   447  		t.Fatalf("input should not be called for plans")
   448  	}
   449  
   450  	// State file should be not be installed
   451  	if _, err := os.Stat(filepath.Join(tmp, DefaultStateFilename)); err == nil {
   452  		t.Fatalf("State path should not exist")
   453  	}
   454  
   455  	// Check for remote state
   456  	if _, err := os.Stat(remoteStatePath); err != nil {
   457  		t.Fatalf("missing remote state: %s", err)
   458  	}
   459  }
   460  
   461  func TestApply_planWithVarFile(t *testing.T) {
   462  	varFileDir := testTempDir(t)
   463  	varFilePath := filepath.Join(varFileDir, "terraform.tfvars")
   464  	if err := ioutil.WriteFile(varFilePath, []byte(applyVarFile), 0644); err != nil {
   465  		t.Fatalf("err: %s", err)
   466  	}
   467  
   468  	planPath := testPlanFile(t, &terraform.Plan{
   469  		Module: testModule(t, "apply"),
   470  	})
   471  	statePath := testTempFile(t)
   472  
   473  	cwd, err := os.Getwd()
   474  	if err != nil {
   475  		t.Fatalf("err: %s", err)
   476  	}
   477  	if err := os.Chdir(varFileDir); err != nil {
   478  		t.Fatalf("err: %s", err)
   479  	}
   480  	defer os.Chdir(cwd)
   481  
   482  	p := testProvider()
   483  	ui := new(cli.MockUi)
   484  	c := &ApplyCommand{
   485  		Meta: Meta{
   486  			ContextOpts: testCtxConfig(p),
   487  			Ui:          ui,
   488  		},
   489  	}
   490  
   491  	args := []string{
   492  		"-state", statePath,
   493  		planPath,
   494  	}
   495  	if code := c.Run(args); code != 0 {
   496  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   497  	}
   498  
   499  	if _, err := os.Stat(statePath); err != nil {
   500  		t.Fatalf("err: %s", err)
   501  	}
   502  
   503  	f, err := os.Open(statePath)
   504  	if err != nil {
   505  		t.Fatalf("err: %s", err)
   506  	}
   507  	defer f.Close()
   508  
   509  	state, err := terraform.ReadState(f)
   510  	if err != nil {
   511  		t.Fatalf("err: %s", err)
   512  	}
   513  	if state == nil {
   514  		t.Fatal("state should not be nil")
   515  	}
   516  }
   517  
   518  func TestApply_planVars(t *testing.T) {
   519  	planPath := testPlanFile(t, &terraform.Plan{
   520  		Module: testModule(t, "apply"),
   521  	})
   522  	statePath := testTempFile(t)
   523  
   524  	p := testProvider()
   525  	ui := new(cli.MockUi)
   526  	c := &ApplyCommand{
   527  		Meta: Meta{
   528  			ContextOpts: testCtxConfig(p),
   529  			Ui:          ui,
   530  		},
   531  	}
   532  
   533  	args := []string{
   534  		"-state", statePath,
   535  		"-var", "foo=bar",
   536  		planPath,
   537  	}
   538  	if code := c.Run(args); code == 0 {
   539  		t.Fatal("should've failed")
   540  	}
   541  }
   542  
   543  func TestApply_refresh(t *testing.T) {
   544  	originalState := &terraform.State{
   545  		Modules: []*terraform.ModuleState{
   546  			&terraform.ModuleState{
   547  				Path: []string{"root"},
   548  				Resources: map[string]*terraform.ResourceState{
   549  					"test_instance.foo": &terraform.ResourceState{
   550  						Type: "test_instance",
   551  						Primary: &terraform.InstanceState{
   552  							ID: "bar",
   553  						},
   554  					},
   555  				},
   556  			},
   557  		},
   558  	}
   559  
   560  	statePath := testStateFile(t, originalState)
   561  
   562  	p := testProvider()
   563  	ui := new(cli.MockUi)
   564  	c := &ApplyCommand{
   565  		Meta: Meta{
   566  			ContextOpts: testCtxConfig(p),
   567  			Ui:          ui,
   568  		},
   569  	}
   570  
   571  	args := []string{
   572  		"-state", statePath,
   573  		testFixturePath("apply"),
   574  	}
   575  	if code := c.Run(args); code != 0 {
   576  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   577  	}
   578  
   579  	if !p.RefreshCalled {
   580  		t.Fatal("should call refresh")
   581  	}
   582  
   583  	if _, err := os.Stat(statePath); err != nil {
   584  		t.Fatalf("err: %s", err)
   585  	}
   586  
   587  	f, err := os.Open(statePath)
   588  	if err != nil {
   589  		t.Fatalf("err: %s", err)
   590  	}
   591  	defer f.Close()
   592  
   593  	state, err := terraform.ReadState(f)
   594  	if err != nil {
   595  		t.Fatalf("err: %s", err)
   596  	}
   597  	if state == nil {
   598  		t.Fatal("state should not be nil")
   599  	}
   600  
   601  	// Should have a backup file
   602  	f, err = os.Open(statePath + DefaultBackupExtention)
   603  	if err != nil {
   604  		t.Fatalf("err: %s", err)
   605  	}
   606  
   607  	backupState, err := terraform.ReadState(f)
   608  	f.Close()
   609  	if err != nil {
   610  		t.Fatalf("err: %s", err)
   611  	}
   612  
   613  	actualStr := strings.TrimSpace(backupState.String())
   614  	expectedStr := strings.TrimSpace(originalState.String())
   615  	if actualStr != expectedStr {
   616  		t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr)
   617  	}
   618  }
   619  
   620  func TestApply_shutdown(t *testing.T) {
   621  	stopped := false
   622  	stopCh := make(chan struct{})
   623  	stopReplyCh := make(chan struct{})
   624  
   625  	statePath := testTempFile(t)
   626  
   627  	p := testProvider()
   628  	shutdownCh := make(chan struct{})
   629  	ui := new(cli.MockUi)
   630  	c := &ApplyCommand{
   631  		Meta: Meta{
   632  			ContextOpts: testCtxConfig(p),
   633  			Ui:          ui,
   634  		},
   635  
   636  		ShutdownCh: shutdownCh,
   637  	}
   638  
   639  	p.DiffFn = func(
   640  		*terraform.InstanceInfo,
   641  		*terraform.InstanceState,
   642  		*terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
   643  		return &terraform.InstanceDiff{
   644  			Attributes: map[string]*terraform.ResourceAttrDiff{
   645  				"ami": &terraform.ResourceAttrDiff{
   646  					New: "bar",
   647  				},
   648  			},
   649  		}, nil
   650  	}
   651  	p.ApplyFn = func(
   652  		*terraform.InstanceInfo,
   653  		*terraform.InstanceState,
   654  		*terraform.InstanceDiff) (*terraform.InstanceState, error) {
   655  		if !stopped {
   656  			stopped = true
   657  			close(stopCh)
   658  			<-stopReplyCh
   659  		}
   660  
   661  		return &terraform.InstanceState{
   662  			ID: "foo",
   663  			Attributes: map[string]string{
   664  				"ami": "2",
   665  			},
   666  		}, nil
   667  	}
   668  
   669  	go func() {
   670  		<-stopCh
   671  		shutdownCh <- struct{}{}
   672  
   673  		// This is really dirty, but we have no other way to assure that
   674  		// tf.Stop() has been called. This doesn't assure it either, but
   675  		// it makes it much more certain.
   676  		time.Sleep(50 * time.Millisecond)
   677  
   678  		close(stopReplyCh)
   679  	}()
   680  
   681  	args := []string{
   682  		"-state", statePath,
   683  		testFixturePath("apply-shutdown"),
   684  	}
   685  	if code := c.Run(args); code != 0 {
   686  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   687  	}
   688  
   689  	if _, err := os.Stat(statePath); err != nil {
   690  		t.Fatalf("err: %s", err)
   691  	}
   692  
   693  	f, err := os.Open(statePath)
   694  	if err != nil {
   695  		t.Fatalf("err: %s", err)
   696  	}
   697  	defer f.Close()
   698  
   699  	state, err := terraform.ReadState(f)
   700  	if err != nil {
   701  		t.Fatalf("err: %s", err)
   702  	}
   703  	if state == nil {
   704  		t.Fatal("state should not be nil")
   705  	}
   706  
   707  	if len(state.RootModule().Resources) != 1 {
   708  		t.Fatalf("bad: %d", len(state.RootModule().Resources))
   709  	}
   710  }
   711  
   712  func TestApply_state(t *testing.T) {
   713  	originalState := &terraform.State{
   714  		Modules: []*terraform.ModuleState{
   715  			&terraform.ModuleState{
   716  				Path: []string{"root"},
   717  				Resources: map[string]*terraform.ResourceState{
   718  					"test_instance.foo": &terraform.ResourceState{
   719  						Type: "test_instance",
   720  						Primary: &terraform.InstanceState{
   721  							ID: "bar",
   722  						},
   723  					},
   724  				},
   725  			},
   726  		},
   727  	}
   728  
   729  	statePath := testStateFile(t, originalState)
   730  
   731  	p := testProvider()
   732  	p.DiffReturn = &terraform.InstanceDiff{
   733  		Attributes: map[string]*terraform.ResourceAttrDiff{
   734  			"ami": &terraform.ResourceAttrDiff{
   735  				New: "bar",
   736  			},
   737  		},
   738  	}
   739  
   740  	ui := new(cli.MockUi)
   741  	c := &ApplyCommand{
   742  		Meta: Meta{
   743  			ContextOpts: testCtxConfig(p),
   744  			Ui:          ui,
   745  		},
   746  	}
   747  
   748  	// Run the apply command pointing to our existing state
   749  	args := []string{
   750  		"-state", statePath,
   751  		testFixturePath("apply"),
   752  	}
   753  	if code := c.Run(args); code != 0 {
   754  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   755  	}
   756  
   757  	// Verify that the provider was called with the existing state
   758  	actual := strings.TrimSpace(p.DiffState.String())
   759  	expected := strings.TrimSpace(testApplyStateDiffStr)
   760  	if actual != expected {
   761  		t.Fatalf("bad:\n\n%s", actual)
   762  	}
   763  
   764  	actual = strings.TrimSpace(p.ApplyState.String())
   765  	expected = strings.TrimSpace(testApplyStateStr)
   766  	if actual != expected {
   767  		t.Fatalf("bad:\n\n%s", actual)
   768  	}
   769  
   770  	// Verify a new state exists
   771  	if _, err := os.Stat(statePath); err != nil {
   772  		t.Fatalf("err: %s", err)
   773  	}
   774  
   775  	f, err := os.Open(statePath)
   776  	if err != nil {
   777  		t.Fatalf("err: %s", err)
   778  	}
   779  	defer f.Close()
   780  
   781  	state, err := terraform.ReadState(f)
   782  	if err != nil {
   783  		t.Fatalf("err: %s", err)
   784  	}
   785  	if state == nil {
   786  		t.Fatal("state should not be nil")
   787  	}
   788  
   789  	// Should have a backup file
   790  	f, err = os.Open(statePath + DefaultBackupExtention)
   791  	if err != nil {
   792  		t.Fatalf("err: %s", err)
   793  	}
   794  
   795  	backupState, err := terraform.ReadState(f)
   796  	f.Close()
   797  	if err != nil {
   798  		t.Fatalf("err: %s", err)
   799  	}
   800  
   801  	// nil out the ConnInfo since that should not be restored
   802  	originalState.RootModule().Resources["test_instance.foo"].Primary.Ephemeral.ConnInfo = nil
   803  
   804  	actualStr := strings.TrimSpace(backupState.String())
   805  	expectedStr := strings.TrimSpace(originalState.String())
   806  	if actualStr != expectedStr {
   807  		t.Fatalf("bad:\n\n%s\n\n%s", actualStr, expectedStr)
   808  	}
   809  }
   810  
   811  func TestApply_stateNoExist(t *testing.T) {
   812  	p := testProvider()
   813  	ui := new(cli.MockUi)
   814  	c := &ApplyCommand{
   815  		Meta: Meta{
   816  			ContextOpts: testCtxConfig(p),
   817  			Ui:          ui,
   818  		},
   819  	}
   820  
   821  	args := []string{
   822  		"idontexist.tfstate",
   823  		testFixturePath("apply"),
   824  	}
   825  	if code := c.Run(args); code != 1 {
   826  		t.Fatalf("bad: \n%s", ui.OutputWriter.String())
   827  	}
   828  }
   829  
   830  func TestApply_vars(t *testing.T) {
   831  	statePath := testTempFile(t)
   832  
   833  	p := testProvider()
   834  	ui := new(cli.MockUi)
   835  	c := &ApplyCommand{
   836  		Meta: Meta{
   837  			ContextOpts: testCtxConfig(p),
   838  			Ui:          ui,
   839  		},
   840  	}
   841  
   842  	actual := ""
   843  	p.DiffFn = func(
   844  		info *terraform.InstanceInfo,
   845  		s *terraform.InstanceState,
   846  		c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
   847  		if v, ok := c.Config["value"]; ok {
   848  			actual = v.(string)
   849  		}
   850  
   851  		return &terraform.InstanceDiff{}, nil
   852  	}
   853  
   854  	args := []string{
   855  		"-var", "foo=bar",
   856  		"-state", statePath,
   857  		testFixturePath("apply-vars"),
   858  	}
   859  	if code := c.Run(args); code != 0 {
   860  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   861  	}
   862  
   863  	if actual != "bar" {
   864  		t.Fatal("didn't work")
   865  	}
   866  }
   867  
   868  func TestApply_varFile(t *testing.T) {
   869  	varFilePath := testTempFile(t)
   870  	if err := ioutil.WriteFile(varFilePath, []byte(applyVarFile), 0644); err != nil {
   871  		t.Fatalf("err: %s", err)
   872  	}
   873  
   874  	statePath := testTempFile(t)
   875  
   876  	p := testProvider()
   877  	ui := new(cli.MockUi)
   878  	c := &ApplyCommand{
   879  		Meta: Meta{
   880  			ContextOpts: testCtxConfig(p),
   881  			Ui:          ui,
   882  		},
   883  	}
   884  
   885  	actual := ""
   886  	p.DiffFn = func(
   887  		info *terraform.InstanceInfo,
   888  		s *terraform.InstanceState,
   889  		c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
   890  		if v, ok := c.Config["value"]; ok {
   891  			actual = v.(string)
   892  		}
   893  
   894  		return &terraform.InstanceDiff{}, nil
   895  	}
   896  
   897  	args := []string{
   898  		"-var-file", varFilePath,
   899  		"-state", statePath,
   900  		testFixturePath("apply-vars"),
   901  	}
   902  	if code := c.Run(args); code != 0 {
   903  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   904  	}
   905  
   906  	if actual != "bar" {
   907  		t.Fatal("didn't work")
   908  	}
   909  }
   910  
   911  func TestApply_varFileDefault(t *testing.T) {
   912  	varFileDir := testTempDir(t)
   913  	varFilePath := filepath.Join(varFileDir, "terraform.tfvars")
   914  	if err := ioutil.WriteFile(varFilePath, []byte(applyVarFile), 0644); err != nil {
   915  		t.Fatalf("err: %s", err)
   916  	}
   917  
   918  	statePath := testTempFile(t)
   919  
   920  	cwd, err := os.Getwd()
   921  	if err != nil {
   922  		t.Fatalf("err: %s", err)
   923  	}
   924  	if err := os.Chdir(varFileDir); err != nil {
   925  		t.Fatalf("err: %s", err)
   926  	}
   927  	defer os.Chdir(cwd)
   928  
   929  	p := testProvider()
   930  	ui := new(cli.MockUi)
   931  	c := &ApplyCommand{
   932  		Meta: Meta{
   933  			ContextOpts: testCtxConfig(p),
   934  			Ui:          ui,
   935  		},
   936  	}
   937  
   938  	actual := ""
   939  	p.DiffFn = func(
   940  		info *terraform.InstanceInfo,
   941  		s *terraform.InstanceState,
   942  		c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
   943  		if v, ok := c.Config["value"]; ok {
   944  			actual = v.(string)
   945  		}
   946  
   947  		return &terraform.InstanceDiff{}, nil
   948  	}
   949  
   950  	args := []string{
   951  		"-state", statePath,
   952  		testFixturePath("apply-vars"),
   953  	}
   954  	if code := c.Run(args); code != 0 {
   955  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   956  	}
   957  
   958  	if actual != "bar" {
   959  		t.Fatal("didn't work")
   960  	}
   961  }
   962  
   963  func TestApply_varFileDefaultJSON(t *testing.T) {
   964  	varFileDir := testTempDir(t)
   965  	varFilePath := filepath.Join(varFileDir, "terraform.tfvars.json")
   966  	if err := ioutil.WriteFile(varFilePath, []byte(applyVarFileJSON), 0644); err != nil {
   967  		t.Fatalf("err: %s", err)
   968  	}
   969  
   970  	statePath := testTempFile(t)
   971  
   972  	cwd, err := os.Getwd()
   973  	if err != nil {
   974  		t.Fatalf("err: %s", err)
   975  	}
   976  	if err := os.Chdir(varFileDir); err != nil {
   977  		t.Fatalf("err: %s", err)
   978  	}
   979  	defer os.Chdir(cwd)
   980  
   981  	p := testProvider()
   982  	ui := new(cli.MockUi)
   983  	c := &ApplyCommand{
   984  		Meta: Meta{
   985  			ContextOpts: testCtxConfig(p),
   986  			Ui:          ui,
   987  		},
   988  	}
   989  
   990  	actual := ""
   991  	p.DiffFn = func(
   992  		info *terraform.InstanceInfo,
   993  		s *terraform.InstanceState,
   994  		c *terraform.ResourceConfig) (*terraform.InstanceDiff, error) {
   995  		if v, ok := c.Config["value"]; ok {
   996  			actual = v.(string)
   997  		}
   998  
   999  		return &terraform.InstanceDiff{}, nil
  1000  	}
  1001  
  1002  	args := []string{
  1003  		"-state", statePath,
  1004  		testFixturePath("apply-vars"),
  1005  	}
  1006  	if code := c.Run(args); code != 0 {
  1007  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
  1008  	}
  1009  
  1010  	if actual != "bar" {
  1011  		t.Fatal("didn't work")
  1012  	}
  1013  }
  1014  
  1015  func TestApply_backup(t *testing.T) {
  1016  	originalState := &terraform.State{
  1017  		Modules: []*terraform.ModuleState{
  1018  			&terraform.ModuleState{
  1019  				Path: []string{"root"},
  1020  				Resources: map[string]*terraform.ResourceState{
  1021  					"test_instance.foo": &terraform.ResourceState{
  1022  						Type: "test_instance",
  1023  						Primary: &terraform.InstanceState{
  1024  							ID: "bar",
  1025  						},
  1026  					},
  1027  				},
  1028  			},
  1029  		},
  1030  	}
  1031  
  1032  	statePath := testStateFile(t, originalState)
  1033  	backupPath := testTempFile(t)
  1034  
  1035  	p := testProvider()
  1036  	p.DiffReturn = &terraform.InstanceDiff{
  1037  		Attributes: map[string]*terraform.ResourceAttrDiff{
  1038  			"ami": &terraform.ResourceAttrDiff{
  1039  				New: "bar",
  1040  			},
  1041  		},
  1042  	}
  1043  
  1044  	ui := new(cli.MockUi)
  1045  	c := &ApplyCommand{
  1046  		Meta: Meta{
  1047  			ContextOpts: testCtxConfig(p),
  1048  			Ui:          ui,
  1049  		},
  1050  	}
  1051  
  1052  	// Run the apply command pointing to our existing state
  1053  	args := []string{
  1054  		"-state", statePath,
  1055  		"-backup", backupPath,
  1056  		testFixturePath("apply"),
  1057  	}
  1058  	if code := c.Run(args); code != 0 {
  1059  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
  1060  	}
  1061  
  1062  	// Verify a new state exists
  1063  	if _, err := os.Stat(statePath); err != nil {
  1064  		t.Fatalf("err: %s", err)
  1065  	}
  1066  
  1067  	f, err := os.Open(statePath)
  1068  	if err != nil {
  1069  		t.Fatalf("err: %s", err)
  1070  	}
  1071  	defer f.Close()
  1072  
  1073  	state, err := terraform.ReadState(f)
  1074  	if err != nil {
  1075  		t.Fatalf("err: %s", err)
  1076  	}
  1077  	if state == nil {
  1078  		t.Fatal("state should not be nil")
  1079  	}
  1080  
  1081  	// Should have a backup file
  1082  	f, err = os.Open(backupPath)
  1083  	if err != nil {
  1084  		t.Fatalf("err: %s", err)
  1085  	}
  1086  
  1087  	backupState, err := terraform.ReadState(f)
  1088  	f.Close()
  1089  	if err != nil {
  1090  		t.Fatalf("err: %s", err)
  1091  	}
  1092  
  1093  	actual := backupState.RootModule().Resources["test_instance.foo"]
  1094  	expected := originalState.RootModule().Resources["test_instance.foo"]
  1095  	if !reflect.DeepEqual(actual, expected) {
  1096  		t.Fatalf("bad: %#v %#v", actual, expected)
  1097  	}
  1098  }
  1099  
  1100  func TestApply_disableBackup(t *testing.T) {
  1101  	originalState := testState()
  1102  	statePath := testStateFile(t, originalState)
  1103  
  1104  	p := testProvider()
  1105  	p.DiffReturn = &terraform.InstanceDiff{
  1106  		Attributes: map[string]*terraform.ResourceAttrDiff{
  1107  			"ami": &terraform.ResourceAttrDiff{
  1108  				New: "bar",
  1109  			},
  1110  		},
  1111  	}
  1112  
  1113  	ui := new(cli.MockUi)
  1114  	c := &ApplyCommand{
  1115  		Meta: Meta{
  1116  			ContextOpts: testCtxConfig(p),
  1117  			Ui:          ui,
  1118  		},
  1119  	}
  1120  
  1121  	// Run the apply command pointing to our existing state
  1122  	args := []string{
  1123  		"-state", statePath,
  1124  		"-backup", "-",
  1125  		testFixturePath("apply"),
  1126  	}
  1127  	if code := c.Run(args); code != 0 {
  1128  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
  1129  	}
  1130  
  1131  	// Verify that the provider was called with the existing state
  1132  	actual := strings.TrimSpace(p.DiffState.String())
  1133  	expected := strings.TrimSpace(testApplyDisableBackupStr)
  1134  	if actual != expected {
  1135  		t.Fatalf("bad:\n\n%s", actual)
  1136  	}
  1137  
  1138  	actual = strings.TrimSpace(p.ApplyState.String())
  1139  	expected = strings.TrimSpace(testApplyDisableBackupStateStr)
  1140  	if actual != expected {
  1141  		t.Fatalf("bad:\n\n%s", actual)
  1142  	}
  1143  
  1144  	// Verify a new state exists
  1145  	if _, err := os.Stat(statePath); err != nil {
  1146  		t.Fatalf("err: %s", err)
  1147  	}
  1148  
  1149  	f, err := os.Open(statePath)
  1150  	if err != nil {
  1151  		t.Fatalf("err: %s", err)
  1152  	}
  1153  	defer f.Close()
  1154  
  1155  	state, err := terraform.ReadState(f)
  1156  	if err != nil {
  1157  		t.Fatalf("err: %s", err)
  1158  	}
  1159  	if state == nil {
  1160  		t.Fatal("state should not be nil")
  1161  	}
  1162  
  1163  	// Ensure there is no backup
  1164  	_, err = os.Stat(statePath + DefaultBackupExtention)
  1165  	if err == nil || !os.IsNotExist(err) {
  1166  		t.Fatalf("backup should not exist")
  1167  	}
  1168  
  1169  	// Ensure there is no literal "-"
  1170  	_, err = os.Stat("-")
  1171  	if err == nil || !os.IsNotExist(err) {
  1172  		t.Fatalf("backup should not exist")
  1173  	}
  1174  }
  1175  
  1176  func testHttpServer(t *testing.T) net.Listener {
  1177  	ln, err := net.Listen("tcp", "127.0.0.1:0")
  1178  	if err != nil {
  1179  		t.Fatalf("err: %s", err)
  1180  	}
  1181  
  1182  	mux := http.NewServeMux()
  1183  	mux.HandleFunc("/header", testHttpHandlerHeader)
  1184  
  1185  	var server http.Server
  1186  	server.Handler = mux
  1187  	go server.Serve(ln)
  1188  
  1189  	return ln
  1190  }
  1191  
  1192  func testHttpHandlerHeader(w http.ResponseWriter, r *http.Request) {
  1193  	var url url.URL
  1194  	url.Scheme = "file"
  1195  	url.Path = filepath.ToSlash(testFixturePath("init"))
  1196  
  1197  	w.Header().Add("X-Terraform-Get", url.String())
  1198  	w.WriteHeader(200)
  1199  }
  1200  
  1201  const applyVarFile = `
  1202  foo = "bar"
  1203  `
  1204  
  1205  const applyVarFileJSON = `
  1206  { "foo": "bar" }
  1207  `
  1208  
  1209  const testApplyDisableBackupStr = `
  1210  ID = bar
  1211  `
  1212  
  1213  const testApplyDisableBackupStateStr = `
  1214  ID = bar
  1215  `
  1216  
  1217  const testApplyStateStr = `
  1218  ID = bar
  1219  `
  1220  
  1221  const testApplyStateDiffStr = `
  1222  ID = bar
  1223  `