From 8b208a597dc09973a566c4c986a74a4da2eeb7f9 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 16 Mar 2017 10:47:48 -0700 Subject: [PATCH 1/2] command/push: don't allow pushing with local backend --- command/meta_backend.go | 14 ++++++++++ command/push.go | 29 ++++++++------------ command/push_test.go | 21 ++++++++++++++ command/test-fixtures/push-no-remote/main.tf | 5 ++++ 4 files changed, 51 insertions(+), 18 deletions(-) create mode 100644 command/test-fixtures/push-no-remote/main.tf diff --git a/command/meta_backend.go b/command/meta_backend.go index ea96e7455..31dc9b566 100644 --- a/command/meta_backend.go +++ b/command/meta_backend.go @@ -138,6 +138,20 @@ func (m *Meta) Backend(opts *BackendOpts) (backend.Enhanced, error) { return local, nil } +// IsLocalBackend returns true if the backend is a local backend. We use this +// for some checks that require a remote backend. +func (m *Meta) IsLocalBackend(b backend.Backend) bool { + // Is it a local backend? + bLocal, ok := b.(*backendlocal.Local) + + // If it is, does it not have an alternate state backend? + if ok { + ok = bLocal.Backend == nil + } + + return ok +} + // Operation initializes a new backend.Operation struct. // // This prepares the operation. After calling this, the caller is expected diff --git a/command/push.go b/command/push.go index f3e173ec4..7dc1567f9 100644 --- a/command/push.go +++ b/command/push.go @@ -71,24 +71,6 @@ func (c *PushCommand) Run(args []string) int { return 1 } - /* - // Verify the state is remote, we can't push without a remote state - s, err := c.State() - if err != nil { - c.Ui.Error(fmt.Sprintf("Failed to read state: %s", err)) - return 1 - } - if !s.State().IsRemote() { - c.Ui.Error( - "Remote state is not enabled. For Atlas to run Terraform\n" + - "for you, remote state must be used and configured. Remote\n" + - "state via any backend is accepted, not just Atlas. To\n" + - "configure remote state, use the `terraform remote config`\n" + - "command.") - return 1 - } - */ - // Check if the path is a plan plan, err := c.Plan(configPath) if err != nil { @@ -125,6 +107,17 @@ func (c *PushCommand) Run(args []string) int { return 1 } + // We require a non-local backend + if c.IsLocalBackend(b) { + c.Ui.Error( + "Remote state is not enabled. For Atlas to run Terraform\n" + + "for you, remote state must be used and configured. Remote\n" + + "state via any backend is accepted, not just Atlas. To\n" + + "configure remote state, use the `terraform remote config`\n" + + "command.") + return 1 + } + // We require a local backend local, ok := b.(backend.Local) if !ok { diff --git a/command/push_test.go b/command/push_test.go index 94573765f..0d1f25413 100644 --- a/command/push_test.go +++ b/command/push_test.go @@ -9,9 +9,11 @@ import ( "path/filepath" "reflect" "sort" + "strings" "testing" atlas "github.com/hashicorp/atlas-go/v1" + "github.com/hashicorp/terraform/helper/copy" "github.com/hashicorp/terraform/terraform" "github.com/mitchellh/cli" ) @@ -662,6 +664,12 @@ func TestPush_noState(t *testing.T) { } func TestPush_noRemoteState(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + copy.CopyDir(testFixturePath("push-no-remote"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + state := &terraform.State{ Modules: []*terraform.ModuleState{ &terraform.ModuleState{ @@ -679,19 +687,32 @@ func TestPush_noRemoteState(t *testing.T) { } statePath := testStateFile(t, state) + // 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{ Ui: ui, }, + client: client, } args := []string{ + "-vcs=false", "-state", statePath, + td, } if code := c.Run(args); code != 1 { t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) } + + errStr := ui.ErrorWriter.String() + if !strings.Contains(errStr, "Remote state") { + t.Fatalf("bad: %s", errStr) + } } func TestPush_plan(t *testing.T) { diff --git a/command/test-fixtures/push-no-remote/main.tf b/command/test-fixtures/push-no-remote/main.tf new file mode 100644 index 000000000..265162636 --- /dev/null +++ b/command/test-fixtures/push-no-remote/main.tf @@ -0,0 +1,5 @@ +resource "aws_instance" "foo" {} + +atlas { + name = "foo" +} From 87201ec854b84e308bd2a78ac684df4b70b91fd0 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 16 Mar 2017 10:52:58 -0700 Subject: [PATCH 2/2] command/push: test for push with new backends --- command/push_test.go | 64 +++++++++++++++++++ .../test-fixtures/push-backend-new/main.tf | 5 ++ 2 files changed, 69 insertions(+) create mode 100644 command/test-fixtures/push-backend-new/main.tf diff --git a/command/push_test.go b/command/push_test.go index 0d1f25413..88c169ade 100644 --- a/command/push_test.go +++ b/command/push_test.go @@ -75,6 +75,70 @@ func TestPush_good(t *testing.T) { } } +func TestPush_goodBackendInit(t *testing.T) { + // Create a temporary working directory that is empty + td := tempDir(t) + copy.CopyDir(testFixturePath("push-backend-new"), td) + defer os.RemoveAll(td) + defer testChdir(t, td)() + + // init backend + ui := new(cli.MockUi) + ci := &InitCommand{ + Meta: Meta{ + Ui: ui, + }, + } + if code := ci.Run(nil); code != 0 { + t.Fatalf("bad: %d\n%s", code, ui.ErrorWriter) + } + + // 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, + } + + args := []string{ + "-vcs=false", + td, + } + if code := c.Run(args); code != 0 { + t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String()) + } + + actual := testArchiveStr(t, archivePath) + expected := []string{ + // Expected weird behavior, doesn't affect unpackaging + ".terraform/", + ".terraform/", + ".terraform/terraform.tfstate", + ".terraform/terraform.tfstate", + "main.tf", + } + if !reflect.DeepEqual(actual, expected) { + t.Fatalf("bad: %#v", actual) + } + + variables := make(map[string]interface{}) + if !reflect.DeepEqual(client.UpsertOptions.Variables, variables) { + t.Fatalf("bad: %#v", client.UpsertOptions) + } + + if client.UpsertOptions.Name != "hello" { + t.Fatalf("bad: %#v", client.UpsertOptions) + } +} + func TestPush_noUploadModules(t *testing.T) { // Path where the archive will be "uploaded" to archivePath := testTempFile(t) diff --git a/command/test-fixtures/push-backend-new/main.tf b/command/test-fixtures/push-backend-new/main.tf new file mode 100644 index 000000000..68a49b44a --- /dev/null +++ b/command/test-fixtures/push-backend-new/main.tf @@ -0,0 +1,5 @@ +terraform { + backend "inmem" {} +} + +atlas { name = "hello" }