github.com/wtlangford/terraform@v0.7.0-rc2.0.20160709103016-0e07a2776833/command/push_test.go (about)

     1  package command
     2  
     3  import (
     4  	"archive/tar"
     5  	"bytes"
     6  	"compress/gzip"
     7  	"io"
     8  	"os"
     9  	"reflect"
    10  	"sort"
    11  	"testing"
    12  
    13  	"github.com/hashicorp/terraform/terraform"
    14  	"github.com/mitchellh/cli"
    15  )
    16  
    17  func TestPush_good(t *testing.T) {
    18  	tmp, cwd := testCwd(t)
    19  	defer testFixCwd(t, tmp, cwd)
    20  
    21  	// Create remote state file, this should be pulled
    22  	conf, srv := testRemoteState(t, testState(), 200)
    23  	defer srv.Close()
    24  
    25  	// Persist local remote state
    26  	s := terraform.NewState()
    27  	s.Serial = 5
    28  	s.Remote = conf
    29  	testStateFileRemote(t, s)
    30  
    31  	// Path where the archive will be "uploaded" to
    32  	archivePath := testTempFile(t)
    33  	defer os.Remove(archivePath)
    34  
    35  	client := &mockPushClient{File: archivePath}
    36  	ui := new(cli.MockUi)
    37  	c := &PushCommand{
    38  		Meta: Meta{
    39  			ContextOpts: testCtxConfig(testProvider()),
    40  			Ui:          ui,
    41  		},
    42  
    43  		client: client,
    44  	}
    45  
    46  	args := []string{
    47  		"-vcs=false",
    48  		testFixturePath("push"),
    49  	}
    50  	if code := c.Run(args); code != 0 {
    51  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
    52  	}
    53  
    54  	actual := testArchiveStr(t, archivePath)
    55  	expected := []string{
    56  		".terraform/",
    57  		".terraform/terraform.tfstate",
    58  		"main.tf",
    59  	}
    60  	if !reflect.DeepEqual(actual, expected) {
    61  		t.Fatalf("bad: %#v", actual)
    62  	}
    63  
    64  	variables := make(map[string]string)
    65  	if !reflect.DeepEqual(client.UpsertOptions.Variables, variables) {
    66  		t.Fatalf("bad: %#v", client.UpsertOptions)
    67  	}
    68  
    69  	if client.UpsertOptions.Name != "foo" {
    70  		t.Fatalf("bad: %#v", client.UpsertOptions)
    71  	}
    72  }
    73  
    74  func TestPush_input(t *testing.T) {
    75  	tmp, cwd := testCwd(t)
    76  	defer testFixCwd(t, tmp, cwd)
    77  
    78  	// Create remote state file, this should be pulled
    79  	conf, srv := testRemoteState(t, testState(), 200)
    80  	defer srv.Close()
    81  
    82  	// Persist local remote state
    83  	s := terraform.NewState()
    84  	s.Serial = 5
    85  	s.Remote = conf
    86  	testStateFileRemote(t, s)
    87  
    88  	// Path where the archive will be "uploaded" to
    89  	archivePath := testTempFile(t)
    90  	defer os.Remove(archivePath)
    91  
    92  	client := &mockPushClient{File: archivePath}
    93  	ui := new(cli.MockUi)
    94  	c := &PushCommand{
    95  		Meta: Meta{
    96  			ContextOpts: testCtxConfig(testProvider()),
    97  			Ui:          ui,
    98  		},
    99  
   100  		client: client,
   101  	}
   102  
   103  	// Disable test mode so input would be asked and setup the
   104  	// input reader/writers.
   105  	test = false
   106  	defer func() { test = true }()
   107  	defaultInputReader = bytes.NewBufferString("foo\n")
   108  	defaultInputWriter = new(bytes.Buffer)
   109  
   110  	args := []string{
   111  		"-vcs=false",
   112  		testFixturePath("push-input"),
   113  	}
   114  	if code := c.Run(args); code != 0 {
   115  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   116  	}
   117  
   118  	variables := map[string]string{
   119  		"foo": "foo",
   120  	}
   121  	if !reflect.DeepEqual(client.UpsertOptions.Variables, variables) {
   122  		t.Fatalf("bad: %#v", client.UpsertOptions.Variables)
   123  	}
   124  }
   125  
   126  func TestPush_inputPartial(t *testing.T) {
   127  	tmp, cwd := testCwd(t)
   128  	defer testFixCwd(t, tmp, cwd)
   129  
   130  	// Create remote state file, this should be pulled
   131  	conf, srv := testRemoteState(t, testState(), 200)
   132  	defer srv.Close()
   133  
   134  	// Persist local remote state
   135  	s := terraform.NewState()
   136  	s.Serial = 5
   137  	s.Remote = conf
   138  	testStateFileRemote(t, s)
   139  
   140  	// Path where the archive will be "uploaded" to
   141  	archivePath := testTempFile(t)
   142  	defer os.Remove(archivePath)
   143  
   144  	client := &mockPushClient{
   145  		File:      archivePath,
   146  		GetResult: map[string]string{"foo": "bar"},
   147  	}
   148  	ui := new(cli.MockUi)
   149  	c := &PushCommand{
   150  		Meta: Meta{
   151  			ContextOpts: testCtxConfig(testProvider()),
   152  			Ui:          ui,
   153  		},
   154  
   155  		client: client,
   156  	}
   157  
   158  	// Disable test mode so input would be asked and setup the
   159  	// input reader/writers.
   160  	test = false
   161  	defer func() { test = true }()
   162  	defaultInputReader = bytes.NewBufferString("foo\n")
   163  	defaultInputWriter = new(bytes.Buffer)
   164  
   165  	args := []string{
   166  		"-vcs=false",
   167  		testFixturePath("push-input-partial"),
   168  	}
   169  	if code := c.Run(args); code != 0 {
   170  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   171  	}
   172  
   173  	variables := map[string]string{
   174  		"foo": "bar",
   175  		"bar": "foo",
   176  	}
   177  	if !reflect.DeepEqual(client.UpsertOptions.Variables, variables) {
   178  		t.Fatalf("bad: %#v", client.UpsertOptions)
   179  	}
   180  }
   181  
   182  // This tests that the push command will override Atlas variables
   183  // if requested.
   184  func TestPush_localOverride(t *testing.T) {
   185  	// Disable test mode so input would be asked and setup the
   186  	// input reader/writers.
   187  	test = false
   188  	defer func() { test = true }()
   189  	defaultInputReader = bytes.NewBufferString("nope\n")
   190  	defaultInputWriter = new(bytes.Buffer)
   191  
   192  	tmp, cwd := testCwd(t)
   193  	defer testFixCwd(t, tmp, cwd)
   194  
   195  	// Create remote state file, this should be pulled
   196  	conf, srv := testRemoteState(t, testState(), 200)
   197  	defer srv.Close()
   198  
   199  	// Persist local remote state
   200  	s := terraform.NewState()
   201  	s.Serial = 5
   202  	s.Remote = conf
   203  	testStateFileRemote(t, s)
   204  
   205  	// Path where the archive will be "uploaded" to
   206  	archivePath := testTempFile(t)
   207  	defer os.Remove(archivePath)
   208  
   209  	client := &mockPushClient{File: archivePath}
   210  	// Provided vars should override existing ones
   211  	client.GetResult = map[string]string{
   212  		"foo": "old",
   213  	}
   214  	ui := new(cli.MockUi)
   215  	c := &PushCommand{
   216  		Meta: Meta{
   217  			ContextOpts: testCtxConfig(testProvider()),
   218  			Ui:          ui,
   219  		},
   220  
   221  		client: client,
   222  	}
   223  
   224  	path := testFixturePath("push-tfvars")
   225  	args := []string{
   226  		"-var-file", path + "/terraform.tfvars",
   227  		"-vcs=false",
   228  		"-overwrite=foo",
   229  		path,
   230  	}
   231  	if code := c.Run(args); code != 0 {
   232  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   233  	}
   234  
   235  	actual := testArchiveStr(t, archivePath)
   236  	expected := []string{
   237  		".terraform/",
   238  		".terraform/terraform.tfstate",
   239  		"main.tf",
   240  		"terraform.tfvars",
   241  	}
   242  	if !reflect.DeepEqual(actual, expected) {
   243  		t.Fatalf("bad: %#v", actual)
   244  	}
   245  
   246  	if client.UpsertOptions.Name != "foo" {
   247  		t.Fatalf("bad: %#v", client.UpsertOptions)
   248  	}
   249  
   250  	variables := map[string]string{
   251  		"foo": "bar",
   252  		"bar": "foo",
   253  	}
   254  	if !reflect.DeepEqual(client.UpsertOptions.Variables, variables) {
   255  		t.Fatalf("bad: %#v", client.UpsertOptions)
   256  	}
   257  }
   258  
   259  // This tests that the push command prefers Atlas variables over
   260  // local ones.
   261  func TestPush_preferAtlas(t *testing.T) {
   262  	// Disable test mode so input would be asked and setup the
   263  	// input reader/writers.
   264  	test = false
   265  	defer func() { test = true }()
   266  	defaultInputReader = bytes.NewBufferString("nope\n")
   267  	defaultInputWriter = new(bytes.Buffer)
   268  
   269  	tmp, cwd := testCwd(t)
   270  	defer testFixCwd(t, tmp, cwd)
   271  
   272  	// Create remote state file, this should be pulled
   273  	conf, srv := testRemoteState(t, testState(), 200)
   274  	defer srv.Close()
   275  
   276  	// Persist local remote state
   277  	s := terraform.NewState()
   278  	s.Serial = 5
   279  	s.Remote = conf
   280  	testStateFileRemote(t, s)
   281  
   282  	// Path where the archive will be "uploaded" to
   283  	archivePath := testTempFile(t)
   284  	defer os.Remove(archivePath)
   285  
   286  	client := &mockPushClient{File: archivePath}
   287  	// Provided vars should override existing ones
   288  	client.GetResult = map[string]string{
   289  		"foo": "old",
   290  	}
   291  	ui := new(cli.MockUi)
   292  	c := &PushCommand{
   293  		Meta: Meta{
   294  			ContextOpts: testCtxConfig(testProvider()),
   295  			Ui:          ui,
   296  		},
   297  
   298  		client: client,
   299  	}
   300  
   301  	path := testFixturePath("push-tfvars")
   302  	args := []string{
   303  		"-var-file", path + "/terraform.tfvars",
   304  		"-vcs=false",
   305  		path,
   306  	}
   307  	if code := c.Run(args); code != 0 {
   308  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   309  	}
   310  
   311  	actual := testArchiveStr(t, archivePath)
   312  	expected := []string{
   313  		".terraform/",
   314  		".terraform/terraform.tfstate",
   315  		"main.tf",
   316  		"terraform.tfvars",
   317  	}
   318  	if !reflect.DeepEqual(actual, expected) {
   319  		t.Fatalf("bad: %#v", actual)
   320  	}
   321  
   322  	if client.UpsertOptions.Name != "foo" {
   323  		t.Fatalf("bad: %#v", client.UpsertOptions)
   324  	}
   325  
   326  	variables := map[string]string{
   327  		"foo": "old",
   328  		"bar": "foo",
   329  	}
   330  	if !reflect.DeepEqual(client.UpsertOptions.Variables, variables) {
   331  		t.Fatalf("bad: %#v", client.UpsertOptions)
   332  	}
   333  }
   334  
   335  // This tests that the push command will send the variables in tfvars
   336  func TestPush_tfvars(t *testing.T) {
   337  	// Disable test mode so input would be asked and setup the
   338  	// input reader/writers.
   339  	test = false
   340  	defer func() { test = true }()
   341  	defaultInputReader = bytes.NewBufferString("nope\n")
   342  	defaultInputWriter = new(bytes.Buffer)
   343  
   344  	tmp, cwd := testCwd(t)
   345  	defer testFixCwd(t, tmp, cwd)
   346  
   347  	// Create remote state file, this should be pulled
   348  	conf, srv := testRemoteState(t, testState(), 200)
   349  	defer srv.Close()
   350  
   351  	// Persist local remote state
   352  	s := terraform.NewState()
   353  	s.Serial = 5
   354  	s.Remote = conf
   355  	testStateFileRemote(t, s)
   356  
   357  	// Path where the archive will be "uploaded" to
   358  	archivePath := testTempFile(t)
   359  	defer os.Remove(archivePath)
   360  
   361  	client := &mockPushClient{File: archivePath}
   362  	ui := new(cli.MockUi)
   363  	c := &PushCommand{
   364  		Meta: Meta{
   365  			ContextOpts: testCtxConfig(testProvider()),
   366  			Ui:          ui,
   367  		},
   368  
   369  		client: client,
   370  	}
   371  
   372  	path := testFixturePath("push-tfvars")
   373  	args := []string{
   374  		"-var-file", path + "/terraform.tfvars",
   375  		"-vcs=false",
   376  		path,
   377  	}
   378  	if code := c.Run(args); code != 0 {
   379  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   380  	}
   381  
   382  	actual := testArchiveStr(t, archivePath)
   383  	expected := []string{
   384  		".terraform/",
   385  		".terraform/terraform.tfstate",
   386  		"main.tf",
   387  		"terraform.tfvars",
   388  	}
   389  	if !reflect.DeepEqual(actual, expected) {
   390  		t.Fatalf("bad: %#v", actual)
   391  	}
   392  
   393  	if client.UpsertOptions.Name != "foo" {
   394  		t.Fatalf("bad: %#v", client.UpsertOptions)
   395  	}
   396  
   397  	variables := map[string]string{
   398  		"foo": "bar",
   399  		"bar": "foo",
   400  	}
   401  	if !reflect.DeepEqual(client.UpsertOptions.Variables, variables) {
   402  		t.Fatalf("bad: %#v", client.UpsertOptions)
   403  	}
   404  }
   405  
   406  func TestPush_name(t *testing.T) {
   407  	tmp, cwd := testCwd(t)
   408  	defer testFixCwd(t, tmp, cwd)
   409  
   410  	// Create remote state file, this should be pulled
   411  	conf, srv := testRemoteState(t, testState(), 200)
   412  	defer srv.Close()
   413  
   414  	// Persist local remote state
   415  	s := terraform.NewState()
   416  	s.Serial = 5
   417  	s.Remote = conf
   418  	testStateFileRemote(t, s)
   419  
   420  	// Path where the archive will be "uploaded" to
   421  	archivePath := testTempFile(t)
   422  	defer os.Remove(archivePath)
   423  
   424  	client := &mockPushClient{File: archivePath}
   425  	ui := new(cli.MockUi)
   426  	c := &PushCommand{
   427  		Meta: Meta{
   428  			ContextOpts: testCtxConfig(testProvider()),
   429  			Ui:          ui,
   430  		},
   431  
   432  		client: client,
   433  	}
   434  
   435  	args := []string{
   436  		"-name", "bar",
   437  		"-vcs=false",
   438  		testFixturePath("push"),
   439  	}
   440  	if code := c.Run(args); code != 0 {
   441  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   442  	}
   443  
   444  	if client.UpsertOptions.Name != "bar" {
   445  		t.Fatalf("bad: %#v", client.UpsertOptions)
   446  	}
   447  }
   448  
   449  func TestPush_noState(t *testing.T) {
   450  	tmp, cwd := testCwd(t)
   451  	defer testFixCwd(t, tmp, cwd)
   452  
   453  	ui := new(cli.MockUi)
   454  	c := &PushCommand{
   455  		Meta: Meta{
   456  			ContextOpts: testCtxConfig(testProvider()),
   457  			Ui:          ui,
   458  		},
   459  	}
   460  
   461  	args := []string{}
   462  	if code := c.Run(args); code != 1 {
   463  		t.Fatalf("bad: \n%s", ui.ErrorWriter.String())
   464  	}
   465  }
   466  
   467  func TestPush_noRemoteState(t *testing.T) {
   468  	state := &terraform.State{
   469  		Modules: []*terraform.ModuleState{
   470  			&terraform.ModuleState{
   471  				Path: []string{"root"},
   472  				Resources: map[string]*terraform.ResourceState{
   473  					"test_instance.foo": &terraform.ResourceState{
   474  						Type: "test_instance",
   475  						Primary: &terraform.InstanceState{
   476  							ID: "bar",
   477  						},
   478  					},
   479  				},
   480  			},
   481  		},
   482  	}
   483  	statePath := testStateFile(t, state)
   484  
   485  	ui := new(cli.MockUi)
   486  	c := &PushCommand{
   487  		Meta: Meta{
   488  			Ui: ui,
   489  		},
   490  	}
   491  
   492  	args := []string{
   493  		"-state", statePath,
   494  	}
   495  	if code := c.Run(args); code != 1 {
   496  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   497  	}
   498  }
   499  
   500  func TestPush_plan(t *testing.T) {
   501  	tmp, cwd := testCwd(t)
   502  	defer testFixCwd(t, tmp, cwd)
   503  
   504  	// Create remote state file, this should be pulled
   505  	conf, srv := testRemoteState(t, testState(), 200)
   506  	defer srv.Close()
   507  
   508  	// Persist local remote state
   509  	s := terraform.NewState()
   510  	s.Serial = 5
   511  	s.Remote = conf
   512  	testStateFileRemote(t, s)
   513  
   514  	// Create a plan
   515  	planPath := testPlanFile(t, &terraform.Plan{
   516  		Module: testModule(t, "apply"),
   517  	})
   518  
   519  	ui := new(cli.MockUi)
   520  	c := &PushCommand{
   521  		Meta: Meta{
   522  			ContextOpts: testCtxConfig(testProvider()),
   523  			Ui:          ui,
   524  		},
   525  	}
   526  
   527  	args := []string{planPath}
   528  	if code := c.Run(args); code != 1 {
   529  		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
   530  	}
   531  }
   532  
   533  func testArchiveStr(t *testing.T, path string) []string {
   534  	f, err := os.Open(path)
   535  	if err != nil {
   536  		t.Fatalf("err: %s", err)
   537  	}
   538  	defer f.Close()
   539  
   540  	// Ungzip
   541  	gzipR, err := gzip.NewReader(f)
   542  	if err != nil {
   543  		t.Fatalf("err: %s", err)
   544  	}
   545  
   546  	// Accumulator
   547  	result := make([]string, 0, 10)
   548  
   549  	// Untar
   550  	tarR := tar.NewReader(gzipR)
   551  	for {
   552  		header, err := tarR.Next()
   553  		if err == io.EOF {
   554  			break
   555  		}
   556  		if err != nil {
   557  			t.Fatalf("err: %s", err)
   558  		}
   559  
   560  		result = append(result, header.Name)
   561  	}
   562  
   563  	sort.Strings(result)
   564  	return result
   565  }