Skip to content

Commit 39db91f

Browse files
Terraform Provider Developerm-ildefons
authored andcommitted
fix(schema): clear all computed-only-illegal attributes in DataSourceSchemaWrap
DataSourceSchemaWrap makes data-source fields computed-only, but several attributes are illegal on computed-only fields and make the provider fail schema.InternalValidate at startup. Clear the complete set the SDK rejects (DiffSuppressFunc, DiffSuppressOnRefresh, ValidateDiagFunc, StateFunc, InputDefault, ...). DiffSuppressOnRefresh in particular must be cleared together with DiffSuppressFunc, otherwise InternalValidate fails with "cannot set DiffSuppressOnRefresh without DiffSuppressFunc". Add a test that exercises every cleared attribute (including in nested list and set Elem schemas), checks the name/namespace inputs stay configurable, and runs InternalValidate on the wrapped schema. Signed-off-by: Terraform Provider Developer <terraform@harvester.local>
1 parent e74162d commit 39db91f

2 files changed

Lines changed: 119 additions & 2 deletions

File tree

internal/util/schema.go

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,22 @@ func DataSourceSchemaWrap(s map[string]*schema.Schema) map[string]*schema.Schema
8282
}
8383
}
8484
}
85+
// Make the field computed-only and strip every attribute the
86+
// terraform-plugin-sdk rejects on a computed-only field (see
87+
// (*schema.Schema).InternalValidate); otherwise the data source fails
88+
// the provider's InternalValidate at startup.
8589
v.ForceNew = false
8690
v.Computed = true
87-
v.Default = nil
88-
v.DefaultFunc = nil
8991
v.Optional = false
9092
v.Required = false
93+
v.Default = nil
94+
v.DefaultFunc = nil
95+
v.InputDefault = ""
96+
v.StateFunc = nil
9197
v.ValidateFunc = nil
98+
v.ValidateDiagFunc = nil
99+
v.DiffSuppressFunc = nil
100+
v.DiffSuppressOnRefresh = false
92101
v.ConflictsWith = nil
93102
v.AtLeastOneOf = nil
94103
v.ExactlyOneOf = nil
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
package util
2+
3+
import (
4+
"testing"
5+
6+
"github.com/hashicorp/go-cty/cty"
7+
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
8+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
9+
10+
"github.com/harvester/terraform-provider-harvester/pkg/constants"
11+
)
12+
13+
func dummyDiffSuppress(_, _, _ string, _ *schema.ResourceData) bool { return false }
14+
func dummyValidateDiag(_ interface{}, _ cty.Path) diag.Diagnostics { return nil }
15+
func dummyValidate(_ interface{}, _ string) ([]string, []error) { return nil, nil }
16+
func dummyStateFunc(_ interface{}) string { return "" }
17+
18+
// TestDataSourceSchemaWrap verifies that DataSourceSchemaWrap makes fields
19+
// computed-only and clears every attribute the terraform-plugin-sdk rejects on a
20+
// computed-only field, recursively through nested Elem schemas (list and set),
21+
// while leaving the name/namespace query inputs configurable. The final
22+
// InternalValidate assertion reproduces the provider startup check that
23+
// previously failed.
24+
func TestDataSourceSchemaWrap(t *testing.T) {
25+
src := map[string]*schema.Schema{
26+
// name/namespace are the data-source query inputs and must stay configurable.
27+
constants.FieldCommonName: {
28+
Type: schema.TypeString,
29+
Required: true,
30+
},
31+
// a field exercising every attribute that is illegal on computed-only fields.
32+
"flag": {
33+
Type: schema.TypeString,
34+
Optional: true,
35+
Default: "x",
36+
InputDefault: "x",
37+
StateFunc: dummyStateFunc,
38+
DiffSuppressFunc: dummyDiffSuppress,
39+
DiffSuppressOnRefresh: true,
40+
ValidateDiagFunc: dummyValidateDiag,
41+
ConflictsWith: []string{"flag2"},
42+
},
43+
"flag2": {
44+
Type: schema.TypeString,
45+
Optional: true,
46+
ValidateFunc: dummyValidate,
47+
},
48+
// nested set-of-resource (depth 2) to exercise the recursion.
49+
"nested": {
50+
Type: schema.TypeList,
51+
Optional: true,
52+
MaxItems: 1,
53+
Elem: &schema.Resource{
54+
Schema: map[string]*schema.Schema{
55+
"inner_set": {
56+
Type: schema.TypeSet,
57+
Optional: true,
58+
Elem: &schema.Resource{
59+
Schema: map[string]*schema.Schema{
60+
"deep": {
61+
Type: schema.TypeInt,
62+
Optional: true,
63+
Default: 1,
64+
DiffSuppressFunc: dummyDiffSuppress,
65+
StateFunc: dummyStateFunc,
66+
},
67+
},
68+
},
69+
},
70+
},
71+
},
72+
},
73+
}
74+
75+
wrapped := DataSourceSchemaWrap(src)
76+
77+
// name/namespace stay configurable inputs (not forced to computed-only).
78+
if name := wrapped[constants.FieldCommonName]; !name.Required || name.Computed {
79+
t.Errorf("%s should remain a required input, got computed=%v required=%v", constants.FieldCommonName, name.Computed, name.Required)
80+
}
81+
82+
computedOnly := func(t *testing.T, f *schema.Schema, field string) {
83+
t.Helper()
84+
if !f.Computed || f.Optional || f.Required {
85+
t.Errorf("%s: want computed-only, got computed=%v optional=%v required=%v", field, f.Computed, f.Optional, f.Required)
86+
}
87+
if f.Default != nil || f.DefaultFunc != nil || f.InputDefault != "" || f.StateFunc != nil ||
88+
f.ValidateFunc != nil || f.ValidateDiagFunc != nil ||
89+
f.DiffSuppressFunc != nil || f.DiffSuppressOnRefresh ||
90+
f.ConflictsWith != nil || f.AtLeastOneOf != nil || f.ExactlyOneOf != nil || f.RequiredWith != nil ||
91+
f.MaxItems != 0 || f.MinItems != 0 {
92+
t.Errorf("%s: an attribute illegal on a computed-only field was not cleared: %+v", field, f)
93+
}
94+
}
95+
computedOnly(t, wrapped["flag"], "flag")
96+
computedOnly(t, wrapped["flag2"], "flag2")
97+
computedOnly(t, wrapped["nested"], "nested")
98+
innerSet := wrapped["nested"].Elem.(*schema.Resource).Schema["inner_set"]
99+
computedOnly(t, innerSet, "nested.inner_set")
100+
computedOnly(t, innerSet.Elem.(*schema.Resource).Schema["deep"], "nested.inner_set.deep")
101+
102+
// Definitive check: a data source built from the wrapped schema must pass the
103+
// provider's InternalValidate (the check that previously failed at startup).
104+
ds := &schema.Resource{Schema: wrapped}
105+
if err := ds.InternalValidate(nil, false); err != nil {
106+
t.Fatalf("InternalValidate failed for wrapped data-source schema: %v", err)
107+
}
108+
}

0 commit comments

Comments
 (0)