Skip to content

Commit e0c0dbe

Browse files
backport(fix): validate agent & resource metadata keys during plan (#282)
1 parent da10a93 commit e0c0dbe

File tree

5 files changed

+59
-22
lines changed

5 files changed

+59
-22
lines changed

Diff for: provider/agent.go

+26-12
Original file line numberDiff line numberDiff line change
@@ -43,18 +43,6 @@ func agentResource() *schema.Resource {
4343
}
4444
}
4545

46-
rawPlan := resourceData.GetRawPlan()
47-
items := rawPlan.GetAttr("metadata").AsValueSlice()
48-
itemKeys := map[string]struct{}{}
49-
for _, item := range items {
50-
key := valueAsString(item.GetAttr("key"))
51-
_, exists := itemKeys[key]
52-
if exists {
53-
return diag.FromErr(xerrors.Errorf("duplicate agent metadata key %q", key))
54-
}
55-
itemKeys[key] = struct{}{}
56-
}
57-
5846
return updateInitScript(resourceData, i)
5947
},
6048
ReadWithoutTimeout: func(ctx context.Context, resourceData *schema.ResourceData, i interface{}) diag.Diagnostics {
@@ -307,6 +295,32 @@ func agentResource() *schema.Resource {
307295
Optional: true,
308296
},
309297
},
298+
CustomizeDiff: func(ctx context.Context, rd *schema.ResourceDiff, i any) error {
299+
if !rd.HasChange("metadata") {
300+
return nil
301+
}
302+
303+
keys := map[string]bool{}
304+
metadata, ok := rd.Get("metadata").([]any)
305+
if !ok {
306+
return xerrors.Errorf("unexpected type %T for metadata, expected []any", rd.Get("metadata"))
307+
}
308+
for _, t := range metadata {
309+
obj, ok := t.(map[string]any)
310+
if !ok {
311+
return xerrors.Errorf("unexpected type %T for metadata, expected map[string]any", t)
312+
}
313+
key, ok := obj["key"].(string)
314+
if !ok {
315+
return xerrors.Errorf("unexpected type %T for metadata key, expected string", obj["key"])
316+
}
317+
if keys[key] {
318+
return xerrors.Errorf("duplicate agent metadata key %q", key)
319+
}
320+
keys[key] = true
321+
}
322+
return nil
323+
},
310324
}
311325
}
312326

Diff for: provider/agent_test.go

+4-3
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@ func TestAgent_MetadataDuplicateKeys(t *testing.T) {
286286
}
287287
`,
288288
ExpectError: regexp.MustCompile("duplicate agent metadata key"),
289+
PlanOnly: true,
289290
}},
290291
})
291292
}
@@ -313,7 +314,7 @@ func TestAgent_DisplayApps(t *testing.T) {
313314
web_terminal = false
314315
port_forwarding_helper = false
315316
ssh_helper = false
316-
}
317+
}
317318
}
318319
`,
319320
Check: func(state *terraform.State) error {
@@ -363,7 +364,7 @@ func TestAgent_DisplayApps(t *testing.T) {
363364
display_apps {
364365
vscode_insiders = true
365366
web_terminal = true
366-
}
367+
}
367368
}
368369
`,
369370
Check: func(state *terraform.State) error {
@@ -458,7 +459,7 @@ func TestAgent_DisplayApps(t *testing.T) {
458459
web_terminal = false
459460
port_forwarding_helper = false
460461
ssh_helper = false
461-
}
462+
}
462463
}
463464
`,
464465
ExpectError: regexp.MustCompile(`An argument named "fake_app" is not expected here.`),

Diff for: provider/metadata.go

+27
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"github.com/google/uuid"
88
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
99
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
10+
"golang.org/x/xerrors"
1011
)
1112

1213
func metadataResource() *schema.Resource {
@@ -111,5 +112,31 @@ func metadataResource() *schema.Resource {
111112
},
112113
},
113114
},
115+
CustomizeDiff: func(ctx context.Context, rd *schema.ResourceDiff, i interface{}) error {
116+
if !rd.HasChange("item") {
117+
return nil
118+
}
119+
120+
keys := map[string]bool{}
121+
metadata, ok := rd.Get("item").([]any)
122+
if !ok {
123+
return xerrors.Errorf("unexpected type %T for items, expected []any", rd.Get("metadata"))
124+
}
125+
for _, t := range metadata {
126+
obj, ok := t.(map[string]any)
127+
if !ok {
128+
return xerrors.Errorf("unexpected type %T for item, expected map[string]any", t)
129+
}
130+
key, ok := obj["key"].(string)
131+
if !ok {
132+
return xerrors.Errorf("unexpected type %T for items 'key' attribute, expected string", obj["key"])
133+
}
134+
if keys[key] {
135+
return xerrors.Errorf("duplicate resource metadata key %q", key)
136+
}
137+
keys[key] = true
138+
}
139+
return nil
140+
},
114141
}
115142
}

Diff for: provider/metadata_test.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,8 @@ func TestMetadataDuplicateKeys(t *testing.T) {
123123
}
124124
}
125125
`,
126-
ExpectError: regexp.MustCompile("duplicate metadata key"),
126+
PlanOnly: true,
127+
ExpectError: regexp.MustCompile("duplicate resource metadata key"),
127128
}},
128129
})
129130
}

Diff for: provider/provider.go

-6
Original file line numberDiff line numberDiff line change
@@ -105,14 +105,8 @@ func populateIsNull(resourceData *schema.ResourceData) (result interface{}, err
105105
items := rawPlan.GetAttr("item").AsValueSlice()
106106

107107
var resultItems []interface{}
108-
itemKeys := map[string]struct{}{}
109108
for _, item := range items {
110109
key := valueAsString(item.GetAttr("key"))
111-
_, exists := itemKeys[key]
112-
if exists {
113-
return nil, xerrors.Errorf("duplicate metadata key %q", key)
114-
}
115-
itemKeys[key] = struct{}{}
116110
resultItem := map[string]interface{}{
117111
"key": key,
118112
"value": valueAsString(item.GetAttr("value")),

0 commit comments

Comments
 (0)