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 }