Skip to content

Commit ce86a65

Browse files
authored
✨ reduce use of carvel validators (#1269)
* reduce use of carvel validators by implementing our own. This gives us more freedom in maing changes and fixing bugs in the actual validations instead of having to push all fixes up to the carvel/kapp project. Signed-off-by: everettraven <everettraven@gmail.com> * generic min/max verification functions Signed-off-by: everettraven <everettraven@gmail.com> --------- Signed-off-by: everettraven <everettraven@gmail.com>
1 parent 8d535a6 commit ce86a65

File tree

4 files changed

+1179
-34
lines changed

4 files changed

+1179
-34
lines changed

internal/rukpak/preflights/crdupgradesafety/checks.go

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
package crdupgradesafety
22

33
import (
4+
"bytes"
5+
"cmp"
46
"errors"
57
"fmt"
8+
"reflect"
69
"slices"
710

811
kappcus "carvel.dev/kapp/pkg/kapp/crdupgradesafety"
912
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
13+
"k8s.io/apimachinery/pkg/util/sets"
1014
versionhelper "k8s.io/apimachinery/pkg/version"
1115
)
1216

@@ -70,3 +74,236 @@ func (c *ServedVersionValidator) Validate(old, new apiextensionsv1.CustomResourc
7074
func (c *ServedVersionValidator) Name() string {
7175
return "ServedVersionValidator"
7276
}
77+
78+
type resetFunc func(diff kappcus.FieldDiff) kappcus.FieldDiff
79+
80+
func isHandled(diff kappcus.FieldDiff, reset resetFunc) bool {
81+
diff = reset(diff)
82+
return reflect.DeepEqual(diff.Old, diff.New)
83+
}
84+
85+
func Enum(diff kappcus.FieldDiff) (bool, error) {
86+
reset := func(diff kappcus.FieldDiff) kappcus.FieldDiff {
87+
diff.Old.Enum = []apiextensionsv1.JSON{}
88+
diff.New.Enum = []apiextensionsv1.JSON{}
89+
return diff
90+
}
91+
92+
oldEnums := sets.New[string]()
93+
for _, json := range diff.Old.Enum {
94+
oldEnums.Insert(string(json.Raw))
95+
}
96+
97+
newEnums := sets.New[string]()
98+
for _, json := range diff.New.Enum {
99+
newEnums.Insert(string(json.Raw))
100+
}
101+
diffEnums := oldEnums.Difference(newEnums)
102+
var err error
103+
104+
switch {
105+
case oldEnums.Len() == 0 && newEnums.Len() > 0:
106+
err = fmt.Errorf("enum constraints %v added when there were no restrictions previously", newEnums.UnsortedList())
107+
case diffEnums.Len() > 0:
108+
err = fmt.Errorf("enums %v removed from the set of previously allowed values", diffEnums.UnsortedList())
109+
}
110+
111+
return isHandled(diff, reset), err
112+
}
113+
114+
func Required(diff kappcus.FieldDiff) (bool, error) {
115+
reset := func(diff kappcus.FieldDiff) kappcus.FieldDiff {
116+
diff.Old.Required = []string{}
117+
diff.New.Required = []string{}
118+
return diff
119+
}
120+
121+
oldRequired := sets.New(diff.Old.Required...)
122+
newRequired := sets.New(diff.New.Required...)
123+
diffRequired := newRequired.Difference(oldRequired)
124+
var err error
125+
126+
if diffRequired.Len() > 0 {
127+
err = fmt.Errorf("new required fields %v added", diffRequired.UnsortedList())
128+
}
129+
130+
return isHandled(diff, reset), err
131+
}
132+
133+
func maxVerification[T cmp.Ordered](older *T, newer *T) error {
134+
var err error
135+
switch {
136+
case older == nil && newer != nil:
137+
err = fmt.Errorf("constraint %v added when there were no restrictions previously", *newer)
138+
case older != nil && newer != nil && *newer < *older:
139+
err = fmt.Errorf("constraint decreased from %v to %v", *older, *newer)
140+
}
141+
return err
142+
}
143+
144+
func Maximum(diff kappcus.FieldDiff) (bool, error) {
145+
reset := func(diff kappcus.FieldDiff) kappcus.FieldDiff {
146+
diff.Old.Maximum = nil
147+
diff.New.Maximum = nil
148+
return diff
149+
}
150+
151+
err := maxVerification(diff.Old.Maximum, diff.New.Maximum)
152+
if err != nil {
153+
err = fmt.Errorf("maximum: %s", err.Error())
154+
}
155+
156+
return isHandled(diff, reset), err
157+
}
158+
159+
func MaxItems(diff kappcus.FieldDiff) (bool, error) {
160+
reset := func(diff kappcus.FieldDiff) kappcus.FieldDiff {
161+
diff.Old.MaxItems = nil
162+
diff.New.MaxItems = nil
163+
return diff
164+
}
165+
166+
err := maxVerification(diff.Old.MaxItems, diff.New.MaxItems)
167+
if err != nil {
168+
err = fmt.Errorf("maxItems: %s", err.Error())
169+
}
170+
171+
return isHandled(diff, reset), err
172+
}
173+
174+
func MaxLength(diff kappcus.FieldDiff) (bool, error) {
175+
reset := func(diff kappcus.FieldDiff) kappcus.FieldDiff {
176+
diff.Old.MaxLength = nil
177+
diff.New.MaxLength = nil
178+
return diff
179+
}
180+
181+
err := maxVerification(diff.Old.MaxLength, diff.New.MaxLength)
182+
if err != nil {
183+
err = fmt.Errorf("maxLength: %s", err.Error())
184+
}
185+
186+
return isHandled(diff, reset), err
187+
}
188+
189+
func MaxProperties(diff kappcus.FieldDiff) (bool, error) {
190+
reset := func(diff kappcus.FieldDiff) kappcus.FieldDiff {
191+
diff.Old.MaxProperties = nil
192+
diff.New.MaxProperties = nil
193+
return diff
194+
}
195+
196+
err := maxVerification(diff.Old.MaxProperties, diff.New.MaxProperties)
197+
if err != nil {
198+
err = fmt.Errorf("maxProperties: %s", err.Error())
199+
}
200+
201+
return isHandled(diff, reset), err
202+
}
203+
204+
func minVerification[T cmp.Ordered](older *T, newer *T) error {
205+
var err error
206+
switch {
207+
case older == nil && newer != nil:
208+
err = fmt.Errorf("constraint %v added when there were no restrictions previously", *newer)
209+
case older != nil && newer != nil && *newer > *older:
210+
err = fmt.Errorf("constraint increased from %v to %v", *older, *newer)
211+
}
212+
return err
213+
}
214+
215+
func Minimum(diff kappcus.FieldDiff) (bool, error) {
216+
reset := func(diff kappcus.FieldDiff) kappcus.FieldDiff {
217+
diff.Old.Minimum = nil
218+
diff.New.Minimum = nil
219+
return diff
220+
}
221+
222+
err := minVerification(diff.Old.Minimum, diff.New.Minimum)
223+
if err != nil {
224+
err = fmt.Errorf("minimum: %s", err.Error())
225+
}
226+
227+
return isHandled(diff, reset), err
228+
}
229+
230+
func MinItems(diff kappcus.FieldDiff) (bool, error) {
231+
reset := func(diff kappcus.FieldDiff) kappcus.FieldDiff {
232+
diff.Old.MinItems = nil
233+
diff.New.MinItems = nil
234+
return diff
235+
}
236+
237+
err := minVerification(diff.Old.MinItems, diff.New.MinItems)
238+
if err != nil {
239+
err = fmt.Errorf("minItems: %s", err.Error())
240+
}
241+
242+
return isHandled(diff, reset), err
243+
}
244+
245+
func MinLength(diff kappcus.FieldDiff) (bool, error) {
246+
reset := func(diff kappcus.FieldDiff) kappcus.FieldDiff {
247+
diff.Old.MinLength = nil
248+
diff.New.MinLength = nil
249+
return diff
250+
}
251+
252+
err := minVerification(diff.Old.MinLength, diff.New.MinLength)
253+
if err != nil {
254+
err = fmt.Errorf("minLength: %s", err.Error())
255+
}
256+
257+
return isHandled(diff, reset), err
258+
}
259+
260+
func MinProperties(diff kappcus.FieldDiff) (bool, error) {
261+
reset := func(diff kappcus.FieldDiff) kappcus.FieldDiff {
262+
diff.Old.MinProperties = nil
263+
diff.New.MinProperties = nil
264+
return diff
265+
}
266+
267+
err := minVerification(diff.Old.MinProperties, diff.New.MinProperties)
268+
if err != nil {
269+
err = fmt.Errorf("minProperties: %s", err.Error())
270+
}
271+
272+
return isHandled(diff, reset), err
273+
}
274+
275+
func Default(diff kappcus.FieldDiff) (bool, error) {
276+
reset := func(diff kappcus.FieldDiff) kappcus.FieldDiff {
277+
diff.Old.Default = nil
278+
diff.New.Default = nil
279+
return diff
280+
}
281+
282+
var err error
283+
284+
switch {
285+
case diff.Old.Default == nil && diff.New.Default != nil:
286+
err = fmt.Errorf("default value %q added when there was no default previously", string(diff.New.Default.Raw))
287+
case diff.Old.Default != nil && diff.New.Default == nil:
288+
err = fmt.Errorf("default value %q removed", string(diff.Old.Default.Raw))
289+
case diff.Old.Default != nil && diff.New.Default != nil && !bytes.Equal(diff.Old.Default.Raw, diff.New.Default.Raw):
290+
err = fmt.Errorf("default value changed from %q to %q", string(diff.Old.Default.Raw), string(diff.New.Default.Raw))
291+
}
292+
293+
return isHandled(diff, reset), err
294+
}
295+
296+
func Type(diff kappcus.FieldDiff) (bool, error) {
297+
reset := func(diff kappcus.FieldDiff) kappcus.FieldDiff {
298+
diff.Old.Type = ""
299+
diff.New.Type = ""
300+
return diff
301+
}
302+
303+
var err error
304+
if diff.Old.Type != diff.New.Type {
305+
err = fmt.Errorf("type changed from %q to %q", diff.Old.Type, diff.New.Type)
306+
}
307+
308+
return isHandled(diff, reset), err
309+
}

0 commit comments

Comments
 (0)