diff --git a/command/command_test.go b/command/command_test.go index 01863ee39..2104d33a9 100644 --- a/command/command_test.go +++ b/command/command_test.go @@ -256,6 +256,42 @@ func testTempDir(t *testing.T) string { return d } +// testRename renames the path to new and returns a function to defer to +// revert the rename. +func testRename(t *testing.T, base, path, new string) func() { + if base != "" { + path = filepath.Join(base, path) + new = filepath.Join(base, new) + } + + if err := os.Rename(path, new); err != nil { + t.Fatalf("err: %s", err) + } + + return func() { + // Just re-rename and ignore the return value + testRename(t, "", new, path) + } +} + +// testChdir changes the directory and returns a function to defer to +// revert the old cwd. +func testChdir(t *testing.T, new string) func() { + old, err := os.Getwd() + if err != nil { + t.Fatalf("err: %s", err) + } + + if err := os.Chdir(new); err != nil { + t.Fatalf("err: %v", err) + } + + return func() { + // Re-run the function ignoring the defer result + testChdir(t, old) + } +} + // testCwd is used to change the current working directory // into a test directory that should be remoted after func testCwd(t *testing.T) (string, string) { diff --git a/command/push.go b/command/push.go index d9dda82b7..db2ae3db4 100644 --- a/command/push.go +++ b/command/push.go @@ -191,16 +191,42 @@ func (c *PushCommand) Run(args []string) int { return 1 } + // Get the absolute path for our data directory, since the Extra field + // value below needs to be absolute. + dataDirAbs, err := filepath.Abs(c.DataDir()) + if err != nil { + c.Ui.Error(fmt.Sprintf( + "Error while expanding the data directory %q: %s", c.DataDir(), err)) + return 1 + } + // Build the archiving options, which includes everything it can // by default according to VCS rules but forcing the data directory. archiveOpts := &archive.ArchiveOpts{ VCS: archiveVCS, Extra: map[string]string{ - DefaultDataDir: c.DataDir(), + DefaultDataDir: archive.ExtraEntryDir, }, } - if !moduleUpload { - // If we're not uploading modules, then exclude the modules dir. + + // Always store the state file in here so we can find state + statePathKey := fmt.Sprintf("%s/%s", DefaultDataDir, DefaultStateFilename) + archiveOpts.Extra[statePathKey] = filepath.Join(dataDirAbs, DefaultStateFilename) + if moduleUpload { + // If we're uploading modules, explicitly add that directory if exists. + moduleKey := fmt.Sprintf("%s/%s", DefaultDataDir, "modules") + moduleDir := filepath.Join(dataDirAbs, "modules") + _, err := os.Stat(moduleDir) + if err == nil { + archiveOpts.Extra[moduleKey] = filepath.Join(dataDirAbs, "modules") + } + if err != nil && !os.IsNotExist(err) { + c.Ui.Error(fmt.Sprintf( + "Error checking for module dir %q: %s", moduleDir, err)) + return 1 + } + } else { + // If we're not uploading modules, explicitly exclude add that archiveOpts.Exclude = append( archiveOpts.Exclude, filepath.Join(c.DataDir(), "modules")) diff --git a/command/push_test.go b/command/push_test.go index 2f78dd6fa..2fdbf2f26 100644 --- a/command/push_test.go +++ b/command/push_test.go @@ -72,6 +72,62 @@ func TestPush_good(t *testing.T) { } } +func TestPush_noUploadModules(t *testing.T) { + // Path where the archive will be "uploaded" to + archivePath := testTempFile(t) + defer os.Remove(archivePath) + + client := &mockPushClient{File: archivePath} + ui := new(cli.MockUi) + c := &PushCommand{ + Meta: Meta{ + ContextOpts: testCtxConfig(testProvider()), + Ui: ui, + }, + + client: client, + } + + // Path of the test. We have to do some renaming to avoid our own + // VCS getting in the way. + path := testFixturePath("push-no-upload") + defer testRename(t, path, "DOTterraform", ".terraform")() + + // Move into that directory + defer testChdir(t, path)() + + // Create remote state file, this should be pulled + conf, srv := testRemoteState(t, testState(), 200) + defer srv.Close() + + // Persist local remote state + s := terraform.NewState() + s.Serial = 5 + s.Remote = conf + defer os.Remove(testStateFileRemote(t, s)) + + args := []string{ + "-name=mitchellh/tf-test", + "-upload-modules=false", + path, + } + if code := c.Run(args); code != 0 { + t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + } + + actual := testArchiveStr(t, archivePath) + expected := []string{ + ".terraform/", + ".terraform/terraform.tfstate", + "child/", + "child/main.tf", + "main.tf", + } + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad: %#v", actual) + } +} + func TestPush_input(t *testing.T) { tmp, cwd := testCwd(t) defer testFixCwd(t, tmp, cwd) diff --git a/command/test-fixtures/push-no-upload/DOTterraform/modules/0aec430d87a09fa44453780d5bb00156/main.tf b/command/test-fixtures/push-no-upload/DOTterraform/modules/0aec430d87a09fa44453780d5bb00156/main.tf new file mode 100644 index 000000000..fec56017d --- /dev/null +++ b/command/test-fixtures/push-no-upload/DOTterraform/modules/0aec430d87a09fa44453780d5bb00156/main.tf @@ -0,0 +1 @@ +# Hello diff --git a/command/test-fixtures/push-no-upload/child/main.tf b/command/test-fixtures/push-no-upload/child/main.tf new file mode 100644 index 000000000..b7db25411 --- /dev/null +++ b/command/test-fixtures/push-no-upload/child/main.tf @@ -0,0 +1 @@ +# Empty diff --git a/command/test-fixtures/push-no-upload/main.tf b/command/test-fixtures/push-no-upload/main.tf new file mode 100644 index 000000000..c70c8b611 --- /dev/null +++ b/command/test-fixtures/push-no-upload/main.tf @@ -0,0 +1 @@ +module "example" { source = "./child" } diff --git a/vendor/github.com/hashicorp/atlas-go/archive/archive.go b/vendor/github.com/hashicorp/atlas-go/archive/archive.go index d9232b4f0..0a025b21f 100644 --- a/vendor/github.com/hashicorp/atlas-go/archive/archive.go +++ b/vendor/github.com/hashicorp/atlas-go/archive/archive.go @@ -50,6 +50,12 @@ func (o *ArchiveOpts) IsSet() bool { return len(o.Exclude) > 0 || len(o.Include) > 0 || o.VCS } +// Constants related to setting special values for Extra in ArchiveOpts. +const ( + // ExtraEntryDir just creates the Extra key as a directory entry. + ExtraEntryDir = "" +) + // CreateArchive takes the given path and ArchiveOpts and archives it. // // The archive will be fully completed and put into a temporary file. @@ -419,7 +425,29 @@ func copyConcreteEntry( } func copyExtras(w *tar.Writer, extra map[string]string) error { + var tmpDir string + defer func() { + if tmpDir != "" { + os.RemoveAll(tmpDir) + } + }() + for entry, path := range extra { + // If the path is empty, then we set it to a generic empty directory + if path == "" { + // If tmpDir is still empty, then we create an empty dir + if tmpDir == "" { + td, err := ioutil.TempDir("", "archive") + if err != nil { + return err + } + + tmpDir = td + } + + path = tmpDir + } + info, err := os.Stat(path) if err != nil { return err diff --git a/vendor/vendor.json b/vendor/vendor.json index f1401fc40..78227755a 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -1083,9 +1083,11 @@ "revision": "c0cf0cb802adad24252ce1307c4c896edd566870" }, { + "checksumSHA1": "FUiF2WLrih0JdHsUTMMDz3DRokw=", "comment": "20141209094003-92-g95fa852", "path": "github.com/hashicorp/atlas-go/archive", - "revision": "95fa852edca41c06c4ce526af4bb7dec4eaad434" + "revision": "8e45a6c8b2de014db767a42c3ee777f101e11624", + "revisionTime": "2016-08-24T17:34:10Z" }, { "checksumSHA1": "yylO3hSRKd0T4mveT9ho2OSARwU=",