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"
|
||||
|
||||
"github.com/hashicorp/terraform/addrs"
|
||||
"github.com/hashicorp/terraform/configs/configschema"
|
||||
"github.com/hashicorp/terraform/plans"
|
||||
|
||||
"github.com/mitchellh/colorstring"
|
||||
|
||||
"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.
|
||||
|
@ -20,6 +19,9 @@ 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
|
||||
}
|
||||
|
@ -30,6 +32,10 @@ func State(opts *StateOpts) string {
|
|||
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."
|
||||
|
@ -45,7 +51,7 @@ func State(opts *StateOpts) string {
|
|||
|
||||
// Format all the modules
|
||||
for _, m := range s.Modules {
|
||||
formatStateModule(&buf, m, opts)
|
||||
formatStateModule(p, m, opts.Schemas)
|
||||
}
|
||||
|
||||
// Write the outputs for the root module
|
||||
|
@ -53,7 +59,7 @@ func State(opts *StateOpts) string {
|
|||
|
||||
if m.OutputValues != nil {
|
||||
if len(m.OutputValues) > 0 {
|
||||
buf.WriteString("\nOutputs:\n\n")
|
||||
p.buf.WriteString("\nOutputs:\n\n")
|
||||
}
|
||||
|
||||
// Sort the outputs
|
||||
|
@ -66,17 +72,17 @@ func State(opts *StateOpts) string {
|
|||
// Output each output k/v pair
|
||||
for _, k := range ks {
|
||||
v := m.OutputValues[k]
|
||||
buf.WriteString(fmt.Sprintf("%s = ", k))
|
||||
p.buf.WriteString(fmt.Sprintf("%s = ", k))
|
||||
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(
|
||||
buf *bytes.Buffer, m *states.Module, opts *StateOpts) {
|
||||
p blockBodyDiffPrinter, m *states.Module, schemas *terraform.Schemas) {
|
||||
|
||||
var moduleName string
|
||||
if !m.Addr.IsRoot() {
|
||||
|
@ -92,75 +98,64 @@ func formatStateModule(
|
|||
sort.Strings(names)
|
||||
|
||||
// Go through each resource and begin building up the output.
|
||||
for _, k := range names {
|
||||
name := k
|
||||
if moduleName != "" {
|
||||
name = moduleName + "." + name
|
||||
for _, key := range names {
|
||||
taintStr := ""
|
||||
instances := m.Resources[key].Instances
|
||||
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")
|
||||
|
||||
}
|
||||
|
||||
// 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")
|
||||
p.buf.WriteString("[reset]\n")
|
||||
}
|
||||
|
||||
func formatNestedList(indent string, outputList []interface{}) string {
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
package format
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"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/terraform"
|
||||
"github.com/mitchellh/colorstring"
|
||||
"github.com/zclconf/go-cty/cty"
|
||||
)
|
||||
|
@ -28,7 +30,7 @@ func TestState(t *testing.T) {
|
|||
rootModule.SetResourceInstanceCurrent(
|
||||
addrs.Resource{
|
||||
Mode: addrs.ManagedResourceMode,
|
||||
Type: "test_thing",
|
||||
Type: "test_resource",
|
||||
Name: "baz",
|
||||
}.Instance(addrs.IntKey(0)),
|
||||
&states.ResourceInstanceObjectSrc{
|
||||
|
@ -47,17 +49,19 @@ func TestState(t *testing.T) {
|
|||
}{
|
||||
{
|
||||
&StateOpts{
|
||||
State: &states.State{},
|
||||
Color: disabledColorize,
|
||||
State: &states.State{},
|
||||
Color: disabledColorize,
|
||||
Schemas: &terraform.Schemas{},
|
||||
},
|
||||
"The state file is empty. No resources are represented.",
|
||||
},
|
||||
{
|
||||
&StateOpts{
|
||||
State: state,
|
||||
Color: disabledColorize,
|
||||
State: state,
|
||||
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 {
|
||||
addr, err := addrs.ParseModuleInstanceStr(s)
|
||||
if err != nil {
|
||||
fmt.Printf(err.Err().Error())
|
||||
panic(err)
|
||||
func testProvider() *terraform.MockProvider {
|
||||
p := new(terraform.MockProvider)
|
||||
p.ReadResourceFn = func(req providers.ReadResourceRequest) providers.ReadResourceResponse {
|
||||
return providers.ReadResourceResponse{NewState: req.PriorState}
|
||||
}
|
||||
|
||||
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"
|
||||
"strings"
|
||||
|
||||
"github.com/hashicorp/terraform/backend"
|
||||
"github.com/hashicorp/terraform/plans/planfile"
|
||||
"github.com/hashicorp/terraform/states/statefile"
|
||||
"github.com/hashicorp/terraform/tfdiags"
|
||||
|
||||
"github.com/hashicorp/terraform/command/format"
|
||||
"github.com/hashicorp/terraform/plans"
|
||||
|
@ -43,6 +45,52 @@ func (c *ShowCommand) Run(args []string) int {
|
|||
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 path string
|
||||
var plan *plans.Plan
|
||||
|
@ -72,15 +120,6 @@ func (c *ShowCommand) Run(args []string) int {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
// Load the backend
|
||||
b, backendDiags := c.Backend(nil)
|
||||
if backendDiags.HasErrors() {
|
||||
c.showDiagnostics(backendDiags)
|
||||
return 1
|
||||
}
|
||||
|
||||
env := c.Workspace()
|
||||
|
||||
// Get the state
|
||||
stateStore, err := b.StateMgr(env)
|
||||
if err != nil {
|
||||
|
@ -118,8 +157,9 @@ func (c *ShowCommand) Run(args []string) int {
|
|||
}
|
||||
|
||||
c.Ui.Output(format.State(&format.StateOpts{
|
||||
State: state,
|
||||
Color: c.Colorize(),
|
||||
State: state,
|
||||
Color: c.Colorize(),
|
||||
Schemas: schemas,
|
||||
}))
|
||||
return 0
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue