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