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 }