github.com/kevholditch/terraform@v0.9.7-0.20170613192930-9706042ddd51/command/push_test.go (about)

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