github.com/paultyng/terraform@v0.6.11-0.20180227224804-66ff8f8bed40/command/format/state.go (about)

     1  package format
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"sort"
     7  	"strings"
     8  
     9  	"github.com/hashicorp/terraform/terraform"
    10  	"github.com/mitchellh/colorstring"
    11  )
    12  
    13  // StateOpts are the options for formatting a state.
    14  type StateOpts struct {
    15  	// State is the state to format. This is required.
    16  	State *terraform.State
    17  
    18  	// Color is the colorizer. This is optional.
    19  	Color *colorstring.Colorize
    20  
    21  	// ModuleDepth is the depth of the modules to expand. By default this
    22  	// is zero which will not expand modules at all.
    23  	ModuleDepth int
    24  }
    25  
    26  // State takes a state and returns a string
    27  func State(opts *StateOpts) string {
    28  	if opts.Color == nil {
    29  		panic("colorize not given")
    30  	}
    31  
    32  	s := opts.State
    33  	if len(s.Modules) == 0 {
    34  		return "The state file is empty. No resources are represented."
    35  	}
    36  
    37  	var buf bytes.Buffer
    38  	buf.WriteString("[reset]")
    39  
    40  	// Format all the modules
    41  	for _, m := range s.Modules {
    42  		if len(m.Path)-1 <= opts.ModuleDepth || opts.ModuleDepth == -1 {
    43  			formatStateModuleExpand(&buf, m, opts)
    44  		} else {
    45  			formatStateModuleSingle(&buf, m, opts)
    46  		}
    47  	}
    48  
    49  	// Write the outputs for the root module
    50  	m := s.RootModule()
    51  	if len(m.Outputs) > 0 {
    52  		buf.WriteString("\nOutputs:\n\n")
    53  
    54  		// Sort the outputs
    55  		ks := make([]string, 0, len(m.Outputs))
    56  		for k, _ := range m.Outputs {
    57  			ks = append(ks, k)
    58  		}
    59  		sort.Strings(ks)
    60  
    61  		// Output each output k/v pair
    62  		for _, k := range ks {
    63  			v := m.Outputs[k]
    64  			switch output := v.Value.(type) {
    65  			case string:
    66  				buf.WriteString(fmt.Sprintf("%s = %s", k, output))
    67  				buf.WriteString("\n")
    68  			case []interface{}:
    69  				buf.WriteString(formatListOutput("", k, output))
    70  				buf.WriteString("\n")
    71  			case map[string]interface{}:
    72  				buf.WriteString(formatMapOutput("", k, output))
    73  				buf.WriteString("\n")
    74  			}
    75  		}
    76  	}
    77  
    78  	return opts.Color.Color(strings.TrimSpace(buf.String()))
    79  }
    80  
    81  func formatStateModuleExpand(
    82  	buf *bytes.Buffer, m *terraform.ModuleState, opts *StateOpts) {
    83  	var moduleName string
    84  	if !m.IsRoot() {
    85  		moduleName = fmt.Sprintf("module.%s", strings.Join(m.Path[1:], "."))
    86  	}
    87  
    88  	// First get the names of all the resources so we can show them
    89  	// in alphabetical order.
    90  	names := make([]string, 0, len(m.Resources))
    91  	for name, _ := range m.Resources {
    92  		names = append(names, name)
    93  	}
    94  	sort.Strings(names)
    95  
    96  	// Go through each resource and begin building up the output.
    97  	for _, k := range names {
    98  		name := k
    99  		if moduleName != "" {
   100  			name = moduleName + "." + name
   101  		}
   102  
   103  		rs := m.Resources[k]
   104  		is := rs.Primary
   105  		var id string
   106  		if is != nil {
   107  			id = is.ID
   108  		}
   109  		if id == "" {
   110  			id = "<not created>"
   111  		}
   112  
   113  		taintStr := ""
   114  		if rs.Primary != nil && rs.Primary.Tainted {
   115  			taintStr = " (tainted)"
   116  		}
   117  
   118  		buf.WriteString(fmt.Sprintf("%s:%s\n", name, taintStr))
   119  		buf.WriteString(fmt.Sprintf("  id = %s\n", id))
   120  
   121  		if is != nil {
   122  			// Sort the attributes
   123  			attrKeys := make([]string, 0, len(is.Attributes))
   124  			for ak, _ := range is.Attributes {
   125  				// Skip the id attribute since we just show the id directly
   126  				if ak == "id" {
   127  					continue
   128  				}
   129  
   130  				attrKeys = append(attrKeys, ak)
   131  			}
   132  			sort.Strings(attrKeys)
   133  
   134  			// Output each attribute
   135  			for _, ak := range attrKeys {
   136  				av := is.Attributes[ak]
   137  				buf.WriteString(fmt.Sprintf("  %s = %s\n", ak, av))
   138  			}
   139  		}
   140  	}
   141  
   142  	buf.WriteString("[reset]\n")
   143  }
   144  
   145  func formatStateModuleSingle(
   146  	buf *bytes.Buffer, m *terraform.ModuleState, opts *StateOpts) {
   147  	// Header with the module name
   148  	buf.WriteString(fmt.Sprintf("module.%s\n", strings.Join(m.Path[1:], ".")))
   149  
   150  	// Now just write how many resources are in here.
   151  	buf.WriteString(fmt.Sprintf("  %d resource(s)\n", len(m.Resources)))
   152  }
   153  
   154  func formatNestedList(indent string, outputList []interface{}) string {
   155  	outputBuf := new(bytes.Buffer)
   156  	outputBuf.WriteString(fmt.Sprintf("%s[", indent))
   157  
   158  	lastIdx := len(outputList) - 1
   159  
   160  	for i, value := range outputList {
   161  		outputBuf.WriteString(fmt.Sprintf("\n%s%s%s", indent, "    ", value))
   162  		if i != lastIdx {
   163  			outputBuf.WriteString(",")
   164  		}
   165  	}
   166  
   167  	outputBuf.WriteString(fmt.Sprintf("\n%s]", indent))
   168  	return strings.TrimPrefix(outputBuf.String(), "\n")
   169  }
   170  
   171  func formatListOutput(indent, outputName string, outputList []interface{}) string {
   172  	keyIndent := ""
   173  
   174  	outputBuf := new(bytes.Buffer)
   175  
   176  	if outputName != "" {
   177  		outputBuf.WriteString(fmt.Sprintf("%s%s = [", indent, outputName))
   178  		keyIndent = "    "
   179  	}
   180  
   181  	lastIdx := len(outputList) - 1
   182  
   183  	for i, value := range outputList {
   184  		switch typedValue := value.(type) {
   185  		case string:
   186  			outputBuf.WriteString(fmt.Sprintf("\n%s%s%s", indent, keyIndent, value))
   187  		case []interface{}:
   188  			outputBuf.WriteString(fmt.Sprintf("\n%s%s", indent,
   189  				formatNestedList(indent+keyIndent, typedValue)))
   190  		case map[string]interface{}:
   191  			outputBuf.WriteString(fmt.Sprintf("\n%s%s", indent,
   192  				formatNestedMap(indent+keyIndent, typedValue)))
   193  		}
   194  
   195  		if lastIdx != i {
   196  			outputBuf.WriteString(",")
   197  		}
   198  	}
   199  
   200  	if outputName != "" {
   201  		if len(outputList) > 0 {
   202  			outputBuf.WriteString(fmt.Sprintf("\n%s]", indent))
   203  		} else {
   204  			outputBuf.WriteString("]")
   205  		}
   206  	}
   207  
   208  	return strings.TrimPrefix(outputBuf.String(), "\n")
   209  }
   210  
   211  func formatNestedMap(indent string, outputMap map[string]interface{}) string {
   212  	ks := make([]string, 0, len(outputMap))
   213  	for k, _ := range outputMap {
   214  		ks = append(ks, k)
   215  	}
   216  	sort.Strings(ks)
   217  
   218  	outputBuf := new(bytes.Buffer)
   219  	outputBuf.WriteString(fmt.Sprintf("%s{", indent))
   220  
   221  	lastIdx := len(outputMap) - 1
   222  	for i, k := range ks {
   223  		v := outputMap[k]
   224  		outputBuf.WriteString(fmt.Sprintf("\n%s%s = %v", indent+"    ", k, v))
   225  
   226  		if lastIdx != i {
   227  			outputBuf.WriteString(",")
   228  		}
   229  	}
   230  
   231  	outputBuf.WriteString(fmt.Sprintf("\n%s}", indent))
   232  
   233  	return strings.TrimPrefix(outputBuf.String(), "\n")
   234  }
   235  
   236  func formatMapOutput(indent, outputName string, outputMap map[string]interface{}) string {
   237  	ks := make([]string, 0, len(outputMap))
   238  	for k, _ := range outputMap {
   239  		ks = append(ks, k)
   240  	}
   241  	sort.Strings(ks)
   242  
   243  	keyIndent := ""
   244  
   245  	outputBuf := new(bytes.Buffer)
   246  	if outputName != "" {
   247  		outputBuf.WriteString(fmt.Sprintf("%s%s = {", indent, outputName))
   248  		keyIndent = "  "
   249  	}
   250  
   251  	for _, k := range ks {
   252  		v := outputMap[k]
   253  		outputBuf.WriteString(fmt.Sprintf("\n%s%s%s = %v", indent, keyIndent, k, v))
   254  	}
   255  
   256  	if outputName != "" {
   257  		if len(outputMap) > 0 {
   258  			outputBuf.WriteString(fmt.Sprintf("\n%s}", indent))
   259  		} else {
   260  			outputBuf.WriteString("}")
   261  		}
   262  	}
   263  
   264  	return strings.TrimPrefix(outputBuf.String(), "\n")
   265  }