Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

command/push: properly test for local state #12773

Merged
merged 2 commits into from
Mar 16, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions command/meta_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
29 changes: 11 additions & 18 deletions command/push.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down
85 changes: 85 additions & 0 deletions command/push_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
)
Expand Down Expand Up @@ -73,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)
Expand Down Expand Up @@ -662,6 +728,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{
Expand All @@ -679,19 +751,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) {
Expand Down
5 changes: 5 additions & 0 deletions command/test-fixtures/push-backend-new/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
terraform {
backend "inmem" {}
}

atlas { name = "hello" }
5 changes: 5 additions & 0 deletions command/test-fixtures/push-no-remote/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
resource "aws_instance" "foo" {}

atlas {
name = "foo"
}