@@ -33,6 +33,18 @@ type agentAppAttributes struct {
33
33
RelativePath bool `mapstructure:"relative_path"`
34
34
}
35
35
36
+ // A mapping of attributes on the "coder_metadata" resource.
37
+ type metadataAttributes struct {
38
+ ResourceID string `mapstructure:"resource_id"`
39
+ Items []metadataItem `mapstructure:"pair"`
40
+ }
41
+
42
+ type metadataItem struct {
43
+ Key string `mapstructure:"key"`
44
+ Value string `mapstructure:"value"`
45
+ Sensitive bool `mapstructure:"sensitive"`
46
+ }
47
+
36
48
// ConvertResources consumes Terraform state and a GraphViz representation produced by
37
49
// `terraform graph` to produce resources consumable by Coder.
38
50
func ConvertResources (module * tfjson.StateModule , rawGraph string ) ([]* proto.Resource , error ) {
@@ -48,16 +60,30 @@ func ConvertResources(module *tfjson.StateModule, rawGraph string) ([]*proto.Res
48
60
resources := make ([]* proto.Resource , 0 )
49
61
resourceAgents := map [string ][]* proto.Agent {}
50
62
51
- // Indexes Terraform resources by it's label. The label
52
- // is what "terraform graph" uses to reference nodes.
63
+ // Indexes Terraform resources by their label and ID.
64
+ // The label is what "terraform graph" uses to reference nodes, and the ID
65
+ // is used by "coder_metadata" resources to refer to their targets. (The ID
66
+ // field is only available when reading a state file, and not when reading a
67
+ // plan file.)
53
68
tfResourceByLabel := map [string ]* tfjson.StateResource {}
69
+ resourceLabelByID := map [string ]string {}
54
70
var findTerraformResources func (mod * tfjson.StateModule )
55
71
findTerraformResources = func (mod * tfjson.StateModule ) {
56
72
for _ , module := range mod .ChildModules {
57
73
findTerraformResources (module )
58
74
}
59
75
for _ , resource := range mod .Resources {
60
- tfResourceByLabel [convertAddressToLabel (resource .Address )] = resource
76
+ label := convertAddressToLabel (resource .Address )
77
+ // index by label
78
+ tfResourceByLabel [label ] = resource
79
+ // index by ID, if it exists
80
+ id , ok := resource .AttributeValues ["id" ]
81
+ if ok {
82
+ idString , ok := id .(string )
83
+ if ok {
84
+ resourceLabelByID [idString ] = label
85
+ }
86
+ }
61
87
}
62
88
}
63
89
findTerraformResources (module )
@@ -205,23 +231,58 @@ func ConvertResources(module *tfjson.StateModule, rawGraph string) ([]*proto.Res
205
231
}
206
232
}
207
233
234
+ // Associate metadata blocks with resources.
235
+ resourceMetadata := map [string ][]* proto.Resource_Metadata {}
236
+ for label , resource := range tfResourceByLabel {
237
+ if resource .Type != "coder_metadata" {
238
+ continue
239
+ }
240
+ var attrs metadataAttributes
241
+ err = mapstructure .Decode (resource .AttributeValues , & attrs )
242
+ if err != nil {
243
+ return nil , xerrors .Errorf ("decode metadata attributes: %w" , err )
244
+ }
245
+
246
+ if attrs .ResourceID == "" {
247
+ // TODO: detect this as an error
248
+ // At plan time, change.after_unknown.resource_id should be "true".
249
+ // At provision time, values.resource_id should be set.
250
+ continue
251
+ }
252
+ targetLabel , ok := resourceLabelByID [attrs .ResourceID ]
253
+ if ! ok {
254
+ return nil , xerrors .Errorf ("attribute %s.resource_id = %q does not refer to a valid resource" , label , attrs .ResourceID )
255
+ }
256
+
257
+ for _ , item := range attrs .Items {
258
+ resourceMetadata [targetLabel ] = append (resourceMetadata [targetLabel ],
259
+ & proto.Resource_Metadata {
260
+ Key : item .Key ,
261
+ Value : item .Value ,
262
+ Sensitive : item .Sensitive ,
263
+ })
264
+ }
265
+ }
266
+
208
267
for _ , resource := range tfResourceByLabel {
209
268
if resource .Mode == tfjson .DataResourceMode {
210
269
continue
211
270
}
212
- if resource .Type == "coder_agent" || resource .Type == "coder_agent_instance" || resource .Type == "coder_app" {
271
+ if resource .Type == "coder_agent" || resource .Type == "coder_agent_instance" || resource .Type == "coder_app" || resource . Type == "coder_metadata" {
213
272
continue
214
273
}
274
+ label := convertAddressToLabel (resource .Address )
215
275
216
- agents , exists := resourceAgents [convertAddressToLabel ( resource . Address ) ]
276
+ agents , exists := resourceAgents [label ]
217
277
if exists {
218
278
applyAutomaticInstanceID (resource , agents )
219
279
}
220
280
221
281
resources = append (resources , & proto.Resource {
222
- Name : resource .Name ,
223
- Type : resource .Type ,
224
- Agents : agents ,
282
+ Name : resource .Name ,
283
+ Type : resource .Type ,
284
+ Agents : agents ,
285
+ Metadata : resourceMetadata [label ],
225
286
})
226
287
}
227
288
0 commit comments