@@ -22,43 +22,71 @@ import (
22
22
"github.com/hashicorp/terraform/internal/tfdiags"
23
23
)
24
24
25
+ // ImportGroup represents one or more resource and import configuration blocks.
26
+ type ImportGroup struct {
27
+ Imports []ResourceImport
28
+ }
29
+
30
+ // ResourceImport pairs up the import and associated resource when generating
31
+ // configuration, so that query output can be more structured for easier
32
+ // consumption.
33
+ type ResourceImport struct {
34
+ ImportBody []byte
35
+ Resource Resource
36
+ }
37
+
25
38
type Resource struct {
39
+ Addr addrs.AbsResourceInstance
40
+
26
41
// HCL Body of the resource, which is the attributes and blocks
27
42
// that are part of the resource.
28
43
Body []byte
44
+ }
29
45
30
- // Import is the HCL code for the import block. This is only
31
- // generated for list resource results.
32
- Import []byte
33
- Addr addrs.AbsResourceInstance
34
- Results []* Resource
46
+ func (r Resource ) String () string {
47
+ var buf strings.Builder
48
+ buf .WriteString (fmt .Sprintf ("resource %q %q {\n " , r .Addr .Resource .Resource .Type , r .Addr .Resource .Resource .Name ))
49
+ buf .Write (r .Body )
50
+ buf .WriteString ("}" )
51
+
52
+ formatted := hclwrite .Format ([]byte (buf .String ()))
53
+ return string (formatted )
35
54
}
36
55
37
- func (r * Resource ) String () string {
56
+ func (i ImportGroup ) String () string {
38
57
var buf strings.Builder
39
- switch r .Addr .Resource .Resource .Mode {
40
- case addrs .ListResourceMode :
41
- last := len (r .Results ) - 1
42
- // sort the results by their keys so the output is consistent
43
- for idx , managed := range r .Results {
44
- if managed .Body != nil {
45
- buf .WriteString (managed .String ())
46
- buf .WriteString ("\n " )
47
- }
48
- if managed .Import != nil {
49
- buf .WriteString (string (managed .Import ))
50
- buf .WriteString ("\n " )
51
- }
52
- if idx != last {
53
- buf .WriteString ("\n " )
54
- }
55
- }
56
- case addrs .ManagedResourceMode :
57
- buf .WriteString (fmt .Sprintf ("resource %q %q {\n " , r .Addr .Resource .Resource .Type , r .Addr .Resource .Resource .Name ))
58
- buf .Write (r .Body )
59
- buf .WriteString ("}" )
60
- default :
61
- panic (fmt .Errorf ("unsupported resource mode %s" , r .Addr .Resource .Resource .Mode ))
58
+
59
+ for _ , imp := range i .Imports {
60
+ buf .WriteString (imp .Resource .String ())
61
+ buf .WriteString ("\n \n " )
62
+ buf .WriteString (string (imp .ImportBody ))
63
+ buf .WriteString ("\n \n " )
64
+ }
65
+
66
+ // The output better be valid HCL which can be parsed and formatted.
67
+ formatted := hclwrite .Format ([]byte (buf .String ()))
68
+ return string (formatted )
69
+ }
70
+
71
+ func (i ImportGroup ) ResourcesString () string {
72
+ var buf strings.Builder
73
+
74
+ for _ , imp := range i .Imports {
75
+ buf .WriteString (imp .Resource .String ())
76
+ buf .WriteString ("\n " )
77
+ }
78
+
79
+ // The output better be valid HCL which can be parsed and formatted.
80
+ formatted := hclwrite .Format ([]byte (buf .String ()))
81
+ return string (formatted )
82
+ }
83
+
84
+ func (i ImportGroup ) ImportsString () string {
85
+ var buf strings.Builder
86
+
87
+ for _ , imp := range i .Imports {
88
+ buf .WriteString (string (imp .ImportBody ))
89
+ buf .WriteString ("\n " )
62
90
}
63
91
64
92
// The output better be valid HCL which can be parsed and formatted.
@@ -75,9 +103,9 @@ func (r *Resource) String() string {
75
103
func GenerateResourceContents (addr addrs.AbsResourceInstance ,
76
104
schema * configschema.Block ,
77
105
pc addrs.LocalProviderConfig ,
78
- stateVal cty.Value ,
106
+ configVal cty.Value ,
79
107
forceProviderAddr bool ,
80
- ) (* Resource , tfdiags.Diagnostics ) {
108
+ ) (Resource , tfdiags.Diagnostics ) {
81
109
var buf strings.Builder
82
110
83
111
var diags tfdiags.Diagnostics
@@ -89,49 +117,40 @@ func GenerateResourceContents(addr addrs.AbsResourceInstance,
89
117
buf .WriteString (fmt .Sprintf ("provider = %s\n " , pc .StringCompact ()))
90
118
}
91
119
92
- // This is generating configuration, so the only marks should be coming from
93
- // the schema itself.
94
- stateVal , _ = stateVal .UnmarkDeep ()
95
-
96
- // filter the state down to a suitable config value
97
- stateVal = extractConfigFromState (schema , stateVal )
98
-
99
- if stateVal .RawEquals (cty .NilVal ) {
120
+ if configVal .RawEquals (cty .NilVal ) {
100
121
diags = diags .Append (writeConfigAttributes (addr , & buf , schema .Attributes , 2 ))
101
122
diags = diags .Append (writeConfigBlocks (addr , & buf , schema .BlockTypes , 2 ))
102
123
} else {
103
- diags = diags .Append (writeConfigAttributesFromExisting (addr , & buf , stateVal , schema .Attributes , 2 ))
104
- diags = diags .Append (writeConfigBlocksFromExisting (addr , & buf , stateVal , schema .BlockTypes , 2 ))
124
+ diags = diags .Append (writeConfigAttributesFromExisting (addr , & buf , configVal , schema .Attributes , 2 ))
125
+ diags = diags .Append (writeConfigBlocksFromExisting (addr , & buf , configVal , schema .BlockTypes , 2 ))
105
126
}
106
127
107
128
// The output better be valid HCL which can be parsed and formatted.
108
129
formatted := hclwrite .Format ([]byte (buf .String ()))
109
- return & Resource {
110
- Body : formatted ,
111
- Addr : addr ,
112
- }, diags
130
+ return Resource {Addr : addr , Body : formatted }, diags
131
+ }
132
+
133
+ // ResourceListElement is a single Resource state and identity pair derived from
134
+ // a list resource response.
135
+ type ResourceListElement struct {
136
+ // Config is the cty value extracted from the resource state which is
137
+ // intended to be written into the HCL resource block.
138
+ Config cty.Value
139
+
140
+ Identity cty.Value
113
141
}
114
142
115
143
func GenerateListResourceContents (addr addrs.AbsResourceInstance ,
116
144
schema * configschema.Block ,
117
145
idSchema * configschema.Object ,
118
146
pc addrs.LocalProviderConfig ,
119
- stateVal cty.Value ,
120
- ) (* Resource , tfdiags.Diagnostics ) {
147
+ resources []ResourceListElement ,
148
+ ) (ImportGroup , tfdiags.Diagnostics ) {
149
+
121
150
var diags tfdiags.Diagnostics
122
- if ! stateVal .CanIterateElements () {
123
- diags = diags .Append (
124
- hcl.Diagnostic {
125
- Severity : hcl .DiagError ,
126
- Summary : "Invalid resource instance value" ,
127
- Detail : fmt .Sprintf ("Resource instance %s has nil or non-iterable value" , addr ),
128
- })
129
- return nil , diags
130
- }
151
+ ret := ImportGroup {}
131
152
132
- ret := make ([]* Resource , stateVal .LengthInt ())
133
- iter := stateVal .ElementIterator ()
134
- for idx := 0 ; iter .Next (); idx ++ {
153
+ for idx , res := range resources {
135
154
// Generate a unique resource name for each instance in the list.
136
155
resAddr := addrs.AbsResourceInstance {
137
156
Module : addr .Module ,
@@ -144,39 +163,34 @@ func GenerateListResourceContents(addr addrs.AbsResourceInstance,
144
163
Key : addr .Resource .Key ,
145
164
},
146
165
}
147
- ls := & Resource {Addr : resAddr }
148
- ret [idx ] = ls
149
-
150
- _ , val := iter .Element ()
151
- // we still need to generate the resource block even if the state is not given,
152
- // so that the import block can reference it.
153
- stateVal := cty .NilVal
154
- if val .Type ().HasAttribute ("state" ) {
155
- stateVal = val .GetAttr ("state" )
156
- }
157
- content , gDiags := GenerateResourceContents (resAddr , schema , pc , stateVal , true )
166
+
167
+ content , gDiags := GenerateResourceContents (resAddr , schema , pc , res .Config , true )
158
168
if gDiags .HasErrors () {
159
169
diags = diags .Append (gDiags )
160
170
continue
161
171
}
162
- ls .Body = content .Body
163
172
164
- idVal := val .GetAttr ("identity" )
165
- importContent , gDiags := generateImportBlock (resAddr , idSchema , pc , idVal )
173
+ resImport := ResourceImport {
174
+ Resource : Resource {
175
+ Addr : resAddr ,
176
+ Body : content .Body ,
177
+ },
178
+ }
179
+
180
+ importContent , gDiags := GenerateImportBlock (resAddr , idSchema , pc , res .Identity )
166
181
if gDiags .HasErrors () {
167
182
diags = diags .Append (gDiags )
168
183
continue
169
184
}
170
- ls .Import = bytes .TrimSpace (hclwrite .Format ([]byte (importContent )))
185
+
186
+ resImport .ImportBody = bytes .TrimSpace (hclwrite .Format (importContent .ImportBody ))
187
+ ret .Imports = append (ret .Imports , resImport )
171
188
}
172
189
173
- return & Resource {
174
- Results : ret ,
175
- Addr : addr ,
176
- }, diags
190
+ return ret , diags
177
191
}
178
192
179
- func generateImportBlock (addr addrs.AbsResourceInstance , idSchema * configschema.Object , pc addrs.LocalProviderConfig , identity cty.Value ) (string , tfdiags.Diagnostics ) {
193
+ func GenerateImportBlock (addr addrs.AbsResourceInstance , idSchema * configschema.Object , pc addrs.LocalProviderConfig , identity cty.Value ) (ResourceImport , tfdiags.Diagnostics ) {
180
194
var buf strings.Builder
181
195
var diags tfdiags.Diagnostics
182
196
@@ -190,7 +204,7 @@ func generateImportBlock(addr addrs.AbsResourceInstance, idSchema *configschema.
190
204
buf .WriteString ("}\n }\n " )
191
205
192
206
formatted := hclwrite .Format ([]byte (buf .String ()))
193
- return string ( formatted ) , diags
207
+ return ResourceImport { ImportBody : formatted } , diags
194
208
}
195
209
196
210
func writeConfigAttributes (addr addrs.AbsResourceInstance , buf * strings.Builder , attrs map [string ]* configschema.Attribute , indent int ) tfdiags.Diagnostics {
@@ -638,11 +652,11 @@ func hclEscapeString(str string) string {
638
652
return str
639
653
}
640
654
641
- // extractConfigFromState takes the state value of a resource, and filters the
655
+ // ExtractLegacyConfigFromState takes the state value of a resource, and filters the
642
656
// value down to what would be acceptable as a resource configuration value.
643
657
// This is used when the provider does not implement GenerateResourceConfig to
644
658
// create a suitable value.
645
- func extractConfigFromState (schema * configschema.Block , state cty.Value ) cty.Value {
659
+ func ExtractLegacyConfigFromState (schema * configschema.Block , state cty.Value ) cty.Value {
646
660
config , _ := cty .Transform (state , func (path cty.Path , v cty.Value ) (cty.Value , error ) {
647
661
if v .IsNull () {
648
662
return v , nil
0 commit comments