github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/command/format/state.go (about)

     1  package format
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"sort"
     7  	"strings"
     8  
     9  	"github.com/zclconf/go-cty/cty"
    10  
    11  	"github.com/hashicorp/terraform/internal/addrs"
    12  	"github.com/hashicorp/terraform/internal/configs/configschema"
    13  	"github.com/hashicorp/terraform/internal/plans"
    14  	"github.com/hashicorp/terraform/internal/states"
    15  	"github.com/hashicorp/terraform/internal/terraform"
    16  	"github.com/mitchellh/colorstring"
    17  )
    18  
    19  // StateOpts are the options for formatting a state.
    20  type StateOpts struct {
    21  	// State is the state to format. This is required.
    22  	State *states.State
    23  
    24  	// Schemas are used to decode attributes. This is required.
    25  	Schemas *terraform.Schemas
    26  
    27  	// Color is the colorizer. This is optional.
    28  	Color *colorstring.Colorize
    29  }
    30  
    31  // State takes a state and returns a string
    32  func State(opts *StateOpts) string {
    33  	if opts.Color == nil {
    34  		panic("colorize not given")
    35  	}
    36  
    37  	if opts.Schemas == nil {
    38  		panic("schemas not given")
    39  	}
    40  
    41  	s := opts.State
    42  	if len(s.Modules) == 0 {
    43  		return "The state file is empty. No resources are represented."
    44  	}
    45  
    46  	buf := bytes.NewBufferString("[reset]")
    47  	p := blockBodyDiffPrinter{
    48  		buf:     buf,
    49  		color:   opts.Color,
    50  		action:  plans.NoOp,
    51  		verbose: true,
    52  	}
    53  
    54  	// Format all the modules
    55  	for _, m := range s.Modules {
    56  		formatStateModule(p, m, opts.Schemas)
    57  	}
    58  
    59  	// Write the outputs for the root module
    60  	m := s.RootModule()
    61  
    62  	if m.OutputValues != nil {
    63  		if len(m.OutputValues) > 0 {
    64  			p.buf.WriteString("Outputs:\n\n")
    65  		}
    66  
    67  		// Sort the outputs
    68  		ks := make([]string, 0, len(m.OutputValues))
    69  		for k := range m.OutputValues {
    70  			ks = append(ks, k)
    71  		}
    72  		sort.Strings(ks)
    73  
    74  		// Output each output k/v pair
    75  		for _, k := range ks {
    76  			v := m.OutputValues[k]
    77  			p.buf.WriteString(fmt.Sprintf("%s = ", k))
    78  			if v.Sensitive {
    79  				p.buf.WriteString("(sensitive value)")
    80  			} else {
    81  				p.writeValue(v.Value, plans.NoOp, 0)
    82  			}
    83  			p.buf.WriteString("\n")
    84  		}
    85  	}
    86  
    87  	trimmedOutput := strings.TrimSpace(p.buf.String())
    88  	trimmedOutput += "[reset]"
    89  
    90  	return opts.Color.Color(trimmedOutput)
    91  
    92  }
    93  
    94  func formatStateModule(p blockBodyDiffPrinter, m *states.Module, schemas *terraform.Schemas) {
    95  	// First get the names of all the resources so we can show them
    96  	// in alphabetical order.
    97  	names := make([]string, 0, len(m.Resources))
    98  	for name := range m.Resources {
    99  		names = append(names, name)
   100  	}
   101  	sort.Strings(names)
   102  
   103  	// Go through each resource and begin building up the output.
   104  	for _, key := range names {
   105  		for k, v := range m.Resources[key].Instances {
   106  			// keep these in order to keep the current object first, and
   107  			// provide deterministic output for the deposed objects
   108  			type obj struct {
   109  				header   string
   110  				instance *states.ResourceInstanceObjectSrc
   111  			}
   112  			instances := []obj{}
   113  
   114  			addr := m.Resources[key].Addr
   115  			resAddr := addr.Resource
   116  
   117  			taintStr := ""
   118  			if v.Current != nil && v.Current.Status == 'T' {
   119  				taintStr = " (tainted)"
   120  			}
   121  
   122  			instances = append(instances,
   123  				obj{fmt.Sprintf("# %s:%s\n", addr.Instance(k), taintStr), v.Current})
   124  
   125  			for dk, v := range v.Deposed {
   126  				instances = append(instances,
   127  					obj{fmt.Sprintf("# %s: (deposed object %s)\n", addr.Instance(k), dk), v})
   128  			}
   129  
   130  			// Sort the instances for consistent output.
   131  			// Starting the sort from the second index, so the current instance
   132  			// is always first.
   133  			sort.Slice(instances[1:], func(i, j int) bool {
   134  				return instances[i+1].header < instances[j+1].header
   135  			})
   136  
   137  			for _, obj := range instances {
   138  				header := obj.header
   139  				instance := obj.instance
   140  				p.buf.WriteString(header)
   141  				if instance == nil {
   142  					// this shouldn't happen, but there's nothing to do here so
   143  					// don't panic below.
   144  					continue
   145  				}
   146  
   147  				var schema *configschema.Block
   148  
   149  				provider := m.Resources[key].ProviderConfig.Provider
   150  				if _, exists := schemas.Providers[provider]; !exists {
   151  					// This should never happen in normal use because we should've
   152  					// loaded all of the schemas and checked things prior to this
   153  					// point. We can't return errors here, but since this is UI code
   154  					// we will try to do _something_ reasonable.
   155  					p.buf.WriteString(fmt.Sprintf("# missing schema for provider %q\n\n", provider.String()))
   156  					continue
   157  				}
   158  
   159  				switch resAddr.Mode {
   160  				case addrs.ManagedResourceMode:
   161  					schema, _ = schemas.ResourceTypeConfig(
   162  						provider,
   163  						resAddr.Mode,
   164  						resAddr.Type,
   165  					)
   166  					if schema == nil {
   167  						p.buf.WriteString(fmt.Sprintf(
   168  							"# missing schema for provider %q resource type %s\n\n", provider, resAddr.Type))
   169  						continue
   170  					}
   171  
   172  					p.buf.WriteString(fmt.Sprintf(
   173  						"resource %q %q {",
   174  						resAddr.Type,
   175  						resAddr.Name,
   176  					))
   177  				case addrs.DataResourceMode:
   178  					schema, _ = schemas.ResourceTypeConfig(
   179  						provider,
   180  						resAddr.Mode,
   181  						resAddr.Type,
   182  					)
   183  					if schema == nil {
   184  						p.buf.WriteString(fmt.Sprintf(
   185  							"# missing schema for provider %q data source %s\n\n", provider, resAddr.Type))
   186  						continue
   187  					}
   188  
   189  					p.buf.WriteString(fmt.Sprintf(
   190  						"data %q %q {",
   191  						resAddr.Type,
   192  						resAddr.Name,
   193  					))
   194  				default:
   195  					// should never happen, since the above is exhaustive
   196  					p.buf.WriteString(resAddr.String())
   197  				}
   198  
   199  				val, err := instance.Decode(schema.ImpliedType())
   200  				if err != nil {
   201  					fmt.Println(err.Error())
   202  					break
   203  				}
   204  
   205  				path := make(cty.Path, 0, 3)
   206  				result := p.writeBlockBodyDiff(schema, val.Value, val.Value, 2, path)
   207  				if result.bodyWritten {
   208  					p.buf.WriteString("\n")
   209  				}
   210  
   211  				p.buf.WriteString("}\n\n")
   212  			}
   213  		}
   214  	}
   215  	p.buf.WriteString("\n")
   216  }