Merge pull request #19197 from hashicorp/f-state-mv
command/state: update and fix the state mv command
This commit is contained in:
commit
292ec47e66
|
@ -2,6 +2,7 @@ package command
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/addrs"
|
"github.com/hashicorp/terraform/addrs"
|
||||||
|
@ -111,6 +112,25 @@ func (c *StateMeta) filter(state *states.State, args []string) ([]*states.Filter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sort the results
|
||||||
|
sort.Slice(results, func(i, j int) bool {
|
||||||
|
a, b := results[i], results[j]
|
||||||
|
|
||||||
|
// If the length is different, sort on the length so that the
|
||||||
|
// best match is the first result.
|
||||||
|
if len(a.Address.String()) != len(b.Address.String()) {
|
||||||
|
return len(a.Address.String()) < len(b.Address.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the addresses are different it is just lexographic sorting
|
||||||
|
if a.Address.String() != b.Address.String() {
|
||||||
|
return a.Address.String() < b.Address.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Addresses are the same, which means it matters on the type
|
||||||
|
return a.SortedType() < b.SortedType()
|
||||||
|
})
|
||||||
|
|
||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/addrs"
|
||||||
"github.com/hashicorp/terraform/states"
|
"github.com/hashicorp/terraform/states"
|
||||||
"github.com/mitchellh/cli"
|
"github.com/mitchellh/cli"
|
||||||
)
|
)
|
||||||
|
@ -22,7 +23,9 @@ func (c *StateMvCommand) Run(args []string) int {
|
||||||
// We create two metas to track the two states
|
// We create two metas to track the two states
|
||||||
var backupPathOut, statePathOut string
|
var backupPathOut, statePathOut string
|
||||||
|
|
||||||
|
var dryRun bool
|
||||||
cmdFlags := c.Meta.flagSet("state mv")
|
cmdFlags := c.Meta.flagSet("state mv")
|
||||||
|
cmdFlags.BoolVar(&dryRun, "dry-run", false, "dry run")
|
||||||
cmdFlags.StringVar(&c.backupPath, "backup", "-", "backup")
|
cmdFlags.StringVar(&c.backupPath, "backup", "-", "backup")
|
||||||
cmdFlags.StringVar(&c.statePath, "state", "", "path")
|
cmdFlags.StringVar(&c.statePath, "state", "", "path")
|
||||||
cmdFlags.StringVar(&backupPathOut, "backup-out", "-", "backup")
|
cmdFlags.StringVar(&backupPathOut, "backup-out", "-", "backup")
|
||||||
|
@ -37,127 +40,228 @@ func (c *StateMvCommand) Run(args []string) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the from state
|
// Read the from state
|
||||||
stateFrom, err := c.State()
|
stateFromMgr, err := c.State()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Ui.Error(fmt.Sprintf(errStateLoadingState, err))
|
c.Ui.Error(fmt.Sprintf(errStateLoadingState, err))
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
if err := stateFromMgr.RefreshState(); err != nil {
|
||||||
if err := stateFrom.RefreshState(); err != nil {
|
c.Ui.Error(fmt.Sprintf("Failed to refresh state: %s", err))
|
||||||
c.Ui.Error(fmt.Sprintf("Failed to load state: %s", err))
|
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
stateFromReal := stateFrom.State()
|
stateFrom := stateFromMgr.State()
|
||||||
if stateFromReal == nil {
|
if stateFrom == nil {
|
||||||
c.Ui.Error(fmt.Sprintf(errStateNotFound))
|
c.Ui.Error(fmt.Sprintf(errStateNotFound))
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the destination state
|
// Read the destination state
|
||||||
|
stateToMgr := stateFromMgr
|
||||||
stateTo := stateFrom
|
stateTo := stateFrom
|
||||||
stateToReal := stateFromReal
|
|
||||||
|
|
||||||
if statePathOut != "" {
|
if statePathOut != "" {
|
||||||
c.statePath = statePathOut
|
c.statePath = statePathOut
|
||||||
c.backupPath = backupPathOut
|
c.backupPath = backupPathOut
|
||||||
stateTo, err = c.State()
|
|
||||||
|
stateToMgr, err = c.State()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Ui.Error(fmt.Sprintf(errStateLoadingState, err))
|
c.Ui.Error(fmt.Sprintf(errStateLoadingState, err))
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
if err := stateToMgr.RefreshState(); err != nil {
|
||||||
if err := stateTo.RefreshState(); err != nil {
|
c.Ui.Error(fmt.Sprintf("Failed to refresh state: %s", err))
|
||||||
c.Ui.Error(fmt.Sprintf("Failed to load state: %s", err))
|
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
stateToReal = stateTo.State()
|
stateTo = stateToMgr.State()
|
||||||
if stateToReal == nil {
|
if stateTo == nil {
|
||||||
stateToReal = states.NewState()
|
stateTo = states.NewState()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Ui.Error("state mv command not yet updated for new state types")
|
// Filter what we are moving.
|
||||||
return 1
|
results, err := c.filter(stateFrom, []string{args[0]})
|
||||||
/*
|
if err != nil {
|
||||||
// Filter what we're moving
|
c.Ui.Error(fmt.Sprintf(errStateFilter, err))
|
||||||
filter := &terraform.StateFilter{State: stateFromReal}
|
return cli.RunResultHelp
|
||||||
results, err := filter.Filter(args[0])
|
}
|
||||||
if err != nil {
|
|
||||||
c.Ui.Error(fmt.Sprintf(errStateMv, err))
|
|
||||||
return cli.RunResultHelp
|
|
||||||
}
|
|
||||||
if len(results) == 0 {
|
|
||||||
c.Ui.Output(fmt.Sprintf("Item to move doesn't exist: %s", args[0]))
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the item to add to the state
|
// If we have no results, exit early as we're not going to do anything.
|
||||||
add := c.addableResult(results)
|
if len(results) == 0 {
|
||||||
|
if dryRun {
|
||||||
// Do the actual move
|
c.Ui.Output("Would have moved nothing.")
|
||||||
if err := stateFromReal.Remove(args[0]); err != nil {
|
} else {
|
||||||
c.Ui.Error(fmt.Sprintf(errStateMv, err))
|
c.Ui.Output("No matching objects found.")
|
||||||
return 1
|
|
||||||
}
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
if err := stateToReal.Add(args[0], args[1], add); err != nil {
|
prefix := "Move"
|
||||||
c.Ui.Error(fmt.Sprintf(errStateMv, err))
|
if dryRun {
|
||||||
return 1
|
prefix = "Would move"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the new state
|
var moved int
|
||||||
if err := stateTo.WriteState(stateToReal); err != nil {
|
ssFrom := stateFrom.SyncWrapper()
|
||||||
c.Ui.Error(fmt.Sprintf(errStateMvPersist, err))
|
for _, result := range c.moveableResult(results) {
|
||||||
return 1
|
switch addrFrom := result.Address.(type) {
|
||||||
}
|
case addrs.ModuleInstance:
|
||||||
|
search, err := addrs.ParseModuleInstanceStr(args[0])
|
||||||
if err := stateTo.PersistState(); err != nil {
|
if err != nil {
|
||||||
c.Ui.Error(fmt.Sprintf(errStateMvPersist, err))
|
c.Ui.Error(fmt.Sprintf(errStateMv, err))
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
addrTo, err := addrs.ParseModuleInstanceStr(args[1])
|
||||||
// Write the old state if it is different
|
if err != nil {
|
||||||
if stateTo != stateFrom {
|
c.Ui.Error(fmt.Sprintf(errStateMv, err))
|
||||||
if err := stateFrom.WriteState(stateFromReal); err != nil {
|
|
||||||
c.Ui.Error(fmt.Sprintf(errStateMvPersist, err))
|
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := stateFrom.PersistState(); err != nil {
|
if len(search) < len(addrFrom) {
|
||||||
c.Ui.Error(fmt.Sprintf(errStateMvPersist, err))
|
addrTo = append(addrTo, addrFrom[len(search):]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
if stateTo.Module(addrTo) != nil {
|
||||||
|
c.Ui.Error(fmt.Sprintf(errStateMv, "destination module already exists"))
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
c.Ui.Output(fmt.Sprintf(
|
moved++
|
||||||
"Moved %s to %s", args[0], args[1]))
|
c.Ui.Output(fmt.Sprintf("%s %q to %q", prefix, addrFrom.String(), addrTo.String()))
|
||||||
|
if !dryRun {
|
||||||
|
ssFrom.RemoveModule(addrFrom)
|
||||||
|
|
||||||
|
// Update the address before adding it to the state.
|
||||||
|
m := result.Value.(*states.Module)
|
||||||
|
m.Addr = addrTo
|
||||||
|
stateTo.Modules[addrTo.String()] = m
|
||||||
|
}
|
||||||
|
|
||||||
|
case addrs.AbsResource:
|
||||||
|
addrTo, err := addrs.ParseAbsResourceStr(args[1])
|
||||||
|
if err != nil {
|
||||||
|
c.Ui.Error(fmt.Sprintf(errStateMv, err))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if addrFrom.Resource.Type != addrTo.Resource.Type {
|
||||||
|
c.Ui.Error(fmt.Sprintf(
|
||||||
|
errStateMv, "resource types do not match"))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if stateTo.Module(addrTo.Module) == nil {
|
||||||
|
c.Ui.Error(fmt.Sprintf(
|
||||||
|
errStateMv, "destination module does not exist"))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if stateTo.Resource(addrTo) != nil {
|
||||||
|
c.Ui.Error(fmt.Sprintf(
|
||||||
|
errStateMv, "destination resource already exists"))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
moved++
|
||||||
|
c.Ui.Output(fmt.Sprintf("%s %q to %q", prefix, addrFrom.String(), addrTo.String()))
|
||||||
|
if !dryRun {
|
||||||
|
ssFrom.RemoveResource(addrFrom)
|
||||||
|
|
||||||
|
// Update the address before adding it to the state.
|
||||||
|
rs := result.Value.(*states.Resource)
|
||||||
|
rs.Addr = addrTo.Resource
|
||||||
|
stateTo.Module(addrTo.Module).Resources[addrTo.Resource.String()] = rs
|
||||||
|
}
|
||||||
|
|
||||||
|
case addrs.AbsResourceInstance:
|
||||||
|
addrTo, err := addrs.ParseAbsResourceInstanceStr(args[1])
|
||||||
|
if err != nil {
|
||||||
|
c.Ui.Error(fmt.Sprintf(errStateMv, err))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if stateTo.Module(addrTo.Module) == nil {
|
||||||
|
c.Ui.Error(fmt.Sprintf(
|
||||||
|
errStateMv, "destination module does not exist"))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if stateTo.Resource(addrTo.ContainingResource()) == nil {
|
||||||
|
c.Ui.Error(fmt.Sprintf(
|
||||||
|
errStateMv, "destination resource does not exist"))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if stateTo.ResourceInstance(addrTo) != nil {
|
||||||
|
c.Ui.Error(fmt.Sprintf(
|
||||||
|
errStateMv, "destination resource instance already exists"))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
moved++
|
||||||
|
c.Ui.Output(fmt.Sprintf("%s %q to %q", prefix, addrFrom.String(), args[1]))
|
||||||
|
if !dryRun {
|
||||||
|
ssFrom.ForgetResourceInstanceAll(addrFrom)
|
||||||
|
ssFrom.RemoveResourceIfEmpty(addrFrom.ContainingResource())
|
||||||
|
|
||||||
|
rs := stateTo.Resource(addrTo.ContainingResource())
|
||||||
|
rs.Instances[addrTo.Resource.Key] = result.Value.(*states.ResourceInstance)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if dryRun {
|
||||||
|
if moved == 0 {
|
||||||
|
c.Ui.Output("Would have moved nothing.")
|
||||||
|
}
|
||||||
|
return 0 // This is as far as we go in dry-run mode
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the new state
|
||||||
|
if err := stateToMgr.WriteState(stateTo); err != nil {
|
||||||
|
c.Ui.Error(fmt.Sprintf(errStateRmPersist, err))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if err := stateToMgr.PersistState(); err != nil {
|
||||||
|
c.Ui.Error(fmt.Sprintf(errStateRmPersist, err))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write the old state if it is different
|
||||||
|
if stateTo != stateFrom {
|
||||||
|
if err := stateFromMgr.WriteState(stateFrom); err != nil {
|
||||||
|
c.Ui.Error(fmt.Sprintf(errStateRmPersist, err))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
if err := stateFromMgr.PersistState(); err != nil {
|
||||||
|
c.Ui.Error(fmt.Sprintf(errStateRmPersist, err))
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if moved == 0 {
|
||||||
|
c.Ui.Output("No matching objects found.")
|
||||||
|
} else {
|
||||||
|
c.Ui.Output(fmt.Sprintf("Successfully moved %d object(s).", moved))
|
||||||
|
}
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// addableResult takes the result from a filter operation and returns what to
|
// moveableResult takes the result from a filter operation and returns what
|
||||||
// call State.Add with. The reason we do this is because in the module case
|
// object(s) to move. The reason we do this is because in the module case
|
||||||
// we must add the list of all modules returned versus just the root module.
|
// we must add the list of all modules returned versus just the root module.
|
||||||
func (c *StateMvCommand) addableResult(results []*states.FilterResult) interface{} {
|
func (c *StateMvCommand) moveableResult(results []*states.FilterResult) []*states.FilterResult {
|
||||||
switch v := results[0].Value.(type) {
|
result := results[:1]
|
||||||
case *states.Module:
|
|
||||||
// If a state module then we should add the full list of modules
|
if len(results) > 1 {
|
||||||
result := []*states.Module{v}
|
// If a state module then we should add the full list of modules.
|
||||||
if len(results) > 1 {
|
if _, ok := result[0].Address.(addrs.ModuleInstance); ok {
|
||||||
for _, r := range results[1:] {
|
for _, r := range results[1:] {
|
||||||
if ms, ok := r.Value.(*states.Module); ok {
|
if _, ok := r.Address.(addrs.ModuleInstance); ok {
|
||||||
result = append(result, ms)
|
result = append(result, r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
|
||||||
|
|
||||||
default:
|
|
||||||
// By default just add the first result
|
|
||||||
return v
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *StateMvCommand) Help() string {
|
func (c *StateMvCommand) Help() string {
|
||||||
|
@ -182,6 +286,9 @@ Usage: terraform state mv [options] SOURCE DESTINATION
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
|
|
||||||
|
-dry-run If set, prints out what would've been moved but doesn't
|
||||||
|
actually move anything.
|
||||||
|
|
||||||
-backup=PATH Path where Terraform should write the backup for the original
|
-backup=PATH Path where Terraform should write the backup for the original
|
||||||
state. This can't be disabled. If not set, Terraform
|
state. This can't be disabled. If not set, Terraform
|
||||||
will write it to the same path as the statefile with
|
will write it to the same path as the statefile with
|
||||||
|
@ -209,7 +316,7 @@ func (c *StateMvCommand) Synopsis() string {
|
||||||
return "Move an item in the state"
|
return "Move an item in the state"
|
||||||
}
|
}
|
||||||
|
|
||||||
const errStateMv = `Error moving state: %[1]s
|
const errStateMv = `Error moving state: %s
|
||||||
|
|
||||||
Please ensure your addresses and state paths are valid. No
|
Please ensure your addresses and state paths are valid. No
|
||||||
state was persisted. Your existing states are untouched.`
|
state was persisted. Your existing states are untouched.`
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/mitchellh/cli"
|
"github.com/mitchellh/cli"
|
||||||
|
@ -74,6 +75,48 @@ func TestStateMv(t *testing.T) {
|
||||||
testStateOutput(t, backups[0], testStateMvOutputOriginal)
|
testStateOutput(t, backups[0], testStateMvOutputOriginal)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStateMv_differentResourceTypes(t *testing.T) {
|
||||||
|
state := states.BuildState(func(s *states.SyncState) {
|
||||||
|
s.SetResourceInstanceCurrent(
|
||||||
|
addrs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "test_instance",
|
||||||
|
Name: "foo",
|
||||||
|
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
||||||
|
&states.ResourceInstanceObjectSrc{
|
||||||
|
AttrsJSON: []byte(`{"id":"bar","foo":"value","bar":"value"}`),
|
||||||
|
Status: states.ObjectReady,
|
||||||
|
},
|
||||||
|
addrs.ProviderConfig{Type: "test"}.Absolute(addrs.RootModuleInstance),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
statePath := testStateFile(t, state)
|
||||||
|
|
||||||
|
p := testProvider()
|
||||||
|
ui := new(cli.MockUi)
|
||||||
|
c := &StateMvCommand{
|
||||||
|
StateMeta{
|
||||||
|
Meta: Meta{
|
||||||
|
testingOverrides: metaOverridesForProvider(p),
|
||||||
|
Ui: ui,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []string{
|
||||||
|
"-state", statePath,
|
||||||
|
"test_instance.foo",
|
||||||
|
"test_network.bar",
|
||||||
|
}
|
||||||
|
if code := c.Run(args); code == 0 {
|
||||||
|
t.Fatalf("expected error output, got:\n%s", ui.OutputWriter.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.Contains(ui.ErrorWriter.String(), "resource types do not match") {
|
||||||
|
t.Fatalf("expected initialization error, got:\n%s", ui.ErrorWriter.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// don't modify backend state is we supply a -state flag
|
// don't modify backend state is we supply a -state flag
|
||||||
func TestStateMv_explicitWithBackend(t *testing.T) {
|
func TestStateMv_explicitWithBackend(t *testing.T) {
|
||||||
td := tempDir(t)
|
td := tempDir(t)
|
||||||
|
@ -152,10 +195,6 @@ func TestStateMv_explicitWithBackend(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStateMv_backupExplicit(t *testing.T) {
|
func TestStateMv_backupExplicit(t *testing.T) {
|
||||||
td := tempDir(t)
|
|
||||||
defer os.RemoveAll(td)
|
|
||||||
backupPath := filepath.Join(td, "backup")
|
|
||||||
|
|
||||||
state := states.BuildState(func(s *states.SyncState) {
|
state := states.BuildState(func(s *states.SyncState) {
|
||||||
s.SetResourceInstanceCurrent(
|
s.SetResourceInstanceCurrent(
|
||||||
addrs.Resource{
|
addrs.Resource{
|
||||||
|
@ -183,6 +222,7 @@ func TestStateMv_backupExplicit(t *testing.T) {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
statePath := testStateFile(t, state)
|
statePath := testStateFile(t, state)
|
||||||
|
backupPath := statePath + ".backup.test"
|
||||||
|
|
||||||
p := testProvider()
|
p := testProvider()
|
||||||
ui := new(cli.MockUi)
|
ui := new(cli.MockUi)
|
||||||
|
@ -913,8 +953,6 @@ test_instance.foo.10:
|
||||||
|
|
||||||
const testStateMvNestedModule_stateOut = `
|
const testStateMvNestedModule_stateOut = `
|
||||||
<no state>
|
<no state>
|
||||||
module.bar:
|
|
||||||
<no state>
|
|
||||||
module.bar.child1:
|
module.bar.child1:
|
||||||
test_instance.foo:
|
test_instance.foo:
|
||||||
ID = bar
|
ID = bar
|
||||||
|
@ -935,8 +973,6 @@ const testStateMvNestedModule_stateOutSrc = `
|
||||||
|
|
||||||
const testStateMvNestedModule_stateOutOriginal = `
|
const testStateMvNestedModule_stateOutOriginal = `
|
||||||
<no state>
|
<no state>
|
||||||
module.foo:
|
|
||||||
<no state>
|
|
||||||
module.foo.child1:
|
module.foo.child1:
|
||||||
test_instance.foo:
|
test_instance.foo:
|
||||||
ID = bar
|
ID = bar
|
||||||
|
@ -983,6 +1019,7 @@ test_instance.bar:
|
||||||
foo = value
|
foo = value
|
||||||
test_instance.qux:
|
test_instance.qux:
|
||||||
ID = bar
|
ID = bar
|
||||||
|
provider = provider.test
|
||||||
`
|
`
|
||||||
|
|
||||||
const testStateMvExisting_stateSrcOriginal = `
|
const testStateMvExisting_stateSrcOriginal = `
|
||||||
|
|
|
@ -5,10 +5,9 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/mitchellh/cli"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/addrs"
|
"github.com/hashicorp/terraform/addrs"
|
||||||
"github.com/hashicorp/terraform/states"
|
"github.com/hashicorp/terraform/states"
|
||||||
|
"github.com/mitchellh/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StateRmCommand is a Command implementation that shows a single resource.
|
// StateRmCommand is a Command implementation that shows a single resource.
|
||||||
|
@ -22,18 +21,19 @@ func (c *StateRmCommand) Run(args []string) int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var dryRun bool
|
||||||
cmdFlags := c.Meta.flagSet("state show")
|
cmdFlags := c.Meta.flagSet("state show")
|
||||||
|
cmdFlags.BoolVar(&dryRun, "dry-run", false, "dry run")
|
||||||
cmdFlags.StringVar(&c.backupPath, "backup", "-", "backup")
|
cmdFlags.StringVar(&c.backupPath, "backup", "-", "backup")
|
||||||
cmdFlags.StringVar(&c.statePath, "state", "", "path")
|
cmdFlags.StringVar(&c.statePath, "state", "", "path")
|
||||||
dryRun := cmdFlags.Bool("dry-run", false, "dry run")
|
|
||||||
if err := cmdFlags.Parse(args); err != nil {
|
if err := cmdFlags.Parse(args); err != nil {
|
||||||
return cli.RunResultHelp
|
return cli.RunResultHelp
|
||||||
}
|
}
|
||||||
args = cmdFlags.Args()
|
args = cmdFlags.Args()
|
||||||
|
|
||||||
if len(args) < 1 {
|
if len(args) < 1 {
|
||||||
c.Ui.Error("At least one resource address is required.")
|
c.Ui.Error("At least one address is required.\n")
|
||||||
return 1
|
return cli.RunResultHelp
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the state
|
// Get the state
|
||||||
|
@ -53,18 +53,16 @@ func (c *StateRmCommand) Run(args []string) int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Filter what we are removing.
|
||||||
results, err := c.filter(state, args)
|
results, err := c.filter(state, args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Ui.Error(fmt.Sprintf(errStateFilter, err))
|
c.Ui.Error(fmt.Sprintf(errStateFilter, err))
|
||||||
return cli.RunResultHelp
|
return cli.RunResultHelp
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we have no results, just exit early, we're not going to do anything.
|
// If we have no results, exit early as we're not going to do anything.
|
||||||
// While what happens below is fairly fast, this is an important early
|
|
||||||
// exit since the prune below might modify the state more and we don't
|
|
||||||
// want to modify the state if we don't have to.
|
|
||||||
if len(results) == 0 {
|
if len(results) == 0 {
|
||||||
if *dryRun {
|
if dryRun {
|
||||||
c.Ui.Output("Would have removed nothing.")
|
c.Ui.Output("Would have removed nothing.")
|
||||||
} else {
|
} else {
|
||||||
c.Ui.Output("No matching resources found.")
|
c.Ui.Output("No matching resources found.")
|
||||||
|
@ -73,7 +71,7 @@ func (c *StateRmCommand) Run(args []string) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
prefix := "Remove resource "
|
prefix := "Remove resource "
|
||||||
if *dryRun {
|
if dryRun {
|
||||||
prefix = "Would remove resource "
|
prefix = "Would remove resource "
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,7 +90,7 @@ func (c *StateRmCommand) Run(args []string) int {
|
||||||
if len(output) > 0 {
|
if len(output) > 0 {
|
||||||
c.Ui.Output(strings.Join(sort.StringSlice(output), "\n"))
|
c.Ui.Output(strings.Join(sort.StringSlice(output), "\n"))
|
||||||
}
|
}
|
||||||
if !*dryRun {
|
if !dryRun {
|
||||||
ss.RemoveModule(addr)
|
ss.RemoveModule(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,29 +103,27 @@ func (c *StateRmCommand) Run(args []string) int {
|
||||||
if len(output) > 0 {
|
if len(output) > 0 {
|
||||||
c.Ui.Output(strings.Join(sort.StringSlice(output), "\n"))
|
c.Ui.Output(strings.Join(sort.StringSlice(output), "\n"))
|
||||||
}
|
}
|
||||||
if !*dryRun {
|
if !dryRun {
|
||||||
ss.RemoveResource(addr)
|
ss.RemoveResource(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
case addrs.AbsResourceInstance:
|
case addrs.AbsResourceInstance:
|
||||||
isCount++
|
isCount++
|
||||||
c.Ui.Output(prefix + addr.String())
|
c.Ui.Output(prefix + addr.String())
|
||||||
if !*dryRun {
|
if !dryRun {
|
||||||
ss.ForgetResourceInstanceAll(addr)
|
ss.ForgetResourceInstanceAll(addr)
|
||||||
|
ss.RemoveResourceIfEmpty(addr.ContainingResource())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if *dryRun {
|
if dryRun {
|
||||||
if isCount == 0 {
|
if isCount == 0 {
|
||||||
c.Ui.Output("Would have removed nothing.")
|
c.Ui.Output("Would have removed nothing.")
|
||||||
}
|
}
|
||||||
return 0 // This is as far as we go in dry-run mode
|
return 0 // This is as far as we go in dry-run mode
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prune the state before writing and persisting it.
|
|
||||||
state.PruneResourceHusks()
|
|
||||||
|
|
||||||
if err := stateMgr.WriteState(state); err != nil {
|
if err := stateMgr.WriteState(state); err != nil {
|
||||||
c.Ui.Error(fmt.Sprintf(errStateRmPersist, err))
|
c.Ui.Error(fmt.Sprintf(errStateRmPersist, err))
|
||||||
return 1
|
return 1
|
||||||
|
|
|
@ -115,11 +115,11 @@ func TestStateRmNoArgs(t *testing.T) {
|
||||||
args := []string{
|
args := []string{
|
||||||
"-state", statePath,
|
"-state", statePath,
|
||||||
}
|
}
|
||||||
if code := c.Run(args); code != 1 {
|
if code := c.Run(args); code == 0 {
|
||||||
t.Errorf("wrong exit status %d; want %d", code, 1)
|
t.Errorf("expected non-zero exit code, got: %d", code)
|
||||||
}
|
}
|
||||||
|
|
||||||
if msg := ui.ErrorWriter.String(); !strings.Contains(msg, "At least one resource address") {
|
if msg := ui.ErrorWriter.String(); !strings.Contains(msg, "At least one address") {
|
||||||
t.Errorf("not the error we were looking for:\n%s", msg)
|
t.Errorf("not the error we were looking for:\n%s", msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,7 +207,7 @@ func TestStateRm_backupExplicit(t *testing.T) {
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
statePath := testStateFile(t, state)
|
statePath := testStateFile(t, state)
|
||||||
backupPath := statePath + ".mybackup"
|
backupPath := statePath + ".backup.test"
|
||||||
|
|
||||||
p := testProvider()
|
p := testProvider()
|
||||||
ui := new(cli.MockUi)
|
ui := new(cli.MockUi)
|
||||||
|
@ -251,7 +251,7 @@ func TestStateRm_noState(t *testing.T) {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
args := []string{}
|
args := []string{"foo"}
|
||||||
if code := c.Run(args); code != 1 {
|
if code := c.Run(args); code != 1 {
|
||||||
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,8 +63,19 @@ func (f *Filter) Filter(fs ...string) ([]*FilterResult, error) {
|
||||||
results = append(results, v)
|
results = append(results, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort them and return
|
// Sort the results
|
||||||
sort.Sort(FilterResultSlice(results))
|
sort.Slice(results, func(i, j int) bool {
|
||||||
|
a, b := results[i], results[j]
|
||||||
|
|
||||||
|
// If the addresses are different it is just lexographic sorting
|
||||||
|
if a.Address.String() != b.Address.String() {
|
||||||
|
return a.Address.String() < b.Address.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Addresses are the same, which means it matters on the type
|
||||||
|
return a.SortedType() < b.SortedType()
|
||||||
|
})
|
||||||
|
|
||||||
return results, nil
|
return results, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -155,7 +166,7 @@ func (r *FilterResult) String() string {
|
||||||
return fmt.Sprintf("%T: %s", r.Value, r.Address)
|
return fmt.Sprintf("%T: %s", r.Value, r.Address)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *FilterResult) sortedType() int {
|
func (r *FilterResult) SortedType() int {
|
||||||
switch r.Value.(type) {
|
switch r.Value.(type) {
|
||||||
case *Module:
|
case *Module:
|
||||||
return 0
|
return 0
|
||||||
|
@ -167,22 +178,3 @@ func (r *FilterResult) sortedType() int {
|
||||||
return 50
|
return 50
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FilterResultSlice is a slice of results that implements
|
|
||||||
// sort.Interface. The sorting goal is what is most appealing to
|
|
||||||
// human output.
|
|
||||||
type FilterResultSlice []*FilterResult
|
|
||||||
|
|
||||||
func (s FilterResultSlice) Len() int { return len(s) }
|
|
||||||
func (s FilterResultSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
|
||||||
func (s FilterResultSlice) Less(i, j int) bool {
|
|
||||||
a, b := s[i], s[j]
|
|
||||||
|
|
||||||
// If the addresses are different it is just lexographic sorting
|
|
||||||
if a.Address.String() != b.Address.String() {
|
|
||||||
return a.Address.String() < b.Address.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Addresses are the same, which means it matters on the type
|
|
||||||
return a.sortedType() < b.sortedType()
|
|
||||||
}
|
|
||||||
|
|
|
@ -399,7 +399,7 @@ func testStateSmall() *State {
|
||||||
root := addrs.RootModuleInstance
|
root := addrs.RootModuleInstance
|
||||||
boot, _ := addrs.ParseModuleInstanceStr("module.boot")
|
boot, _ := addrs.ParseModuleInstanceStr("module.boot")
|
||||||
|
|
||||||
state := BuildState(func(s *SyncState) {
|
return BuildState(func(s *SyncState) {
|
||||||
s.SetResourceInstanceCurrent(
|
s.SetResourceInstanceCurrent(
|
||||||
addrs.Resource{
|
addrs.Resource{
|
||||||
Mode: addrs.ManagedResourceMode,
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
@ -457,9 +457,6 @@ func testStateSmall() *State {
|
||||||
}.Absolute(boot),
|
}.Absolute(boot),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
// fmt.Printf("mods: %#v\n", state.Modules)
|
|
||||||
// fmt.Printf("boot: %#+v\n", state.Modules["module.boot"])
|
|
||||||
return state
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// testStateSmallTestInstance returns a test State structure.
|
// testStateSmallTestInstance returns a test State structure.
|
||||||
|
|
|
@ -1,374 +0,0 @@
|
||||||
package terraform
|
|
||||||
|
|
||||||
import "fmt"
|
|
||||||
|
|
||||||
// Add adds the item in the state at the given address.
|
|
||||||
//
|
|
||||||
// The item can be a ModuleState, ResourceState, or InstanceState. Depending
|
|
||||||
// on the item type, the address may or may not be valid. For example, a
|
|
||||||
// module cannot be moved to a resource address, however a resource can be
|
|
||||||
// moved to a module address (it retains the same name, under that resource).
|
|
||||||
//
|
|
||||||
// The item can also be a []*ModuleState, which is the case for nested
|
|
||||||
// modules. In this case, Add will expect the zero-index to be the top-most
|
|
||||||
// module to add and will only nest children from there. For semantics, this
|
|
||||||
// is equivalent to module => module.
|
|
||||||
//
|
|
||||||
// The full semantics of Add:
|
|
||||||
//
|
|
||||||
// ┌───────────────────┬───────────────────┬───────────────────┐
|
|
||||||
// │ Module Address │ Resource Address │ Instance Address │
|
|
||||||
// ┌─────────────────┼───────────────────┼───────────────────┼───────────────────┤
|
|
||||||
// │ ModuleState │ ✓ │ x │ x │
|
|
||||||
// ├─────────────────┼───────────────────┼───────────────────┼───────────────────┤
|
|
||||||
// │ ResourceState │ ✓ │ ✓ │ maybe* │
|
|
||||||
// ├─────────────────┼───────────────────┼───────────────────┼───────────────────┤
|
|
||||||
// │ Instance State │ ✓ │ ✓ │ ✓ │
|
|
||||||
// └─────────────────┴───────────────────┴───────────────────┴───────────────────┘
|
|
||||||
//
|
|
||||||
// *maybe - Resources can be added at an instance address only if the resource
|
|
||||||
// represents a single instance (primary). Example:
|
|
||||||
// "aws_instance.foo" can be moved to "aws_instance.bar.tainted"
|
|
||||||
//
|
|
||||||
func (s *State) Add(fromAddrRaw string, toAddrRaw string, raw interface{}) error {
|
|
||||||
// Parse the address
|
|
||||||
|
|
||||||
toAddr, err := ParseResourceAddress(toAddrRaw)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the from address
|
|
||||||
fromAddr, err := ParseResourceAddress(fromAddrRaw)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine the types
|
|
||||||
from := detectValueAddLoc(raw)
|
|
||||||
to := detectAddrAddLoc(toAddr)
|
|
||||||
|
|
||||||
// Find the function to do this
|
|
||||||
fromMap, ok := stateAddFuncs[from]
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("invalid source to add to state: %T", raw)
|
|
||||||
}
|
|
||||||
f, ok := fromMap[to]
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("invalid destination: %s (%d)", toAddr, to)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call the migrator
|
|
||||||
if err := f(s, fromAddr, toAddr, raw); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prune the state
|
|
||||||
s.prune()
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func stateAddFunc_Module_Module(s *State, fromAddr, addr *ResourceAddress, raw interface{}) error {
|
|
||||||
// raw can be either *ModuleState or []*ModuleState. The former means
|
|
||||||
// we're moving just one module. The latter means we're moving a module
|
|
||||||
// and children.
|
|
||||||
root := raw
|
|
||||||
var rest []*ModuleState
|
|
||||||
if list, ok := raw.([]*ModuleState); ok {
|
|
||||||
// We need at least one item
|
|
||||||
if len(list) == 0 {
|
|
||||||
return fmt.Errorf("module move with no value to: %s", addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// The first item is always the root
|
|
||||||
root = list[0]
|
|
||||||
if len(list) > 1 {
|
|
||||||
rest = list[1:]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the actual module state
|
|
||||||
src := root.(*ModuleState).deepcopy()
|
|
||||||
|
|
||||||
// If the target module exists, it is an error
|
|
||||||
path := normalizeModulePath(addr.Path)
|
|
||||||
if s.ModuleByPath(path) != nil {
|
|
||||||
return fmt.Errorf("module target is not empty: %s", addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create it and copy our outputs and dependencies
|
|
||||||
mod := s.AddModule(path)
|
|
||||||
mod.Outputs = src.Outputs
|
|
||||||
mod.Dependencies = src.Dependencies
|
|
||||||
|
|
||||||
// Go through the resources perform an add for each of those
|
|
||||||
for k, v := range src.Resources {
|
|
||||||
resourceKey, err := ParseResourceStateKey(k)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the resource address for this
|
|
||||||
addrCopy := *addr
|
|
||||||
addrCopy.Type = resourceKey.Type
|
|
||||||
addrCopy.Name = resourceKey.Name
|
|
||||||
addrCopy.Index = resourceKey.Index
|
|
||||||
addrCopy.Mode = resourceKey.Mode
|
|
||||||
|
|
||||||
// Perform an add
|
|
||||||
if err := s.Add(fromAddr.String(), addrCopy.String(), v); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add all the children if we have them
|
|
||||||
for _, item := range rest {
|
|
||||||
// If item isn't a descendent of our root, then ignore it
|
|
||||||
if !src.IsDescendent(item) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// It is! Strip the leading prefix and attach that to our address
|
|
||||||
extra := item.Path[len(src.Path):]
|
|
||||||
addrCopy := addr.Copy()
|
|
||||||
addrCopy.Path = append(addrCopy.Path, extra...)
|
|
||||||
|
|
||||||
// Add it
|
|
||||||
s.Add(fromAddr.String(), addrCopy.String(), item)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func stateAddFunc_Resource_Module(
|
|
||||||
s *State, from, to *ResourceAddress, raw interface{}) error {
|
|
||||||
// Build the more specific to addr
|
|
||||||
addr := *to
|
|
||||||
addr.Type = from.Type
|
|
||||||
addr.Name = from.Name
|
|
||||||
|
|
||||||
return s.Add(from.String(), addr.String(), raw)
|
|
||||||
}
|
|
||||||
|
|
||||||
func stateAddFunc_Resource_Resource(s *State, fromAddr, addr *ResourceAddress, raw interface{}) error {
|
|
||||||
// raw can be either *ResourceState or []*ResourceState. The former means
|
|
||||||
// we're moving just one resource. The latter means we're moving a count
|
|
||||||
// of resources.
|
|
||||||
if list, ok := raw.([]*ResourceState); ok {
|
|
||||||
// We need at least one item
|
|
||||||
if len(list) == 0 {
|
|
||||||
return fmt.Errorf("resource move with no value to: %s", addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If there is an index, this is an error since we can't assign
|
|
||||||
// a set of resources to a single index
|
|
||||||
if addr.Index >= 0 && len(list) > 1 {
|
|
||||||
return fmt.Errorf(
|
|
||||||
"multiple resources can't be moved to a single index: "+
|
|
||||||
"%s => %s", fromAddr, addr)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add each with a specific index
|
|
||||||
for i, rs := range list {
|
|
||||||
addrCopy := addr.Copy()
|
|
||||||
addrCopy.Index = i
|
|
||||||
|
|
||||||
if err := s.Add(fromAddr.String(), addrCopy.String(), rs); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
src := raw.(*ResourceState).deepcopy()
|
|
||||||
|
|
||||||
// Initialize the resource
|
|
||||||
resourceRaw, exists := stateAddInitAddr(s, addr)
|
|
||||||
if exists {
|
|
||||||
return fmt.Errorf("resource exists and not empty: %s", addr)
|
|
||||||
}
|
|
||||||
resource := resourceRaw.(*ResourceState)
|
|
||||||
resource.Type = src.Type
|
|
||||||
resource.Dependencies = src.Dependencies
|
|
||||||
resource.Provider = src.Provider
|
|
||||||
|
|
||||||
// Move the primary
|
|
||||||
if src.Primary != nil {
|
|
||||||
addrCopy := *addr
|
|
||||||
addrCopy.InstanceType = TypePrimary
|
|
||||||
addrCopy.InstanceTypeSet = true
|
|
||||||
if err := s.Add(fromAddr.String(), addrCopy.String(), src.Primary); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move all deposed
|
|
||||||
if len(src.Deposed) > 0 {
|
|
||||||
resource.Deposed = src.Deposed
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func stateAddFunc_Instance_Instance(s *State, fromAddr, addr *ResourceAddress, raw interface{}) error {
|
|
||||||
src := raw.(*InstanceState).DeepCopy()
|
|
||||||
|
|
||||||
// Create the instance
|
|
||||||
instanceRaw, _ := stateAddInitAddr(s, addr)
|
|
||||||
instance := instanceRaw.(*InstanceState)
|
|
||||||
|
|
||||||
// Set it
|
|
||||||
instance.Set(src)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func stateAddFunc_Instance_Module(
|
|
||||||
s *State, from, to *ResourceAddress, raw interface{}) error {
|
|
||||||
addr := *to
|
|
||||||
addr.Type = from.Type
|
|
||||||
addr.Name = from.Name
|
|
||||||
|
|
||||||
return s.Add(from.String(), addr.String(), raw)
|
|
||||||
}
|
|
||||||
|
|
||||||
func stateAddFunc_Instance_Resource(
|
|
||||||
s *State, from, to *ResourceAddress, raw interface{}) error {
|
|
||||||
addr := *to
|
|
||||||
addr.InstanceType = TypePrimary
|
|
||||||
addr.InstanceTypeSet = true
|
|
||||||
|
|
||||||
return s.Add(from.String(), addr.String(), raw)
|
|
||||||
}
|
|
||||||
|
|
||||||
// stateAddFunc is the type of function for adding an item to a state
|
|
||||||
type stateAddFunc func(s *State, from, to *ResourceAddress, item interface{}) error
|
|
||||||
|
|
||||||
// stateAddFuncs has the full matrix mapping of the state adders.
|
|
||||||
var stateAddFuncs map[stateAddLoc]map[stateAddLoc]stateAddFunc
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
stateAddFuncs = map[stateAddLoc]map[stateAddLoc]stateAddFunc{
|
|
||||||
stateAddModule: {
|
|
||||||
stateAddModule: stateAddFunc_Module_Module,
|
|
||||||
},
|
|
||||||
stateAddResource: {
|
|
||||||
stateAddModule: stateAddFunc_Resource_Module,
|
|
||||||
stateAddResource: stateAddFunc_Resource_Resource,
|
|
||||||
},
|
|
||||||
stateAddInstance: {
|
|
||||||
stateAddInstance: stateAddFunc_Instance_Instance,
|
|
||||||
stateAddModule: stateAddFunc_Instance_Module,
|
|
||||||
stateAddResource: stateAddFunc_Instance_Resource,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// stateAddLoc is an enum to represent the location where state is being
|
|
||||||
// moved from/to. We use this for quick lookups in a function map.
|
|
||||||
type stateAddLoc uint
|
|
||||||
|
|
||||||
const (
|
|
||||||
stateAddInvalid stateAddLoc = iota
|
|
||||||
stateAddModule
|
|
||||||
stateAddResource
|
|
||||||
stateAddInstance
|
|
||||||
)
|
|
||||||
|
|
||||||
// detectAddrAddLoc detects the state type for the given address. This
|
|
||||||
// function is specifically not unit tested since we consider the State.Add
|
|
||||||
// functionality to be comprehensive enough to cover this.
|
|
||||||
func detectAddrAddLoc(addr *ResourceAddress) stateAddLoc {
|
|
||||||
if addr.Name == "" {
|
|
||||||
return stateAddModule
|
|
||||||
}
|
|
||||||
|
|
||||||
if !addr.InstanceTypeSet {
|
|
||||||
return stateAddResource
|
|
||||||
}
|
|
||||||
|
|
||||||
return stateAddInstance
|
|
||||||
}
|
|
||||||
|
|
||||||
// detectValueAddLoc determines the stateAddLoc value from the raw value
|
|
||||||
// that is some State structure.
|
|
||||||
func detectValueAddLoc(raw interface{}) stateAddLoc {
|
|
||||||
switch raw.(type) {
|
|
||||||
case *ModuleState:
|
|
||||||
return stateAddModule
|
|
||||||
case []*ModuleState:
|
|
||||||
return stateAddModule
|
|
||||||
case *ResourceState:
|
|
||||||
return stateAddResource
|
|
||||||
case []*ResourceState:
|
|
||||||
return stateAddResource
|
|
||||||
case *InstanceState:
|
|
||||||
return stateAddInstance
|
|
||||||
default:
|
|
||||||
return stateAddInvalid
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// stateAddInitAddr takes a ResourceAddress and creates the non-existing
|
|
||||||
// resources up to that point, returning the empty (or existing) interface
|
|
||||||
// at that address.
|
|
||||||
func stateAddInitAddr(s *State, addr *ResourceAddress) (interface{}, bool) {
|
|
||||||
addType := detectAddrAddLoc(addr)
|
|
||||||
|
|
||||||
// Get the module
|
|
||||||
path := normalizeModulePath(addr.Path)
|
|
||||||
exists := true
|
|
||||||
mod := s.ModuleByPath(path)
|
|
||||||
if mod == nil {
|
|
||||||
mod = s.AddModule(path)
|
|
||||||
exists = false
|
|
||||||
}
|
|
||||||
if addType == stateAddModule {
|
|
||||||
return mod, exists
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the resource
|
|
||||||
resourceKey := (&ResourceStateKey{
|
|
||||||
Name: addr.Name,
|
|
||||||
Type: addr.Type,
|
|
||||||
Index: addr.Index,
|
|
||||||
Mode: addr.Mode,
|
|
||||||
}).String()
|
|
||||||
exists = true
|
|
||||||
resource, ok := mod.Resources[resourceKey]
|
|
||||||
if !ok {
|
|
||||||
resource = &ResourceState{Type: addr.Type}
|
|
||||||
resource.init()
|
|
||||||
mod.Resources[resourceKey] = resource
|
|
||||||
exists = false
|
|
||||||
}
|
|
||||||
if addType == stateAddResource {
|
|
||||||
return resource, exists
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the instance
|
|
||||||
exists = true
|
|
||||||
instance := &InstanceState{}
|
|
||||||
switch addr.InstanceType {
|
|
||||||
case TypePrimary, TypeTainted:
|
|
||||||
if v := resource.Primary; v != nil {
|
|
||||||
instance = resource.Primary
|
|
||||||
} else {
|
|
||||||
exists = false
|
|
||||||
}
|
|
||||||
case TypeDeposed:
|
|
||||||
idx := addr.Index
|
|
||||||
if addr.Index < 0 {
|
|
||||||
idx = 0
|
|
||||||
}
|
|
||||||
if len(resource.Deposed) > idx {
|
|
||||||
instance = resource.Deposed[idx]
|
|
||||||
} else {
|
|
||||||
resource.Deposed = append(resource.Deposed, instance)
|
|
||||||
exists = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return instance, exists
|
|
||||||
}
|
|
|
@ -1,695 +0,0 @@
|
||||||
package terraform
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStateAdd(t *testing.T) {
|
|
||||||
cases := []struct {
|
|
||||||
Name string
|
|
||||||
Err bool
|
|
||||||
From, To string
|
|
||||||
Value interface{}
|
|
||||||
One, Two *State
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
"ModuleState => Module Addr (new)",
|
|
||||||
false,
|
|
||||||
"",
|
|
||||||
"module.foo",
|
|
||||||
&ModuleState{
|
|
||||||
Path: rootModulePath,
|
|
||||||
Resources: map[string]*ResourceState{
|
|
||||||
"test_instance.foo": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
"test_instance.bar": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
&State{},
|
|
||||||
&State{
|
|
||||||
Modules: []*ModuleState{
|
|
||||||
&ModuleState{
|
|
||||||
Path: []string{"root", "foo"},
|
|
||||||
Resources: map[string]*ResourceState{
|
|
||||||
"test_instance.foo": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
"test_instance.bar": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"ModuleState => Nested Module Addr (new)",
|
|
||||||
false,
|
|
||||||
"",
|
|
||||||
"module.foo.module.bar",
|
|
||||||
&ModuleState{
|
|
||||||
Path: rootModulePath,
|
|
||||||
Resources: map[string]*ResourceState{
|
|
||||||
"test_instance.foo": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
"test_instance.bar": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
&State{},
|
|
||||||
&State{
|
|
||||||
Modules: []*ModuleState{
|
|
||||||
&ModuleState{
|
|
||||||
Path: []string{"root", "foo", "bar"},
|
|
||||||
Resources: map[string]*ResourceState{
|
|
||||||
"test_instance.foo": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
"test_instance.bar": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"ModuleState w/ outputs and deps => Module Addr (new)",
|
|
||||||
false,
|
|
||||||
"",
|
|
||||||
"module.foo",
|
|
||||||
&ModuleState{
|
|
||||||
Path: rootModulePath,
|
|
||||||
Outputs: map[string]*OutputState{
|
|
||||||
"foo": &OutputState{
|
|
||||||
Type: "string",
|
|
||||||
Sensitive: false,
|
|
||||||
Value: "bar",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Dependencies: []string{"foo"},
|
|
||||||
Resources: map[string]*ResourceState{
|
|
||||||
"test_instance.foo": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
"test_instance.bar": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
&State{},
|
|
||||||
&State{
|
|
||||||
Modules: []*ModuleState{
|
|
||||||
&ModuleState{
|
|
||||||
Path: []string{"root", "foo"},
|
|
||||||
Outputs: map[string]*OutputState{
|
|
||||||
"foo": &OutputState{
|
|
||||||
Type: "string",
|
|
||||||
Sensitive: false,
|
|
||||||
Value: "bar",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Dependencies: []string{"foo"},
|
|
||||||
Resources: map[string]*ResourceState{
|
|
||||||
"test_instance.foo": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
"test_instance.bar": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"ModuleState => Module Addr (existing)",
|
|
||||||
true,
|
|
||||||
"",
|
|
||||||
"module.foo",
|
|
||||||
&ModuleState{},
|
|
||||||
&State{
|
|
||||||
Modules: []*ModuleState{
|
|
||||||
&ModuleState{
|
|
||||||
Path: []string{"root", "foo"},
|
|
||||||
Resources: map[string]*ResourceState{
|
|
||||||
"test_instance.baz": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"ModuleState with children => Module Addr (new)",
|
|
||||||
false,
|
|
||||||
"module.foo",
|
|
||||||
"module.bar",
|
|
||||||
|
|
||||||
[]*ModuleState{
|
|
||||||
&ModuleState{
|
|
||||||
Path: []string{"root", "foo"},
|
|
||||||
Resources: map[string]*ResourceState{},
|
|
||||||
},
|
|
||||||
|
|
||||||
&ModuleState{
|
|
||||||
Path: []string{"root", "foo", "child1"},
|
|
||||||
Resources: map[string]*ResourceState{
|
|
||||||
"test_instance.foo": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
&ModuleState{
|
|
||||||
Path: []string{"root", "foo", "child2"},
|
|
||||||
Resources: map[string]*ResourceState{
|
|
||||||
"test_instance.foo": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Should be ignored
|
|
||||||
&ModuleState{
|
|
||||||
Path: []string{"root", "baz", "child2"},
|
|
||||||
Resources: map[string]*ResourceState{
|
|
||||||
"test_instance.foo": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
&State{},
|
|
||||||
&State{
|
|
||||||
Modules: []*ModuleState{
|
|
||||||
&ModuleState{
|
|
||||||
Path: []string{"root", "bar"},
|
|
||||||
Resources: map[string]*ResourceState{},
|
|
||||||
},
|
|
||||||
|
|
||||||
&ModuleState{
|
|
||||||
Path: []string{"root", "bar", "child1"},
|
|
||||||
Resources: map[string]*ResourceState{
|
|
||||||
"test_instance.foo": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
&ModuleState{
|
|
||||||
Path: []string{"root", "bar", "child2"},
|
|
||||||
Resources: map[string]*ResourceState{
|
|
||||||
"test_instance.foo": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"ResourceState => Resource Addr (new)",
|
|
||||||
false,
|
|
||||||
"aws_instance.bar",
|
|
||||||
"aws_instance.foo",
|
|
||||||
&ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
&State{},
|
|
||||||
&State{
|
|
||||||
Modules: []*ModuleState{
|
|
||||||
&ModuleState{
|
|
||||||
Path: []string{"root"},
|
|
||||||
Resources: map[string]*ResourceState{
|
|
||||||
"aws_instance.foo": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"ResourceState w/ deps, provider => Resource Addr (new)",
|
|
||||||
false,
|
|
||||||
"aws_instance.bar",
|
|
||||||
"aws_instance.foo",
|
|
||||||
&ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Provider: "foo",
|
|
||||||
Dependencies: []string{"bar"},
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
&State{},
|
|
||||||
&State{
|
|
||||||
Modules: []*ModuleState{
|
|
||||||
&ModuleState{
|
|
||||||
Path: []string{"root"},
|
|
||||||
Resources: map[string]*ResourceState{
|
|
||||||
"aws_instance.foo": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Provider: "foo",
|
|
||||||
Dependencies: []string{"bar"},
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"ResourceState tainted => Resource Addr (new)",
|
|
||||||
false,
|
|
||||||
"aws_instance.bar",
|
|
||||||
"aws_instance.foo",
|
|
||||||
&ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
Tainted: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
&State{},
|
|
||||||
&State{
|
|
||||||
Modules: []*ModuleState{
|
|
||||||
&ModuleState{
|
|
||||||
Path: []string{"root"},
|
|
||||||
Resources: map[string]*ResourceState{
|
|
||||||
"aws_instance.foo": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
Tainted: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"ResourceState with count unspecified => Resource Addr (new)",
|
|
||||||
false,
|
|
||||||
"aws_instance.bar",
|
|
||||||
"aws_instance.foo",
|
|
||||||
[]*ResourceState{
|
|
||||||
&ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
&ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "bar",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
&State{},
|
|
||||||
&State{
|
|
||||||
Modules: []*ModuleState{
|
|
||||||
&ModuleState{
|
|
||||||
Path: []string{"root"},
|
|
||||||
Resources: map[string]*ResourceState{
|
|
||||||
"aws_instance.foo.0": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
"aws_instance.foo.1": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "bar",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"ResourceState with count unspecified => Resource Addr (new with count)",
|
|
||||||
true,
|
|
||||||
"aws_instance.bar",
|
|
||||||
"aws_instance.foo[0]",
|
|
||||||
[]*ResourceState{
|
|
||||||
&ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
&ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "bar",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
&State{},
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"ResourceState with single count unspecified => Resource Addr (new with count)",
|
|
||||||
false,
|
|
||||||
"aws_instance.bar",
|
|
||||||
"aws_instance.foo[0]",
|
|
||||||
[]*ResourceState{
|
|
||||||
&ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
&State{},
|
|
||||||
&State{
|
|
||||||
Modules: []*ModuleState{
|
|
||||||
&ModuleState{
|
|
||||||
Path: []string{"root"},
|
|
||||||
Resources: map[string]*ResourceState{
|
|
||||||
"aws_instance.foo.0": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"ResourceState => Resource Addr (new with count)",
|
|
||||||
false,
|
|
||||||
"aws_instance.bar",
|
|
||||||
"aws_instance.foo[0]",
|
|
||||||
&ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
&State{},
|
|
||||||
&State{
|
|
||||||
Modules: []*ModuleState{
|
|
||||||
&ModuleState{
|
|
||||||
Path: []string{"root"},
|
|
||||||
Resources: map[string]*ResourceState{
|
|
||||||
"aws_instance.foo.0": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"ResourceState => Resource Addr (existing)",
|
|
||||||
true,
|
|
||||||
"aws_instance.bar",
|
|
||||||
"aws_instance.foo",
|
|
||||||
&ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
&State{
|
|
||||||
Modules: []*ModuleState{
|
|
||||||
&ModuleState{
|
|
||||||
Path: []string{"root"},
|
|
||||||
Resources: map[string]*ResourceState{
|
|
||||||
"aws_instance.foo": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
nil,
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"ResourceState => Module (new)",
|
|
||||||
false,
|
|
||||||
"aws_instance.bar",
|
|
||||||
"module.foo",
|
|
||||||
&ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
&State{},
|
|
||||||
&State{
|
|
||||||
Modules: []*ModuleState{
|
|
||||||
&ModuleState{
|
|
||||||
Path: []string{"root", "foo"},
|
|
||||||
Resources: map[string]*ResourceState{
|
|
||||||
"aws_instance.bar": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"InstanceState => Resource (new)",
|
|
||||||
false,
|
|
||||||
"aws_instance.bar.primary",
|
|
||||||
"aws_instance.baz",
|
|
||||||
&InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
|
|
||||||
&State{},
|
|
||||||
&State{
|
|
||||||
Modules: []*ModuleState{
|
|
||||||
&ModuleState{
|
|
||||||
Path: []string{"root"},
|
|
||||||
Resources: map[string]*ResourceState{
|
|
||||||
"aws_instance.baz": &ResourceState{
|
|
||||||
Type: "aws_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"InstanceState => Module (new)",
|
|
||||||
false,
|
|
||||||
"aws_instance.bar.primary",
|
|
||||||
"module.foo",
|
|
||||||
&InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
|
|
||||||
&State{},
|
|
||||||
&State{
|
|
||||||
Modules: []*ModuleState{
|
|
||||||
&ModuleState{
|
|
||||||
Path: []string{"root", "foo"},
|
|
||||||
Resources: map[string]*ResourceState{
|
|
||||||
"aws_instance.bar": &ResourceState{
|
|
||||||
Type: "aws_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
"ModuleState => Module Addr (new with data source)",
|
|
||||||
false,
|
|
||||||
"",
|
|
||||||
"module.foo",
|
|
||||||
&ModuleState{
|
|
||||||
Path: rootModulePath,
|
|
||||||
Resources: map[string]*ResourceState{
|
|
||||||
"data.test_instance.foo": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
&State{},
|
|
||||||
&State{
|
|
||||||
Modules: []*ModuleState{
|
|
||||||
&ModuleState{
|
|
||||||
Path: []string{"root", "foo"},
|
|
||||||
Resources: map[string]*ResourceState{
|
|
||||||
"data.test_instance.foo": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, tc := range cases {
|
|
||||||
t.Run(fmt.Sprintf("%d-%s", i, tc.Name), func(t *testing.T) {
|
|
||||||
// Make sure they're both initialized as normal
|
|
||||||
tc.One.init()
|
|
||||||
if tc.Two != nil {
|
|
||||||
tc.Two.init()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the value
|
|
||||||
err := tc.One.Add(tc.From, tc.To, tc.Value)
|
|
||||||
if (err != nil) != tc.Err {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
if tc.Err {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prune them both to be sure
|
|
||||||
tc.One.prune()
|
|
||||||
tc.Two.prune()
|
|
||||||
|
|
||||||
// Verify equality
|
|
||||||
if !tc.One.Equal(tc.Two) {
|
|
||||||
//t.Fatalf("Bad: %s\n\n%#v\n\n%#v", k, tc.One, tc.Two)
|
|
||||||
t.Fatalf("Bad: \n\n%s\n\n%s", tc.One.String(), tc.Two.String())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue