cli: format/state refactor to use blockBodyDiffPrinter
Use functions from format/diff to print values
This commit is contained in:
parent
8063f69e5c
commit
14c28b8de4
|
@ -7,12 +7,11 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/addrs"
|
"github.com/hashicorp/terraform/addrs"
|
||||||
|
"github.com/hashicorp/terraform/configs/configschema"
|
||||||
"github.com/hashicorp/terraform/plans"
|
"github.com/hashicorp/terraform/plans"
|
||||||
|
|
||||||
"github.com/mitchellh/colorstring"
|
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/states"
|
"github.com/hashicorp/terraform/states"
|
||||||
// "github.com/hashicorp/terraform/terraform"
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
"github.com/mitchellh/colorstring"
|
||||||
)
|
)
|
||||||
|
|
||||||
// StateOpts are the options for formatting a state.
|
// StateOpts are the options for formatting a state.
|
||||||
|
@ -20,6 +19,9 @@ type StateOpts struct {
|
||||||
// State is the state to format. This is required.
|
// State is the state to format. This is required.
|
||||||
State *states.State
|
State *states.State
|
||||||
|
|
||||||
|
// Schemas are used to decode attributes. This is required.
|
||||||
|
Schemas *terraform.Schemas
|
||||||
|
|
||||||
// Color is the colorizer. This is optional.
|
// Color is the colorizer. This is optional.
|
||||||
Color *colorstring.Colorize
|
Color *colorstring.Colorize
|
||||||
}
|
}
|
||||||
|
@ -30,6 +32,10 @@ func State(opts *StateOpts) string {
|
||||||
panic("colorize not given")
|
panic("colorize not given")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if opts.Schemas == nil {
|
||||||
|
panic("schemas not given")
|
||||||
|
}
|
||||||
|
|
||||||
s := opts.State
|
s := opts.State
|
||||||
if len(s.Modules) == 0 {
|
if len(s.Modules) == 0 {
|
||||||
return "The state file is empty. No resources are represented."
|
return "The state file is empty. No resources are represented."
|
||||||
|
@ -45,7 +51,7 @@ func State(opts *StateOpts) string {
|
||||||
|
|
||||||
// Format all the modules
|
// Format all the modules
|
||||||
for _, m := range s.Modules {
|
for _, m := range s.Modules {
|
||||||
formatStateModule(&buf, m, opts)
|
formatStateModule(p, m, opts.Schemas)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write the outputs for the root module
|
// Write the outputs for the root module
|
||||||
|
@ -53,7 +59,7 @@ func State(opts *StateOpts) string {
|
||||||
|
|
||||||
if m.OutputValues != nil {
|
if m.OutputValues != nil {
|
||||||
if len(m.OutputValues) > 0 {
|
if len(m.OutputValues) > 0 {
|
||||||
buf.WriteString("\nOutputs:\n\n")
|
p.buf.WriteString("\nOutputs:\n\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort the outputs
|
// Sort the outputs
|
||||||
|
@ -66,17 +72,17 @@ func State(opts *StateOpts) string {
|
||||||
// Output each output k/v pair
|
// Output each output k/v pair
|
||||||
for _, k := range ks {
|
for _, k := range ks {
|
||||||
v := m.OutputValues[k]
|
v := m.OutputValues[k]
|
||||||
buf.WriteString(fmt.Sprintf("%s = ", k))
|
p.buf.WriteString(fmt.Sprintf("%s = ", k))
|
||||||
p.writeValue(v.Value, plans.NoOp, 0)
|
p.writeValue(v.Value, plans.NoOp, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return opts.Color.Color(strings.TrimSpace(buf.String()))
|
return opts.Color.Color(strings.TrimSpace(p.buf.String()))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func formatStateModule(
|
func formatStateModule(
|
||||||
buf *bytes.Buffer, m *states.Module, opts *StateOpts) {
|
p blockBodyDiffPrinter, m *states.Module, schemas *terraform.Schemas) {
|
||||||
|
|
||||||
var moduleName string
|
var moduleName string
|
||||||
if !m.Addr.IsRoot() {
|
if !m.Addr.IsRoot() {
|
||||||
|
@ -92,75 +98,64 @@ func formatStateModule(
|
||||||
sort.Strings(names)
|
sort.Strings(names)
|
||||||
|
|
||||||
// Go through each resource and begin building up the output.
|
// Go through each resource and begin building up the output.
|
||||||
for _, k := range names {
|
for _, key := range names {
|
||||||
name := k
|
taintStr := ""
|
||||||
if moduleName != "" {
|
instances := m.Resources[key].Instances
|
||||||
name = moduleName + "." + name
|
for k, v := range instances {
|
||||||
|
name := key
|
||||||
|
if moduleName != "" {
|
||||||
|
name = moduleName + "." + name
|
||||||
|
}
|
||||||
|
|
||||||
|
addr := m.Resources[key].Addr
|
||||||
|
if v.Current.Status == 'T' {
|
||||||
|
taintStr = "(tainted)"
|
||||||
|
}
|
||||||
|
p.buf.WriteString(fmt.Sprintf("# %s: %s\n", addr.Instance(k), taintStr))
|
||||||
|
taintStr = ""
|
||||||
|
|
||||||
|
var schema *configschema.Block
|
||||||
|
provider := m.Resources[key].ProviderConfig.ProviderConfig.StringCompact()
|
||||||
|
|
||||||
|
switch addr.Mode {
|
||||||
|
case addrs.ManagedResourceMode:
|
||||||
|
p.buf.WriteString(fmt.Sprintf(
|
||||||
|
"resource %q %q {\n",
|
||||||
|
addr.Type,
|
||||||
|
addr.Name,
|
||||||
|
))
|
||||||
|
schema = schemas.Providers[provider].ResourceTypes[addr.Type]
|
||||||
|
case addrs.DataResourceMode:
|
||||||
|
p.buf.WriteString(fmt.Sprintf(
|
||||||
|
"data %q %q {\n",
|
||||||
|
addr.Type,
|
||||||
|
addr.Name,
|
||||||
|
))
|
||||||
|
schema = schemas.Providers[provider].DataSources[addr.Type]
|
||||||
|
default:
|
||||||
|
// should never happen, since the above is exhaustive
|
||||||
|
p.buf.WriteString(addr.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
val, err := v.Current.Decode(schema.ImpliedType())
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
break
|
||||||
|
}
|
||||||
|
for name := range schema.Attributes {
|
||||||
|
attr := ctyGetAttrMaybeNull(val.Value, name)
|
||||||
|
if !attr.IsNull() {
|
||||||
|
p.buf.WriteString(fmt.Sprintf(" %s = ", name))
|
||||||
|
attr := ctyGetAttrMaybeNull(val.Value, name)
|
||||||
|
p.writeValue(attr, plans.NoOp, 4)
|
||||||
|
p.buf.WriteString("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.buf.WriteString("}\n\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
addr := m.Resources[k].Addr
|
|
||||||
switch addr.Mode {
|
|
||||||
case addrs.ManagedResourceMode:
|
|
||||||
buf.WriteString(fmt.Sprintf(
|
|
||||||
"resource %q %q",
|
|
||||||
addr.Type,
|
|
||||||
addr.Name,
|
|
||||||
))
|
|
||||||
case addrs.DataResourceMode:
|
|
||||||
buf.WriteString(fmt.Sprintf(
|
|
||||||
"data %q %q ",
|
|
||||||
addr.Type,
|
|
||||||
addr.Name,
|
|
||||||
))
|
|
||||||
default:
|
|
||||||
// should never happen, since the above is exhaustive
|
|
||||||
buf.WriteString(addr.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
buf.WriteString(" { attrs go here! }\n")
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
p.buf.WriteString("[reset]\n")
|
||||||
// rs := m.Resources[k]
|
|
||||||
// is := rs.Instance
|
|
||||||
// var id string
|
|
||||||
// if is != nil {
|
|
||||||
// id = is
|
|
||||||
// }
|
|
||||||
// if id == "" {
|
|
||||||
// id = "<not created>"
|
|
||||||
// }
|
|
||||||
|
|
||||||
// taintStr := ""
|
|
||||||
// // if rs.Primary != nil && rs.Primary.Tainted {
|
|
||||||
// // taintStr = " (tainted)"
|
|
||||||
// // }
|
|
||||||
|
|
||||||
// buf.WriteString(fmt.Sprintf("%s:%s\n", name, taintStr))
|
|
||||||
// buf.WriteString(fmt.Sprintf(" id = %s\n", id))
|
|
||||||
|
|
||||||
// if is != nil {
|
|
||||||
// // Sort the attributes
|
|
||||||
// attrKeys := make([]string, 0, len(is.Attributes))
|
|
||||||
// for ak, _ := range is.Attributes {
|
|
||||||
// // Skip the id attribute since we just show the id directly
|
|
||||||
// if ak == "id" {
|
|
||||||
// continue
|
|
||||||
// }
|
|
||||||
|
|
||||||
// attrKeys = append(attrKeys, ak)
|
|
||||||
// }
|
|
||||||
// sort.Strings(attrKeys)
|
|
||||||
|
|
||||||
// // Output each attribute
|
|
||||||
// for _, ak := range attrKeys {
|
|
||||||
// av := is.Attributes[ak]
|
|
||||||
// buf.WriteString(fmt.Sprintf(" %s = %s\n", ak, av))
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
buf.WriteString("[reset]\n")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func formatNestedList(indent string, outputList []interface{}) string {
|
func formatNestedList(indent string, outputList []interface{}) string {
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
package format
|
package format
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/addrs"
|
"github.com/hashicorp/terraform/addrs"
|
||||||
|
"github.com/hashicorp/terraform/configs/configschema"
|
||||||
|
"github.com/hashicorp/terraform/providers"
|
||||||
"github.com/hashicorp/terraform/states"
|
"github.com/hashicorp/terraform/states"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
"github.com/mitchellh/colorstring"
|
"github.com/mitchellh/colorstring"
|
||||||
"github.com/zclconf/go-cty/cty"
|
"github.com/zclconf/go-cty/cty"
|
||||||
)
|
)
|
||||||
|
@ -28,7 +30,7 @@ func TestState(t *testing.T) {
|
||||||
rootModule.SetResourceInstanceCurrent(
|
rootModule.SetResourceInstanceCurrent(
|
||||||
addrs.Resource{
|
addrs.Resource{
|
||||||
Mode: addrs.ManagedResourceMode,
|
Mode: addrs.ManagedResourceMode,
|
||||||
Type: "test_thing",
|
Type: "test_resource",
|
||||||
Name: "baz",
|
Name: "baz",
|
||||||
}.Instance(addrs.IntKey(0)),
|
}.Instance(addrs.IntKey(0)),
|
||||||
&states.ResourceInstanceObjectSrc{
|
&states.ResourceInstanceObjectSrc{
|
||||||
|
@ -47,17 +49,19 @@ func TestState(t *testing.T) {
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
&StateOpts{
|
&StateOpts{
|
||||||
State: &states.State{},
|
State: &states.State{},
|
||||||
Color: disabledColorize,
|
Color: disabledColorize,
|
||||||
|
Schemas: &terraform.Schemas{},
|
||||||
},
|
},
|
||||||
"The state file is empty. No resources are represented.",
|
"The state file is empty. No resources are represented.",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
&StateOpts{
|
&StateOpts{
|
||||||
State: state,
|
State: state,
|
||||||
Color: disabledColorize,
|
Color: disabledColorize,
|
||||||
|
Schemas: testSchemas(),
|
||||||
},
|
},
|
||||||
"module.test_module.test_resource.foo",
|
"test_resource.baz",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,11 +76,49 @@ func TestState(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func mustParseModuleInstanceStr(s string) addrs.ModuleInstance {
|
func testProvider() *terraform.MockProvider {
|
||||||
addr, err := addrs.ParseModuleInstanceStr(s)
|
p := new(terraform.MockProvider)
|
||||||
if err != nil {
|
p.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse {
|
||||||
fmt.Printf(err.Err().Error())
|
return providers.ReadResourceResponse{NewState: req.PriorState}
|
||||||
panic(err)
|
}
|
||||||
|
|
||||||
|
p.GetSchemaReturn = testProviderSchema()
|
||||||
|
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
func testProviderSchema() *terraform.ProviderSchema {
|
||||||
|
return &terraform.ProviderSchema{
|
||||||
|
Provider: &configschema.Block{
|
||||||
|
Attributes: map[string]*configschema.Attribute{
|
||||||
|
"region": {Type: cty.String, Optional: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ResourceTypes: map[string]*configschema.Block{
|
||||||
|
"test_resource": {
|
||||||
|
Attributes: map[string]*configschema.Attribute{
|
||||||
|
"id": {Type: cty.String, Computed: true},
|
||||||
|
"foo": {Type: cty.String, Optional: true},
|
||||||
|
"woozles": {Type: cty.String, Optional: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DataSources: map[string]*configschema.Block{
|
||||||
|
"test_data_source": {
|
||||||
|
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[string]*terraform.ProviderSchema{
|
||||||
|
"test": provider.GetSchemaReturn,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
return addr
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,10 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/backend"
|
||||||
"github.com/hashicorp/terraform/plans/planfile"
|
"github.com/hashicorp/terraform/plans/planfile"
|
||||||
"github.com/hashicorp/terraform/states/statefile"
|
"github.com/hashicorp/terraform/states/statefile"
|
||||||
|
"github.com/hashicorp/terraform/tfdiags"
|
||||||
|
|
||||||
"github.com/hashicorp/terraform/command/format"
|
"github.com/hashicorp/terraform/command/format"
|
||||||
"github.com/hashicorp/terraform/plans"
|
"github.com/hashicorp/terraform/plans"
|
||||||
|
@ -43,6 +45,52 @@ func (c *ShowCommand) Run(args []string) int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
configPath, err := ModulePath(cmdFlags.Args())
|
||||||
|
if err != nil {
|
||||||
|
c.Ui.Error(err.Error())
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
var diags tfdiags.Diagnostics
|
||||||
|
|
||||||
|
// Load the backend
|
||||||
|
b, backendDiags := c.Backend(nil)
|
||||||
|
diags = diags.Append(backendDiags)
|
||||||
|
if backendDiags.HasErrors() {
|
||||||
|
c.showDiagnostics(diags)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// We require a local backend
|
||||||
|
local, ok := b.(backend.Local)
|
||||||
|
if !ok {
|
||||||
|
c.showDiagnostics(diags) // in case of any warnings in here
|
||||||
|
c.Ui.Error(ErrUnsupportedLocalOp)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build the operation
|
||||||
|
opReq := c.Operation(b)
|
||||||
|
opReq.ConfigDir = configPath
|
||||||
|
opReq.ConfigLoader, err = c.initConfigLoader()
|
||||||
|
if err != nil {
|
||||||
|
diags = diags.Append(err)
|
||||||
|
c.showDiagnostics(diags)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the context
|
||||||
|
ctx, _, ctxDiags := local.Context(opReq)
|
||||||
|
diags = diags.Append(ctxDiags)
|
||||||
|
if ctxDiags.HasErrors() {
|
||||||
|
c.showDiagnostics(diags)
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
schemas := ctx.Schemas()
|
||||||
|
|
||||||
|
env := c.Workspace()
|
||||||
|
|
||||||
var planErr, stateErr error
|
var planErr, stateErr error
|
||||||
var path string
|
var path string
|
||||||
var plan *plans.Plan
|
var plan *plans.Plan
|
||||||
|
@ -72,15 +120,6 @@ func (c *ShowCommand) Run(args []string) int {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Load the backend
|
|
||||||
b, backendDiags := c.Backend(nil)
|
|
||||||
if backendDiags.HasErrors() {
|
|
||||||
c.showDiagnostics(backendDiags)
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
env := c.Workspace()
|
|
||||||
|
|
||||||
// Get the state
|
// Get the state
|
||||||
stateStore, err := b.StateMgr(env)
|
stateStore, err := b.StateMgr(env)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -118,8 +157,9 @@ func (c *ShowCommand) Run(args []string) int {
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Ui.Output(format.State(&format.StateOpts{
|
c.Ui.Output(format.State(&format.StateOpts{
|
||||||
State: state,
|
State: state,
|
||||||
Color: c.Colorize(),
|
Color: c.Colorize(),
|
||||||
|
Schemas: schemas,
|
||||||
}))
|
}))
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue