2017-01-19 05:50:04 +01:00
|
|
|
package command
|
|
|
|
|
|
|
|
import (
|
2017-01-27 17:28:27 +01:00
|
|
|
"io/ioutil"
|
2017-01-19 05:50:04 +01:00
|
|
|
"os"
|
|
|
|
"path/filepath"
|
2017-03-01 21:35:59 +01:00
|
|
|
"reflect"
|
|
|
|
"sort"
|
2017-01-19 05:50:04 +01:00
|
|
|
"strings"
|
|
|
|
"testing"
|
|
|
|
|
2017-02-28 19:13:03 +01:00
|
|
|
"github.com/hashicorp/terraform/backend"
|
2018-07-04 12:07:44 +02:00
|
|
|
backendInit "github.com/hashicorp/terraform/backend/init"
|
|
|
|
backendLocal "github.com/hashicorp/terraform/backend/local"
|
2017-01-19 05:50:04 +01:00
|
|
|
"github.com/hashicorp/terraform/helper/copy"
|
2017-02-03 19:07:34 +01:00
|
|
|
"github.com/hashicorp/terraform/state"
|
2017-01-19 05:50:04 +01:00
|
|
|
"github.com/hashicorp/terraform/terraform"
|
|
|
|
"github.com/mitchellh/cli"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Test empty directory with no config/state creates a local state.
|
|
|
|
func TestMetaBackend_emptyDir(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
os.MkdirAll(td, 0755)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
b, err := m.Backend(&BackendOpts{Init: true})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write some state
|
2017-02-28 19:13:03 +01:00
|
|
|
s, err := b.State(backend.DefaultStateName)
|
2017-01-19 05:50:04 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
s.WriteState(testState())
|
|
|
|
if err := s.PersistState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify it exists where we expect it to
|
2017-01-28 02:53:43 +01:00
|
|
|
if isEmptyState(DefaultStateFilename) {
|
|
|
|
t.Fatalf("no state was written")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify no backup since it was empty to start
|
2017-01-28 02:53:43 +01:00
|
|
|
if !isEmptyState(DefaultStateFilename + DefaultBackupExtension) {
|
|
|
|
t.Fatal("backup state should be empty")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify no backend state was made
|
2017-01-27 17:28:27 +01:00
|
|
|
if !isEmptyState(filepath.Join(m.DataDir(), DefaultStateFilename)) {
|
2017-01-30 23:38:32 +01:00
|
|
|
t.Fatal("backend state should be empty")
|
2017-01-27 17:28:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// check for no state. Either the file doesn't exist, or is empty
|
|
|
|
func isEmptyState(path string) bool {
|
|
|
|
fi, err := os.Stat(path)
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
if fi.Size() == 0 {
|
|
|
|
return true
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
2017-01-27 17:28:27 +01:00
|
|
|
|
|
|
|
return false
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Test a directory with a legacy state and no config continues to
|
|
|
|
// use the legacy state.
|
|
|
|
func TestMetaBackend_emptyWithDefaultState(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
os.MkdirAll(td, 0755)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Write the legacy state
|
|
|
|
statePath := DefaultStateFilename
|
|
|
|
{
|
|
|
|
f, err := os.Create(statePath)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
err = terraform.WriteState(testState(), f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
b, err := m.Backend(&BackendOpts{Init: true})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
2017-02-28 19:13:03 +01:00
|
|
|
s, err := b.State(backend.DefaultStateName)
|
2017-01-19 05:50:04 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
2017-02-22 22:01:16 +01:00
|
|
|
if err := s.RefreshState(); err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
2017-01-19 05:50:04 +01:00
|
|
|
if actual := s.State().String(); actual != testState().String() {
|
|
|
|
t.Fatalf("bad: %s", actual)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify it exists where we expect it to
|
|
|
|
if _, err := os.Stat(DefaultStateFilename); err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
2017-01-27 17:28:27 +01:00
|
|
|
|
|
|
|
stateName := filepath.Join(m.DataDir(), DefaultStateFilename)
|
|
|
|
if !isEmptyState(stateName) {
|
|
|
|
t.Fatal("expected no state at", stateName)
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Write some state
|
|
|
|
next := testState()
|
|
|
|
next.Modules[0].Outputs["foo"] = &terraform.OutputState{Value: "bar"}
|
|
|
|
s.WriteState(testState())
|
|
|
|
if err := s.PersistState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify a backup was made since we're modifying a pre-existing state
|
2017-01-30 23:38:32 +01:00
|
|
|
if isEmptyState(DefaultStateFilename + DefaultBackupExtension) {
|
|
|
|
t.Fatal("backup state should not be empty")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test an empty directory with an explicit state path (outside the dir)
|
|
|
|
func TestMetaBackend_emptyWithExplicitState(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
os.MkdirAll(td, 0755)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Create another directory to store our state
|
|
|
|
stateDir := tempDir(t)
|
|
|
|
os.MkdirAll(stateDir, 0755)
|
|
|
|
defer os.RemoveAll(stateDir)
|
|
|
|
|
|
|
|
// Write the legacy state
|
|
|
|
statePath := filepath.Join(stateDir, "foo")
|
|
|
|
{
|
|
|
|
f, err := os.Create(statePath)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
err = terraform.WriteState(testState(), f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
m.statePath = statePath
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
b, err := m.Backend(&BackendOpts{Init: true})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
2017-02-28 19:13:03 +01:00
|
|
|
s, err := b.State(backend.DefaultStateName)
|
2017-01-19 05:50:04 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
2017-02-22 22:01:16 +01:00
|
|
|
if err := s.RefreshState(); err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
2017-01-19 05:50:04 +01:00
|
|
|
if actual := s.State().String(); actual != testState().String() {
|
|
|
|
t.Fatalf("bad: %s", actual)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify neither defaults exist
|
|
|
|
if _, err := os.Stat(DefaultStateFilename); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
2017-01-27 17:28:27 +01:00
|
|
|
|
|
|
|
stateName := filepath.Join(m.DataDir(), DefaultStateFilename)
|
|
|
|
if !isEmptyState(stateName) {
|
|
|
|
t.Fatal("expected no state at", stateName)
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Write some state
|
|
|
|
next := testState()
|
|
|
|
next.Modules[0].Outputs["foo"] = &terraform.OutputState{Value: "bar"}
|
|
|
|
s.WriteState(testState())
|
|
|
|
if err := s.PersistState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify a backup was made since we're modifying a pre-existing state
|
2017-01-30 23:38:32 +01:00
|
|
|
if isEmptyState(statePath + DefaultBackupExtension) {
|
|
|
|
t.Fatal("backup state should not be empty")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Empty directory with legacy remote state
|
|
|
|
func TestMetaBackend_emptyLegacyRemote(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
os.MkdirAll(td, 0755)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Create some legacy remote state
|
|
|
|
legacyState := testState()
|
|
|
|
_, srv := testRemoteState(t, legacyState, 200)
|
|
|
|
defer srv.Close()
|
|
|
|
statePath := testStateFileRemote(t, legacyState)
|
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
b, err := m.Backend(&BackendOpts{Init: true})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
2017-02-28 19:13:03 +01:00
|
|
|
s, err := b.State(backend.DefaultStateName)
|
2017-01-19 05:50:04 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
if err := s.RefreshState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
state := s.State()
|
|
|
|
if actual := state.String(); actual != legacyState.String() {
|
|
|
|
t.Fatalf("bad: %s", actual)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify we didn't setup the backend state
|
|
|
|
if !state.Backend.Empty() {
|
|
|
|
t.Fatal("shouldn't configure backend")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the default paths don't exist
|
|
|
|
if _, err := os.Stat(DefaultStateFilename); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify a backup doesn't exist
|
|
|
|
if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
if _, err := os.Stat(statePath + DefaultBackupExtension); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-02 23:07:49 +01:00
|
|
|
// Verify that interpolations result in an error
|
|
|
|
func TestMetaBackend_configureInterpolation(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("backend-new-interp"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
_, err := m.Backend(&BackendOpts{Init: true})
|
|
|
|
if err == nil {
|
|
|
|
t.Fatal("should error")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-19 05:50:04 +01:00
|
|
|
// Newly configured backend
|
|
|
|
func TestMetaBackend_configureNew(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("backend-new"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
b, err := m.Backend(&BackendOpts{Init: true})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
2017-02-28 19:13:03 +01:00
|
|
|
s, err := b.State(backend.DefaultStateName)
|
2017-01-19 05:50:04 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
if err := s.RefreshState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
state := s.State()
|
|
|
|
if state != nil {
|
|
|
|
t.Fatal("state should be nil")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write some state
|
|
|
|
state = terraform.NewState()
|
|
|
|
state.Lineage = "changing"
|
|
|
|
s.WriteState(state)
|
|
|
|
if err := s.PersistState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the state is where we expect
|
|
|
|
{
|
|
|
|
f, err := os.Open("local-state.tfstate")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
actual, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if actual.Lineage != state.Lineage {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the default paths don't exist
|
2017-01-30 23:38:32 +01:00
|
|
|
if _, err := os.Stat(DefaultStateFilename); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify a backup doesn't exist
|
2017-01-30 23:38:32 +01:00
|
|
|
if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Newly configured backend with prior local state and no remote state
|
|
|
|
func TestMetaBackend_configureNewWithState(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("backend-new-migrate"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Ask input
|
|
|
|
defer testInteractiveInput(t, []string{"yes"})()
|
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
b, err := m.Backend(&BackendOpts{Init: true})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
2017-02-28 19:13:03 +01:00
|
|
|
s, err := b.State(backend.DefaultStateName)
|
2017-01-19 05:50:04 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
if err := s.RefreshState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
state := s.State()
|
|
|
|
if state == nil {
|
|
|
|
t.Fatal("state is nil")
|
|
|
|
}
|
2017-02-09 21:35:49 +01:00
|
|
|
|
2017-01-19 05:50:04 +01:00
|
|
|
if state.Lineage != "backend-new-migrate" {
|
|
|
|
t.Fatalf("bad: %#v", state)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write some state
|
|
|
|
state = terraform.NewState()
|
|
|
|
state.Lineage = "changing"
|
|
|
|
s.WriteState(state)
|
|
|
|
if err := s.PersistState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the state is where we expect
|
|
|
|
{
|
|
|
|
f, err := os.Open("local-state.tfstate")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
actual, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if actual.Lineage != state.Lineage {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the default paths don't exist
|
2017-01-27 17:28:27 +01:00
|
|
|
if !isEmptyState(DefaultStateFilename) {
|
|
|
|
data, _ := ioutil.ReadFile(DefaultStateFilename)
|
|
|
|
|
|
|
|
t.Fatal("state should not exist, but contains:\n", string(data))
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify a backup does exist
|
2017-01-27 17:28:27 +01:00
|
|
|
if isEmptyState(DefaultStateFilename + DefaultBackupExtension) {
|
|
|
|
t.Fatal("backup state is empty or missing")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-30 21:53:21 +02:00
|
|
|
// Newly configured backend with matching local and remote state doesn't prompt
|
|
|
|
// for copy.
|
|
|
|
func TestMetaBackend_configureNewWithoutCopy(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("backend-new-migrate"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
if err := copy.CopyFile(DefaultStateFilename, "local-state.tfstate"); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
m.input = false
|
|
|
|
|
|
|
|
// init the backend
|
|
|
|
_, err := m.Backend(&BackendOpts{Init: true})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the state is where we expect
|
|
|
|
f, err := os.Open("local-state.tfstate")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
actual, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if actual.Lineage != "backend-new-migrate" {
|
|
|
|
t.Fatalf("incorrect state lineage: %q", actual.Lineage)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the default paths don't exist
|
|
|
|
if !isEmptyState(DefaultStateFilename) {
|
|
|
|
data, _ := ioutil.ReadFile(DefaultStateFilename)
|
|
|
|
|
|
|
|
t.Fatal("state should not exist, but contains:\n", string(data))
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify a backup does exist
|
|
|
|
if isEmptyState(DefaultStateFilename + DefaultBackupExtension) {
|
|
|
|
t.Fatal("backup state is empty or missing")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-19 05:50:04 +01:00
|
|
|
// Newly configured backend with prior local state and no remote state,
|
|
|
|
// but opting to not migrate.
|
|
|
|
func TestMetaBackend_configureNewWithStateNoMigrate(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("backend-new-migrate"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Ask input
|
|
|
|
defer testInteractiveInput(t, []string{"no"})()
|
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
b, err := m.Backend(&BackendOpts{Init: true})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
2017-02-28 19:13:03 +01:00
|
|
|
s, err := b.State(backend.DefaultStateName)
|
2017-01-19 05:50:04 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
if err := s.RefreshState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
if state := s.State(); state != nil {
|
|
|
|
t.Fatal("state is not nil")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the default paths don't exist
|
2017-01-27 17:28:27 +01:00
|
|
|
if !isEmptyState(DefaultStateFilename) {
|
|
|
|
data, _ := ioutil.ReadFile(DefaultStateFilename)
|
|
|
|
|
|
|
|
t.Fatal("state should not exist, but contains:\n", string(data))
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify a backup does exist
|
2017-01-27 17:28:27 +01:00
|
|
|
if isEmptyState(DefaultStateFilename + DefaultBackupExtension) {
|
|
|
|
t.Fatal("backup state is empty or missing")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Newly configured backend with prior local state and remote state
|
|
|
|
func TestMetaBackend_configureNewWithStateExisting(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("backend-new-migrate-existing"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
2017-03-21 20:05:51 +01:00
|
|
|
// suppress input
|
|
|
|
m.forceInitCopy = true
|
2017-01-19 05:50:04 +01:00
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
b, err := m.Backend(&BackendOpts{Init: true})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
2017-02-28 19:13:03 +01:00
|
|
|
s, err := b.State(backend.DefaultStateName)
|
2017-01-19 05:50:04 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
if err := s.RefreshState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
state := s.State()
|
|
|
|
if state == nil {
|
|
|
|
t.Fatal("state is nil")
|
|
|
|
}
|
|
|
|
if state.Lineage != "local" {
|
|
|
|
t.Fatalf("bad: %#v", state)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write some state
|
|
|
|
state = terraform.NewState()
|
|
|
|
state.Lineage = "changing"
|
|
|
|
s.WriteState(state)
|
|
|
|
if err := s.PersistState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the state is where we expect
|
|
|
|
{
|
|
|
|
f, err := os.Open("local-state.tfstate")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
actual, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if actual.Lineage != state.Lineage {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the default paths don't exist
|
2017-01-27 17:28:27 +01:00
|
|
|
if !isEmptyState(DefaultStateFilename) {
|
|
|
|
data, _ := ioutil.ReadFile(DefaultStateFilename)
|
|
|
|
|
|
|
|
t.Fatal("state should not exist, but contains:\n", string(data))
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify a backup does exist
|
2017-01-27 17:28:27 +01:00
|
|
|
if isEmptyState(DefaultStateFilename + DefaultBackupExtension) {
|
|
|
|
t.Fatal("backup state is empty or missing")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Newly configured backend with prior local state and remote state
|
|
|
|
func TestMetaBackend_configureNewWithStateExistingNoMigrate(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("backend-new-migrate-existing"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Ask input
|
|
|
|
defer testInteractiveInput(t, []string{"no"})()
|
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
b, err := m.Backend(&BackendOpts{Init: true})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
2017-02-28 19:13:03 +01:00
|
|
|
s, err := b.State(backend.DefaultStateName)
|
2017-01-19 05:50:04 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
if err := s.RefreshState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
state := s.State()
|
|
|
|
if state == nil {
|
|
|
|
t.Fatal("state is nil")
|
|
|
|
}
|
|
|
|
if state.Lineage != "remote" {
|
|
|
|
t.Fatalf("bad: %#v", state)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write some state
|
|
|
|
state = terraform.NewState()
|
|
|
|
state.Lineage = "changing"
|
|
|
|
s.WriteState(state)
|
|
|
|
if err := s.PersistState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the state is where we expect
|
|
|
|
{
|
|
|
|
f, err := os.Open("local-state.tfstate")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
actual, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if actual.Lineage != state.Lineage {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the default paths don't exist
|
2017-01-27 17:28:27 +01:00
|
|
|
if !isEmptyState(DefaultStateFilename) {
|
|
|
|
data, _ := ioutil.ReadFile(DefaultStateFilename)
|
|
|
|
|
|
|
|
t.Fatal("state should not exist, but contains:\n", string(data))
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify a backup does exist
|
2017-01-27 17:28:27 +01:00
|
|
|
if isEmptyState(DefaultStateFilename + DefaultBackupExtension) {
|
|
|
|
t.Fatal("backup state is empty or missing")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Newly configured backend with lgacy
|
|
|
|
func TestMetaBackend_configureNewLegacy(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("backend-new-legacy"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Ask input
|
|
|
|
defer testInteractiveInput(t, []string{"no"})()
|
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
b, err := m.Backend(&BackendOpts{Init: true})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
2017-02-28 19:13:03 +01:00
|
|
|
s, err := b.State(backend.DefaultStateName)
|
2017-01-19 05:50:04 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
if err := s.RefreshState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
state := s.State()
|
|
|
|
if state != nil {
|
|
|
|
t.Fatal("state should be nil")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify we have no configured legacy
|
|
|
|
{
|
|
|
|
path := filepath.Join(m.DataDir(), DefaultStateFilename)
|
|
|
|
f, err := os.Open(path)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
actual, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !actual.Remote.Empty() {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
if actual.Backend.Empty() {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write some state
|
|
|
|
state = terraform.NewState()
|
|
|
|
state.Lineage = "changing"
|
|
|
|
s.WriteState(state)
|
|
|
|
if err := s.PersistState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the state is where we expect
|
|
|
|
{
|
|
|
|
f, err := os.Open("local-state.tfstate")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
actual, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if actual.Lineage != state.Lineage {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the default paths don't exist
|
2017-01-27 17:28:27 +01:00
|
|
|
if !isEmptyState(DefaultStateFilename) {
|
|
|
|
data, _ := ioutil.ReadFile(DefaultStateFilename)
|
|
|
|
|
|
|
|
t.Fatal("state should not exist, but contains:\n", string(data))
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify a backup doesn't exist
|
2017-01-27 17:28:27 +01:00
|
|
|
if !isEmptyState(DefaultStateFilename + DefaultBackupExtension) {
|
|
|
|
data, _ := ioutil.ReadFile(DefaultStateFilename)
|
|
|
|
|
|
|
|
t.Fatal("backup should be empty, but contains:\n", string(data))
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Newly configured backend with legacy
|
|
|
|
func TestMetaBackend_configureNewLegacyCopy(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("backend-new-legacy"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
|
2017-03-21 20:05:51 +01:00
|
|
|
// suppress input
|
|
|
|
m.forceInitCopy = true
|
|
|
|
|
2017-01-19 05:50:04 +01:00
|
|
|
// Get the backend
|
|
|
|
b, err := m.Backend(&BackendOpts{Init: true})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
2017-02-28 19:13:03 +01:00
|
|
|
s, err := b.State(backend.DefaultStateName)
|
2017-01-19 05:50:04 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
if err := s.RefreshState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
state := s.State()
|
|
|
|
if state == nil {
|
|
|
|
t.Fatal("nil state")
|
|
|
|
}
|
|
|
|
if state.Lineage != "backend-new-legacy" {
|
|
|
|
t.Fatalf("bad: %#v", state)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify we have no configured legacy
|
|
|
|
{
|
|
|
|
path := filepath.Join(m.DataDir(), DefaultStateFilename)
|
|
|
|
f, err := os.Open(path)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
actual, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !actual.Remote.Empty() {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
if actual.Backend.Empty() {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-20 18:05:24 +01:00
|
|
|
// Verify we have no configured legacy in the state itself
|
|
|
|
{
|
|
|
|
if !state.Remote.Empty() {
|
|
|
|
t.Fatalf("legacy has remote state: %#v", state.Remote)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-19 05:50:04 +01:00
|
|
|
// Write some state
|
|
|
|
state = terraform.NewState()
|
|
|
|
state.Lineage = "changing"
|
|
|
|
s.WriteState(state)
|
|
|
|
if err := s.PersistState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the state is where we expect
|
|
|
|
{
|
|
|
|
f, err := os.Open("local-state.tfstate")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
actual, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if actual.Lineage != state.Lineage {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the default paths don't exist
|
|
|
|
if _, err := os.Stat(DefaultStateFilename); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify a backup doesn't exist
|
|
|
|
if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Saved backend state matching config
|
|
|
|
func TestMetaBackend_configuredUnchanged(t *testing.T) {
|
|
|
|
defer testChdir(t, testFixturePath("backend-unchanged"))()
|
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
b, err := m.Backend(&BackendOpts{Init: true})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
2017-02-28 19:13:03 +01:00
|
|
|
s, err := b.State(backend.DefaultStateName)
|
2017-01-19 05:50:04 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
if err := s.RefreshState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
state := s.State()
|
|
|
|
if state == nil {
|
|
|
|
t.Fatal("nil state")
|
|
|
|
}
|
|
|
|
if state.Lineage != "configuredUnchanged" {
|
|
|
|
t.Fatalf("bad: %#v", state)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the default paths don't exist
|
|
|
|
if _, err := os.Stat(DefaultStateFilename); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify a backup doesn't exist
|
|
|
|
if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Changing a configured backend
|
|
|
|
func TestMetaBackend_configuredChange(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("backend-change"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Ask input
|
|
|
|
defer testInteractiveInput(t, []string{"no"})()
|
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
b, err := m.Backend(&BackendOpts{Init: true})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
2017-02-28 19:13:03 +01:00
|
|
|
s, err := b.State(backend.DefaultStateName)
|
2017-01-19 05:50:04 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
if err := s.RefreshState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
state := s.State()
|
|
|
|
if state != nil {
|
|
|
|
t.Fatal("state should be nil")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the default paths don't exist
|
|
|
|
if _, err := os.Stat(DefaultStateFilename); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify a backup doesn't exist
|
|
|
|
if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Write some state
|
|
|
|
state = terraform.NewState()
|
|
|
|
state.Lineage = "changing"
|
|
|
|
s.WriteState(state)
|
|
|
|
if err := s.PersistState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the state is where we expect
|
|
|
|
{
|
|
|
|
f, err := os.Open("local-state-2.tfstate")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
actual, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if actual.Lineage != state.Lineage {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify no local state
|
|
|
|
if _, err := os.Stat(DefaultStateFilename); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify no local backup
|
|
|
|
if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-20 23:29:40 +02:00
|
|
|
// Reconfiguring with an already configured backend.
|
|
|
|
// This should ignore the existing backend config, and configure the new
|
|
|
|
// backend is if this is the first time.
|
|
|
|
func TestMetaBackend_reconfigureChange(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("backend-change-single-to-single"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Register the single-state backend
|
2018-07-04 12:07:44 +02:00
|
|
|
backendInit.Set("local-single", backendLocal.TestNewLocalSingle)
|
|
|
|
defer backendInit.Set("local-single", nil)
|
2017-04-20 23:29:40 +02:00
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
|
|
|
|
// this should not ask for input
|
|
|
|
m.input = false
|
|
|
|
|
|
|
|
// cli flag -reconfigure
|
|
|
|
m.reconfigure = true
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
b, err := m.Backend(&BackendOpts{Init: true})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
|
|
|
s, err := b.State(backend.DefaultStateName)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
if err := s.RefreshState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
newState := s.State()
|
|
|
|
if newState != nil || !newState.Empty() {
|
|
|
|
t.Fatal("state should be nil/empty after forced reconfiguration")
|
|
|
|
}
|
|
|
|
|
|
|
|
// verify that the old state is still there
|
|
|
|
s = (&state.LocalState{Path: "local-state.tfstate"})
|
|
|
|
if err := s.RefreshState(); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
oldState := s.State()
|
|
|
|
if oldState == nil || oldState.Empty() {
|
|
|
|
t.Fatal("original state should be untouched")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-19 05:50:04 +01:00
|
|
|
// Changing a configured backend, copying state
|
|
|
|
func TestMetaBackend_configuredChangeCopy(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("backend-change"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Ask input
|
|
|
|
defer testInteractiveInput(t, []string{"yes", "yes"})()
|
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
b, err := m.Backend(&BackendOpts{Init: true})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
2017-02-28 19:13:03 +01:00
|
|
|
s, err := b.State(backend.DefaultStateName)
|
2017-01-19 05:50:04 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
if err := s.RefreshState(); err != nil {
|
2017-03-01 19:59:17 +01:00
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
state := s.State()
|
|
|
|
if state == nil {
|
|
|
|
t.Fatal("state should not be nil")
|
|
|
|
}
|
|
|
|
if state.Lineage != "backend-change" {
|
|
|
|
t.Fatalf("bad: %#v", state)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify no local state
|
|
|
|
if _, err := os.Stat(DefaultStateFilename); err == nil {
|
|
|
|
t.Fatal("file should not exist")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify no local backup
|
|
|
|
if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
|
|
|
|
t.Fatal("file should not exist")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Changing a configured backend that supports only single states to another
|
|
|
|
// backend that only supports single states.
|
|
|
|
func TestMetaBackend_configuredChangeCopy_singleState(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("backend-change-single-to-single"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Register the single-state backend
|
2018-07-04 12:07:44 +02:00
|
|
|
backendInit.Set("local-single", backendLocal.TestNewLocalSingle)
|
|
|
|
defer backendInit.Set("local-single", nil)
|
2017-03-01 19:59:17 +01:00
|
|
|
|
|
|
|
// Ask input
|
|
|
|
defer testInputMap(t, map[string]string{
|
|
|
|
"backend-migrate-to-new": "yes",
|
|
|
|
"backend-migrate-copy-to-empty": "yes",
|
|
|
|
})()
|
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
b, err := m.Backend(&BackendOpts{Init: true})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
|
|
|
s, err := b.State(backend.DefaultStateName)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
if err := s.RefreshState(); err != nil {
|
2017-03-01 20:08:39 +01:00
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
state := s.State()
|
|
|
|
if state == nil {
|
|
|
|
t.Fatal("state should not be nil")
|
|
|
|
}
|
|
|
|
if state.Lineage != "backend-change" {
|
|
|
|
t.Fatalf("bad: %#v", state)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify no local state
|
|
|
|
if _, err := os.Stat(DefaultStateFilename); err == nil {
|
|
|
|
t.Fatal("file should not exist")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify no local backup
|
|
|
|
if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
|
|
|
|
t.Fatal("file should not exist")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Changing a configured backend that supports multi-state to a
|
|
|
|
// backend that only supports single states. The multi-state only has
|
|
|
|
// a default state.
|
|
|
|
func TestMetaBackend_configuredChangeCopy_multiToSingleDefault(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("backend-change-multi-default-to-single"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Register the single-state backend
|
2018-07-04 12:07:44 +02:00
|
|
|
backendInit.Set("local-single", backendLocal.TestNewLocalSingle)
|
|
|
|
defer backendInit.Set("local-single", nil)
|
2017-03-01 20:08:39 +01:00
|
|
|
|
|
|
|
// Ask input
|
|
|
|
defer testInputMap(t, map[string]string{
|
|
|
|
"backend-migrate-to-new": "yes",
|
|
|
|
"backend-migrate-copy-to-empty": "yes",
|
|
|
|
})()
|
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
b, err := m.Backend(&BackendOpts{Init: true})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
|
|
|
s, err := b.State(backend.DefaultStateName)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
if err := s.RefreshState(); err != nil {
|
2017-01-19 05:50:04 +01:00
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
state := s.State()
|
|
|
|
if state == nil {
|
|
|
|
t.Fatal("state should not be nil")
|
|
|
|
}
|
|
|
|
if state.Lineage != "backend-change" {
|
|
|
|
t.Fatalf("bad: %#v", state)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify no local state
|
|
|
|
if _, err := os.Stat(DefaultStateFilename); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify no local backup
|
|
|
|
if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-01 20:34:45 +01:00
|
|
|
// Changing a configured backend that supports multi-state to a
|
|
|
|
// backend that only supports single states.
|
|
|
|
func TestMetaBackend_configuredChangeCopy_multiToSingle(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("backend-change-multi-to-single"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Register the single-state backend
|
2018-07-04 12:07:44 +02:00
|
|
|
backendInit.Set("local-single", backendLocal.TestNewLocalSingle)
|
|
|
|
defer backendInit.Set("local-single", nil)
|
2017-03-01 20:34:45 +01:00
|
|
|
|
|
|
|
// Ask input
|
|
|
|
defer testInputMap(t, map[string]string{
|
|
|
|
"backend-migrate-to-new": "yes",
|
|
|
|
"backend-migrate-multistate-to-single": "yes",
|
|
|
|
"backend-migrate-copy-to-empty": "yes",
|
|
|
|
})()
|
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
b, err := m.Backend(&BackendOpts{Init: true})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
|
|
|
s, err := b.State(backend.DefaultStateName)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
if err := s.RefreshState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
state := s.State()
|
|
|
|
if state == nil {
|
|
|
|
t.Fatal("state should not be nil")
|
|
|
|
}
|
|
|
|
if state.Lineage != "backend-change" {
|
|
|
|
t.Fatalf("bad: %#v", state)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify no local state
|
2017-03-01 20:40:28 +01:00
|
|
|
if _, err := os.Stat(DefaultStateFilename); err == nil {
|
|
|
|
t.Fatal("file should not exist")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify no local backup
|
|
|
|
if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
|
|
|
|
t.Fatal("file should not exist")
|
|
|
|
}
|
|
|
|
|
2017-05-31 00:06:13 +02:00
|
|
|
// Verify existing workspaces exist
|
2018-07-04 12:07:44 +02:00
|
|
|
envPath := filepath.Join(backendLocal.DefaultWorkspaceDir, "env2", backendLocal.DefaultStateFilename)
|
2017-03-01 20:40:28 +01:00
|
|
|
if _, err := os.Stat(envPath); err != nil {
|
|
|
|
t.Fatal("env should exist")
|
|
|
|
}
|
2017-03-16 20:31:50 +01:00
|
|
|
|
|
|
|
// Verify we are now in the default env, or we may not be able to access the new backend
|
2017-05-31 02:13:43 +02:00
|
|
|
if env := m.Workspace(); env != backend.DefaultStateName {
|
2017-03-16 20:31:50 +01:00
|
|
|
t.Fatal("using non-default env with single-env backend")
|
|
|
|
}
|
2017-03-01 20:40:28 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Changing a configured backend that supports multi-state to a
|
|
|
|
// backend that only supports single states.
|
|
|
|
func TestMetaBackend_configuredChangeCopy_multiToSingleCurrentEnv(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("backend-change-multi-to-single"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Register the single-state backend
|
2018-07-04 12:07:44 +02:00
|
|
|
backendInit.Set("local-single", backendLocal.TestNewLocalSingle)
|
|
|
|
defer backendInit.Set("local-single", nil)
|
2017-03-01 20:40:28 +01:00
|
|
|
|
|
|
|
// Ask input
|
|
|
|
defer testInputMap(t, map[string]string{
|
|
|
|
"backend-migrate-to-new": "yes",
|
|
|
|
"backend-migrate-multistate-to-single": "yes",
|
|
|
|
"backend-migrate-copy-to-empty": "yes",
|
|
|
|
})()
|
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
|
|
|
|
// Change env
|
2017-05-31 02:13:43 +02:00
|
|
|
if err := m.SetWorkspace("env2"); err != nil {
|
2017-03-01 20:40:28 +01:00
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
b, err := m.Backend(&BackendOpts{Init: true})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
|
|
|
s, err := b.State(backend.DefaultStateName)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
if err := s.RefreshState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
state := s.State()
|
|
|
|
if state == nil {
|
|
|
|
t.Fatal("state should not be nil")
|
|
|
|
}
|
|
|
|
if state.Lineage != "backend-change-env2" {
|
|
|
|
t.Fatalf("bad: %#v", state)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify no local state
|
2017-03-01 20:34:45 +01:00
|
|
|
if _, err := os.Stat(DefaultStateFilename); err == nil {
|
|
|
|
t.Fatal("file should not exist")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify no local backup
|
|
|
|
if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
|
|
|
|
t.Fatal("file should not exist")
|
|
|
|
}
|
|
|
|
|
2017-05-31 00:06:13 +02:00
|
|
|
// Verify existing workspaces exist
|
2018-07-04 12:07:44 +02:00
|
|
|
envPath := filepath.Join(backendLocal.DefaultWorkspaceDir, "env2", backendLocal.DefaultStateFilename)
|
2017-03-01 20:34:45 +01:00
|
|
|
if _, err := os.Stat(envPath); err != nil {
|
|
|
|
t.Fatal("env should exist")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-01 21:35:59 +01:00
|
|
|
// Changing a configured backend that supports multi-state to a
|
|
|
|
// backend that also supports multi-state.
|
|
|
|
func TestMetaBackend_configuredChangeCopy_multiToMulti(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("backend-change-multi-to-multi"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Ask input
|
|
|
|
defer testInputMap(t, map[string]string{
|
|
|
|
"backend-migrate-to-new": "yes",
|
|
|
|
"backend-migrate-multistate-to-multistate": "yes",
|
|
|
|
})()
|
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
b, err := m.Backend(&BackendOpts{Init: true})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check resulting states
|
|
|
|
states, err := b.States()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
sort.Strings(states)
|
|
|
|
expected := []string{"default", "env2"}
|
|
|
|
if !reflect.DeepEqual(states, expected) {
|
|
|
|
t.Fatalf("bad: %#v", states)
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// Check the default state
|
|
|
|
s, err := b.State(backend.DefaultStateName)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
if err := s.RefreshState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
state := s.State()
|
|
|
|
if state == nil {
|
|
|
|
t.Fatal("state should not be nil")
|
|
|
|
}
|
|
|
|
if state.Lineage != "backend-change" {
|
|
|
|
t.Fatalf("bad: %#v", state)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
// Check the other state
|
|
|
|
s, err := b.State("env2")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
if err := s.RefreshState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
state := s.State()
|
|
|
|
if state == nil {
|
|
|
|
t.Fatal("state should not be nil")
|
|
|
|
}
|
|
|
|
if state.Lineage != "backend-change-env2" {
|
|
|
|
t.Fatalf("bad: %#v", state)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify no local backup
|
|
|
|
if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
|
|
|
|
t.Fatal("file should not exist")
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2017-05-31 00:06:13 +02:00
|
|
|
// Verify existing workspaces exist
|
2018-07-04 12:07:44 +02:00
|
|
|
envPath := filepath.Join(backendLocal.DefaultWorkspaceDir, "env2", backendLocal.DefaultStateFilename)
|
2017-03-01 21:35:59 +01:00
|
|
|
if _, err := os.Stat(envPath); err != nil {
|
|
|
|
t.Fatal("env should exist")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2017-05-31 00:06:13 +02:00
|
|
|
// Verify new workspaces exist
|
2018-07-04 12:07:44 +02:00
|
|
|
envPath := filepath.Join("envdir-new", "env2", backendLocal.DefaultStateFilename)
|
2017-03-01 21:35:59 +01:00
|
|
|
if _, err := os.Stat(envPath); err != nil {
|
|
|
|
t.Fatal("env should exist")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-19 05:50:04 +01:00
|
|
|
// Unsetting a saved backend
|
|
|
|
func TestMetaBackend_configuredUnset(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("backend-unset"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Ask input
|
|
|
|
defer testInteractiveInput(t, []string{"no"})()
|
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
b, err := m.Backend(&BackendOpts{Init: true})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
2017-02-28 19:13:03 +01:00
|
|
|
s, err := b.State(backend.DefaultStateName)
|
2017-01-19 05:50:04 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
if err := s.RefreshState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
state := s.State()
|
|
|
|
if state != nil {
|
|
|
|
t.Fatal("state should be nil")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the default paths don't exist
|
2017-01-27 17:28:27 +01:00
|
|
|
if !isEmptyState(DefaultStateFilename) {
|
|
|
|
data, _ := ioutil.ReadFile(DefaultStateFilename)
|
|
|
|
t.Fatal("state should not exist, but contains:\n", string(data))
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify a backup doesn't exist
|
2017-01-27 17:28:27 +01:00
|
|
|
if !isEmptyState(DefaultStateFilename + DefaultBackupExtension) {
|
|
|
|
data, _ := ioutil.ReadFile(DefaultStateFilename + DefaultBackupExtension)
|
|
|
|
t.Fatal("backup should not exist, but contains:\n", string(data))
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify we have no configured backend/legacy
|
|
|
|
path := filepath.Join(m.DataDir(), DefaultStateFilename)
|
|
|
|
if _, err := os.Stat(path); err == nil {
|
|
|
|
f, err := os.Open(path)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
actual, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !actual.Remote.Empty() {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
if !actual.Backend.Empty() {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write some state
|
|
|
|
s.WriteState(testState())
|
|
|
|
if err := s.PersistState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify it exists where we expect it to
|
2017-01-27 17:28:27 +01:00
|
|
|
if isEmptyState(DefaultStateFilename) {
|
|
|
|
t.Fatal(DefaultStateFilename, "is empty")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify no backup since it was empty to start
|
2017-01-27 17:28:27 +01:00
|
|
|
if !isEmptyState(DefaultStateFilename + DefaultBackupExtension) {
|
|
|
|
data, _ := ioutil.ReadFile(DefaultStateFilename + DefaultBackupExtension)
|
|
|
|
t.Fatal("backup state should be empty, but contains:\n", string(data))
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unsetting a saved backend and copying the remote state
|
|
|
|
func TestMetaBackend_configuredUnsetCopy(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("backend-unset"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Ask input
|
|
|
|
defer testInteractiveInput(t, []string{"yes", "yes"})()
|
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
b, err := m.Backend(&BackendOpts{Init: true})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
2017-02-28 19:13:03 +01:00
|
|
|
s, err := b.State(backend.DefaultStateName)
|
2017-01-19 05:50:04 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
if err := s.RefreshState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
state := s.State()
|
|
|
|
if state == nil {
|
|
|
|
t.Fatal("state is nil")
|
|
|
|
}
|
|
|
|
if state.Lineage != "configuredUnset" {
|
|
|
|
t.Fatalf("bad: %#v", state)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify a backup doesn't exist
|
2017-01-28 02:53:43 +01:00
|
|
|
if !isEmptyState(DefaultStateFilename + DefaultBackupExtension) {
|
|
|
|
t.Fatalf("backup state should be empty")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify we have no configured backend/legacy
|
|
|
|
path := filepath.Join(m.DataDir(), DefaultStateFilename)
|
|
|
|
if _, err := os.Stat(path); err == nil {
|
|
|
|
f, err := os.Open(path)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
actual, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !actual.Remote.Empty() {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
if !actual.Backend.Empty() {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write some state
|
|
|
|
s.WriteState(testState())
|
|
|
|
if err := s.PersistState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify it exists where we expect it to
|
|
|
|
if _, err := os.Stat(DefaultStateFilename); err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify a backup since it wasn't empty to start
|
2017-01-30 23:38:32 +01:00
|
|
|
if isEmptyState(DefaultStateFilename + DefaultBackupExtension) {
|
|
|
|
t.Fatal("backup is empty")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Saved backend state matching config, with legacy
|
|
|
|
func TestMetaBackend_configuredUnchangedLegacy(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("backend-unchanged-with-legacy"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Ask input
|
|
|
|
defer testInteractiveInput(t, []string{"no"})()
|
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
b, err := m.Backend(&BackendOpts{Init: true})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
2017-02-28 19:13:03 +01:00
|
|
|
s, err := b.State(backend.DefaultStateName)
|
2017-01-19 05:50:04 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
if err := s.RefreshState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
state := s.State()
|
|
|
|
if state == nil {
|
|
|
|
t.Fatal("state is nil")
|
|
|
|
}
|
|
|
|
if state.Lineage != "configured" {
|
|
|
|
t.Fatalf("bad: %#v", state)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the default paths don't exist
|
|
|
|
if _, err := os.Stat(DefaultStateFilename); err == nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify a backup doesn't exist
|
|
|
|
if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify we have no configured legacy
|
|
|
|
{
|
|
|
|
path := filepath.Join(m.DataDir(), DefaultStateFilename)
|
|
|
|
f, err := os.Open(path)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
actual, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !actual.Remote.Empty() {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
if actual.Backend.Empty() {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write some state
|
|
|
|
state = terraform.NewState()
|
|
|
|
state.Lineage = "changing"
|
|
|
|
s.WriteState(state)
|
|
|
|
if err := s.PersistState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the state is where we expect
|
|
|
|
{
|
|
|
|
f, err := os.Open("local-state.tfstate")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
actual, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if actual.Lineage != state.Lineage {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify no local state
|
|
|
|
if _, err := os.Stat(DefaultStateFilename); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify no local backup
|
|
|
|
if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Saved backend state matching config, with legacy
|
|
|
|
func TestMetaBackend_configuredUnchangedLegacyCopy(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("backend-unchanged-with-legacy"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
2017-03-21 20:05:51 +01:00
|
|
|
m.forceInitCopy = true
|
2017-01-19 05:50:04 +01:00
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
b, err := m.Backend(&BackendOpts{Init: true})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
2017-02-28 19:13:03 +01:00
|
|
|
s, err := b.State(backend.DefaultStateName)
|
2017-01-19 05:50:04 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
if err := s.RefreshState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
state := s.State()
|
|
|
|
if state == nil {
|
|
|
|
t.Fatal("state is nil")
|
|
|
|
}
|
|
|
|
if state.Lineage != "backend-unchanged-with-legacy" {
|
|
|
|
t.Fatalf("bad: %#v", state)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the default paths don't exist
|
|
|
|
if _, err := os.Stat(DefaultStateFilename); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify a backup doesn't exist
|
|
|
|
if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify we have no configured legacy
|
|
|
|
{
|
|
|
|
path := filepath.Join(m.DataDir(), DefaultStateFilename)
|
|
|
|
f, err := os.Open(path)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
actual, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !actual.Remote.Empty() {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
if actual.Backend.Empty() {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write some state
|
|
|
|
state = terraform.NewState()
|
|
|
|
state.Lineage = "changing"
|
|
|
|
s.WriteState(state)
|
|
|
|
if err := s.PersistState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the state is where we expect
|
|
|
|
{
|
|
|
|
f, err := os.Open("local-state.tfstate")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
actual, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if actual.Lineage != state.Lineage {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify no local state
|
|
|
|
if _, err := os.Stat(DefaultStateFilename); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify no local backup
|
|
|
|
if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Saved backend state, new config, legacy remote state
|
|
|
|
func TestMetaBackend_configuredChangedLegacy(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("backend-changed-with-legacy"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Ask input
|
|
|
|
defer testInteractiveInput(t, []string{"no", "no"})()
|
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
b, err := m.Backend(&BackendOpts{Init: true})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
2017-02-28 19:13:03 +01:00
|
|
|
s, err := b.State(backend.DefaultStateName)
|
2017-01-19 05:50:04 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
if err := s.RefreshState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
state := s.State()
|
|
|
|
if state != nil {
|
|
|
|
t.Fatal("state should be nil")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the default paths don't exist
|
|
|
|
if _, err := os.Stat(DefaultStateFilename); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify a backup doesn't exist
|
|
|
|
if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify we have no configured legacy
|
|
|
|
{
|
|
|
|
path := filepath.Join(m.DataDir(), DefaultStateFilename)
|
|
|
|
f, err := os.Open(path)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
actual, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !actual.Remote.Empty() {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
if actual.Backend.Empty() {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write some state
|
|
|
|
state = terraform.NewState()
|
|
|
|
state.Lineage = "changing"
|
|
|
|
s.WriteState(state)
|
|
|
|
if err := s.PersistState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the state is where we expect
|
|
|
|
{
|
|
|
|
f, err := os.Open("local-state-2.tfstate")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
actual, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if actual.Lineage != state.Lineage {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify no local state
|
|
|
|
if _, err := os.Stat(DefaultStateFilename); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify no local backup
|
|
|
|
if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Saved backend state, new config, legacy remote state
|
|
|
|
func TestMetaBackend_configuredChangedLegacyCopyBackend(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("backend-changed-with-legacy"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Ask input
|
2017-12-18 17:13:18 +01:00
|
|
|
defer testInteractiveInput(t, []string{"yes", "no"})()
|
2017-01-19 05:50:04 +01:00
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
b, err := m.Backend(&BackendOpts{Init: true})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
2017-02-28 19:13:03 +01:00
|
|
|
s, err := b.State(backend.DefaultStateName)
|
2017-01-19 05:50:04 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
if err := s.RefreshState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
state := s.State()
|
|
|
|
if state == nil {
|
|
|
|
t.Fatal("state is nil")
|
|
|
|
}
|
|
|
|
if state.Lineage != "configured" {
|
|
|
|
t.Fatalf("bad: %#v", state)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the default paths don't exist
|
|
|
|
if _, err := os.Stat(DefaultStateFilename); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify a backup doesn't exist
|
|
|
|
if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify we have no configured legacy
|
|
|
|
{
|
|
|
|
path := filepath.Join(m.DataDir(), DefaultStateFilename)
|
|
|
|
f, err := os.Open(path)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
actual, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !actual.Remote.Empty() {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
if actual.Backend.Empty() {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write some state
|
|
|
|
state = terraform.NewState()
|
|
|
|
state.Lineage = "changing"
|
|
|
|
s.WriteState(state)
|
|
|
|
if err := s.PersistState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the state is where we expect
|
|
|
|
{
|
|
|
|
f, err := os.Open("local-state-2.tfstate")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
actual, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if actual.Lineage != state.Lineage {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify no local state
|
|
|
|
if _, err := os.Stat(DefaultStateFilename); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify no local backup
|
|
|
|
if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Saved backend state, new config, legacy remote state
|
|
|
|
func TestMetaBackend_configuredChangedLegacyCopyLegacy(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("backend-changed-with-legacy"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Ask input
|
|
|
|
defer testInteractiveInput(t, []string{"no", "yes", "yes"})()
|
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
b, err := m.Backend(&BackendOpts{Init: true})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
2017-02-28 19:13:03 +01:00
|
|
|
s, err := b.State(backend.DefaultStateName)
|
2017-01-19 05:50:04 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
if err := s.RefreshState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
state := s.State()
|
|
|
|
if state == nil {
|
|
|
|
t.Fatal("state is nil")
|
|
|
|
}
|
|
|
|
if state.Lineage != "legacy" {
|
|
|
|
t.Fatalf("bad: %#v", state)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the default paths don't exist
|
|
|
|
if _, err := os.Stat(DefaultStateFilename); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify a backup doesn't exist
|
|
|
|
if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify we have no configured legacy
|
|
|
|
{
|
|
|
|
path := filepath.Join(m.DataDir(), DefaultStateFilename)
|
|
|
|
f, err := os.Open(path)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
actual, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !actual.Remote.Empty() {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
if actual.Backend.Empty() {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write some state
|
|
|
|
state = terraform.NewState()
|
|
|
|
state.Lineage = "changing"
|
|
|
|
s.WriteState(state)
|
|
|
|
if err := s.PersistState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the state is where we expect
|
|
|
|
{
|
|
|
|
f, err := os.Open("local-state-2.tfstate")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
actual, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if actual.Lineage != state.Lineage {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify no local state
|
|
|
|
if _, err := os.Stat(DefaultStateFilename); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify no local backup
|
|
|
|
if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Saved backend state, new config, legacy remote state
|
|
|
|
func TestMetaBackend_configuredChangedLegacyCopyBoth(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("backend-changed-with-legacy"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Ask input
|
|
|
|
defer testInteractiveInput(t, []string{"yes", "yes", "yes", "yes"})()
|
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
b, err := m.Backend(&BackendOpts{Init: true})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
2017-02-28 19:13:03 +01:00
|
|
|
s, err := b.State(backend.DefaultStateName)
|
2017-01-19 05:50:04 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
if err := s.RefreshState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
state := s.State()
|
|
|
|
if state == nil {
|
|
|
|
t.Fatal("state is nil")
|
|
|
|
}
|
|
|
|
if state.Lineage != "legacy" {
|
|
|
|
t.Fatalf("bad: %#v", state)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the default paths don't exist
|
|
|
|
if _, err := os.Stat(DefaultStateFilename); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify a backup doesn't exist
|
|
|
|
if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify we have no configured legacy
|
|
|
|
{
|
|
|
|
path := filepath.Join(m.DataDir(), DefaultStateFilename)
|
|
|
|
f, err := os.Open(path)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
actual, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !actual.Remote.Empty() {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
if actual.Backend.Empty() {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write some state
|
|
|
|
state = terraform.NewState()
|
|
|
|
state.Lineage = "changing"
|
|
|
|
s.WriteState(state)
|
|
|
|
if err := s.PersistState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the state is where we expect
|
|
|
|
{
|
|
|
|
f, err := os.Open("local-state-2.tfstate")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
actual, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if actual.Lineage != state.Lineage {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify no local state
|
|
|
|
if _, err := os.Stat(DefaultStateFilename); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify no local backup
|
|
|
|
if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Saved backend state, unset config, legacy remote state
|
|
|
|
func TestMetaBackend_configuredUnsetWithLegacyNoCopy(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("backend-unset-with-legacy"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Ask input
|
|
|
|
defer testInteractiveInput(t, []string{"no", "no"})()
|
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
b, err := m.Backend(&BackendOpts{Init: true})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
2017-02-28 19:13:03 +01:00
|
|
|
s, err := b.State(backend.DefaultStateName)
|
2017-01-19 05:50:04 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
if err := s.RefreshState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
state := s.State()
|
|
|
|
if state != nil {
|
|
|
|
t.Fatal("state should be nil")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the default paths dont exist since we had no state
|
2017-01-27 17:28:27 +01:00
|
|
|
if !isEmptyState(DefaultStateFilename) {
|
|
|
|
t.Fatal("state should be empty")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify a backup doesn't exist
|
2017-01-27 17:28:27 +01:00
|
|
|
if !isEmptyState(DefaultStateFilename + DefaultBackupExtension) {
|
|
|
|
t.Fatal("backup should be empty")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify we have no configured backend/legacy
|
|
|
|
path := filepath.Join(m.DataDir(), DefaultStateFilename)
|
|
|
|
if _, err := os.Stat(path); err == nil {
|
|
|
|
f, err := os.Open(path)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
actual, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !actual.Remote.Empty() {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
if !actual.Backend.Empty() {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write some state
|
|
|
|
state = terraform.NewState()
|
|
|
|
state.Lineage = "changing"
|
|
|
|
s.WriteState(state)
|
|
|
|
if err := s.PersistState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the state is where we expect
|
|
|
|
{
|
|
|
|
f, err := os.Open(DefaultStateFilename)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
actual, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if actual.Lineage != state.Lineage {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Saved backend state, unset config, legacy remote state
|
|
|
|
func TestMetaBackend_configuredUnsetWithLegacyCopyBackend(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("backend-unset-with-legacy"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Ask input
|
2017-12-18 17:13:18 +01:00
|
|
|
defer testInteractiveInput(t, []string{"yes", "no"})()
|
2017-01-19 05:50:04 +01:00
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
b, err := m.Backend(&BackendOpts{Init: true})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
2017-02-28 19:13:03 +01:00
|
|
|
s, err := b.State(backend.DefaultStateName)
|
2017-01-19 05:50:04 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
if err := s.RefreshState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
state := s.State()
|
|
|
|
if state == nil {
|
|
|
|
t.Fatal("state is nil")
|
|
|
|
}
|
|
|
|
if state.Lineage != "backend" {
|
|
|
|
t.Fatalf("bad: %#v", state)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the default paths exist
|
2017-01-28 02:53:43 +01:00
|
|
|
if isEmptyState(DefaultStateFilename) {
|
|
|
|
t.Fatalf("default state was empty")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify a backup doesn't exist
|
2017-01-28 02:53:43 +01:00
|
|
|
if !isEmptyState(DefaultStateFilename + DefaultBackupExtension) {
|
|
|
|
t.Fatal("backupstate should be empty")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify we have no configured backend/legacy
|
|
|
|
path := filepath.Join(m.DataDir(), DefaultStateFilename)
|
|
|
|
if _, err := os.Stat(path); err == nil {
|
|
|
|
f, err := os.Open(path)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
actual, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !actual.Remote.Empty() {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
if !actual.Backend.Empty() {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write some state
|
|
|
|
state = terraform.NewState()
|
|
|
|
state.Lineage = "changing"
|
|
|
|
s.WriteState(state)
|
|
|
|
if err := s.PersistState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the state is where we expect
|
|
|
|
{
|
|
|
|
f, err := os.Open(DefaultStateFilename)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
actual, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if actual.Lineage != state.Lineage {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify a local backup
|
2017-01-30 23:38:32 +01:00
|
|
|
if isEmptyState(DefaultStateFilename + DefaultBackupExtension) {
|
|
|
|
t.Fatal("backup is empty")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Saved backend state, unset config, legacy remote state
|
|
|
|
func TestMetaBackend_configuredUnsetWithLegacyCopyLegacy(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("backend-unset-with-legacy"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Ask input
|
|
|
|
defer testInteractiveInput(t, []string{"no", "yes", "yes"})()
|
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
b, err := m.Backend(&BackendOpts{Init: true})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
2017-02-28 19:13:03 +01:00
|
|
|
s, err := b.State(backend.DefaultStateName)
|
2017-01-19 05:50:04 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
if err := s.RefreshState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
state := s.State()
|
|
|
|
if state == nil {
|
|
|
|
t.Fatal("state is nil")
|
|
|
|
}
|
|
|
|
if state.Lineage != "legacy" {
|
|
|
|
t.Fatalf("bad: %#v", state)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the default paths exist
|
2017-01-28 02:53:43 +01:00
|
|
|
if isEmptyState(DefaultStateFilename) {
|
|
|
|
t.Fatalf("default state was empty")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify a backup doesn't exist
|
2017-01-28 02:53:43 +01:00
|
|
|
if !isEmptyState(DefaultStateFilename + DefaultBackupExtension) {
|
|
|
|
t.Fatal("backupstate should be empty")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify we have no configured backend/legacy
|
|
|
|
path := filepath.Join(m.DataDir(), DefaultStateFilename)
|
|
|
|
if _, err := os.Stat(path); err == nil {
|
|
|
|
f, err := os.Open(path)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
actual, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !actual.Remote.Empty() {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
if !actual.Backend.Empty() {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write some state
|
|
|
|
state = terraform.NewState()
|
|
|
|
state.Lineage = "changing"
|
|
|
|
s.WriteState(state)
|
|
|
|
if err := s.PersistState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the state is where we expect
|
|
|
|
{
|
|
|
|
f, err := os.Open(DefaultStateFilename)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
actual, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if actual.Lineage != state.Lineage {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify a local backup
|
2017-01-30 23:38:32 +01:00
|
|
|
if isEmptyState(DefaultStateFilename + DefaultBackupExtension) {
|
|
|
|
t.Fatal("backup is empty")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Saved backend state, unset config, legacy remote state
|
|
|
|
func TestMetaBackend_configuredUnsetWithLegacyCopyBoth(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("backend-unset-with-legacy"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Ask input
|
|
|
|
defer testInteractiveInput(t, []string{"yes", "yes", "yes", "yes"})()
|
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
b, err := m.Backend(&BackendOpts{Init: true})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
2017-02-28 19:13:03 +01:00
|
|
|
s, err := b.State(backend.DefaultStateName)
|
2017-01-19 05:50:04 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
if err := s.RefreshState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
state := s.State()
|
|
|
|
if state == nil {
|
|
|
|
t.Fatal("state is nil")
|
|
|
|
}
|
|
|
|
if state.Lineage != "legacy" {
|
|
|
|
t.Fatalf("bad: %#v", state)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the default paths exist
|
2017-01-30 23:38:32 +01:00
|
|
|
if isEmptyState(DefaultStateFilename) {
|
|
|
|
t.Fatal("state is empty")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify a backup exists
|
2017-01-30 23:38:32 +01:00
|
|
|
if isEmptyState(DefaultStateFilename + DefaultBackupExtension) {
|
|
|
|
t.Fatal("backup is empty")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify we have no configured backend/legacy
|
|
|
|
path := filepath.Join(m.DataDir(), DefaultStateFilename)
|
|
|
|
if _, err := os.Stat(path); err == nil {
|
|
|
|
f, err := os.Open(path)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
actual, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if !actual.Remote.Empty() {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
if !actual.Backend.Empty() {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write some state
|
|
|
|
state = terraform.NewState()
|
|
|
|
state.Lineage = "changing"
|
|
|
|
s.WriteState(state)
|
|
|
|
if err := s.PersistState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the state is where we expect
|
|
|
|
{
|
|
|
|
f, err := os.Open(DefaultStateFilename)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
actual, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if actual.Lineage != state.Lineage {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify a local backup
|
2017-01-30 23:38:32 +01:00
|
|
|
if isEmptyState(DefaultStateFilename + DefaultBackupExtension) {
|
|
|
|
t.Fatal("backup is empty")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// A plan that has no backend config
|
|
|
|
func TestMetaBackend_planLocal(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("backend-plan-local"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Create the plan
|
|
|
|
plan := &terraform.Plan{
|
|
|
|
Module: testModule(t, "backend-plan-local"),
|
|
|
|
State: nil,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
b, err := m.Backend(&BackendOpts{Plan: plan})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
2017-02-28 19:13:03 +01:00
|
|
|
s, err := b.State(backend.DefaultStateName)
|
2017-01-19 05:50:04 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
if err := s.RefreshState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
state := s.State()
|
|
|
|
if state != nil {
|
|
|
|
t.Fatalf("state should be nil: %#v", state)
|
|
|
|
}
|
|
|
|
|
2017-01-27 17:28:27 +01:00
|
|
|
// Verify the default path doens't exist
|
|
|
|
if !isEmptyState(DefaultStateFilename) {
|
|
|
|
t.Fatal("expected empty state")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify a backup doesn't exists
|
2017-01-27 17:28:27 +01:00
|
|
|
if !isEmptyState(DefaultStateFilename + DefaultBackupExtension) {
|
|
|
|
t.Fatal("expected empty backup")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify we have no configured backend/legacy
|
|
|
|
path := filepath.Join(m.DataDir(), DefaultStateFilename)
|
|
|
|
if _, err := os.Stat(path); err == nil {
|
|
|
|
t.Fatalf("should not have backend configured")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write some state
|
|
|
|
state = terraform.NewState()
|
|
|
|
state.Lineage = "changing"
|
|
|
|
s.WriteState(state)
|
|
|
|
if err := s.PersistState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the state is where we expect
|
|
|
|
{
|
|
|
|
f, err := os.Open(DefaultStateFilename)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
actual, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if actual.Lineage != state.Lineage {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify no local backup
|
2017-01-28 02:53:43 +01:00
|
|
|
if !isEmptyState(DefaultStateFilename + DefaultBackupExtension) {
|
|
|
|
t.Fatalf("backup state should be empty")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// A plan with a custom state save path
|
|
|
|
func TestMetaBackend_planLocalStatePath(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("backend-plan-local"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Create our state
|
|
|
|
original := testState()
|
|
|
|
original.Lineage = "hello"
|
|
|
|
|
|
|
|
// Create the plan
|
|
|
|
plan := &terraform.Plan{
|
|
|
|
Module: testModule(t, "backend-plan-local"),
|
|
|
|
State: original,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create an alternate output path
|
|
|
|
statePath := "foo.tfstate"
|
|
|
|
|
2017-02-03 19:07:34 +01:00
|
|
|
// put a initial state there that needs to be backed up
|
|
|
|
err := (&state.LocalState{Path: statePath}).WriteState(original)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2017-01-19 05:50:04 +01:00
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
m.stateOutPath = statePath
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
b, err := m.Backend(&BackendOpts{Plan: plan})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
2017-02-28 19:13:03 +01:00
|
|
|
s, err := b.State(backend.DefaultStateName)
|
2017-01-19 05:50:04 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
if err := s.RefreshState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
state := s.State()
|
|
|
|
if state == nil {
|
|
|
|
t.Fatal("state is nil")
|
|
|
|
}
|
|
|
|
if state.Lineage != "hello" {
|
|
|
|
t.Fatalf("bad: %#v", state)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the default path doesn't exist
|
|
|
|
if _, err := os.Stat(DefaultStateFilename); err == nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify a backup doesn't exists
|
|
|
|
if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify we have no configured backend/legacy
|
|
|
|
path := filepath.Join(m.DataDir(), DefaultStateFilename)
|
|
|
|
if _, err := os.Stat(path); err == nil {
|
|
|
|
t.Fatalf("should not have backend configured")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write some state
|
|
|
|
state = terraform.NewState()
|
|
|
|
state.Lineage = "changing"
|
|
|
|
s.WriteState(state)
|
|
|
|
if err := s.PersistState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the state is where we expect
|
|
|
|
{
|
|
|
|
f, err := os.Open(statePath)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
actual, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if actual.Lineage != state.Lineage {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify we have a backup
|
2017-01-30 23:38:32 +01:00
|
|
|
if isEmptyState(statePath + DefaultBackupExtension) {
|
|
|
|
t.Fatal("backup is empty")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// A plan that has no backend config, matching local state
|
|
|
|
func TestMetaBackend_planLocalMatch(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("backend-plan-local-match"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Create the plan
|
|
|
|
plan := &terraform.Plan{
|
|
|
|
Module: testModule(t, "backend-plan-local-match"),
|
|
|
|
State: testStateRead(t, DefaultStateFilename),
|
|
|
|
}
|
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
b, err := m.Backend(&BackendOpts{Plan: plan})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
2017-02-28 19:13:03 +01:00
|
|
|
s, err := b.State(backend.DefaultStateName)
|
2017-01-19 05:50:04 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
if err := s.RefreshState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
state := s.State()
|
|
|
|
if state == nil {
|
|
|
|
t.Fatal("should is nil")
|
|
|
|
}
|
|
|
|
if state.Lineage != "hello" {
|
|
|
|
t.Fatalf("bad: %#v", state)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the default path
|
2017-01-30 23:38:32 +01:00
|
|
|
if isEmptyState(DefaultStateFilename) {
|
|
|
|
t.Fatal("state is empty")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify a backup exists
|
|
|
|
if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify we have no configured backend/legacy
|
|
|
|
path := filepath.Join(m.DataDir(), DefaultStateFilename)
|
|
|
|
if _, err := os.Stat(path); err == nil {
|
|
|
|
t.Fatalf("should not have backend configured")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write some state
|
|
|
|
state = terraform.NewState()
|
|
|
|
state.Lineage = "changing"
|
|
|
|
s.WriteState(state)
|
|
|
|
if err := s.PersistState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the state is where we expect
|
|
|
|
{
|
|
|
|
f, err := os.Open(DefaultStateFilename)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
actual, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if actual.Lineage != state.Lineage {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify local backup
|
2017-01-30 23:38:32 +01:00
|
|
|
if isEmptyState(DefaultStateFilename + DefaultBackupExtension) {
|
|
|
|
t.Fatal("backup is empty")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// A plan that has no backend config, mismatched lineage
|
|
|
|
func TestMetaBackend_planLocalMismatchLineage(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("backend-plan-local-mismatch-lineage"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Save the original
|
|
|
|
original := testStateRead(t, DefaultStateFilename)
|
|
|
|
|
|
|
|
// Change the lineage
|
|
|
|
planState := testStateRead(t, DefaultStateFilename)
|
|
|
|
planState.Lineage = "bad"
|
|
|
|
|
|
|
|
// Create the plan
|
|
|
|
plan := &terraform.Plan{
|
|
|
|
Module: testModule(t, "backend-plan-local-mismatch-lineage"),
|
|
|
|
State: planState,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
_, err := m.Backend(&BackendOpts{Plan: plan})
|
|
|
|
if err == nil {
|
|
|
|
t.Fatal("should have error")
|
|
|
|
}
|
|
|
|
if !strings.Contains(err.Error(), "lineage") {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify our local state didn't change
|
|
|
|
actual := testStateRead(t, DefaultStateFilename)
|
|
|
|
if !actual.Equal(original) {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify a backup doesn't exists
|
|
|
|
if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify we have no configured backend/legacy
|
|
|
|
path := filepath.Join(m.DataDir(), DefaultStateFilename)
|
|
|
|
if _, err := os.Stat(path); err == nil {
|
|
|
|
t.Fatalf("should not have backend configured")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// A plan that has no backend config, newer local
|
|
|
|
func TestMetaBackend_planLocalNewer(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("backend-plan-local-newer"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Save the original
|
|
|
|
original := testStateRead(t, DefaultStateFilename)
|
|
|
|
|
|
|
|
// Change the serial
|
|
|
|
planState := testStateRead(t, DefaultStateFilename)
|
|
|
|
planState.Serial = 7
|
|
|
|
planState.RootModule().Dependencies = []string{"foo"}
|
|
|
|
|
|
|
|
// Create the plan
|
|
|
|
plan := &terraform.Plan{
|
|
|
|
Module: testModule(t, "backend-plan-local-newer"),
|
|
|
|
State: planState,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
_, err := m.Backend(&BackendOpts{Plan: plan})
|
|
|
|
if err == nil {
|
|
|
|
t.Fatal("should have error")
|
|
|
|
}
|
|
|
|
if !strings.Contains(err.Error(), "older") {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify our local state didn't change
|
|
|
|
actual := testStateRead(t, DefaultStateFilename)
|
|
|
|
if !actual.Equal(original) {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify a backup doesn't exists
|
|
|
|
if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify we have no configured backend/legacy
|
|
|
|
path := filepath.Join(m.DataDir(), DefaultStateFilename)
|
|
|
|
if _, err := os.Stat(path); err == nil {
|
|
|
|
t.Fatalf("should not have backend configured")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// A plan that has a backend in an empty dir
|
|
|
|
func TestMetaBackend_planBackendEmptyDir(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("backend-plan-backend-empty"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Get the state for the plan by getting the real state and
|
|
|
|
// adding the backend config to it.
|
|
|
|
original := testStateRead(t, filepath.Join(
|
|
|
|
testFixturePath("backend-plan-backend-empty-config"),
|
|
|
|
"local-state.tfstate"))
|
|
|
|
backendState := testStateRead(t, filepath.Join(
|
|
|
|
testFixturePath("backend-plan-backend-empty-config"),
|
|
|
|
DefaultDataDir, DefaultStateFilename))
|
|
|
|
planState := original.DeepCopy()
|
|
|
|
|
|
|
|
// Create the plan
|
|
|
|
plan := &terraform.Plan{
|
2017-03-16 23:42:32 +01:00
|
|
|
Module: testModule(t, "backend-plan-backend-empty-config"),
|
|
|
|
State: planState,
|
|
|
|
Backend: backendState.Backend,
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
b, err := m.Backend(&BackendOpts{Plan: plan})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
2017-02-28 19:13:03 +01:00
|
|
|
s, err := b.State(backend.DefaultStateName)
|
2017-01-19 05:50:04 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
if err := s.RefreshState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
state := s.State()
|
|
|
|
if state == nil {
|
|
|
|
t.Fatal("should is nil")
|
|
|
|
}
|
|
|
|
if state.Lineage != "hello" {
|
|
|
|
t.Fatalf("bad: %#v", state)
|
|
|
|
}
|
|
|
|
|
2017-01-30 23:55:49 +01:00
|
|
|
// Verify the default path doesn't exist
|
|
|
|
if !isEmptyState(DefaultStateFilename) {
|
|
|
|
t.Fatal("state is not empty")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
2017-01-30 23:55:49 +01:00
|
|
|
// Verify a backup doesn't exist
|
|
|
|
if !isEmptyState(DefaultStateFilename + DefaultBackupExtension) {
|
|
|
|
t.Fatal("backup is not empty")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify we have no configured backend/legacy
|
|
|
|
path := filepath.Join(m.DataDir(), DefaultStateFilename)
|
|
|
|
if _, err := os.Stat(path); err == nil {
|
|
|
|
t.Fatalf("should not have backend configured")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write some state
|
|
|
|
state = terraform.NewState()
|
|
|
|
state.Lineage = "changing"
|
|
|
|
s.WriteState(state)
|
|
|
|
if err := s.PersistState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the state is where we expect
|
|
|
|
{
|
|
|
|
f, err := os.Open("local-state.tfstate")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
actual, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if actual.Lineage != state.Lineage {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify no default path
|
|
|
|
if _, err := os.Stat(DefaultStateFilename); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify no local backup
|
|
|
|
if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// A plan that has a backend with matching state
|
|
|
|
func TestMetaBackend_planBackendMatch(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("backend-plan-backend-match"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Get the state for the plan by getting the real state and
|
|
|
|
// adding the backend config to it.
|
|
|
|
original := testStateRead(t, filepath.Join(
|
|
|
|
testFixturePath("backend-plan-backend-empty-config"),
|
|
|
|
"local-state.tfstate"))
|
|
|
|
backendState := testStateRead(t, filepath.Join(
|
|
|
|
testFixturePath("backend-plan-backend-empty-config"),
|
|
|
|
DefaultDataDir, DefaultStateFilename))
|
|
|
|
planState := original.DeepCopy()
|
|
|
|
|
|
|
|
// Create the plan
|
|
|
|
plan := &terraform.Plan{
|
2017-03-16 23:42:32 +01:00
|
|
|
Module: testModule(t, "backend-plan-backend-empty-config"),
|
|
|
|
State: planState,
|
|
|
|
Backend: backendState.Backend,
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
b, err := m.Backend(&BackendOpts{Plan: plan})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
2017-02-28 19:13:03 +01:00
|
|
|
s, err := b.State(backend.DefaultStateName)
|
2017-01-19 05:50:04 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
if err := s.RefreshState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
state := s.State()
|
|
|
|
if state == nil {
|
|
|
|
t.Fatal("should is nil")
|
|
|
|
}
|
|
|
|
if state.Lineage != "hello" {
|
|
|
|
t.Fatalf("bad: %#v", state)
|
|
|
|
}
|
|
|
|
|
2017-01-30 23:38:32 +01:00
|
|
|
// Verify the default path exists
|
2017-01-19 05:50:04 +01:00
|
|
|
if _, err := os.Stat(DefaultStateFilename); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
2017-01-30 23:38:32 +01:00
|
|
|
// Verify a backup doesn't exist
|
2017-01-19 05:50:04 +01:00
|
|
|
if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify we have no configured backend/legacy
|
|
|
|
path := filepath.Join(m.DataDir(), DefaultStateFilename)
|
|
|
|
if _, err := os.Stat(path); err == nil {
|
|
|
|
t.Fatalf("should not have backend configured")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write some state
|
|
|
|
state = terraform.NewState()
|
|
|
|
state.Lineage = "changing"
|
|
|
|
s.WriteState(state)
|
|
|
|
if err := s.PersistState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the state is where we expect
|
|
|
|
{
|
|
|
|
f, err := os.Open("local-state.tfstate")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
actual, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if actual.Lineage != state.Lineage {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify no default path
|
|
|
|
if _, err := os.Stat(DefaultStateFilename); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify no local backup
|
|
|
|
if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// A plan that has a backend with mismatching lineage
|
|
|
|
func TestMetaBackend_planBackendMismatchLineage(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("backend-plan-backend-mismatch"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Get the state for the plan by getting the real state and
|
|
|
|
// adding the backend config to it.
|
|
|
|
original := testStateRead(t, filepath.Join(
|
|
|
|
testFixturePath("backend-plan-backend-empty-config"),
|
|
|
|
"local-state.tfstate"))
|
|
|
|
backendState := testStateRead(t, filepath.Join(
|
|
|
|
testFixturePath("backend-plan-backend-empty-config"),
|
|
|
|
DefaultDataDir, DefaultStateFilename))
|
|
|
|
planState := original.DeepCopy()
|
|
|
|
|
|
|
|
// Get the real original
|
|
|
|
original = testStateRead(t, "local-state.tfstate")
|
|
|
|
|
|
|
|
// Create the plan
|
|
|
|
plan := &terraform.Plan{
|
2017-03-16 23:42:32 +01:00
|
|
|
Module: testModule(t, "backend-plan-backend-empty-config"),
|
|
|
|
State: planState,
|
|
|
|
Backend: backendState.Backend,
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
_, err := m.Backend(&BackendOpts{Plan: plan})
|
|
|
|
if err == nil {
|
|
|
|
t.Fatal("should have error")
|
|
|
|
}
|
|
|
|
if !strings.Contains(err.Error(), "lineage") {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify our local state didn't change
|
|
|
|
actual := testStateRead(t, "local-state.tfstate")
|
|
|
|
if !actual.Equal(original) {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
|
2017-01-30 23:38:32 +01:00
|
|
|
// Verify a backup doesn't exist
|
2017-01-19 05:50:04 +01:00
|
|
|
if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify we have no configured backend/legacy
|
|
|
|
path := filepath.Join(m.DataDir(), DefaultStateFilename)
|
|
|
|
if _, err := os.Stat(path); err == nil {
|
|
|
|
t.Fatalf("should not have backend configured")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify we have no default state
|
|
|
|
if _, err := os.Stat(DefaultStateFilename); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// A plan that has a legacy remote state
|
|
|
|
func TestMetaBackend_planLegacy(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("backend-plan-legacy"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// Get the state for the plan by getting the real state and
|
|
|
|
// adding the backend config to it.
|
|
|
|
original := testStateRead(t, filepath.Join(
|
|
|
|
testFixturePath("backend-plan-legacy-data"), "local-state.tfstate"))
|
|
|
|
dataState := testStateRead(t, filepath.Join(
|
|
|
|
testFixturePath("backend-plan-legacy-data"), "state.tfstate"))
|
|
|
|
planState := original.DeepCopy()
|
|
|
|
planState.Remote = dataState.Remote
|
|
|
|
|
|
|
|
// Create the plan
|
|
|
|
plan := &terraform.Plan{
|
|
|
|
Module: testModule(t, "backend-plan-legacy-data"),
|
|
|
|
State: planState,
|
|
|
|
}
|
|
|
|
|
|
|
|
// Setup the meta
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
|
|
|
|
// Get the backend
|
|
|
|
b, err := m.Backend(&BackendOpts{Plan: plan})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
2017-02-28 19:13:03 +01:00
|
|
|
s, err := b.State(backend.DefaultStateName)
|
2017-01-19 05:50:04 +01:00
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
if err := s.RefreshState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
state := s.State()
|
|
|
|
if state == nil {
|
|
|
|
t.Fatal("should is nil")
|
|
|
|
}
|
|
|
|
if state.Lineage != "hello" {
|
|
|
|
t.Fatalf("bad: %#v", state)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the default path
|
|
|
|
if _, err := os.Stat(DefaultStateFilename); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify a backup doesn't exist
|
|
|
|
if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify we have no configured backend/legacy
|
|
|
|
path := filepath.Join(m.DataDir(), DefaultStateFilename)
|
|
|
|
if _, err := os.Stat(path); err == nil {
|
|
|
|
t.Fatalf("should not have backend configured")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write some state
|
|
|
|
state = terraform.NewState()
|
|
|
|
state.Lineage = "changing"
|
|
|
|
s.WriteState(state)
|
|
|
|
if err := s.PersistState(); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify the state is where we expect
|
|
|
|
{
|
|
|
|
f, err := os.Open("local-state.tfstate")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
actual, err := terraform.ReadState(f)
|
|
|
|
f.Close()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("err: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if actual.Lineage != state.Lineage {
|
|
|
|
t.Fatalf("bad: %#v", actual)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify no default path
|
|
|
|
if _, err := os.Stat(DefaultStateFilename); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Verify no local backup
|
|
|
|
if _, err := os.Stat(DefaultStateFilename + DefaultBackupExtension); err == nil {
|
2017-01-31 00:02:35 +01:00
|
|
|
t.Fatal("file should not exist")
|
2017-01-19 05:50:04 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-29 23:50:55 +02:00
|
|
|
// init a backend using -backend-config options multiple times
|
|
|
|
func TestMetaBackend_configureWithExtra(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("init-backend-empty"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
extras := map[string]interface{}{"path": "hello"}
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
opts := &BackendOpts{
|
|
|
|
ConfigExtra: extras,
|
|
|
|
Init: true,
|
|
|
|
}
|
|
|
|
|
|
|
|
backendCfg, err := m.backendConfig(opts)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// init the backend
|
|
|
|
_, err = m.Backend(&BackendOpts{
|
|
|
|
ConfigExtra: extras,
|
|
|
|
Init: true,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
2018-07-04 12:07:44 +02:00
|
|
|
s := testStateRead(t, filepath.Join(DefaultDataDir, backendLocal.DefaultStateFilename))
|
2017-03-29 23:50:55 +02:00
|
|
|
if s.Backend.Hash != backendCfg.Hash {
|
|
|
|
t.Fatal("mismatched state and config backend hashes")
|
|
|
|
}
|
|
|
|
if s.Backend.Rehash() == s.Backend.Hash {
|
|
|
|
t.Fatal("saved hash should not match actual hash")
|
|
|
|
}
|
|
|
|
if s.Backend.Rehash() != backendCfg.Rehash() {
|
|
|
|
t.Fatal("mismatched state and config re-hashes")
|
|
|
|
}
|
|
|
|
|
|
|
|
// init the backend again with the same options
|
|
|
|
m = testMetaBackend(t, nil)
|
|
|
|
_, err = m.Backend(&BackendOpts{
|
|
|
|
ConfigExtra: extras,
|
|
|
|
Init: true,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
2018-07-04 12:07:44 +02:00
|
|
|
s = testStateRead(t, filepath.Join(DefaultDataDir, backendLocal.DefaultStateFilename))
|
2017-03-29 23:50:55 +02:00
|
|
|
if s.Backend.Hash != backendCfg.Hash {
|
|
|
|
t.Fatal("mismatched state and config backend hashes")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-31 21:21:32 +02:00
|
|
|
// when confniguring a default local state, don't delete local state
|
|
|
|
func TestMetaBackend_localDoesNotDeleteLocal(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("init-backend-empty"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// create our local state
|
|
|
|
orig := &terraform.State{
|
|
|
|
Modules: []*terraform.ModuleState{
|
|
|
|
{
|
|
|
|
Path: []string{"root"},
|
|
|
|
Outputs: map[string]*terraform.OutputState{
|
|
|
|
"foo": {
|
|
|
|
Value: "bar",
|
|
|
|
Type: "string",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
err := (&state.LocalState{Path: DefaultStateFilename}).WriteState(orig)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
m.forceInitCopy = true
|
|
|
|
// init the backend
|
|
|
|
_, err = m.Backend(&BackendOpts{
|
|
|
|
Init: true,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// check that we can read the state
|
|
|
|
s := testStateRead(t, DefaultStateFilename)
|
|
|
|
if s.Empty() {
|
|
|
|
t.Fatal("our state was deleted")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-29 23:50:55 +02:00
|
|
|
// move options from config to -backend-config
|
|
|
|
func TestMetaBackend_configToExtra(t *testing.T) {
|
|
|
|
// Create a temporary working directory that is empty
|
|
|
|
td := tempDir(t)
|
|
|
|
copy.CopyDir(testFixturePath("init-backend"), td)
|
|
|
|
defer os.RemoveAll(td)
|
|
|
|
defer testChdir(t, td)()
|
|
|
|
|
|
|
|
// init the backend
|
|
|
|
m := testMetaBackend(t, nil)
|
|
|
|
_, err := m.Backend(&BackendOpts{
|
|
|
|
Init: true,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check the state
|
2018-07-04 12:07:44 +02:00
|
|
|
s := testStateRead(t, filepath.Join(DefaultDataDir, backendLocal.DefaultStateFilename))
|
2017-03-29 23:50:55 +02:00
|
|
|
backendHash := s.Backend.Hash
|
|
|
|
|
|
|
|
// init again but remove the path option from the config
|
|
|
|
cfg := "terraform {\n backend \"local\" {}\n}\n"
|
|
|
|
if err := ioutil.WriteFile("main.tf", []byte(cfg), 0644); err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// init the backend again with the options
|
|
|
|
extras := map[string]interface{}{"path": "hello"}
|
|
|
|
m = testMetaBackend(t, nil)
|
|
|
|
m.forceInitCopy = true
|
|
|
|
_, err = m.Backend(&BackendOpts{
|
|
|
|
ConfigExtra: extras,
|
|
|
|
Init: true,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
2018-07-04 12:07:44 +02:00
|
|
|
s = testStateRead(t, filepath.Join(DefaultDataDir, backendLocal.DefaultStateFilename))
|
2017-03-29 23:50:55 +02:00
|
|
|
|
|
|
|
if s.Backend.Hash == backendHash {
|
|
|
|
t.Fatal("state.Backend.Hash was not updated")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-19 05:50:04 +01:00
|
|
|
func testMetaBackend(t *testing.T, args []string) *Meta {
|
|
|
|
var m Meta
|
|
|
|
m.Ui = new(cli.MockUi)
|
|
|
|
m.process(args, true)
|
|
|
|
f := m.flagSet("test")
|
|
|
|
if err := f.Parse(args); err != nil {
|
|
|
|
t.Fatalf("bad: %s", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return &m
|
|
|
|
}
|