Skip to content

Commit 8c1d23d

Browse files
authored
Merge pull request #5 from griffindvs/gcd/removed-field
fix: detect removed fields without nil deref error
2 parents f1ade27 + 0dfa793 commit 8c1d23d

File tree

2 files changed

+165
-0
lines changed

2 files changed

+165
-0
lines changed

pkg/validations/validators/version/util.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ func FlattenedCRDVersionDiff(old, new map[string]*apiextensionsv1.JSONSchemaProp
5353
// create a diff entry with the new value being empty
5454
if !ok {
5555
diffMap[prop] = property.NewDiff(oldSchemaCopy, &apiextensionsv1.JSONSchemaProps{})
56+
// Continue as there is no newSchema to copy and evaluate for this prop.
57+
continue
5658
}
5759

5860
// Do the same copy and unset logic for the new schema properties
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
package version
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
7+
"github.com/stretchr/testify/require"
8+
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
9+
)
10+
11+
func TestFlattenedCRDVersionDiff(t *testing.T) {
12+
type testcase struct {
13+
name string
14+
old apiextensionsv1.CustomResourceDefinitionVersion
15+
new apiextensionsv1.CustomResourceDefinitionVersion
16+
diffKey string
17+
oldDiff apiextensionsv1.JSONSchemaProps
18+
newDiff apiextensionsv1.JSONSchemaProps
19+
}
20+
21+
for _, tc := range []testcase{
22+
{
23+
name: "removed field",
24+
old: apiextensionsv1.CustomResourceDefinitionVersion{
25+
Name: "v1alpha1",
26+
Served: true,
27+
Storage: true,
28+
Schema: &apiextensionsv1.CustomResourceValidation{
29+
OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{
30+
Properties: map[string]apiextensionsv1.JSONSchemaProps{
31+
"foo": {
32+
Type: "string",
33+
},
34+
"bar": {
35+
Type: "string",
36+
},
37+
},
38+
},
39+
},
40+
},
41+
new: apiextensionsv1.CustomResourceDefinitionVersion{
42+
Name: "v1alpha1",
43+
Served: true,
44+
Storage: true,
45+
Schema: &apiextensionsv1.CustomResourceValidation{
46+
OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{
47+
Properties: map[string]apiextensionsv1.JSONSchemaProps{
48+
"foo": {
49+
Type: "string",
50+
},
51+
// Removed "bar".
52+
},
53+
},
54+
},
55+
},
56+
// The field that has changed is <Root>.bar
57+
diffKey: "^.bar",
58+
// Removed field type should be included in the old.
59+
oldDiff: apiextensionsv1.JSONSchemaProps{
60+
Type: "string",
61+
},
62+
// The new schema in the diff should be equal to a zero schema because the field was removed.
63+
newDiff: apiextensionsv1.JSONSchemaProps{},
64+
},
65+
{
66+
name: "added required field",
67+
old: apiextensionsv1.CustomResourceDefinitionVersion{
68+
Name: "v1alpha1",
69+
Served: true,
70+
Storage: true,
71+
Schema: &apiextensionsv1.CustomResourceValidation{
72+
OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{
73+
Properties: map[string]apiextensionsv1.JSONSchemaProps{
74+
"foo": {
75+
Type: "string",
76+
},
77+
},
78+
},
79+
},
80+
},
81+
new: apiextensionsv1.CustomResourceDefinitionVersion{
82+
Name: "v1alpha1",
83+
Served: true,
84+
Storage: true,
85+
Schema: &apiextensionsv1.CustomResourceValidation{
86+
OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{
87+
Properties: map[string]apiextensionsv1.JSONSchemaProps{
88+
"foo": {
89+
Type: "string",
90+
},
91+
// Added field "bar".
92+
"bar": {
93+
Type: "string",
94+
},
95+
},
96+
Required: []string{"bar"},
97+
},
98+
},
99+
},
100+
// Added a new field at root (^).
101+
diffKey: "^",
102+
// Field added in new schema required.
103+
oldDiff: apiextensionsv1.JSONSchemaProps{},
104+
newDiff: apiextensionsv1.JSONSchemaProps{
105+
Required: []string{"bar"},
106+
},
107+
},
108+
{
109+
name: "no change",
110+
old: apiextensionsv1.CustomResourceDefinitionVersion{
111+
Name: "v1alpha1",
112+
Served: true,
113+
Storage: true,
114+
Schema: &apiextensionsv1.CustomResourceValidation{
115+
OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{
116+
Properties: map[string]apiextensionsv1.JSONSchemaProps{
117+
"foo": {
118+
Type: "string",
119+
},
120+
"bar": {
121+
Type: "string",
122+
},
123+
},
124+
},
125+
},
126+
},
127+
new: apiextensionsv1.CustomResourceDefinitionVersion{
128+
Name: "v1alpha1",
129+
Served: true,
130+
Storage: true,
131+
Schema: &apiextensionsv1.CustomResourceValidation{
132+
OpenAPIV3Schema: &apiextensionsv1.JSONSchemaProps{
133+
Properties: map[string]apiextensionsv1.JSONSchemaProps{
134+
"foo": {
135+
Type: "string",
136+
},
137+
"bar": {
138+
Type: "string",
139+
},
140+
},
141+
},
142+
},
143+
},
144+
// No fields have changed, so no diff.
145+
},
146+
} {
147+
t.Run(tc.name, func(t *testing.T) {
148+
oldFlattened := FlattenCRDVersion(tc.old)
149+
newFlattened := FlattenCRDVersion(tc.new)
150+
151+
diffs := FlattenedCRDVersionDiff(oldFlattened, newFlattened)
152+
153+
if tc.diffKey != "" {
154+
// If a diff is expected, so the result should be validated.
155+
require.True(t, reflect.DeepEqual(*diffs[tc.diffKey].Old(), tc.oldDiff))
156+
require.True(t, reflect.DeepEqual(*diffs[tc.diffKey].New(), tc.newDiff))
157+
} else {
158+
// If a diff is not expected, so there should not be one.
159+
require.Len(t, diffs, 0)
160+
}
161+
})
162+
}
163+
}

0 commit comments

Comments
 (0)