github.com/paultyng/terraform@v0.6.11-0.20180227224804-66ff8f8bed40/command/push_test.go (about)

     1  package command
     2  
     3  import (
     4  	"archive/tar"
     5  	"bytes"
     6  	"compress/gzip"
     7  	"io"
     8  	"os"
     9  	"path/filepath"
    10  	"reflect"
    11  	"sort"
    12  	"strings"
    13  	"testing"
    14  
    15  	atlas "github.com/hashicorp/atlas-go/v1"
    16  	"github.com/hashicorp/terraform/helper/copy"
    17  	"github.com/hashicorp/terraform/terraform"
    18  	"github.com/mitchellh/cli"
    19  )
    20  
    21  func TestPush_good(t *testing.T) {
    22  	tmp, cwd := testCwd(t)
    23  	defer testFixCwd(t, tmp, cwd)
    24  
    25  	// Create remote state file, this should be pulled
    26  	conf, srv := testRemoteState(t, testState(), 200)
    27  	defer srv.Close()
    28  
    29  	// Persist local remote state
    30  	s := terraform.NewState()
    31  	s.Serial = 5
    32  	s.Remote = conf
    33  	testStateFileRemote(t, s)
    34  
    35  	// Path where the archive will be "uploaded" to
    36  	archivePath := testTempFile(t)
    37  	defer os.Remove(archivePath)
    38  
    39  	client := &mockPushClient{File: archivePath}
    40  	ui := new(cli.MockUi)
    41  	c := &PushCommand{
    42  		Meta: Meta{
    43  			testingOverrides: metaOverridesForProvider(testProvider()),
    44  			Ui:               ui,
    45  		},
    46  
    47  		client: client,
    48  	}
    49  
    50  	args := []string{
    51  		"-vcs=false",
    52  		testFixturePath("push"),
    53  	}
    54  	if code := c.Run(args); code != 0 {
    55  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
    56  	}
    57  
    58  	actual := testArchiveStr(t, archivePath)
    59  	expected := []string{
    60  		".terraform/",
    61  		".terraform/terraform.tfstate",
    62  		"main.tf",
    63  	}
    64  	if !reflect.DeepEqual(actual, expected) {
    65  		t.Fatalf("bad: %#v", actual)
    66  	}
    67  
    68  	variables := make(map[string]interface{})
    69  	if !reflect.DeepEqual(client.UpsertOptions.Variables, variables) {
    70  		t.Fatalf("bad: %#v", client.UpsertOptions)
    71  	}
    72  
    73  	if client.UpsertOptions.Name != "foo" {
    74  		t.Fatalf("bad: %#v", client.UpsertOptions)
    75  	}
    76  }
    77  
    78  func TestPush_goodBackendInit(t *testing.T) {
    79  	// Create a temporary working directory that is empty
    80  	td := tempDir(t)
    81  	copy.CopyDir(testFixturePath("push-backend-new"), td)
    82  	defer os.RemoveAll(td)
    83  	defer testChdir(t, td)()
    84  
    85  	// init backend
    86  	ui := new(cli.MockUi)
    87  	ci := &InitCommand{
    88  		Meta: Meta{
    89  			Ui: ui,
    90  		},
    91  	}
    92  	if code := ci.Run(nil); code != 0 {
    93  		t.Fatalf("bad: %d\n%s", code, ui.ErrorWriter)
    94  	}
    95  
    96  	// Path where the archive will be "uploaded" to
    97  	archivePath := testTempFile(t)
    98  	defer os.Remove(archivePath)
    99  
   100  	client := &mockPushClient{File: archivePath}
   101  	ui = new(cli.MockUi)
   102  	c := &PushCommand{
   103  		Meta: Meta{
   104  			testingOverrides: metaOverridesForProvider(testProvider()),
   105  			Ui:               ui,
   106  		},
   107  
   108  		client: client,
   109  	}
   110  
   111  	args := []string{
   112  		"-vcs=false",
   113  		td,
   114  	}
   115  	if code := c.Run(args); code != 0 {
   116  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   117  	}
   118  
   119  	actual := testArchiveStr(t, archivePath)
   120  	expected := []string{
   121  		// Expected weird behavior, doesn't affect unpackaging
   122  		".terraform/",
   123  		".terraform/",
   124  
   125  		// this config contains no plugins
   126  		// TODO: we should add one or more plugins to this test config, just to
   127  		// verfy the pushed data. The expected additional files are listed below:
   128  		//
   129  		//".terraform/plugins/",
   130  		//fmt.Sprintf(".terraform/plugins/%s_%s/", runtime.GOOS, runtime.GOARCH),
   131  		//fmt.Sprintf(".terraform/plugins/%s_%s/lock.json", runtime.GOOS, runtime.GOARCH),
   132  
   133  		".terraform/terraform.tfstate",
   134  		".terraform/terraform.tfstate",
   135  		"main.tf",
   136  	}
   137  	if !reflect.DeepEqual(actual, expected) {
   138  		t.Fatalf("expected: %#v\ngot: %#v", expected, actual)
   139  	}
   140  
   141  	variables := make(map[string]interface{})
   142  	if !reflect.DeepEqual(client.UpsertOptions.Variables, variables) {
   143  		t.Fatalf("bad: %#v", client.UpsertOptions)
   144  	}
   145  
   146  	if client.UpsertOptions.Name != "hello" {
   147  		t.Fatalf("bad: %#v", client.UpsertOptions)
   148  	}
   149  }
   150  
   151  func TestPush_noUploadModules(t *testing.T) {
   152  	// Path where the archive will be "uploaded" to
   153  	archivePath := testTempFile(t)
   154  	defer os.Remove(archivePath)
   155  
   156  	client := &mockPushClient{File: archivePath}
   157  	ui := new(cli.MockUi)
   158  	c := &PushCommand{
   159  		Meta: Meta{
   160  			testingOverrides: metaOverridesForProvider(testProvider()),
   161  			Ui:               ui,
   162  		},
   163  
   164  		client: client,
   165  	}
   166  
   167  	// Path of the test. We have to do some renaming to avoid our own
   168  	// VCS getting in the way.
   169  	path := testFixturePath("push-no-upload")
   170  	defer os.RemoveAll(filepath.Join(path, ".terraform"))
   171  
   172  	// Move into that directory
   173  	defer testChdir(t, path)()
   174  
   175  	// Do a "terraform get"
   176  	{
   177  		ui := new(cli.MockUi)
   178  		c := &GetCommand{
   179  			Meta: Meta{
   180  				testingOverrides: metaOverridesForProvider(testProvider()),
   181  				Ui:               ui,
   182  			},
   183  		}
   184  
   185  		if code := c.Run([]string{}); code != 0 {
   186  			t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   187  		}
   188  	}
   189  
   190  	// Create remote state file, this should be pulled
   191  	conf, srv := testRemoteState(t, testState(), 200)
   192  	defer srv.Close()
   193  
   194  	// Persist local remote state
   195  	s := terraform.NewState()
   196  	s.Serial = 5
   197  	s.Remote = conf
   198  	defer os.Remove(testStateFileRemote(t, s))
   199  
   200  	args := []string{
   201  		"-vcs=false",
   202  		"-name=mitchellh/tf-test",
   203  		"-upload-modules=false",
   204  		path,
   205  	}
   206  	if code := c.Run(args); code != 0 {
   207  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   208  	}
   209  
   210  	// NOTE: The duplicates below are not ideal but are how things work
   211  	// currently due to how we manually add the files to the archive. This
   212  	// is definitely a "bug" we can fix in the future.
   213  	actual := testArchiveStr(t, archivePath)
   214  	expected := []string{
   215  		".terraform/",
   216  		".terraform/",
   217  		".terraform/terraform.tfstate",
   218  		".terraform/terraform.tfstate",
   219  		"child/",
   220  		"child/main.tf",
   221  		"main.tf",
   222  	}
   223  	if !reflect.DeepEqual(actual, expected) {
   224  		t.Fatalf("bad: %#v", actual)
   225  	}
   226  }
   227  
   228  func TestPush_input(t *testing.T) {
   229  	tmp, cwd := testCwd(t)
   230  	defer testFixCwd(t, tmp, cwd)
   231  
   232  	// Create remote state file, this should be pulled
   233  	conf, srv := testRemoteState(t, testState(), 200)
   234  	defer srv.Close()
   235  
   236  	// Persist local remote state
   237  	s := terraform.NewState()
   238  	s.Serial = 5
   239  	s.Remote = conf
   240  	testStateFileRemote(t, s)
   241  
   242  	// Path where the archive will be "uploaded" to
   243  	archivePath := testTempFile(t)
   244  	defer os.Remove(archivePath)
   245  
   246  	client := &mockPushClient{File: archivePath}
   247  	ui := new(cli.MockUi)
   248  	c := &PushCommand{
   249  		Meta: Meta{
   250  			testingOverrides: metaOverridesForProvider(testProvider()),
   251  			Ui:               ui,
   252  		},
   253  
   254  		client: client,
   255  	}
   256  
   257  	// Disable test mode so input would be asked and setup the
   258  	// input reader/writers.
   259  	test = false
   260  	defer func() { test = true }()
   261  	defaultInputReader = bytes.NewBufferString("foo\n")
   262  	defaultInputWriter = new(bytes.Buffer)
   263  
   264  	args := []string{
   265  		"-vcs=false",
   266  		testFixturePath("push-input"),
   267  	}
   268  	if code := c.Run(args); code != 0 {
   269  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   270  	}
   271  
   272  	variables := map[string]interface{}{
   273  		"foo": "foo",
   274  	}
   275  
   276  	if !reflect.DeepEqual(client.UpsertOptions.Variables, variables) {
   277  		t.Fatalf("bad: %#v", client.UpsertOptions.Variables)
   278  	}
   279  }
   280  
   281  // We want a variable from atlas to fill a missing variable locally
   282  func TestPush_inputPartial(t *testing.T) {
   283  	tmp, cwd := testCwd(t)
   284  	defer testFixCwd(t, tmp, cwd)
   285  
   286  	// Create remote state file, this should be pulled
   287  	conf, srv := testRemoteState(t, testState(), 200)
   288  	defer srv.Close()
   289  
   290  	// Persist local remote state
   291  	s := terraform.NewState()
   292  	s.Serial = 5
   293  	s.Remote = conf
   294  	testStateFileRemote(t, s)
   295  
   296  	// Path where the archive will be "uploaded" to
   297  	archivePath := testTempFile(t)
   298  	defer os.Remove(archivePath)
   299  
   300  	client := &mockPushClient{
   301  		File: archivePath,
   302  		GetResult: map[string]atlas.TFVar{
   303  			"foo": atlas.TFVar{Key: "foo", Value: "bar"},
   304  		},
   305  	}
   306  	ui := new(cli.MockUi)
   307  	c := &PushCommand{
   308  		Meta: Meta{
   309  			testingOverrides: metaOverridesForProvider(testProvider()),
   310  			Ui:               ui,
   311  		},
   312  
   313  		client: client,
   314  	}
   315  
   316  	// Disable test mode so input would be asked and setup the
   317  	// input reader/writers.
   318  	test = false
   319  	defer func() { test = true }()
   320  	defaultInputReader = bytes.NewBufferString("foo\n")
   321  	defaultInputWriter = new(bytes.Buffer)
   322  
   323  	args := []string{
   324  		"-vcs=false",
   325  		testFixturePath("push-input-partial"),
   326  	}
   327  	if code := c.Run(args); code != 0 {
   328  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   329  	}
   330  
   331  	expectedTFVars := []atlas.TFVar{
   332  		{Key: "bar", Value: "foo"},
   333  		{Key: "foo", Value: "bar"},
   334  	}
   335  	if !reflect.DeepEqual(client.UpsertOptions.TFVars, expectedTFVars) {
   336  		t.Logf("expected: %#v", expectedTFVars)
   337  		t.Fatalf("got:      %#v", client.UpsertOptions.TFVars)
   338  	}
   339  }
   340  
   341  // This tests that the push command will override Atlas variables
   342  // if requested.
   343  func TestPush_localOverride(t *testing.T) {
   344  	// Disable test mode so input would be asked and setup the
   345  	// input reader/writers.
   346  	test = false
   347  	defer func() { test = true }()
   348  	defaultInputReader = bytes.NewBufferString("nope\n")
   349  	defaultInputWriter = new(bytes.Buffer)
   350  
   351  	tmp, cwd := testCwd(t)
   352  	defer testFixCwd(t, tmp, cwd)
   353  
   354  	// Create remote state file, this should be pulled
   355  	conf, srv := testRemoteState(t, testState(), 200)
   356  	defer srv.Close()
   357  
   358  	// Persist local remote state
   359  	s := terraform.NewState()
   360  	s.Serial = 5
   361  	s.Remote = conf
   362  	testStateFileRemote(t, s)
   363  
   364  	// Path where the archive will be "uploaded" to
   365  	archivePath := testTempFile(t)
   366  	defer os.Remove(archivePath)
   367  
   368  	client := &mockPushClient{File: archivePath}
   369  	// Provided vars should override existing ones
   370  	client.GetResult = map[string]atlas.TFVar{
   371  		"foo": atlas.TFVar{
   372  			Key:   "foo",
   373  			Value: "old",
   374  		},
   375  	}
   376  	ui := new(cli.MockUi)
   377  	c := &PushCommand{
   378  		Meta: Meta{
   379  			testingOverrides: metaOverridesForProvider(testProvider()),
   380  			Ui:               ui,
   381  		},
   382  
   383  		client: client,
   384  	}
   385  
   386  	path := testFixturePath("push-tfvars")
   387  	args := []string{
   388  		"-var-file", path + "/terraform.tfvars",
   389  		"-vcs=false",
   390  		"-overwrite=foo",
   391  		path,
   392  	}
   393  	if code := c.Run(args); code != 0 {
   394  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   395  	}
   396  
   397  	actual := testArchiveStr(t, archivePath)
   398  	expected := []string{
   399  		".terraform/",
   400  		".terraform/terraform.tfstate",
   401  		"main.tf",
   402  		"terraform.tfvars",
   403  	}
   404  	if !reflect.DeepEqual(actual, expected) {
   405  		t.Fatalf("bad: %#v", actual)
   406  	}
   407  
   408  	if client.UpsertOptions.Name != "foo" {
   409  		t.Fatalf("bad: %#v", client.UpsertOptions)
   410  	}
   411  
   412  	expectedTFVars := pushTFVars()
   413  
   414  	if !reflect.DeepEqual(client.UpsertOptions.TFVars, expectedTFVars) {
   415  		t.Logf("expected: %#v", expectedTFVars)
   416  		t.Fatalf("got:    %#v", client.UpsertOptions.TFVars)
   417  	}
   418  }
   419  
   420  // This tests that the push command will override Atlas variables
   421  // even if we don't have it defined locally
   422  func TestPush_remoteOverride(t *testing.T) {
   423  	// Disable test mode so input would be asked and setup the
   424  	// input reader/writers.
   425  	test = false
   426  	defer func() { test = true }()
   427  	defaultInputReader = bytes.NewBufferString("nope\n")
   428  	defaultInputWriter = new(bytes.Buffer)
   429  
   430  	tmp, cwd := testCwd(t)
   431  	defer testFixCwd(t, tmp, cwd)
   432  
   433  	// Create remote state file, this should be pulled
   434  	conf, srv := testRemoteState(t, testState(), 200)
   435  	defer srv.Close()
   436  
   437  	// Persist local remote state
   438  	s := terraform.NewState()
   439  	s.Serial = 5
   440  	s.Remote = conf
   441  	testStateFileRemote(t, s)
   442  
   443  	// Path where the archive will be "uploaded" to
   444  	archivePath := testTempFile(t)
   445  	defer os.Remove(archivePath)
   446  
   447  	client := &mockPushClient{File: archivePath}
   448  	// Provided vars should override existing ones
   449  	client.GetResult = map[string]atlas.TFVar{
   450  		"remote": atlas.TFVar{
   451  			Key:   "remote",
   452  			Value: "old",
   453  		},
   454  	}
   455  	ui := new(cli.MockUi)
   456  	c := &PushCommand{
   457  		Meta: Meta{
   458  			testingOverrides: metaOverridesForProvider(testProvider()),
   459  			Ui:               ui,
   460  		},
   461  
   462  		client: client,
   463  	}
   464  
   465  	path := testFixturePath("push-tfvars")
   466  	args := []string{
   467  		"-var-file", path + "/terraform.tfvars",
   468  		"-vcs=false",
   469  		"-overwrite=remote",
   470  		"-var",
   471  		"remote=new",
   472  		path,
   473  	}
   474  
   475  	if code := c.Run(args); code != 0 {
   476  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   477  	}
   478  
   479  	actual := testArchiveStr(t, archivePath)
   480  	expected := []string{
   481  		".terraform/",
   482  		".terraform/terraform.tfstate",
   483  		"main.tf",
   484  		"terraform.tfvars",
   485  	}
   486  	if !reflect.DeepEqual(actual, expected) {
   487  		t.Fatalf("bad: %#v", actual)
   488  	}
   489  
   490  	if client.UpsertOptions.Name != "foo" {
   491  		t.Fatalf("bad: %#v", client.UpsertOptions)
   492  	}
   493  
   494  	found := false
   495  	// find the "remote" var and make sure we're going to set it
   496  	for _, tfVar := range client.UpsertOptions.TFVars {
   497  		if tfVar.Key == "remote" {
   498  			found = true
   499  			if tfVar.Value != "new" {
   500  				t.Log("'remote' variable should be set to 'new'")
   501  				t.Fatalf("sending instead: %#v", tfVar)
   502  			}
   503  		}
   504  	}
   505  
   506  	if !found {
   507  		t.Fatal("'remote' variable not being sent to atlas")
   508  	}
   509  }
   510  
   511  // This tests that the push command prefers Atlas variables over
   512  // local ones.
   513  func TestPush_preferAtlas(t *testing.T) {
   514  	// Disable test mode so input would be asked and setup the
   515  	// input reader/writers.
   516  	test = false
   517  	defer func() { test = true }()
   518  	defaultInputReader = bytes.NewBufferString("nope\n")
   519  	defaultInputWriter = new(bytes.Buffer)
   520  
   521  	tmp, cwd := testCwd(t)
   522  	defer testFixCwd(t, tmp, cwd)
   523  
   524  	// Create remote state file, this should be pulled
   525  	conf, srv := testRemoteState(t, testState(), 200)
   526  	defer srv.Close()
   527  
   528  	// Persist local remote state
   529  	s := terraform.NewState()
   530  	s.Serial = 5
   531  	s.Remote = conf
   532  	testStateFileRemote(t, s)
   533  
   534  	// Path where the archive will be "uploaded" to
   535  	archivePath := testTempFile(t)
   536  	defer os.Remove(archivePath)
   537  
   538  	client := &mockPushClient{File: archivePath}
   539  	// Provided vars should override existing ones
   540  	client.GetResult = map[string]atlas.TFVar{
   541  		"foo": atlas.TFVar{
   542  			Key:   "foo",
   543  			Value: "old",
   544  		},
   545  	}
   546  	ui := new(cli.MockUi)
   547  	c := &PushCommand{
   548  		Meta: Meta{
   549  			testingOverrides: metaOverridesForProvider(testProvider()),
   550  			Ui:               ui,
   551  		},
   552  
   553  		client: client,
   554  	}
   555  
   556  	path := testFixturePath("push-tfvars")
   557  	args := []string{
   558  		"-var-file", path + "/terraform.tfvars",
   559  		"-vcs=false",
   560  		path,
   561  	}
   562  	if code := c.Run(args); code != 0 {
   563  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   564  	}
   565  
   566  	actual := testArchiveStr(t, archivePath)
   567  	expected := []string{
   568  		".terraform/",
   569  		".terraform/terraform.tfstate",
   570  		"main.tf",
   571  		"terraform.tfvars",
   572  	}
   573  	if !reflect.DeepEqual(actual, expected) {
   574  		t.Fatalf("bad: %#v", actual)
   575  	}
   576  
   577  	if client.UpsertOptions.Name != "foo" {
   578  		t.Fatalf("bad: %#v", client.UpsertOptions)
   579  	}
   580  
   581  	// change the expected response to match our change
   582  	expectedTFVars := pushTFVars()
   583  	for i, v := range expectedTFVars {
   584  		if v.Key == "foo" {
   585  			expectedTFVars[i] = atlas.TFVar{Key: "foo", Value: "old"}
   586  		}
   587  	}
   588  
   589  	if !reflect.DeepEqual(expectedTFVars, client.UpsertOptions.TFVars) {
   590  		t.Logf("expected: %#v", expectedTFVars)
   591  		t.Fatalf("got:      %#v", client.UpsertOptions.TFVars)
   592  	}
   593  }
   594  
   595  // This tests that the push command will send the variables in tfvars
   596  func TestPush_tfvars(t *testing.T) {
   597  	// Disable test mode so input would be asked and setup the
   598  	// input reader/writers.
   599  	test = false
   600  	defer func() { test = true }()
   601  	defaultInputReader = bytes.NewBufferString("nope\n")
   602  	defaultInputWriter = new(bytes.Buffer)
   603  
   604  	tmp, cwd := testCwd(t)
   605  	defer testFixCwd(t, tmp, cwd)
   606  
   607  	// Create remote state file, this should be pulled
   608  	conf, srv := testRemoteState(t, testState(), 200)
   609  	defer srv.Close()
   610  
   611  	// Persist local remote state
   612  	s := terraform.NewState()
   613  	s.Serial = 5
   614  	s.Remote = conf
   615  	testStateFileRemote(t, s)
   616  
   617  	// Path where the archive will be "uploaded" to
   618  	archivePath := testTempFile(t)
   619  	defer os.Remove(archivePath)
   620  
   621  	client := &mockPushClient{File: archivePath}
   622  	ui := new(cli.MockUi)
   623  	c := &PushCommand{
   624  		Meta: Meta{
   625  			testingOverrides: metaOverridesForProvider(testProvider()),
   626  			Ui:               ui,
   627  		},
   628  
   629  		client: client,
   630  	}
   631  
   632  	path := testFixturePath("push-tfvars")
   633  	args := []string{
   634  		"-var-file", path + "/terraform.tfvars",
   635  		"-vcs=false",
   636  		"-var",
   637  		"bar=[1,2]",
   638  		path,
   639  	}
   640  	if code := c.Run(args); code != 0 {
   641  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   642  	}
   643  
   644  	actual := testArchiveStr(t, archivePath)
   645  	expected := []string{
   646  		".terraform/",
   647  		".terraform/terraform.tfstate",
   648  		"main.tf",
   649  		"terraform.tfvars",
   650  	}
   651  	if !reflect.DeepEqual(actual, expected) {
   652  		t.Fatalf("bad: %#v", actual)
   653  	}
   654  
   655  	if client.UpsertOptions.Name != "foo" {
   656  		t.Fatalf("bad: %#v", client.UpsertOptions)
   657  	}
   658  
   659  	//now check TFVars
   660  	tfvars := pushTFVars()
   661  	// update bar to match cli value
   662  	for i, v := range tfvars {
   663  		if v.Key == "bar" {
   664  			tfvars[i].Value = "[1, 2]"
   665  			tfvars[i].IsHCL = true
   666  		}
   667  	}
   668  
   669  	for i, expected := range tfvars {
   670  		got := client.UpsertOptions.TFVars[i]
   671  		if got != expected {
   672  			t.Logf("%2d expected: %#v", i, expected)
   673  			t.Fatalf("        got: %#v", got)
   674  		}
   675  	}
   676  }
   677  
   678  func TestPush_name(t *testing.T) {
   679  	tmp, cwd := testCwd(t)
   680  	defer testFixCwd(t, tmp, cwd)
   681  
   682  	// Create remote state file, this should be pulled
   683  	conf, srv := testRemoteState(t, testState(), 200)
   684  	defer srv.Close()
   685  
   686  	// Persist local remote state
   687  	s := terraform.NewState()
   688  	s.Serial = 5
   689  	s.Remote = conf
   690  	testStateFileRemote(t, s)
   691  
   692  	// Path where the archive will be "uploaded" to
   693  	archivePath := testTempFile(t)
   694  	defer os.Remove(archivePath)
   695  
   696  	client := &mockPushClient{File: archivePath}
   697  	ui := new(cli.MockUi)
   698  	c := &PushCommand{
   699  		Meta: Meta{
   700  			testingOverrides: metaOverridesForProvider(testProvider()),
   701  			Ui:               ui,
   702  		},
   703  
   704  		client: client,
   705  	}
   706  
   707  	args := []string{
   708  		"-name", "bar",
   709  		"-vcs=false",
   710  		testFixturePath("push"),
   711  	}
   712  	if code := c.Run(args); code != 0 {
   713  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   714  	}
   715  
   716  	if client.UpsertOptions.Name != "bar" {
   717  		t.Fatalf("bad: %#v", client.UpsertOptions)
   718  	}
   719  }
   720  
   721  func TestPush_noState(t *testing.T) {
   722  	tmp, cwd := testCwd(t)
   723  	defer testFixCwd(t, tmp, cwd)
   724  
   725  	ui := new(cli.MockUi)
   726  	c := &PushCommand{
   727  		Meta: Meta{
   728  			testingOverrides: metaOverridesForProvider(testProvider()),
   729  			Ui:               ui,
   730  		},
   731  	}
   732  
   733  	args := []string{}
   734  	if code := c.Run(args); code != 1 {
   735  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   736  	}
   737  }
   738  
   739  func TestPush_noRemoteState(t *testing.T) {
   740  	// Create a temporary working directory that is empty
   741  	td := tempDir(t)
   742  	copy.CopyDir(testFixturePath("push-no-remote"), td)
   743  	defer os.RemoveAll(td)
   744  	defer testChdir(t, td)()
   745  
   746  	state := &terraform.State{
   747  		Modules: []*terraform.ModuleState{
   748  			&terraform.ModuleState{
   749  				Path: []string{"root"},
   750  				Resources: map[string]*terraform.ResourceState{
   751  					"test_instance.foo": &terraform.ResourceState{
   752  						Type: "test_instance",
   753  						Primary: &terraform.InstanceState{
   754  							ID: "bar",
   755  						},
   756  					},
   757  				},
   758  			},
   759  		},
   760  	}
   761  	statePath := testStateFile(t, state)
   762  
   763  	// Path where the archive will be "uploaded" to
   764  	archivePath := testTempFile(t)
   765  	defer os.Remove(archivePath)
   766  
   767  	client := &mockPushClient{File: archivePath}
   768  	ui := new(cli.MockUi)
   769  	c := &PushCommand{
   770  		Meta: Meta{
   771  			Ui: ui,
   772  		},
   773  		client: client,
   774  	}
   775  
   776  	args := []string{
   777  		"-vcs=false",
   778  		"-state", statePath,
   779  		td,
   780  	}
   781  	if code := c.Run(args); code != 1 {
   782  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   783  	}
   784  
   785  	errStr := ui.ErrorWriter.String()
   786  	if !strings.Contains(errStr, "remote backend") {
   787  		t.Fatalf("bad: %s", errStr)
   788  	}
   789  }
   790  
   791  func TestPush_plan(t *testing.T) {
   792  	tmp, cwd := testCwd(t)
   793  	defer testFixCwd(t, tmp, cwd)
   794  
   795  	// Create remote state file, this should be pulled
   796  	conf, srv := testRemoteState(t, testState(), 200)
   797  	defer srv.Close()
   798  
   799  	// Persist local remote state
   800  	s := terraform.NewState()
   801  	s.Serial = 5
   802  	s.Remote = conf
   803  	testStateFileRemote(t, s)
   804  
   805  	// Create a plan
   806  	planPath := testPlanFile(t, &terraform.Plan{
   807  		Module: testModule(t, "apply"),
   808  	})
   809  
   810  	ui := new(cli.MockUi)
   811  	c := &PushCommand{
   812  		Meta: Meta{
   813  			testingOverrides: metaOverridesForProvider(testProvider()),
   814  			Ui:               ui,
   815  		},
   816  	}
   817  
   818  	args := []string{planPath}
   819  	if code := c.Run(args); code != 1 {
   820  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   821  	}
   822  }
   823  
   824  func testArchiveStr(t *testing.T, path string) []string {
   825  	f, err := os.Open(path)
   826  	if err != nil {
   827  		t.Fatalf("err: %s", err)
   828  	}
   829  	defer f.Close()
   830  
   831  	// Ungzip
   832  	gzipR, err := gzip.NewReader(f)
   833  	if err != nil {
   834  		t.Fatalf("err: %s", err)
   835  	}
   836  
   837  	// Accumulator
   838  	result := make([]string, 0, 10)
   839  
   840  	// Untar
   841  	tarR := tar.NewReader(gzipR)
   842  	for {
   843  		header, err := tarR.Next()
   844  		if err == io.EOF {
   845  			break
   846  		}
   847  		if err != nil {
   848  			t.Fatalf("err: %s", err)
   849  		}
   850  
   851  		result = append(result, header.Name)
   852  	}
   853  
   854  	sort.Strings(result)
   855  	return result
   856  }
   857  
   858  // we always quote map keys to be safe
   859  func pushTFVars() []atlas.TFVar {
   860  	return []atlas.TFVar{
   861  		{Key: "bar", Value: "foo", IsHCL: false},
   862  		{Key: "baz", Value: `{
   863    "A" = "a"
   864  }`, IsHCL: true},
   865  		{Key: "fob", Value: `["a", "quotes \"in\" quotes"]`, IsHCL: true},
   866  		{Key: "foo", Value: "bar", IsHCL: false},
   867  	}
   868  }
   869  
   870  // the structure returned from the push-tfvars test fixture
   871  func pushTFVarsMap() map[string]atlas.TFVar {
   872  	vars := make(map[string]atlas.TFVar)
   873  	for _, v := range pushTFVars() {
   874  		vars[v.Key] = v
   875  	}
   876  	return vars
   877  }