mirror of https://github.com/hashicorp/terraform
Migrate 'state show' command to new renderer (#33116)
* Migrate 'state show' command to new renderer * handle errorpull/33457/head
parent
f0b3b74f7c
commit
b5576159da
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,29 +0,0 @@
|
||||
// Code generated by "stringer -type=DiffLanguage diff.go"; DO NOT EDIT.
|
||||
|
||||
package format
|
||||
|
||||
import "strconv"
|
||||
|
||||
func _() {
|
||||
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||
// Re-run the stringer command to generate them again.
|
||||
var x [1]struct{}
|
||||
_ = x[DiffLanguageProposedChange-80]
|
||||
_ = x[DiffLanguageDetectedDrift-68]
|
||||
}
|
||||
|
||||
const (
|
||||
_DiffLanguage_name_0 = "DiffLanguageDetectedDrift"
|
||||
_DiffLanguage_name_1 = "DiffLanguageProposedChange"
|
||||
)
|
||||
|
||||
func (i DiffLanguage) String() string {
|
||||
switch {
|
||||
case i == 68:
|
||||
return _DiffLanguage_name_0
|
||||
case i == 80:
|
||||
return _DiffLanguage_name_1
|
||||
default:
|
||||
return "DiffLanguage(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||
}
|
||||
}
|
||||
@ -1,216 +0,0 @@
|
||||
package format
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/configs/configschema"
|
||||
"github.com/hashicorp/terraform/internal/plans"
|
||||
"github.com/hashicorp/terraform/internal/states"
|
||||
"github.com/hashicorp/terraform/internal/terraform"
|
||||
"github.com/mitchellh/colorstring"
|
||||
)
|
||||
|
||||
// StateOpts are the options for formatting a state.
|
||||
type StateOpts struct {
|
||||
// State is the state to format. This is required.
|
||||
State *states.State
|
||||
|
||||
// Schemas are used to decode attributes. This is required.
|
||||
Schemas *terraform.Schemas
|
||||
|
||||
// Color is the colorizer. This is optional.
|
||||
Color *colorstring.Colorize
|
||||
}
|
||||
|
||||
// State takes a state and returns a string
|
||||
func State(opts *StateOpts) string {
|
||||
if opts.Color == nil {
|
||||
panic("colorize not given")
|
||||
}
|
||||
|
||||
if opts.Schemas == nil {
|
||||
panic("schemas not given")
|
||||
}
|
||||
|
||||
s := opts.State
|
||||
if len(s.Modules) == 0 {
|
||||
return "The state file is empty. No resources are represented."
|
||||
}
|
||||
|
||||
buf := bytes.NewBufferString("[reset]")
|
||||
p := blockBodyDiffPrinter{
|
||||
buf: buf,
|
||||
color: opts.Color,
|
||||
action: plans.NoOp,
|
||||
verbose: true,
|
||||
}
|
||||
|
||||
// Format all the modules
|
||||
for _, m := range s.Modules {
|
||||
formatStateModule(p, m, opts.Schemas)
|
||||
}
|
||||
|
||||
// Write the outputs for the root module
|
||||
m := s.RootModule()
|
||||
|
||||
if m.OutputValues != nil {
|
||||
if len(m.OutputValues) > 0 {
|
||||
p.buf.WriteString("Outputs:\n\n")
|
||||
}
|
||||
|
||||
// Sort the outputs
|
||||
ks := make([]string, 0, len(m.OutputValues))
|
||||
for k := range m.OutputValues {
|
||||
ks = append(ks, k)
|
||||
}
|
||||
sort.Strings(ks)
|
||||
|
||||
// Output each output k/v pair
|
||||
for _, k := range ks {
|
||||
v := m.OutputValues[k]
|
||||
p.buf.WriteString(fmt.Sprintf("%s = ", k))
|
||||
if v.Sensitive {
|
||||
p.buf.WriteString("(sensitive value)")
|
||||
} else {
|
||||
p.writeValue(v.Value, plans.NoOp, 0)
|
||||
}
|
||||
p.buf.WriteString("\n")
|
||||
}
|
||||
}
|
||||
|
||||
trimmedOutput := strings.TrimSpace(p.buf.String())
|
||||
trimmedOutput += "[reset]"
|
||||
|
||||
return opts.Color.Color(trimmedOutput)
|
||||
|
||||
}
|
||||
|
||||
func formatStateModule(p blockBodyDiffPrinter, m *states.Module, schemas *terraform.Schemas) {
|
||||
// First get the names of all the resources so we can show them
|
||||
// in alphabetical order.
|
||||
names := make([]string, 0, len(m.Resources))
|
||||
for name := range m.Resources {
|
||||
names = append(names, name)
|
||||
}
|
||||
sort.Strings(names)
|
||||
|
||||
// Go through each resource and begin building up the output.
|
||||
for _, key := range names {
|
||||
for k, v := range m.Resources[key].Instances {
|
||||
// keep these in order to keep the current object first, and
|
||||
// provide deterministic output for the deposed objects
|
||||
type obj struct {
|
||||
header string
|
||||
instance *states.ResourceInstanceObjectSrc
|
||||
}
|
||||
instances := []obj{}
|
||||
|
||||
addr := m.Resources[key].Addr
|
||||
resAddr := addr.Resource
|
||||
|
||||
taintStr := ""
|
||||
if v.Current != nil && v.Current.Status == 'T' {
|
||||
taintStr = " (tainted)"
|
||||
}
|
||||
|
||||
instances = append(instances,
|
||||
obj{fmt.Sprintf("# %s:%s\n", addr.Instance(k), taintStr), v.Current})
|
||||
|
||||
for dk, v := range v.Deposed {
|
||||
instances = append(instances,
|
||||
obj{fmt.Sprintf("# %s: (deposed object %s)\n", addr.Instance(k), dk), v})
|
||||
}
|
||||
|
||||
// Sort the instances for consistent output.
|
||||
// Starting the sort from the second index, so the current instance
|
||||
// is always first.
|
||||
sort.Slice(instances[1:], func(i, j int) bool {
|
||||
return instances[i+1].header < instances[j+1].header
|
||||
})
|
||||
|
||||
for _, obj := range instances {
|
||||
header := obj.header
|
||||
instance := obj.instance
|
||||
p.buf.WriteString(header)
|
||||
if instance == nil {
|
||||
// this shouldn't happen, but there's nothing to do here so
|
||||
// don't panic below.
|
||||
continue
|
||||
}
|
||||
|
||||
var schema *configschema.Block
|
||||
|
||||
provider := m.Resources[key].ProviderConfig.Provider
|
||||
if _, exists := schemas.Providers[provider]; !exists {
|
||||
// This should never happen in normal use because we should've
|
||||
// loaded all of the schemas and checked things prior to this
|
||||
// point. We can't return errors here, but since this is UI code
|
||||
// we will try to do _something_ reasonable.
|
||||
p.buf.WriteString(fmt.Sprintf("# missing schema for provider %q\n\n", provider.String()))
|
||||
continue
|
||||
}
|
||||
|
||||
switch resAddr.Mode {
|
||||
case addrs.ManagedResourceMode:
|
||||
schema, _ = schemas.ResourceTypeConfig(
|
||||
provider,
|
||||
resAddr.Mode,
|
||||
resAddr.Type,
|
||||
)
|
||||
if schema == nil {
|
||||
p.buf.WriteString(fmt.Sprintf(
|
||||
"# missing schema for provider %q resource type %s\n\n", provider, resAddr.Type))
|
||||
continue
|
||||
}
|
||||
|
||||
p.buf.WriteString(fmt.Sprintf(
|
||||
"resource %q %q {",
|
||||
resAddr.Type,
|
||||
resAddr.Name,
|
||||
))
|
||||
case addrs.DataResourceMode:
|
||||
schema, _ = schemas.ResourceTypeConfig(
|
||||
provider,
|
||||
resAddr.Mode,
|
||||
resAddr.Type,
|
||||
)
|
||||
if schema == nil {
|
||||
p.buf.WriteString(fmt.Sprintf(
|
||||
"# missing schema for provider %q data source %s\n\n", provider, resAddr.Type))
|
||||
continue
|
||||
}
|
||||
|
||||
p.buf.WriteString(fmt.Sprintf(
|
||||
"data %q %q {",
|
||||
resAddr.Type,
|
||||
resAddr.Name,
|
||||
))
|
||||
default:
|
||||
// should never happen, since the above is exhaustive
|
||||
p.buf.WriteString(resAddr.String())
|
||||
}
|
||||
|
||||
val, err := instance.Decode(schema.ImpliedType())
|
||||
if err != nil {
|
||||
fmt.Println(err.Error())
|
||||
break
|
||||
}
|
||||
|
||||
path := make(cty.Path, 0, 3)
|
||||
result := p.writeBlockBodyDiff(schema, val.Value, val.Value, 2, path)
|
||||
if result.bodyWritten {
|
||||
p.buf.WriteString("\n")
|
||||
}
|
||||
|
||||
p.buf.WriteString("}\n\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
p.buf.WriteString("\n")
|
||||
}
|
||||
@ -1,400 +0,0 @@
|
||||
package format
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/hashicorp/terraform/internal/addrs"
|
||||
"github.com/hashicorp/terraform/internal/configs/configschema"
|
||||
"github.com/hashicorp/terraform/internal/providers"
|
||||
"github.com/hashicorp/terraform/internal/states"
|
||||
"github.com/hashicorp/terraform/internal/terraform"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
||||
func TestState(t *testing.T) {
|
||||
tests := []struct {
|
||||
State *StateOpts
|
||||
Want string
|
||||
}{
|
||||
{
|
||||
&StateOpts{
|
||||
State: &states.State{},
|
||||
Color: disabledColorize,
|
||||
Schemas: &terraform.Schemas{},
|
||||
},
|
||||
"The state file is empty. No resources are represented.",
|
||||
},
|
||||
{
|
||||
&StateOpts{
|
||||
State: basicState(t),
|
||||
Color: disabledColorize,
|
||||
Schemas: testSchemas(),
|
||||
},
|
||||
basicStateOutput,
|
||||
},
|
||||
{
|
||||
&StateOpts{
|
||||
State: nestedState(t),
|
||||
Color: disabledColorize,
|
||||
Schemas: testSchemas(),
|
||||
},
|
||||
nestedStateOutput,
|
||||
},
|
||||
{
|
||||
&StateOpts{
|
||||
State: deposedState(t),
|
||||
Color: disabledColorize,
|
||||
Schemas: testSchemas(),
|
||||
},
|
||||
deposedNestedStateOutput,
|
||||
},
|
||||
{
|
||||
&StateOpts{
|
||||
State: onlyDeposedState(t),
|
||||
Color: disabledColorize,
|
||||
Schemas: testSchemas(),
|
||||
},
|
||||
onlyDeposedOutput,
|
||||
},
|
||||
{
|
||||
&StateOpts{
|
||||
State: stateWithMoreOutputs(t),
|
||||
Color: disabledColorize,
|
||||
Schemas: testSchemas(),
|
||||
},
|
||||
stateWithMoreOutputsOutput,
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
|
||||
got := State(tt.State)
|
||||
if got != tt.Want {
|
||||
t.Errorf(
|
||||
"wrong result\ninput: %v\ngot: \n%q\nwant: \n%q",
|
||||
tt.State.State, got, tt.Want,
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func testProvider() *terraform.MockProvider {
|
||||
p := new(terraform.MockProvider)
|
||||
p.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse {
|
||||
return providers.ReadResourceResponse{NewState: req.PriorState}
|
||||
}
|
||||
|
||||
p.GetProviderSchemaResponse = testProviderSchema()
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func testProviderSchema() *providers.GetProviderSchemaResponse {
|
||||
return &providers.GetProviderSchemaResponse{
|
||||
Provider: providers.Schema{
|
||||
Block: &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"region": {Type: cty.String, Optional: true},
|
||||
},
|
||||
},
|
||||
},
|
||||
ResourceTypes: map[string]providers.Schema{
|
||||
"test_resource": {
|
||||
Block: &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"id": {Type: cty.String, Computed: true},
|
||||
"foo": {Type: cty.String, Optional: true},
|
||||
"woozles": {Type: cty.String, Optional: true},
|
||||
},
|
||||
BlockTypes: map[string]*configschema.NestedBlock{
|
||||
"nested": {
|
||||
Nesting: configschema.NestingList,
|
||||
Block: configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"compute": {Type: cty.String, Optional: true},
|
||||
"value": {Type: cty.String, Optional: true},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
DataSources: map[string]providers.Schema{
|
||||
"test_data_source": {
|
||||
Block: &configschema.Block{
|
||||
Attributes: map[string]*configschema.Attribute{
|
||||
"compute": {Type: cty.String, Optional: true},
|
||||
"value": {Type: cty.String, Computed: true},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func testSchemas() *terraform.Schemas {
|
||||
provider := testProvider()
|
||||
return &terraform.Schemas{
|
||||
Providers: map[addrs.Provider]*terraform.ProviderSchema{
|
||||
addrs.NewDefaultProvider("test"): provider.ProviderSchema(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const basicStateOutput = `# data.test_data_source.data:
|
||||
data "test_data_source" "data" {
|
||||
compute = "sure"
|
||||
}
|
||||
|
||||
# test_resource.baz[0]:
|
||||
resource "test_resource" "baz" {
|
||||
woozles = "confuzles"
|
||||
}
|
||||
|
||||
|
||||
Outputs:
|
||||
|
||||
bar = "bar value"`
|
||||
|
||||
const nestedStateOutput = `# test_resource.baz[0]:
|
||||
resource "test_resource" "baz" {
|
||||
woozles = "confuzles"
|
||||
|
||||
nested {
|
||||
value = "42"
|
||||
}
|
||||
}`
|
||||
|
||||
const deposedNestedStateOutput = `# test_resource.baz[0]:
|
||||
resource "test_resource" "baz" {
|
||||
woozles = "confuzles"
|
||||
|
||||
nested {
|
||||
value = "42"
|
||||
}
|
||||
}
|
||||
|
||||
# test_resource.baz[0]: (deposed object 1234)
|
||||
resource "test_resource" "baz" {
|
||||
woozles = "confuzles"
|
||||
|
||||
nested {
|
||||
value = "42"
|
||||
}
|
||||
}`
|
||||
|
||||
const onlyDeposedOutput = `# test_resource.baz[0]:
|
||||
# test_resource.baz[0]: (deposed object 1234)
|
||||
resource "test_resource" "baz" {
|
||||
woozles = "confuzles"
|
||||
|
||||
nested {
|
||||
value = "42"
|
||||
}
|
||||
}
|
||||
|
||||
# test_resource.baz[0]: (deposed object 5678)
|
||||
resource "test_resource" "baz" {
|
||||
woozles = "confuzles"
|
||||
|
||||
nested {
|
||||
value = "42"
|
||||
}
|
||||
}`
|
||||
|
||||
const stateWithMoreOutputsOutput = `# test_resource.baz[0]:
|
||||
resource "test_resource" "baz" {
|
||||
woozles = "confuzles"
|
||||
}
|
||||
|
||||
|
||||
Outputs:
|
||||
|
||||
bool_var = true
|
||||
int_var = 42
|
||||
map_var = {
|
||||
"first" = "foo"
|
||||
"second" = "bar"
|
||||
}
|
||||
sensitive_var = (sensitive value)
|
||||
string_var = "string value"`
|
||||
|
||||
func basicState(t *testing.T) *states.State {
|
||||
state := states.NewState()
|
||||
|
||||
rootModule := state.RootModule()
|
||||
if rootModule == nil {
|
||||
t.Errorf("root module is nil; want valid object")
|
||||
}
|
||||
|
||||
rootModule.SetLocalValue("foo", cty.StringVal("foo value"))
|
||||
rootModule.SetOutputValue("bar", cty.StringVal("bar value"), false)
|
||||
rootModule.SetResourceInstanceCurrent(
|
||||
addrs.Resource{
|
||||
Mode: addrs.ManagedResourceMode,
|
||||
Type: "test_resource",
|
||||
Name: "baz",
|
||||
}.Instance(addrs.IntKey(0)),
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectReady,
|
||||
SchemaVersion: 1,
|
||||
AttrsJSON: []byte(`{"woozles":"confuzles"}`),
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
rootModule.SetResourceInstanceCurrent(
|
||||
addrs.Resource{
|
||||
Mode: addrs.DataResourceMode,
|
||||
Type: "test_data_source",
|
||||
Name: "data",
|
||||
}.Instance(addrs.NoKey),
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectReady,
|
||||
SchemaVersion: 1,
|
||||
AttrsJSON: []byte(`{"compute":"sure"}`),
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
return state
|
||||
}
|
||||
|
||||
func stateWithMoreOutputs(t *testing.T) *states.State {
|
||||
state := states.NewState()
|
||||
|
||||
rootModule := state.RootModule()
|
||||
if rootModule == nil {
|
||||
t.Errorf("root module is nil; want valid object")
|
||||
}
|
||||
|
||||
rootModule.SetOutputValue("string_var", cty.StringVal("string value"), false)
|
||||
rootModule.SetOutputValue("int_var", cty.NumberIntVal(42), false)
|
||||
rootModule.SetOutputValue("bool_var", cty.BoolVal(true), false)
|
||||
rootModule.SetOutputValue("sensitive_var", cty.StringVal("secret!!!"), true)
|
||||
rootModule.SetOutputValue("map_var", cty.MapVal(map[string]cty.Value{
|
||||
"first": cty.StringVal("foo"),
|
||||
"second": cty.StringVal("bar"),
|
||||
}), false)
|
||||
|
||||
rootModule.SetResourceInstanceCurrent(
|
||||
addrs.Resource{
|
||||
Mode: addrs.ManagedResourceMode,
|
||||
Type: "test_resource",
|
||||
Name: "baz",
|
||||
}.Instance(addrs.IntKey(0)),
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectReady,
|
||||
SchemaVersion: 1,
|
||||
AttrsJSON: []byte(`{"woozles":"confuzles"}`),
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
return state
|
||||
}
|
||||
|
||||
func nestedState(t *testing.T) *states.State {
|
||||
state := states.NewState()
|
||||
|
||||
rootModule := state.RootModule()
|
||||
if rootModule == nil {
|
||||
t.Errorf("root module is nil; want valid object")
|
||||
}
|
||||
|
||||
rootModule.SetResourceInstanceCurrent(
|
||||
addrs.Resource{
|
||||
Mode: addrs.ManagedResourceMode,
|
||||
Type: "test_resource",
|
||||
Name: "baz",
|
||||
}.Instance(addrs.IntKey(0)),
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectReady,
|
||||
SchemaVersion: 1,
|
||||
AttrsJSON: []byte(`{"woozles":"confuzles","nested": [{"value": "42"}]}`),
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
return state
|
||||
}
|
||||
|
||||
func deposedState(t *testing.T) *states.State {
|
||||
state := nestedState(t)
|
||||
rootModule := state.RootModule()
|
||||
rootModule.SetResourceInstanceDeposed(
|
||||
addrs.Resource{
|
||||
Mode: addrs.ManagedResourceMode,
|
||||
Type: "test_resource",
|
||||
Name: "baz",
|
||||
}.Instance(addrs.IntKey(0)),
|
||||
states.DeposedKey("1234"),
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectReady,
|
||||
SchemaVersion: 1,
|
||||
AttrsJSON: []byte(`{"woozles":"confuzles","nested": [{"value": "42"}]}`),
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
return state
|
||||
}
|
||||
|
||||
// replicate a corrupt resource where only a deposed exists
|
||||
func onlyDeposedState(t *testing.T) *states.State {
|
||||
state := states.NewState()
|
||||
|
||||
rootModule := state.RootModule()
|
||||
if rootModule == nil {
|
||||
t.Errorf("root module is nil; want valid object")
|
||||
}
|
||||
|
||||
rootModule.SetResourceInstanceDeposed(
|
||||
addrs.Resource{
|
||||
Mode: addrs.ManagedResourceMode,
|
||||
Type: "test_resource",
|
||||
Name: "baz",
|
||||
}.Instance(addrs.IntKey(0)),
|
||||
states.DeposedKey("1234"),
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectReady,
|
||||
SchemaVersion: 1,
|
||||
AttrsJSON: []byte(`{"woozles":"confuzles","nested": [{"value": "42"}]}`),
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
rootModule.SetResourceInstanceDeposed(
|
||||
addrs.Resource{
|
||||
Mode: addrs.ManagedResourceMode,
|
||||
Type: "test_resource",
|
||||
Name: "baz",
|
||||
}.Instance(addrs.IntKey(0)),
|
||||
states.DeposedKey("5678"),
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
Status: states.ObjectReady,
|
||||
SchemaVersion: 1,
|
||||
AttrsJSON: []byte(`{"woozles":"confuzles","nested": [{"value": "42"}]}`),
|
||||
},
|
||||
addrs.AbsProviderConfig{
|
||||
Provider: addrs.NewDefaultProvider("test"),
|
||||
Module: addrs.RootModule,
|
||||
},
|
||||
)
|
||||
return state
|
||||
}
|
||||
Loading…
Reference in new issue