1
16
17 package customresource
18
19 import (
20 "context"
21 "reflect"
22 "testing"
23
24 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
25 apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
26 structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
27 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
28 "k8s.io/apimachinery/pkg/runtime/schema"
29 "sigs.k8s.io/yaml"
30 )
31
32 func TestPrepareForUpdate(t *testing.T) {
33 strategy := statusStrategy{}
34 tcs := []struct {
35 old *unstructured.Unstructured
36 obj *unstructured.Unstructured
37 expected *unstructured.Unstructured
38 }{
39 {
40
41 old: &unstructured.Unstructured{
42 Object: map[string]interface{}{
43 "spec": "old",
44 },
45 },
46 obj: &unstructured.Unstructured{
47 Object: map[string]interface{}{
48 "spec": "new",
49 },
50 },
51 expected: &unstructured.Unstructured{
52 Object: map[string]interface{}{
53 "spec": "old",
54 },
55 },
56 },
57 {
58
59 old: &unstructured.Unstructured{
60 Object: map[string]interface{}{
61 "spec": "old",
62 },
63 },
64 obj: &unstructured.Unstructured{
65 Object: map[string]interface{}{
66 "new": "new",
67 },
68 },
69 expected: &unstructured.Unstructured{
70 Object: map[string]interface{}{
71 "spec": "old",
72 },
73 },
74 },
75 {
76
77 old: &unstructured.Unstructured{
78 Object: map[string]interface{}{
79 "spec": "old",
80 "status": "old",
81 },
82 },
83 obj: &unstructured.Unstructured{
84 Object: map[string]interface{}{
85 "spec": "old",
86 },
87 },
88 expected: &unstructured.Unstructured{
89 Object: map[string]interface{}{
90 "spec": "old",
91 },
92 },
93 },
94 {
95
96 old: &unstructured.Unstructured{
97 Object: map[string]interface{}{
98 "spec": "old",
99 "status": "old",
100 },
101 },
102 obj: &unstructured.Unstructured{
103 Object: map[string]interface{}{
104 "status": "new",
105 },
106 },
107 expected: &unstructured.Unstructured{
108 Object: map[string]interface{}{
109 "spec": "old",
110 "status": "new",
111 },
112 },
113 },
114 {
115
116 old: &unstructured.Unstructured{
117 Object: map[string]interface{}{
118 "spec": "old",
119 "status": "old",
120 },
121 },
122 obj: &unstructured.Unstructured{
123 Object: map[string]interface{}{
124 "spec": "new",
125 "new": "new",
126 "status": "new",
127 },
128 },
129 expected: &unstructured.Unstructured{
130 Object: map[string]interface{}{
131 "spec": "old",
132 "status": "new",
133 },
134 },
135 },
136 }
137 for index, tc := range tcs {
138 strategy.PrepareForUpdate(context.TODO(), tc.obj, tc.old)
139 if !reflect.DeepEqual(tc.obj, tc.expected) {
140 t.Errorf("test %d failed: expected: %v, got %v", index, tc.expected, tc.obj)
141 }
142 }
143 }
144
145 const listTypeResourceSchema = `
146 apiVersion: apiextensions.k8s.io/v1
147 kind: CustomResourceDefinition
148 metadata:
149 name: foos.test
150 spec:
151 group: test
152 names:
153 kind: Foo
154 listKind: FooList
155 plural: foos
156 singular: foo
157 scope: Cluster
158 versions:
159 - name: v1
160 schema:
161 openAPIV3Schema:
162 type: object
163 properties:
164 numArray:
165 type: array
166 x-kubernetes-list-type: set
167 items:
168 type: object
169 served: true
170 storage: true
171 - name: v2
172 schema:
173 openAPIV3Schema:
174 type: object
175 properties:
176 numArray2:
177 type: array
178 `
179
180 func TestStatusStrategyValidateUpdate(t *testing.T) {
181 crdV1 := &apiextensionsv1.CustomResourceDefinition{}
182 err := yaml.Unmarshal([]byte(listTypeResourceSchema), &crdV1)
183 if err != nil {
184 t.Fatalf("unexpected decoding error: %v", err)
185 }
186 t.Logf("crd v1 details: %v", crdV1)
187 crd := &apiextensions.CustomResourceDefinition{}
188 if err = apiextensionsv1.Convert_v1_CustomResourceDefinition_To_apiextensions_CustomResourceDefinition(crdV1, crd, nil); err != nil {
189 t.Fatalf("unexpected convert error: %v", err)
190 }
191 t.Logf("crd details: %v", crd)
192
193 strategy := statusStrategy{}
194 kind := schema.GroupVersionKind{
195 Version: crd.Spec.Versions[0].Name,
196 Kind: crd.Spec.Names.Kind,
197 Group: crd.Spec.Group,
198 }
199 strategy.customResourceStrategy.validator.kind = kind
200 ss, _ := structuralschema.NewStructural(crd.Spec.Versions[0].Schema.OpenAPIV3Schema)
201 strategy.structuralSchema = ss
202
203 ctx := context.TODO()
204
205 tcs := []struct {
206 name string
207 old *unstructured.Unstructured
208 obj *unstructured.Unstructured
209 isValid bool
210 }{
211 {
212 name: "bothValid",
213 old: &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "test/v1", "kind": "Foo", "numArray": []interface{}{1, 2}}},
214 obj: &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "test/v1", "kind": "Foo", "numArray": []interface{}{1, 3}, "metadata": map[string]interface{}{"resourceVersion": "1"}}},
215 isValid: true,
216 },
217 {
218 name: "change to invalid",
219 old: &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "test/v1", "kind": "Foo", "spec": "old", "numArray": []interface{}{1, 2}}},
220 obj: &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "test/v1", "kind": "Foo", "spec": "new", "numArray": []interface{}{1, 1}, "metadata": map[string]interface{}{"resourceVersion": "1"}}},
221 isValid: false,
222 },
223 {
224 name: "change to valid",
225 old: &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "test/v1", "kind": "Foo", "spec": "new", "numArray": []interface{}{1, 1}}},
226 obj: &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "test/v1", "kind": "Foo", "spec": "old", "numArray": []interface{}{1, 2}, "metadata": map[string]interface{}{"resourceVersion": "1"}}},
227 isValid: true,
228 },
229 {
230 name: "keeps invalid",
231 old: &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "test/v1", "kind": "Foo", "spec": "new", "numArray": []interface{}{1, 1}}},
232 obj: &unstructured.Unstructured{Object: map[string]interface{}{"apiVersion": "test/v1", "kind": "Foo", "spec": "old", "numArray": []interface{}{1, 1}, "metadata": map[string]interface{}{"resourceVersion": "1"}}},
233 isValid: true,
234 },
235 }
236
237 for _, tc := range tcs {
238 errs := strategy.ValidateUpdate(ctx, tc.obj, tc.old)
239 if tc.isValid && len(errs) > 0 {
240 t.Errorf("%v: unexpected error: %v", tc.name, errs)
241 }
242 if !tc.isValid && len(errs) == 0 {
243 t.Errorf("%v: unexpected non-error", tc.name)
244 }
245 }
246 }
247
View as plain text