mirror of
https://github.com/opentofu/opentofu.git
synced 2025-02-25 18:45:20 -06:00
Merge pull request #19129 from hashicorp/f-show-list
command/state: update and fix the state list command
This commit is contained in:
commit
a88ba31b63
@ -264,6 +264,12 @@ func (m ModuleInstance) String() string {
|
|||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Equal returns true if the receiver and the given other value
|
||||||
|
// contains the exact same parts.
|
||||||
|
func (m ModuleInstance) Equal(o ModuleInstance) bool {
|
||||||
|
return m.String() == o.String()
|
||||||
|
}
|
||||||
|
|
||||||
// Less returns true if the receiver should sort before the given other value
|
// Less returns true if the receiver should sort before the given other value
|
||||||
// in a sorted list of addresses.
|
// in a sorted list of addresses.
|
||||||
func (m ModuleInstance) Less(o ModuleInstance) bool {
|
func (m ModuleInstance) Less(o ModuleInstance) bool {
|
||||||
@ -299,6 +305,27 @@ func (m ModuleInstance) Ancestors() []ModuleInstance {
|
|||||||
return ret
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsAncestor returns true if the receiver is an ancestor of the given
|
||||||
|
// other value.
|
||||||
|
func (m ModuleInstance) IsAncestor(o ModuleInstance) bool {
|
||||||
|
// Longer or equal sized paths means the receiver cannot
|
||||||
|
// be an ancestor of the given module insatnce.
|
||||||
|
if len(m) >= len(o) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, ms := range m {
|
||||||
|
if ms.Name != o[i].Name {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if ms.InstanceKey != NoKey && ms.InstanceKey != o[i].InstanceKey {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
// Call returns the module call address that corresponds to the given module
|
// Call returns the module call address that corresponds to the given module
|
||||||
// instance, along with the address of the module instance that contains it.
|
// instance, along with the address of the module instance that contains it.
|
||||||
//
|
//
|
||||||
|
@ -28,8 +28,8 @@ func (r Resource) String() string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r Resource) Equal(other Resource) bool {
|
func (r Resource) Equal(o Resource) bool {
|
||||||
return r.String() == other.String()
|
return r.String() == o.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Instance produces the address for a specific instance of the receiver
|
// Instance produces the address for a specific instance of the receiver
|
||||||
@ -90,8 +90,8 @@ func (r ResourceInstance) String() string {
|
|||||||
return r.Resource.String() + r.Key.String()
|
return r.Resource.String() + r.Key.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r ResourceInstance) Equal(other ResourceInstance) bool {
|
func (r ResourceInstance) Equal(o ResourceInstance) bool {
|
||||||
return r.String() == other.String()
|
return r.String() == o.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Absolute returns an AbsResourceInstance from the receiver and the given module
|
// Absolute returns an AbsResourceInstance from the receiver and the given module
|
||||||
@ -157,8 +157,8 @@ func (r AbsResource) String() string {
|
|||||||
return fmt.Sprintf("%s.%s", r.Module.String(), r.Resource.String())
|
return fmt.Sprintf("%s.%s", r.Module.String(), r.Resource.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r AbsResource) Equal(other AbsResource) bool {
|
func (r AbsResource) Equal(o AbsResource) bool {
|
||||||
return r.String() == other.String()
|
return r.String() == o.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// AbsResourceInstance is an absolute address for a resource instance under a
|
// AbsResourceInstance is an absolute address for a resource instance under a
|
||||||
@ -216,8 +216,8 @@ func (r AbsResourceInstance) String() string {
|
|||||||
return fmt.Sprintf("%s.%s", r.Module.String(), r.Resource.String())
|
return fmt.Sprintf("%s.%s", r.Module.String(), r.Resource.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r AbsResourceInstance) Equal(other AbsResourceInstance) bool {
|
func (r AbsResourceInstance) Equal(o AbsResourceInstance) bool {
|
||||||
return r.String() == other.String()
|
return r.String() == o.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Less returns true if the receiver should sort before the given other value
|
// Less returns true if the receiver should sort before the given other value
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/states"
|
||||||
"github.com/mitchellh/cli"
|
"github.com/mitchellh/cli"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -21,8 +22,8 @@ func (c *StateListCommand) Run(args []string) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
cmdFlags := c.Meta.flagSet("state list")
|
cmdFlags := c.Meta.flagSet("state list")
|
||||||
cmdFlags.StringVar(&c.Meta.statePath, "state", DefaultStateFilename, "path")
|
cmdFlags.StringVar(&c.Meta.statePath, "state", "", "path")
|
||||||
//lookupId := cmdFlags.String("id", "", "Restrict output to paths with a resource having the specified ID.")
|
lookupId := cmdFlags.String("id", "", "Restrict output to paths with a resource having the specified ID.")
|
||||||
if err := cmdFlags.Parse(args); err != nil {
|
if err := cmdFlags.Parse(args); err != nil {
|
||||||
return cli.RunResultHelp
|
return cli.RunResultHelp
|
||||||
}
|
}
|
||||||
@ -35,30 +36,26 @@ func (c *StateListCommand) Run(args []string) int {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
env := c.Workspace()
|
|
||||||
// Get the state
|
// Get the state
|
||||||
state, err := b.StateMgr(env)
|
env := c.Workspace()
|
||||||
|
stateMgr, err := b.StateMgr(env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Ui.Error(fmt.Sprintf("Failed to load state: %s", err))
|
c.Ui.Error(fmt.Sprintf("Failed to load state: %s", err))
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := state.RefreshState(); err != nil {
|
if err := stateMgr.RefreshState(); err != nil {
|
||||||
c.Ui.Error(fmt.Sprintf("Failed to load state: %s", err))
|
c.Ui.Error(fmt.Sprintf("Failed to load state: %s", err))
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
stateReal := state.State()
|
state := stateMgr.State()
|
||||||
if stateReal == nil {
|
if state == nil {
|
||||||
c.Ui.Error(fmt.Sprintf(errStateNotFound))
|
c.Ui.Error(fmt.Sprintf(errStateNotFound))
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: update this for the new state types
|
filter := &states.Filter{State: state}
|
||||||
c.Ui.Error("state list command not yet updated for new state types")
|
|
||||||
return 1
|
|
||||||
|
|
||||||
/*filter := &terraform.StateFilter{State: stateReal}
|
|
||||||
results, err := filter.Filter(args...)
|
results, err := filter.Filter(args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Ui.Error(fmt.Sprintf(errStateFilter, err))
|
c.Ui.Error(fmt.Sprintf(errStateFilter, err))
|
||||||
@ -66,12 +63,12 @@ func (c *StateListCommand) Run(args []string) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, result := range results {
|
for _, result := range results {
|
||||||
if i, ok := result.Value.(*terraform.InstanceState); ok {
|
if is, ok := result.Value.(*states.ResourceInstance); ok {
|
||||||
if *lookupId == "" || i.ID == *lookupId {
|
if *lookupId == "" || *lookupId == states.LegacyInstanceObjectID(is.Current) {
|
||||||
c.Ui.Output(result.Address)
|
c.Ui.Output(result.Address)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}*/
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
@ -96,10 +96,10 @@ func TestStateListWithNonExistentID(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStateList_backendState(t *testing.T) {
|
func TestStateList_backendDefaultState(t *testing.T) {
|
||||||
// Create a temporary working directory that is empty
|
// Create a temporary working directory that is empty
|
||||||
td := tempDir(t)
|
td := tempDir(t)
|
||||||
copy.CopyDir(testFixturePath("state-list-backend"), td)
|
copy.CopyDir(testFixturePath("state-list-backend-default"), td)
|
||||||
defer os.RemoveAll(td)
|
defer os.RemoveAll(td)
|
||||||
defer testChdir(t, td)()
|
defer testChdir(t, td)()
|
||||||
|
|
||||||
@ -125,6 +125,65 @@ func TestStateList_backendState(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStateList_backendCustomState(t *testing.T) {
|
||||||
|
// Create a temporary working directory that is empty
|
||||||
|
td := tempDir(t)
|
||||||
|
copy.CopyDir(testFixturePath("state-list-backend-custom"), td)
|
||||||
|
defer os.RemoveAll(td)
|
||||||
|
defer testChdir(t, td)()
|
||||||
|
|
||||||
|
p := testProvider()
|
||||||
|
ui := cli.NewMockUi()
|
||||||
|
c := &StateListCommand{
|
||||||
|
Meta: Meta{
|
||||||
|
testingOverrides: metaOverridesForProvider(p),
|
||||||
|
Ui: ui,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []string{}
|
||||||
|
if code := c.Run(args); code != 0 {
|
||||||
|
t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that outputs were displayed
|
||||||
|
expected := "null_resource.a\n"
|
||||||
|
actual := ui.OutputWriter.String()
|
||||||
|
if actual != expected {
|
||||||
|
t.Fatalf("Expected:\n%q\n\nTo equal: %q", actual, expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStateList_backendOverrideState(t *testing.T) {
|
||||||
|
// Create a temporary working directory that is empty
|
||||||
|
td := tempDir(t)
|
||||||
|
copy.CopyDir(testFixturePath("state-list-backend-custom"), td)
|
||||||
|
defer os.RemoveAll(td)
|
||||||
|
defer testChdir(t, td)()
|
||||||
|
|
||||||
|
p := testProvider()
|
||||||
|
ui := cli.NewMockUi()
|
||||||
|
c := &StateListCommand{
|
||||||
|
Meta: Meta{
|
||||||
|
testingOverrides: metaOverridesForProvider(p),
|
||||||
|
Ui: ui,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// This test is configured to use a local backend that has
|
||||||
|
// a custom path defined. So we test if we can still pass
|
||||||
|
// is a user defined state file that will then override the
|
||||||
|
// one configured in the backend. As this file does not exist
|
||||||
|
// it should exit with a no state found error.
|
||||||
|
args := []string{"-state=" + DefaultStateFilename}
|
||||||
|
if code := c.Run(args); code != 1 {
|
||||||
|
t.Fatalf("bad: %d", code)
|
||||||
|
}
|
||||||
|
if !strings.Contains(ui.ErrorWriter.String(), "No state file was found!") {
|
||||||
|
t.Fatalf("expected a no state file error, got: %s", ui.ErrorWriter.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestStateList_noState(t *testing.T) {
|
func TestStateList_noState(t *testing.T) {
|
||||||
tmp, cwd := testCwd(t)
|
tmp, cwd := testCwd(t)
|
||||||
defer testFixCwd(t, tmp, cwd)
|
defer testFixCwd(t, tmp, cwd)
|
||||||
@ -140,7 +199,7 @@ func TestStateList_noState(t *testing.T) {
|
|||||||
|
|
||||||
args := []string{}
|
args := []string{}
|
||||||
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", code)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
backendlocal "github.com/hashicorp/terraform/backend/local"
|
backendlocal "github.com/hashicorp/terraform/backend/local"
|
||||||
"github.com/hashicorp/terraform/state"
|
"github.com/hashicorp/terraform/state"
|
||||||
|
"github.com/hashicorp/terraform/states"
|
||||||
"github.com/hashicorp/terraform/states/statemgr"
|
"github.com/hashicorp/terraform/states/statemgr"
|
||||||
"github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
)
|
)
|
||||||
@ -79,8 +80,8 @@ func (c *StateMeta) State() (state.State, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// filterInstance filters a single instance out of filter results.
|
// filterInstance filters a single instance out of filter results.
|
||||||
func (c *StateMeta) filterInstance(rs []*terraform.StateFilterResult) (*terraform.StateFilterResult, error) {
|
func (c *StateMeta) filterInstance(rs []*states.FilterResult) (*states.FilterResult, error) {
|
||||||
var result *terraform.StateFilterResult
|
var result *states.FilterResult
|
||||||
for _, r := range rs {
|
for _, r := range rs {
|
||||||
if _, ok := r.Value.(*terraform.InstanceState); !ok {
|
if _, ok := r.Value.(*terraform.InstanceState); !ok {
|
||||||
continue
|
continue
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"version": 3,
|
||||||
|
"serial": 0,
|
||||||
|
"lineage": "666f9301-7e65-4b19-ae23-71184bb19b03",
|
||||||
|
"backend": {
|
||||||
|
"type": "local",
|
||||||
|
"config": {
|
||||||
|
"path": "local-state.tfstate",
|
||||||
|
"workspace_dir": null
|
||||||
|
},
|
||||||
|
"hash": 4282859327
|
||||||
|
},
|
||||||
|
"modules": [
|
||||||
|
{
|
||||||
|
"path": [
|
||||||
|
"root"
|
||||||
|
],
|
||||||
|
"outputs": {},
|
||||||
|
"resources": {},
|
||||||
|
"depends_on": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"version": 4,
|
||||||
|
"terraform_version": "0.12.0",
|
||||||
|
"serial": 7,
|
||||||
|
"lineage": "configuredUnchanged",
|
||||||
|
"outputs": {},
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"mode": "managed",
|
||||||
|
"type": "null_resource",
|
||||||
|
"name": "a",
|
||||||
|
"provider": "provider.null",
|
||||||
|
"instances": [
|
||||||
|
{
|
||||||
|
"schema_version": 0,
|
||||||
|
"attributes": {
|
||||||
|
"id": "8521602373864259745",
|
||||||
|
"triggers": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -5,9 +5,10 @@
|
|||||||
"backend": {
|
"backend": {
|
||||||
"type": "local",
|
"type": "local",
|
||||||
"config": {
|
"config": {
|
||||||
"path": "local-state.tfstate"
|
"path": null,
|
||||||
|
"workspace_dir": null
|
||||||
},
|
},
|
||||||
"hash": 9073424445967744180
|
"hash": 666019178
|
||||||
},
|
},
|
||||||
"modules": [
|
"modules": [
|
||||||
{
|
{
|
4
command/test-fixtures/state-list-backend-default/main.tf
Normal file
4
command/test-fixtures/state-list-backend-default/main.tf
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
terraform {
|
||||||
|
backend "local" {
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"version": 4,
|
||||||
|
"terraform_version": "0.12.0",
|
||||||
|
"serial": 7,
|
||||||
|
"lineage": "configuredUnchanged",
|
||||||
|
"outputs": {},
|
||||||
|
"resources": [
|
||||||
|
{
|
||||||
|
"mode": "managed",
|
||||||
|
"type": "null_resource",
|
||||||
|
"name": "a",
|
||||||
|
"provider": "provider.null",
|
||||||
|
"instances": [
|
||||||
|
{
|
||||||
|
"schema_version": 0,
|
||||||
|
"attributes": {
|
||||||
|
"id": "8521602373864259745",
|
||||||
|
"triggers": null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -1,31 +0,0 @@
|
|||||||
{
|
|
||||||
"version": 3,
|
|
||||||
"terraform_version": "0.8.2",
|
|
||||||
"serial": 7,
|
|
||||||
"lineage": "configuredUnchanged",
|
|
||||||
"modules": [
|
|
||||||
{
|
|
||||||
"path": [
|
|
||||||
"root"
|
|
||||||
],
|
|
||||||
"outputs": {},
|
|
||||||
"resources": {
|
|
||||||
"null_resource.a": {
|
|
||||||
"type": "null_resource",
|
|
||||||
"depends_on": [],
|
|
||||||
"primary": {
|
|
||||||
"id": "5416263284413907707",
|
|
||||||
"attributes": {
|
|
||||||
"id": "5416263284413907707"
|
|
||||||
},
|
|
||||||
"meta": {},
|
|
||||||
"tainted": false
|
|
||||||
},
|
|
||||||
"deposed": [],
|
|
||||||
"provider": ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"depends_on": []
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -6,7 +6,6 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
uuid "github.com/hashicorp/go-uuid"
|
uuid "github.com/hashicorp/go-uuid"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/state"
|
"github.com/hashicorp/terraform/state"
|
||||||
"github.com/hashicorp/terraform/states"
|
"github.com/hashicorp/terraform/states"
|
||||||
"github.com/hashicorp/terraform/states/statefile"
|
"github.com/hashicorp/terraform/states/statefile"
|
||||||
|
188
states/state_filter.go
Normal file
188
states/state_filter.go
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
package states
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/addrs"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Filter is responsible for filtering and searching a state.
|
||||||
|
//
|
||||||
|
// This is a separate struct from State rather than a method on State
|
||||||
|
// because Filter might create sidecar data structures to optimize
|
||||||
|
// filtering on the state.
|
||||||
|
//
|
||||||
|
// If you change the State, the filter created is invalid and either
|
||||||
|
// Reset should be called or a new one should be allocated. Filter
|
||||||
|
// will not watch State for changes and do this for you. If you filter after
|
||||||
|
// changing the State without calling Reset, the behavior is not defined.
|
||||||
|
type Filter struct {
|
||||||
|
State *State
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter takes the addresses specified by fs and finds all the matches.
|
||||||
|
// The values of fs are resource addressing syntax that can be parsed by
|
||||||
|
// ParseResourceAddress.
|
||||||
|
func (f *Filter) Filter(fs ...string) ([]*FilterResult, error) {
|
||||||
|
// Parse all the addresses
|
||||||
|
as := make([]addrs.Targetable, len(fs))
|
||||||
|
for i, v := range fs {
|
||||||
|
if addr, diags := addrs.ParseModuleInstanceStr(v); !diags.HasErrors() {
|
||||||
|
as[i] = addr
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if addr, diags := addrs.ParseAbsResourceStr(v); !diags.HasErrors() {
|
||||||
|
as[i] = addr
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if addr, diags := addrs.ParseAbsResourceInstanceStr(v); !diags.HasErrors() {
|
||||||
|
as[i] = addr
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("Error parsing address '%s'", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we weren't given any filters, then we list all
|
||||||
|
if len(fs) == 0 {
|
||||||
|
as = append(as, addrs.Targetable(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter each of the address. We keep track of this in a map to
|
||||||
|
// strip duplicates.
|
||||||
|
resultSet := make(map[string]*FilterResult)
|
||||||
|
for _, addr := range as {
|
||||||
|
for _, v := range f.filterSingle(addr) {
|
||||||
|
resultSet[v.String()] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make the result list
|
||||||
|
results := make([]*FilterResult, 0, len(resultSet))
|
||||||
|
for _, v := range resultSet {
|
||||||
|
results = append(results, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort them and return
|
||||||
|
sort.Sort(FilterResultSlice(results))
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Filter) filterSingle(addr addrs.Targetable) []*FilterResult {
|
||||||
|
// The slice to keep track of results
|
||||||
|
var results []*FilterResult
|
||||||
|
|
||||||
|
// Check if we received a module instance address that
|
||||||
|
// should be used as module filter, and if not set the
|
||||||
|
// filter to the root module instance.
|
||||||
|
filter, ok := addr.(addrs.ModuleInstance)
|
||||||
|
if !ok {
|
||||||
|
filter = addrs.RootModuleInstance
|
||||||
|
}
|
||||||
|
|
||||||
|
// Go through modules first.
|
||||||
|
modules := make([]*Module, 0, len(f.State.Modules))
|
||||||
|
for _, m := range f.State.Modules {
|
||||||
|
if filter.IsRoot() || filter.Equal(m.Addr) || filter.IsAncestor(m.Addr) {
|
||||||
|
modules = append(modules, m)
|
||||||
|
|
||||||
|
// Only add the module to the results if we searched
|
||||||
|
// for a non-root module and found a (partial) match.
|
||||||
|
if (addr == nil && !m.Addr.IsRoot()) ||
|
||||||
|
(!filter.IsRoot() && (filter.Equal(m.Addr) || filter.IsAncestor(m.Addr))) {
|
||||||
|
results = append(results, &FilterResult{
|
||||||
|
Address: m.Addr.String(),
|
||||||
|
Value: m,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// With the modules set, go through all the resources within
|
||||||
|
// the modules to find relevant resources.
|
||||||
|
for _, m := range modules {
|
||||||
|
for _, rs := range m.Resources {
|
||||||
|
if f.relevant(addr, rs.Addr.Absolute(m.Addr), addrs.NoKey) {
|
||||||
|
results = append(results, &FilterResult{
|
||||||
|
Address: rs.Addr.Absolute(m.Addr).String(),
|
||||||
|
Value: rs,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
for key, is := range rs.Instances {
|
||||||
|
if f.relevant(addr, rs.Addr.Absolute(m.Addr), key) {
|
||||||
|
results = append(results, &FilterResult{
|
||||||
|
Address: rs.Addr.Absolute(m.Addr).Instance(key).String(),
|
||||||
|
Value: is,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Filter) relevant(filter addrs.Targetable, rs addrs.AbsResource, key addrs.InstanceKey) bool {
|
||||||
|
switch filter := filter.(type) {
|
||||||
|
case addrs.AbsResource:
|
||||||
|
if filter.Module != nil {
|
||||||
|
return filter.Equal(rs)
|
||||||
|
}
|
||||||
|
return filter.Resource.Equal(rs.Resource)
|
||||||
|
case addrs.AbsResourceInstance:
|
||||||
|
if filter.Module != nil {
|
||||||
|
return filter.Equal(rs.Instance(key))
|
||||||
|
}
|
||||||
|
return filter.Resource.Equal(rs.Resource.Instance(key))
|
||||||
|
default:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterResult is a single result from a filter operation. Filter can
|
||||||
|
// match multiple things within a state (curently modules and resources).
|
||||||
|
type FilterResult struct {
|
||||||
|
// Address is the address that can be used to reference this exact result.
|
||||||
|
Address string
|
||||||
|
|
||||||
|
// Value is the actual value. This must be type switched on. It can be
|
||||||
|
// any either a `Module` or `ResourceInstance`.
|
||||||
|
Value interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *FilterResult) String() string {
|
||||||
|
return fmt.Sprintf("%T: %s", r.Value, r.Address)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *FilterResult) sortedType() int {
|
||||||
|
switch r.Value.(type) {
|
||||||
|
case *Module:
|
||||||
|
return 0
|
||||||
|
case *Resource:
|
||||||
|
return 1
|
||||||
|
case *ResourceInstance:
|
||||||
|
return 2
|
||||||
|
default:
|
||||||
|
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 != b.Address {
|
||||||
|
return a.Address < b.Address
|
||||||
|
}
|
||||||
|
|
||||||
|
// Addresses are the same, which means it matters on the type
|
||||||
|
return a.sortedType() < b.sortedType()
|
||||||
|
}
|
519
states/state_filter_test.go
Normal file
519
states/state_filter_test.go
Normal file
@ -0,0 +1,519 @@
|
|||||||
|
package states
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/addrs"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFilterFilter(t *testing.T) {
|
||||||
|
cases := map[string]struct {
|
||||||
|
State *State
|
||||||
|
Filters []string
|
||||||
|
Expected []string
|
||||||
|
}{
|
||||||
|
"all": {
|
||||||
|
testStateSmall(),
|
||||||
|
[]string{},
|
||||||
|
[]string{
|
||||||
|
"*states.Resource: aws_key_pair.onprem",
|
||||||
|
"*states.ResourceInstance: aws_key_pair.onprem",
|
||||||
|
"*states.Module: module.boot",
|
||||||
|
"*states.Resource: module.boot.aws_route53_record.oasis-consul-boot-a",
|
||||||
|
"*states.ResourceInstance: module.boot.aws_route53_record.oasis-consul-boot-a",
|
||||||
|
"*states.Resource: module.boot.aws_route53_record.oasis-consul-boot-ns",
|
||||||
|
"*states.ResourceInstance: module.boot.aws_route53_record.oasis-consul-boot-ns",
|
||||||
|
"*states.Resource: module.boot.aws_route53_zone.oasis-consul-boot",
|
||||||
|
"*states.ResourceInstance: module.boot.aws_route53_zone.oasis-consul-boot",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"single resource": {
|
||||||
|
testStateSmall(),
|
||||||
|
[]string{"aws_key_pair.onprem"},
|
||||||
|
[]string{
|
||||||
|
"*states.Resource: aws_key_pair.onprem",
|
||||||
|
"*states.ResourceInstance: aws_key_pair.onprem",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"single resource from minimal state": {
|
||||||
|
testStateSingleMinimal(),
|
||||||
|
[]string{"aws_instance.web"},
|
||||||
|
[]string{
|
||||||
|
"*states.Resource: aws_instance.web",
|
||||||
|
"*states.ResourceInstance: aws_instance.web",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"single resource with similar names": {
|
||||||
|
testStateSmallTestInstance(),
|
||||||
|
[]string{"test_instance.foo"},
|
||||||
|
[]string{
|
||||||
|
"*states.Resource: test_instance.foo",
|
||||||
|
"*states.ResourceInstance: test_instance.foo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"module filter": {
|
||||||
|
testStateComplete(),
|
||||||
|
[]string{"module.boot"},
|
||||||
|
[]string{
|
||||||
|
"*states.Module: module.boot",
|
||||||
|
"*states.Resource: module.boot.aws_route53_record.oasis-consul-boot-a",
|
||||||
|
"*states.ResourceInstance: module.boot.aws_route53_record.oasis-consul-boot-a",
|
||||||
|
"*states.Resource: module.boot.aws_route53_record.oasis-consul-boot-ns",
|
||||||
|
"*states.ResourceInstance: module.boot.aws_route53_record.oasis-consul-boot-ns",
|
||||||
|
"*states.Resource: module.boot.aws_route53_zone.oasis-consul-boot",
|
||||||
|
"*states.ResourceInstance: module.boot.aws_route53_zone.oasis-consul-boot",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"resource in module": {
|
||||||
|
testStateComplete(),
|
||||||
|
[]string{"module.boot.aws_route53_zone.oasis-consul-boot"},
|
||||||
|
[]string{
|
||||||
|
"*states.Resource: module.boot.aws_route53_zone.oasis-consul-boot",
|
||||||
|
"*states.ResourceInstance: module.boot.aws_route53_zone.oasis-consul-boot",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"resource in module 2": {
|
||||||
|
testStateResourceInModule(),
|
||||||
|
[]string{"module.foo.aws_instance.foo"},
|
||||||
|
[]string{},
|
||||||
|
},
|
||||||
|
|
||||||
|
"single count index": {
|
||||||
|
testStateComplete(),
|
||||||
|
[]string{"module.consul.aws_instance.consul-green[0]"},
|
||||||
|
[]string{
|
||||||
|
"*states.ResourceInstance: module.consul.aws_instance.consul-green[0]",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"no count index": {
|
||||||
|
testStateComplete(),
|
||||||
|
[]string{"module.consul.aws_instance.consul-green"},
|
||||||
|
[]string{
|
||||||
|
"*states.Resource: module.consul.aws_instance.consul-green",
|
||||||
|
"*states.ResourceInstance: module.consul.aws_instance.consul-green[0]",
|
||||||
|
"*states.ResourceInstance: module.consul.aws_instance.consul-green[1]",
|
||||||
|
"*states.ResourceInstance: module.consul.aws_instance.consul-green[2]",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
"nested modules": {
|
||||||
|
testStateNestedModules(),
|
||||||
|
[]string{"module.outer"},
|
||||||
|
[]string{
|
||||||
|
"*states.Module: module.outer",
|
||||||
|
"*states.Module: module.outer.module.child1",
|
||||||
|
"*states.Resource: module.outer.module.child1.aws_instance.foo",
|
||||||
|
"*states.ResourceInstance: module.outer.module.child1.aws_instance.foo",
|
||||||
|
"*states.Module: module.outer.module.child2",
|
||||||
|
"*states.Resource: module.outer.module.child2.aws_instance.foo",
|
||||||
|
"*states.ResourceInstance: module.outer.module.child2.aws_instance.foo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for n, tc := range cases {
|
||||||
|
// Create the filter
|
||||||
|
filter := &Filter{State: tc.State}
|
||||||
|
|
||||||
|
// Filter!
|
||||||
|
results, err := filter.Filter(tc.Filters...)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%q: err: %s", n, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
actual := make([]string, len(results))
|
||||||
|
for i, result := range results {
|
||||||
|
actual[i] = result.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(actual, tc.Expected) {
|
||||||
|
t.Fatalf("%q: expected, then actual\n\n%#v\n\n%#v", n, tc.Expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// testStateComplete returns a test State structure.
|
||||||
|
func testStateComplete() *State {
|
||||||
|
root := addrs.RootModuleInstance
|
||||||
|
boot, _ := addrs.ParseModuleInstanceStr("module.boot")
|
||||||
|
consul, _ := addrs.ParseModuleInstanceStr("module.consul")
|
||||||
|
vault, _ := addrs.ParseModuleInstanceStr("module.vault")
|
||||||
|
|
||||||
|
return BuildState(func(s *SyncState) {
|
||||||
|
s.SetResourceInstanceCurrent(
|
||||||
|
addrs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "aws_key_pair",
|
||||||
|
Name: "onprem",
|
||||||
|
}.Instance(addrs.NoKey).Absolute(root),
|
||||||
|
&ResourceInstanceObjectSrc{
|
||||||
|
AttrsJSON: []byte(`{"id": "1234567890"}`),
|
||||||
|
Status: ObjectReady,
|
||||||
|
},
|
||||||
|
addrs.ProviderConfig{
|
||||||
|
Type: "aws",
|
||||||
|
}.Absolute(root),
|
||||||
|
)
|
||||||
|
s.SetResourceInstanceCurrent(
|
||||||
|
addrs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "aws_route53_record",
|
||||||
|
Name: "oasis-consul-boot-a",
|
||||||
|
}.Instance(addrs.NoKey).Absolute(boot),
|
||||||
|
&ResourceInstanceObjectSrc{
|
||||||
|
AttrsJSON: []byte(`{"id": "1234567890"}`),
|
||||||
|
Status: ObjectReady,
|
||||||
|
},
|
||||||
|
addrs.ProviderConfig{
|
||||||
|
Type: "aws",
|
||||||
|
}.Absolute(boot),
|
||||||
|
)
|
||||||
|
s.SetResourceInstanceCurrent(
|
||||||
|
addrs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "aws_route53_record",
|
||||||
|
Name: "oasis-consul-boot-ns",
|
||||||
|
}.Instance(addrs.NoKey).Absolute(boot),
|
||||||
|
&ResourceInstanceObjectSrc{
|
||||||
|
AttrsJSON: []byte(`{"id": "1234567890"}`),
|
||||||
|
Status: ObjectReady,
|
||||||
|
},
|
||||||
|
addrs.ProviderConfig{
|
||||||
|
Type: "aws",
|
||||||
|
}.Absolute(boot),
|
||||||
|
)
|
||||||
|
s.SetResourceInstanceCurrent(
|
||||||
|
addrs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "aws_route53_zone",
|
||||||
|
Name: "oasis-consul-boot",
|
||||||
|
}.Instance(addrs.NoKey).Absolute(boot),
|
||||||
|
&ResourceInstanceObjectSrc{
|
||||||
|
AttrsJSON: []byte(`{"id": "1234567890"}`),
|
||||||
|
Status: ObjectReady,
|
||||||
|
},
|
||||||
|
addrs.ProviderConfig{
|
||||||
|
Type: "aws",
|
||||||
|
}.Absolute(boot),
|
||||||
|
)
|
||||||
|
s.SetResourceInstanceCurrent(
|
||||||
|
addrs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "aws_instance",
|
||||||
|
Name: "consul-green",
|
||||||
|
}.Instance(addrs.IntKey(0)).Absolute(consul),
|
||||||
|
&ResourceInstanceObjectSrc{
|
||||||
|
AttrsJSON: []byte(`{"id": "1234567890"}`),
|
||||||
|
Status: ObjectReady,
|
||||||
|
},
|
||||||
|
addrs.ProviderConfig{
|
||||||
|
Type: "aws",
|
||||||
|
}.Absolute(consul),
|
||||||
|
)
|
||||||
|
s.SetResourceInstanceCurrent(
|
||||||
|
addrs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "aws_instance",
|
||||||
|
Name: "consul-green",
|
||||||
|
}.Instance(addrs.IntKey(1)).Absolute(consul),
|
||||||
|
&ResourceInstanceObjectSrc{
|
||||||
|
AttrsJSON: []byte(`{"id": "1234567890"}`),
|
||||||
|
Status: ObjectReady,
|
||||||
|
},
|
||||||
|
addrs.ProviderConfig{
|
||||||
|
Type: "aws",
|
||||||
|
}.Absolute(consul),
|
||||||
|
)
|
||||||
|
s.SetResourceInstanceCurrent(
|
||||||
|
addrs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "aws_instance",
|
||||||
|
Name: "consul-green",
|
||||||
|
}.Instance(addrs.IntKey(2)).Absolute(consul),
|
||||||
|
&ResourceInstanceObjectSrc{
|
||||||
|
AttrsJSON: []byte(`{"id": "1234567890"}`),
|
||||||
|
Status: ObjectReady,
|
||||||
|
},
|
||||||
|
addrs.ProviderConfig{
|
||||||
|
Type: "aws",
|
||||||
|
}.Absolute(consul),
|
||||||
|
)
|
||||||
|
s.SetResourceInstanceCurrent(
|
||||||
|
addrs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "aws_security_group",
|
||||||
|
Name: "consul",
|
||||||
|
}.Instance(addrs.NoKey).Absolute(consul),
|
||||||
|
&ResourceInstanceObjectSrc{
|
||||||
|
AttrsJSON: []byte(`{"id": "1234567890"}`),
|
||||||
|
Status: ObjectReady,
|
||||||
|
},
|
||||||
|
addrs.ProviderConfig{
|
||||||
|
Type: "aws",
|
||||||
|
}.Absolute(consul),
|
||||||
|
)
|
||||||
|
s.SetResourceInstanceCurrent(
|
||||||
|
addrs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "aws_elb",
|
||||||
|
Name: "vault",
|
||||||
|
}.Instance(addrs.NoKey).Absolute(vault),
|
||||||
|
&ResourceInstanceObjectSrc{
|
||||||
|
AttrsJSON: []byte(`{"id": "1234567890"}`),
|
||||||
|
Status: ObjectReady,
|
||||||
|
},
|
||||||
|
addrs.ProviderConfig{
|
||||||
|
Type: "aws",
|
||||||
|
}.Absolute(vault),
|
||||||
|
)
|
||||||
|
s.SetResourceInstanceCurrent(
|
||||||
|
addrs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "aws_instance",
|
||||||
|
Name: "vault",
|
||||||
|
}.Instance(addrs.IntKey(0)).Absolute(vault),
|
||||||
|
&ResourceInstanceObjectSrc{
|
||||||
|
AttrsJSON: []byte(`{"id": "1234567890"}`),
|
||||||
|
Status: ObjectReady,
|
||||||
|
},
|
||||||
|
addrs.ProviderConfig{
|
||||||
|
Type: "aws",
|
||||||
|
}.Absolute(vault),
|
||||||
|
)
|
||||||
|
s.SetResourceInstanceCurrent(
|
||||||
|
addrs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "aws_instance",
|
||||||
|
Name: "vault",
|
||||||
|
}.Instance(addrs.IntKey(1)).Absolute(vault),
|
||||||
|
&ResourceInstanceObjectSrc{
|
||||||
|
AttrsJSON: []byte(`{"id": "1234567890"}`),
|
||||||
|
Status: ObjectReady,
|
||||||
|
},
|
||||||
|
addrs.ProviderConfig{
|
||||||
|
Type: "aws",
|
||||||
|
}.Absolute(vault),
|
||||||
|
)
|
||||||
|
s.SetResourceInstanceCurrent(
|
||||||
|
addrs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "aws_instance",
|
||||||
|
Name: "vault",
|
||||||
|
}.Instance(addrs.IntKey(2)).Absolute(vault),
|
||||||
|
&ResourceInstanceObjectSrc{
|
||||||
|
AttrsJSON: []byte(`{"id": "1234567890"}`),
|
||||||
|
Status: ObjectReady,
|
||||||
|
},
|
||||||
|
addrs.ProviderConfig{
|
||||||
|
Type: "aws",
|
||||||
|
}.Absolute(vault),
|
||||||
|
)
|
||||||
|
s.SetResourceInstanceCurrent(
|
||||||
|
addrs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "aws_security_group",
|
||||||
|
Name: "vault",
|
||||||
|
}.Instance(addrs.NoKey).Absolute(vault),
|
||||||
|
&ResourceInstanceObjectSrc{
|
||||||
|
AttrsJSON: []byte(`{"id": "1234567890"}`),
|
||||||
|
Status: ObjectReady,
|
||||||
|
},
|
||||||
|
addrs.ProviderConfig{
|
||||||
|
Type: "aws",
|
||||||
|
}.Absolute(vault),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// testStateNestedModules returns a test State structure.
|
||||||
|
func testStateNestedModules() *State {
|
||||||
|
outer, _ := addrs.ParseModuleInstanceStr("module.outer")
|
||||||
|
child1, _ := addrs.ParseModuleInstanceStr("module.outer.module.child1")
|
||||||
|
child2, _ := addrs.ParseModuleInstanceStr("module.outer.module.child2")
|
||||||
|
|
||||||
|
state := BuildState(func(s *SyncState) {
|
||||||
|
s.SetResourceInstanceCurrent(
|
||||||
|
addrs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "aws_instance",
|
||||||
|
Name: "foo",
|
||||||
|
}.Instance(addrs.NoKey).Absolute(child1),
|
||||||
|
&ResourceInstanceObjectSrc{
|
||||||
|
AttrsJSON: []byte(`{"id": "1234567890"}`),
|
||||||
|
Status: ObjectReady,
|
||||||
|
},
|
||||||
|
addrs.ProviderConfig{
|
||||||
|
Type: "aws",
|
||||||
|
}.Absolute(child1),
|
||||||
|
)
|
||||||
|
s.SetResourceInstanceCurrent(
|
||||||
|
addrs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "aws_instance",
|
||||||
|
Name: "foo",
|
||||||
|
}.Instance(addrs.NoKey).Absolute(child2),
|
||||||
|
&ResourceInstanceObjectSrc{
|
||||||
|
AttrsJSON: []byte(`{"id": "1234567890"}`),
|
||||||
|
Status: ObjectReady,
|
||||||
|
},
|
||||||
|
addrs.ProviderConfig{
|
||||||
|
Type: "aws",
|
||||||
|
}.Absolute(child2),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
state.Modules[outer.String()] = NewModule(outer)
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
// testStateSingleMinimal returns a test State structure.
|
||||||
|
func testStateSingleMinimal() *State {
|
||||||
|
return BuildState(func(s *SyncState) {
|
||||||
|
s.SetResourceInstanceCurrent(
|
||||||
|
addrs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "aws_instance",
|
||||||
|
Name: "web",
|
||||||
|
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
||||||
|
&ResourceInstanceObjectSrc{
|
||||||
|
AttrsJSON: []byte(`{"id": "1234567890"}`),
|
||||||
|
Status: ObjectReady,
|
||||||
|
},
|
||||||
|
addrs.ProviderConfig{
|
||||||
|
Type: "aws",
|
||||||
|
}.Absolute(addrs.RootModuleInstance),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// testStateSmall returns a test State structure.
|
||||||
|
func testStateSmall() *State {
|
||||||
|
root := addrs.RootModuleInstance
|
||||||
|
boot, _ := addrs.ParseModuleInstanceStr("module.boot")
|
||||||
|
|
||||||
|
state := BuildState(func(s *SyncState) {
|
||||||
|
s.SetResourceInstanceCurrent(
|
||||||
|
addrs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "aws_key_pair",
|
||||||
|
Name: "onprem",
|
||||||
|
}.Instance(addrs.NoKey).Absolute(root),
|
||||||
|
&ResourceInstanceObjectSrc{
|
||||||
|
AttrsJSON: []byte(`{"id": "1234567890"}`),
|
||||||
|
Status: ObjectReady,
|
||||||
|
},
|
||||||
|
addrs.ProviderConfig{
|
||||||
|
Type: "aws",
|
||||||
|
}.Absolute(root),
|
||||||
|
)
|
||||||
|
s.SetResourceInstanceCurrent(
|
||||||
|
addrs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "aws_route53_record",
|
||||||
|
Name: "oasis-consul-boot-a",
|
||||||
|
}.Instance(addrs.NoKey).Absolute(boot),
|
||||||
|
&ResourceInstanceObjectSrc{
|
||||||
|
AttrsJSON: []byte(`{"id": "1234567890"}`),
|
||||||
|
Status: ObjectReady,
|
||||||
|
},
|
||||||
|
addrs.ProviderConfig{
|
||||||
|
Type: "aws",
|
||||||
|
}.Absolute(boot),
|
||||||
|
)
|
||||||
|
s.SetResourceInstanceCurrent(
|
||||||
|
addrs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "aws_route53_record",
|
||||||
|
Name: "oasis-consul-boot-ns",
|
||||||
|
}.Instance(addrs.NoKey).Absolute(boot),
|
||||||
|
&ResourceInstanceObjectSrc{
|
||||||
|
AttrsJSON: []byte(`{"id": "1234567890"}`),
|
||||||
|
Status: ObjectReady,
|
||||||
|
},
|
||||||
|
addrs.ProviderConfig{
|
||||||
|
Type: "aws",
|
||||||
|
}.Absolute(boot),
|
||||||
|
)
|
||||||
|
s.SetResourceInstanceCurrent(
|
||||||
|
addrs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "aws_route53_zone",
|
||||||
|
Name: "oasis-consul-boot",
|
||||||
|
}.Instance(addrs.NoKey).Absolute(boot),
|
||||||
|
&ResourceInstanceObjectSrc{
|
||||||
|
AttrsJSON: []byte(`{"id": "1234567890"}`),
|
||||||
|
Status: ObjectReady,
|
||||||
|
},
|
||||||
|
addrs.ProviderConfig{
|
||||||
|
Type: "aws",
|
||||||
|
}.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.
|
||||||
|
func testStateSmallTestInstance() *State {
|
||||||
|
return BuildState(func(s *SyncState) {
|
||||||
|
s.SetResourceInstanceCurrent(
|
||||||
|
addrs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "test_instance",
|
||||||
|
Name: "foo",
|
||||||
|
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
||||||
|
&ResourceInstanceObjectSrc{
|
||||||
|
AttrsJSON: []byte(`{"id": "1234567890"}`),
|
||||||
|
Status: ObjectReady,
|
||||||
|
},
|
||||||
|
addrs.ProviderConfig{
|
||||||
|
Type: "test",
|
||||||
|
}.Absolute(addrs.RootModuleInstance),
|
||||||
|
)
|
||||||
|
s.SetResourceInstanceCurrent(
|
||||||
|
addrs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "test_instance",
|
||||||
|
Name: "bar",
|
||||||
|
}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
|
||||||
|
&ResourceInstanceObjectSrc{
|
||||||
|
AttrsJSON: []byte(`{"id": "1234567890"}`),
|
||||||
|
Status: ObjectReady,
|
||||||
|
},
|
||||||
|
addrs.ProviderConfig{
|
||||||
|
Type: "test",
|
||||||
|
}.Absolute(addrs.RootModuleInstance),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// testStateResourceInModule returns a test State structure.
|
||||||
|
func testStateResourceInModule() *State {
|
||||||
|
foo, _ := addrs.ParseModuleInstanceStr("module.foo")
|
||||||
|
|
||||||
|
return BuildState(func(s *SyncState) {
|
||||||
|
s.SetResourceInstanceCurrent(
|
||||||
|
addrs.Resource{
|
||||||
|
Mode: addrs.ManagedResourceMode,
|
||||||
|
Type: "aws_instance",
|
||||||
|
Name: "bar",
|
||||||
|
}.Instance(addrs.NoKey).Absolute(foo),
|
||||||
|
&ResourceInstanceObjectSrc{
|
||||||
|
AttrsJSON: []byte(`{"id": "1234567890"}`),
|
||||||
|
Status: ObjectReady,
|
||||||
|
},
|
||||||
|
addrs.ProviderConfig{
|
||||||
|
Type: "aws",
|
||||||
|
}.Absolute(foo),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
@ -121,7 +121,7 @@ func (m *Module) testString() string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
id := legacyInstanceObjectID(is.Current)
|
id := LegacyInstanceObjectID(is.Current)
|
||||||
|
|
||||||
taintStr := ""
|
taintStr := ""
|
||||||
if is.Current != nil && is.Current.Status == ObjectTainted {
|
if is.Current != nil && is.Current.Status == ObjectTainted {
|
||||||
@ -184,7 +184,7 @@ func (m *Module) testString() string {
|
|||||||
// if there is more than one deposed object.
|
// if there is more than one deposed object.
|
||||||
i := 1
|
i := 1
|
||||||
for _, t := range is.Deposed {
|
for _, t := range is.Deposed {
|
||||||
id := legacyInstanceObjectID(t)
|
id := LegacyInstanceObjectID(t)
|
||||||
taintStr := ""
|
taintStr := ""
|
||||||
if t.Status == ObjectTainted {
|
if t.Status == ObjectTainted {
|
||||||
taintStr = " (tainted)"
|
taintStr = " (tainted)"
|
||||||
@ -242,12 +242,12 @@ func (m *Module) testString() string {
|
|||||||
return buf.String()
|
return buf.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// legacyInstanceObjectID is a helper for extracting an object id value from
|
// LegacyInstanceObjectID is a helper for extracting an object id value from
|
||||||
// an instance object in a way that approximates how we used to do this
|
// an instance object in a way that approximates how we used to do this
|
||||||
// for the old state types. ID is no longer first-class, so this is preserved
|
// for the old state types. ID is no longer first-class, so this is preserved
|
||||||
// only for compatibility with old tests that include the id as part of their
|
// only for compatibility with old tests that include the id as part of their
|
||||||
// expected value.
|
// expected value.
|
||||||
func legacyInstanceObjectID(obj *ResourceInstanceObjectSrc) string {
|
func LegacyInstanceObjectID(obj *ResourceInstanceObjectSrc) string {
|
||||||
if obj == nil {
|
if obj == nil {
|
||||||
return "<not created>"
|
return "<not created>"
|
||||||
}
|
}
|
||||||
|
@ -328,126 +328,6 @@ func (s *State) Validate() error {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove removes the item in the state at the given address, returning
|
|
||||||
// any errors that may have occurred.
|
|
||||||
//
|
|
||||||
// If the address references a module state or resource, it will delete
|
|
||||||
// all children as well. To check what will be deleted, use a StateFilter
|
|
||||||
// first.
|
|
||||||
func (s *State) Remove(addr ...string) error {
|
|
||||||
s.Lock()
|
|
||||||
defer s.Unlock()
|
|
||||||
|
|
||||||
// Filter out what we need to delete
|
|
||||||
filter := &StateFilter{State: s}
|
|
||||||
results, err := filter.Filter(addr...)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have no results, just exit early, 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 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Go through each result and grab what we need
|
|
||||||
removed := make(map[interface{}]struct{})
|
|
||||||
for _, r := range results {
|
|
||||||
// Convert the legacy path used by the state filter API into a
|
|
||||||
// new-style module instance address.
|
|
||||||
path := normalizeModulePath(r.Path)
|
|
||||||
|
|
||||||
// If we removed this already, then ignore
|
|
||||||
if _, ok := removed[r.Value]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we removed the parent already, then ignore
|
|
||||||
if r.Parent != nil {
|
|
||||||
if _, ok := removed[r.Parent.Value]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add this to the removed list
|
|
||||||
removed[r.Value] = struct{}{}
|
|
||||||
|
|
||||||
switch v := r.Value.(type) {
|
|
||||||
case *ModuleState:
|
|
||||||
s.removeModule(path, v)
|
|
||||||
case *ResourceState:
|
|
||||||
s.removeResource(path, v)
|
|
||||||
case *InstanceState:
|
|
||||||
s.removeInstance(path, r.Parent.Value.(*ResourceState), v)
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unknown type to delete: %T", r.Value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prune since the removal functions often do the bare minimum to
|
|
||||||
// remove a thing and may leave around dangling empty modules, resources,
|
|
||||||
// etc. Prune will clean that all up.
|
|
||||||
s.prune()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *State) removeModule(path addrs.ModuleInstance, v *ModuleState) {
|
|
||||||
for i, m := range s.Modules {
|
|
||||||
if m == v {
|
|
||||||
s.Modules, s.Modules[len(s.Modules)-1] = append(s.Modules[:i], s.Modules[i+1:]...), nil
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *State) removeResource(path addrs.ModuleInstance, v *ResourceState) {
|
|
||||||
// Get the module this resource lives in. If it doesn't exist, we're done.
|
|
||||||
mod := s.moduleByPath(path)
|
|
||||||
if mod == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find this resource. This is a O(N) lookup when if we had the key
|
|
||||||
// it could be O(1) but even with thousands of resources this shouldn't
|
|
||||||
// matter right now. We can easily up performance here when the time comes.
|
|
||||||
for k, r := range mod.Resources {
|
|
||||||
if r == v {
|
|
||||||
// Found it
|
|
||||||
delete(mod.Resources, k)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *State) removeInstance(path addrs.ModuleInstance, r *ResourceState, v *InstanceState) {
|
|
||||||
// Go through the resource and find the instance that matches this
|
|
||||||
// (if any) and remove it.
|
|
||||||
|
|
||||||
// Check primary
|
|
||||||
if r.Primary == v {
|
|
||||||
r.Primary = nil
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check lists
|
|
||||||
lists := [][]*InstanceState{r.Deposed}
|
|
||||||
for _, is := range lists {
|
|
||||||
for i, instance := range is {
|
|
||||||
if instance == v {
|
|
||||||
// Found it, remove it
|
|
||||||
is, is[len(is)-1] = append(is[:i], is[i+1:]...), nil
|
|
||||||
|
|
||||||
// Done
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RootModule returns the ModuleState for the root module
|
// RootModule returns the ModuleState for the root module
|
||||||
func (s *State) RootModule() *ModuleState {
|
func (s *State) RootModule() *ModuleState {
|
||||||
root := s.ModuleByPath(addrs.RootModuleInstance)
|
root := s.ModuleByPath(addrs.RootModuleInstance)
|
||||||
|
@ -1,267 +0,0 @@
|
|||||||
package terraform
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"sort"
|
|
||||||
)
|
|
||||||
|
|
||||||
// StateFilter is responsible for filtering and searching a state.
|
|
||||||
//
|
|
||||||
// This is a separate struct from State rather than a method on State
|
|
||||||
// because StateFilter might create sidecar data structures to optimize
|
|
||||||
// filtering on the state.
|
|
||||||
//
|
|
||||||
// If you change the State, the filter created is invalid and either
|
|
||||||
// Reset should be called or a new one should be allocated. StateFilter
|
|
||||||
// will not watch State for changes and do this for you. If you filter after
|
|
||||||
// changing the State without calling Reset, the behavior is not defined.
|
|
||||||
type StateFilter struct {
|
|
||||||
State *State
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter takes the addresses specified by fs and finds all the matches.
|
|
||||||
// The values of fs are resource addressing syntax that can be parsed by
|
|
||||||
// ParseResourceAddress.
|
|
||||||
func (f *StateFilter) Filter(fs ...string) ([]*StateFilterResult, error) {
|
|
||||||
// Parse all the addresses
|
|
||||||
as := make([]*ResourceAddress, len(fs))
|
|
||||||
for i, v := range fs {
|
|
||||||
a, err := ParseResourceAddress(v)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("Error parsing address '%s': %s", v, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
as[i] = a
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we weren't given any filters, then we list all
|
|
||||||
if len(fs) == 0 {
|
|
||||||
as = append(as, &ResourceAddress{Index: -1})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter each of the address. We keep track of this in a map to
|
|
||||||
// strip duplicates.
|
|
||||||
resultSet := make(map[string]*StateFilterResult)
|
|
||||||
for _, a := range as {
|
|
||||||
for _, r := range f.filterSingle(a) {
|
|
||||||
resultSet[r.String()] = r
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make the result list
|
|
||||||
results := make([]*StateFilterResult, 0, len(resultSet))
|
|
||||||
for _, v := range resultSet {
|
|
||||||
results = append(results, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort them and return
|
|
||||||
sort.Sort(StateFilterResultSlice(results))
|
|
||||||
return results, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *StateFilter) filterSingle(a *ResourceAddress) []*StateFilterResult {
|
|
||||||
// The slice to keep track of results
|
|
||||||
var results []*StateFilterResult
|
|
||||||
|
|
||||||
// Go through modules first.
|
|
||||||
modules := make([]*ModuleState, 0, len(f.State.Modules))
|
|
||||||
for _, m := range f.State.Modules {
|
|
||||||
if f.relevant(a, m) {
|
|
||||||
modules = append(modules, m)
|
|
||||||
|
|
||||||
// Only add the module to the results if we haven't specified a type.
|
|
||||||
// We also ignore the root module.
|
|
||||||
if a.Type == "" && len(m.Path) > 1 {
|
|
||||||
results = append(results, &StateFilterResult{
|
|
||||||
Path: m.Path[1:],
|
|
||||||
Address: (&ResourceAddress{Path: m.Path[1:]}).String(),
|
|
||||||
Value: m,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// With the modules set, go through all the resources within
|
|
||||||
// the modules to find relevant resources.
|
|
||||||
for _, m := range modules {
|
|
||||||
for n, r := range m.Resources {
|
|
||||||
// The name in the state contains valuable information. Parse.
|
|
||||||
key, err := ParseResourceStateKey(n)
|
|
||||||
if err != nil {
|
|
||||||
// If we get an error parsing, then just ignore it
|
|
||||||
// out of the state.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Older states and test fixtures often don't contain the
|
|
||||||
// type directly on the ResourceState. We add this so StateFilter
|
|
||||||
// is a bit more robust.
|
|
||||||
if r.Type == "" {
|
|
||||||
r.Type = key.Type
|
|
||||||
}
|
|
||||||
|
|
||||||
if f.relevant(a, r) {
|
|
||||||
if a.Name != "" && a.Name != key.Name {
|
|
||||||
// Name doesn't match
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.Index >= 0 && key.Index != a.Index {
|
|
||||||
// Index doesn't match
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.Name != "" && a.Name != key.Name {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build the address for this resource
|
|
||||||
addr := &ResourceAddress{
|
|
||||||
Path: m.Path[1:],
|
|
||||||
Name: key.Name,
|
|
||||||
Type: key.Type,
|
|
||||||
Index: key.Index,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the resource level result
|
|
||||||
resourceResult := &StateFilterResult{
|
|
||||||
Path: addr.Path,
|
|
||||||
Address: addr.String(),
|
|
||||||
Value: r,
|
|
||||||
}
|
|
||||||
if !a.InstanceTypeSet {
|
|
||||||
results = append(results, resourceResult)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add the instances
|
|
||||||
if r.Primary != nil {
|
|
||||||
addr.InstanceType = TypePrimary
|
|
||||||
addr.InstanceTypeSet = false
|
|
||||||
results = append(results, &StateFilterResult{
|
|
||||||
Path: addr.Path,
|
|
||||||
Address: addr.String(),
|
|
||||||
Parent: resourceResult,
|
|
||||||
Value: r.Primary,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, instance := range r.Deposed {
|
|
||||||
if f.relevant(a, instance) {
|
|
||||||
addr.InstanceType = TypeDeposed
|
|
||||||
addr.InstanceTypeSet = true
|
|
||||||
results = append(results, &StateFilterResult{
|
|
||||||
Path: addr.Path,
|
|
||||||
Address: addr.String(),
|
|
||||||
Parent: resourceResult,
|
|
||||||
Value: instance,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return results
|
|
||||||
}
|
|
||||||
|
|
||||||
// relevant checks for relevance of this address against the given value.
|
|
||||||
func (f *StateFilter) relevant(addr *ResourceAddress, raw interface{}) bool {
|
|
||||||
switch v := raw.(type) {
|
|
||||||
case *ModuleState:
|
|
||||||
path := v.Path[1:]
|
|
||||||
|
|
||||||
if len(addr.Path) > len(path) {
|
|
||||||
// Longer path in address means there is no way we match.
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for a prefix match
|
|
||||||
for i, p := range addr.Path {
|
|
||||||
if path[i] != p {
|
|
||||||
// Any mismatches don't match.
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
case *ResourceState:
|
|
||||||
if addr.Type == "" {
|
|
||||||
// If we have no resource type, then we're interested in all!
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the type doesn't match we fail immediately
|
|
||||||
if v.Type != addr.Type {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
// If we don't know about it, let's just say no
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// StateFilterResult is a single result from a filter operation. Filter
|
|
||||||
// can match multiple things within a state (module, resource, instance, etc.)
|
|
||||||
// and this unifies that.
|
|
||||||
type StateFilterResult struct {
|
|
||||||
// Module path of the result
|
|
||||||
Path []string
|
|
||||||
|
|
||||||
// Address is the address that can be used to reference this exact result.
|
|
||||||
Address string
|
|
||||||
|
|
||||||
// Parent, if non-nil, is a parent of this result. For instances, the
|
|
||||||
// parent would be a resource. For resources, the parent would be
|
|
||||||
// a module. For modules, this is currently nil.
|
|
||||||
Parent *StateFilterResult
|
|
||||||
|
|
||||||
// Value is the actual value. This must be type switched on. It can be
|
|
||||||
// any data structures that `State` can hold: `ModuleState`,
|
|
||||||
// `ResourceState`, `InstanceState`.
|
|
||||||
Value interface{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *StateFilterResult) String() string {
|
|
||||||
return fmt.Sprintf("%T: %s", r.Value, r.Address)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *StateFilterResult) sortedType() int {
|
|
||||||
switch r.Value.(type) {
|
|
||||||
case *ModuleState:
|
|
||||||
return 0
|
|
||||||
case *ResourceState:
|
|
||||||
return 1
|
|
||||||
case *InstanceState:
|
|
||||||
return 2
|
|
||||||
default:
|
|
||||||
return 50
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// StateFilterResultSlice is a slice of results that implements
|
|
||||||
// sort.Interface. The sorting goal is what is most appealing to
|
|
||||||
// human output.
|
|
||||||
type StateFilterResultSlice []*StateFilterResult
|
|
||||||
|
|
||||||
func (s StateFilterResultSlice) Len() int { return len(s) }
|
|
||||||
func (s StateFilterResultSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
|
||||||
func (s StateFilterResultSlice) Less(i, j int) bool {
|
|
||||||
a, b := s[i], s[j]
|
|
||||||
|
|
||||||
// if these address contain an index, we want to sort by index rather than name
|
|
||||||
addrA, errA := ParseResourceAddress(a.Address)
|
|
||||||
addrB, errB := ParseResourceAddress(b.Address)
|
|
||||||
if errA == nil && errB == nil && addrA.Name == addrB.Name && addrA.Index != addrB.Index {
|
|
||||||
return addrA.Index < addrB.Index
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the addresses are different it is just lexographic sorting
|
|
||||||
if a.Address != b.Address {
|
|
||||||
return a.Address < b.Address
|
|
||||||
}
|
|
||||||
|
|
||||||
// Addresses are the same, which means it matters on the type
|
|
||||||
return a.sortedType() < b.sortedType()
|
|
||||||
}
|
|
@ -1,164 +0,0 @@
|
|||||||
package terraform
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStateFilterFilter(t *testing.T) {
|
|
||||||
cases := map[string]struct {
|
|
||||||
State string
|
|
||||||
Filters []string
|
|
||||||
Expected []string
|
|
||||||
}{
|
|
||||||
"all": {
|
|
||||||
"small.tfstate",
|
|
||||||
[]string{},
|
|
||||||
[]string{
|
|
||||||
"*terraform.ResourceState: aws_key_pair.onprem",
|
|
||||||
"*terraform.InstanceState: aws_key_pair.onprem",
|
|
||||||
"*terraform.ModuleState: module.bootstrap",
|
|
||||||
"*terraform.ResourceState: module.bootstrap.aws_route53_record.oasis-consul-bootstrap-a",
|
|
||||||
"*terraform.InstanceState: module.bootstrap.aws_route53_record.oasis-consul-bootstrap-a",
|
|
||||||
"*terraform.ResourceState: module.bootstrap.aws_route53_record.oasis-consul-bootstrap-ns",
|
|
||||||
"*terraform.InstanceState: module.bootstrap.aws_route53_record.oasis-consul-bootstrap-ns",
|
|
||||||
"*terraform.ResourceState: module.bootstrap.aws_route53_zone.oasis-consul-bootstrap",
|
|
||||||
"*terraform.InstanceState: module.bootstrap.aws_route53_zone.oasis-consul-bootstrap",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
"single resource": {
|
|
||||||
"small.tfstate",
|
|
||||||
[]string{"aws_key_pair.onprem"},
|
|
||||||
[]string{
|
|
||||||
"*terraform.ResourceState: aws_key_pair.onprem",
|
|
||||||
"*terraform.InstanceState: aws_key_pair.onprem",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
"single resource from minimal state": {
|
|
||||||
"single-minimal-resource.tfstate",
|
|
||||||
[]string{"aws_instance.web"},
|
|
||||||
[]string{
|
|
||||||
"*terraform.ResourceState: aws_instance.web",
|
|
||||||
"*terraform.InstanceState: aws_instance.web",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
"single resource with similar names": {
|
|
||||||
"small_test_instance.tfstate",
|
|
||||||
[]string{"test_instance.foo"},
|
|
||||||
[]string{
|
|
||||||
"*terraform.ResourceState: test_instance.foo",
|
|
||||||
"*terraform.InstanceState: test_instance.foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
"single instance": {
|
|
||||||
"small.tfstate",
|
|
||||||
[]string{"aws_key_pair.onprem.primary"},
|
|
||||||
[]string{
|
|
||||||
"*terraform.InstanceState: aws_key_pair.onprem",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
"module filter": {
|
|
||||||
"complete.tfstate",
|
|
||||||
[]string{"module.bootstrap"},
|
|
||||||
[]string{
|
|
||||||
"*terraform.ModuleState: module.bootstrap",
|
|
||||||
"*terraform.ResourceState: module.bootstrap.aws_route53_record.oasis-consul-bootstrap-a",
|
|
||||||
"*terraform.InstanceState: module.bootstrap.aws_route53_record.oasis-consul-bootstrap-a",
|
|
||||||
"*terraform.ResourceState: module.bootstrap.aws_route53_record.oasis-consul-bootstrap-ns",
|
|
||||||
"*terraform.InstanceState: module.bootstrap.aws_route53_record.oasis-consul-bootstrap-ns",
|
|
||||||
"*terraform.ResourceState: module.bootstrap.aws_route53_zone.oasis-consul-bootstrap",
|
|
||||||
"*terraform.InstanceState: module.bootstrap.aws_route53_zone.oasis-consul-bootstrap",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
"resource in module": {
|
|
||||||
"complete.tfstate",
|
|
||||||
[]string{"module.bootstrap.aws_route53_zone.oasis-consul-bootstrap"},
|
|
||||||
[]string{
|
|
||||||
"*terraform.ResourceState: module.bootstrap.aws_route53_zone.oasis-consul-bootstrap",
|
|
||||||
"*terraform.InstanceState: module.bootstrap.aws_route53_zone.oasis-consul-bootstrap",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
"resource in module 2": {
|
|
||||||
"resource-in-module-2.tfstate",
|
|
||||||
[]string{"module.foo.aws_instance.foo"},
|
|
||||||
[]string{},
|
|
||||||
},
|
|
||||||
|
|
||||||
"single count index": {
|
|
||||||
"complete.tfstate",
|
|
||||||
[]string{"module.consul.aws_instance.consul-green[0]"},
|
|
||||||
[]string{
|
|
||||||
"*terraform.ResourceState: module.consul.aws_instance.consul-green[0]",
|
|
||||||
"*terraform.InstanceState: module.consul.aws_instance.consul-green[0]",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
"no count index": {
|
|
||||||
"complete.tfstate",
|
|
||||||
[]string{"module.consul.aws_instance.consul-green"},
|
|
||||||
[]string{
|
|
||||||
"*terraform.ResourceState: module.consul.aws_instance.consul-green[0]",
|
|
||||||
"*terraform.InstanceState: module.consul.aws_instance.consul-green[0]",
|
|
||||||
"*terraform.ResourceState: module.consul.aws_instance.consul-green[1]",
|
|
||||||
"*terraform.InstanceState: module.consul.aws_instance.consul-green[1]",
|
|
||||||
"*terraform.ResourceState: module.consul.aws_instance.consul-green[2]",
|
|
||||||
"*terraform.InstanceState: module.consul.aws_instance.consul-green[2]",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
"nested modules": {
|
|
||||||
"nested-modules.tfstate",
|
|
||||||
[]string{"module.outer"},
|
|
||||||
[]string{
|
|
||||||
"*terraform.ModuleState: module.outer",
|
|
||||||
"*terraform.ModuleState: module.outer.module.child1",
|
|
||||||
"*terraform.ResourceState: module.outer.module.child1.aws_instance.foo",
|
|
||||||
"*terraform.InstanceState: module.outer.module.child1.aws_instance.foo",
|
|
||||||
"*terraform.ModuleState: module.outer.module.child2",
|
|
||||||
"*terraform.ResourceState: module.outer.module.child2.aws_instance.foo",
|
|
||||||
"*terraform.InstanceState: module.outer.module.child2.aws_instance.foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for n, tc := range cases {
|
|
||||||
// Load our state
|
|
||||||
f, err := os.Open(filepath.Join("./test-fixtures", "state-filter", tc.State))
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("%q: err: %s", n, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
state, err := ReadState(f)
|
|
||||||
f.Close()
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("%q: err: %s", n, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the filter
|
|
||||||
filter := &StateFilter{State: state}
|
|
||||||
|
|
||||||
// Filter!
|
|
||||||
results, err := filter.Filter(tc.Filters...)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("%q: err: %s", n, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
actual := make([]string, len(results))
|
|
||||||
for i, result := range results {
|
|
||||||
actual[i] = result.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(actual, tc.Expected) {
|
|
||||||
t.Fatalf("%q: expected, then actual\n\n%#v\n\n%#v", n, tc.Expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -689,291 +689,6 @@ func TestStateMarshalEqual(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStateRemove(t *testing.T) {
|
|
||||||
cases := map[string]struct {
|
|
||||||
Address string
|
|
||||||
One, Two *State
|
|
||||||
}{
|
|
||||||
"simple resource": {
|
|
||||||
"test_instance.foo",
|
|
||||||
&State{
|
|
||||||
Modules: []*ModuleState{
|
|
||||||
&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{
|
|
||||||
Modules: []*ModuleState{
|
|
||||||
&ModuleState{
|
|
||||||
Path: rootModulePath,
|
|
||||||
Resources: map[string]*ResourceState{
|
|
||||||
"test_instance.bar": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
"single instance": {
|
|
||||||
"test_instance.foo.primary",
|
|
||||||
&State{
|
|
||||||
Modules: []*ModuleState{
|
|
||||||
&ModuleState{
|
|
||||||
Path: rootModulePath,
|
|
||||||
Resources: map[string]*ResourceState{
|
|
||||||
"test_instance.foo": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&State{
|
|
||||||
Modules: []*ModuleState{
|
|
||||||
&ModuleState{
|
|
||||||
Path: rootModulePath,
|
|
||||||
Resources: map[string]*ResourceState{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
"single instance in multi-count": {
|
|
||||||
"test_instance.foo[0]",
|
|
||||||
&State{
|
|
||||||
Modules: []*ModuleState{
|
|
||||||
&ModuleState{
|
|
||||||
Path: rootModulePath,
|
|
||||||
Resources: map[string]*ResourceState{
|
|
||||||
"test_instance.foo.0": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
"test_instance.foo.1": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&State{
|
|
||||||
Modules: []*ModuleState{
|
|
||||||
&ModuleState{
|
|
||||||
Path: rootModulePath,
|
|
||||||
Resources: map[string]*ResourceState{
|
|
||||||
"test_instance.foo.1": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
"single resource, multi-count": {
|
|
||||||
"test_instance.foo",
|
|
||||||
&State{
|
|
||||||
Modules: []*ModuleState{
|
|
||||||
&ModuleState{
|
|
||||||
Path: rootModulePath,
|
|
||||||
Resources: map[string]*ResourceState{
|
|
||||||
"test_instance.foo.0": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
"test_instance.foo.1": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&State{
|
|
||||||
Modules: []*ModuleState{
|
|
||||||
&ModuleState{
|
|
||||||
Path: rootModulePath,
|
|
||||||
Resources: map[string]*ResourceState{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
"full module": {
|
|
||||||
"module.foo",
|
|
||||||
&State{
|
|
||||||
Modules: []*ModuleState{
|
|
||||||
&ModuleState{
|
|
||||||
Path: rootModulePath,
|
|
||||||
Resources: map[string]*ResourceState{
|
|
||||||
"test_instance.foo": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
&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",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&State{
|
|
||||||
Modules: []*ModuleState{
|
|
||||||
&ModuleState{
|
|
||||||
Path: rootModulePath,
|
|
||||||
Resources: map[string]*ResourceState{
|
|
||||||
"test_instance.foo": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
"module and children": {
|
|
||||||
"module.foo",
|
|
||||||
&State{
|
|
||||||
Modules: []*ModuleState{
|
|
||||||
&ModuleState{
|
|
||||||
Path: rootModulePath,
|
|
||||||
Resources: map[string]*ResourceState{
|
|
||||||
"test_instance.foo": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
&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{
|
|
||||||
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",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&State{
|
|
||||||
Modules: []*ModuleState{
|
|
||||||
&ModuleState{
|
|
||||||
Path: rootModulePath,
|
|
||||||
Resources: map[string]*ResourceState{
|
|
||||||
"test_instance.foo": &ResourceState{
|
|
||||||
Type: "test_instance",
|
|
||||||
Primary: &InstanceState{
|
|
||||||
ID: "foo",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, tc := range cases {
|
|
||||||
if err := tc.One.Remove(tc.Address); err != nil {
|
|
||||||
t.Fatalf("bad: %s\n\n%s", k, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !tc.One.Equal(tc.Two) {
|
|
||||||
t.Fatalf("Bad: %s\n\n%s\n\n%s", k, tc.One.String(), tc.Two.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestResourceStateEqual(t *testing.T) {
|
func TestResourceStateEqual(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
Result bool
|
Result bool
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,47 +0,0 @@
|
|||||||
{
|
|
||||||
"version": 1,
|
|
||||||
"serial": 12,
|
|
||||||
"modules": [
|
|
||||||
{
|
|
||||||
"path": [
|
|
||||||
"root",
|
|
||||||
"outer"
|
|
||||||
],
|
|
||||||
"resources": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": [
|
|
||||||
"root",
|
|
||||||
"outer",
|
|
||||||
"child1"
|
|
||||||
],
|
|
||||||
"resources": {
|
|
||||||
"aws_instance.foo": {
|
|
||||||
"type": "aws_instance",
|
|
||||||
"depends_on": [],
|
|
||||||
"primary": {
|
|
||||||
"id": "1",
|
|
||||||
"attributes": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": [
|
|
||||||
"root",
|
|
||||||
"outer",
|
|
||||||
"child2"
|
|
||||||
],
|
|
||||||
"resources": {
|
|
||||||
"aws_instance.foo": {
|
|
||||||
"type": "aws_instance",
|
|
||||||
"depends_on": [],
|
|
||||||
"primary": {
|
|
||||||
"id": "1",
|
|
||||||
"attributes": {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
{
|
|
||||||
"version": 1,
|
|
||||||
"serial": 12,
|
|
||||||
"modules": [
|
|
||||||
{
|
|
||||||
"path": [
|
|
||||||
"root",
|
|
||||||
"foo"
|
|
||||||
],
|
|
||||||
"resources": {
|
|
||||||
"aws_instance.bar": {
|
|
||||||
"type": "aws_instance",
|
|
||||||
"primary": {
|
|
||||||
"id": "bar"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"version": 1,
|
|
||||||
"serial": 12,
|
|
||||||
"modules": [
|
|
||||||
{
|
|
||||||
"path": [
|
|
||||||
"root"
|
|
||||||
],
|
|
||||||
"resources": {
|
|
||||||
"aws_instance.web": {
|
|
||||||
"primary": {
|
|
||||||
"id": "onprem"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,122 +0,0 @@
|
|||||||
{
|
|
||||||
"version": 1,
|
|
||||||
"serial": 12,
|
|
||||||
"modules": [
|
|
||||||
{
|
|
||||||
"path": [
|
|
||||||
"root"
|
|
||||||
],
|
|
||||||
"outputs": {
|
|
||||||
"public_az1_subnet_id": "subnet-d658bba0",
|
|
||||||
"region": "us-west-2",
|
|
||||||
"vpc_cidr": "10.201.0.0/16",
|
|
||||||
"vpc_id": "vpc-65814701"
|
|
||||||
},
|
|
||||||
"resources": {
|
|
||||||
"aws_key_pair.onprem": {
|
|
||||||
"type": "aws_key_pair",
|
|
||||||
"primary": {
|
|
||||||
"id": "onprem",
|
|
||||||
"attributes": {
|
|
||||||
"id": "onprem",
|
|
||||||
"key_name": "onprem",
|
|
||||||
"public_key": "foo"
|
|
||||||
},
|
|
||||||
"meta": {
|
|
||||||
"schema_version": "1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"path": [
|
|
||||||
"root",
|
|
||||||
"bootstrap"
|
|
||||||
],
|
|
||||||
"outputs": {
|
|
||||||
"consul_bootstrap_dns": "consul.bootstrap"
|
|
||||||
},
|
|
||||||
"resources": {
|
|
||||||
"aws_route53_record.oasis-consul-bootstrap-a": {
|
|
||||||
"type": "aws_route53_record",
|
|
||||||
"depends_on": [
|
|
||||||
"aws_route53_zone.oasis-consul-bootstrap"
|
|
||||||
],
|
|
||||||
"primary": {
|
|
||||||
"id": "Z68734P5178QN_consul.bootstrap_A",
|
|
||||||
"attributes": {
|
|
||||||
"failover": "",
|
|
||||||
"fqdn": "consul.bootstrap",
|
|
||||||
"health_check_id": "",
|
|
||||||
"id": "Z68734P5178QN_consul.bootstrap_A",
|
|
||||||
"name": "consul.bootstrap",
|
|
||||||
"records.#": "6",
|
|
||||||
"records.1148461392": "10.201.3.8",
|
|
||||||
"records.1169574759": "10.201.2.8",
|
|
||||||
"records.1206973758": "10.201.1.8",
|
|
||||||
"records.1275070284": "10.201.2.4",
|
|
||||||
"records.1304587643": "10.201.3.4",
|
|
||||||
"records.1313257749": "10.201.1.4",
|
|
||||||
"set_identifier": "",
|
|
||||||
"ttl": "300",
|
|
||||||
"type": "A",
|
|
||||||
"weight": "-1",
|
|
||||||
"zone_id": "Z68734P5178QN"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"aws_route53_record.oasis-consul-bootstrap-ns": {
|
|
||||||
"type": "aws_route53_record",
|
|
||||||
"depends_on": [
|
|
||||||
"aws_route53_zone.oasis-consul-bootstrap",
|
|
||||||
"aws_route53_zone.oasis-consul-bootstrap",
|
|
||||||
"aws_route53_zone.oasis-consul-bootstrap",
|
|
||||||
"aws_route53_zone.oasis-consul-bootstrap",
|
|
||||||
"aws_route53_zone.oasis-consul-bootstrap"
|
|
||||||
],
|
|
||||||
"primary": {
|
|
||||||
"id": "Z68734P5178QN_consul.bootstrap_NS",
|
|
||||||
"attributes": {
|
|
||||||
"failover": "",
|
|
||||||
"fqdn": "consul.bootstrap",
|
|
||||||
"health_check_id": "",
|
|
||||||
"id": "Z68734P5178QN_consul.bootstrap_NS",
|
|
||||||
"name": "consul.bootstrap",
|
|
||||||
"records.#": "4",
|
|
||||||
"records.1796532126": "ns-512.awsdns-00.net.",
|
|
||||||
"records.2728059479": "ns-1536.awsdns-00.co.uk.",
|
|
||||||
"records.4092160370": "ns-1024.awsdns-00.org.",
|
|
||||||
"records.456007465": "ns-0.awsdns-00.com.",
|
|
||||||
"set_identifier": "",
|
|
||||||
"ttl": "30",
|
|
||||||
"type": "NS",
|
|
||||||
"weight": "-1",
|
|
||||||
"zone_id": "Z68734P5178QN"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"aws_route53_zone.oasis-consul-bootstrap": {
|
|
||||||
"type": "aws_route53_zone",
|
|
||||||
"primary": {
|
|
||||||
"id": "Z68734P5178QN",
|
|
||||||
"attributes": {
|
|
||||||
"comment": "Used to bootstrap consul dns",
|
|
||||||
"id": "Z68734P5178QN",
|
|
||||||
"name": "consul.bootstrap",
|
|
||||||
"name_servers.#": "4",
|
|
||||||
"name_servers.0": "ns-0.awsdns-00.com.",
|
|
||||||
"name_servers.1": "ns-1024.awsdns-00.org.",
|
|
||||||
"name_servers.2": "ns-1536.awsdns-00.co.uk.",
|
|
||||||
"name_servers.3": "ns-512.awsdns-00.net.",
|
|
||||||
"tags.#": "0",
|
|
||||||
"vpc_id": "vpc-65814701",
|
|
||||||
"vpc_region": "us-west-2",
|
|
||||||
"zone_id": "Z68734P5178QN"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
{
|
|
||||||
"version": 1,
|
|
||||||
"serial": 12,
|
|
||||||
"modules": [
|
|
||||||
{
|
|
||||||
"path": [
|
|
||||||
"root"
|
|
||||||
],
|
|
||||||
"resources": {
|
|
||||||
"test_instance.foo": {
|
|
||||||
"type": "test_instance",
|
|
||||||
"primary": {
|
|
||||||
"id": "foo"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"resources": {
|
|
||||||
"test_instance.bar": {
|
|
||||||
"type": "test_instance",
|
|
||||||
"primary": {
|
|
||||||
"id": "foo"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user