Skip to content

Commit

Permalink
✨ reduce use of carvel validators (#1269)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>

* generic min/max verification functions

Signed-off-by: everettraven <[email protected]>

---------

Signed-off-by: everettraven <[email protected]>
  • Loading branch information
everettraven authored Nov 7, 2024
1 parent 8d535a6 commit ce86a65
Show file tree
Hide file tree
Showing 4 changed files with 1,179 additions and 34 deletions.
237 changes: 237 additions & 0 deletions internal/rukpak/preflights/crdupgradesafety/checks.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package crdupgradesafety

import (
"bytes"
"cmp"
"errors"
"fmt"
"reflect"
"slices"

kappcus "carvel.dev/kapp/pkg/kapp/crdupgradesafety"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/util/sets"
versionhelper "k8s.io/apimachinery/pkg/version"
)

Expand Down Expand Up @@ -70,3 +74,236 @@ func (c *ServedVersionValidator) Validate(old, new apiextensionsv1.CustomResourc
func (c *ServedVersionValidator) Name() string {
return "ServedVersionValidator"
}

type resetFunc func(diff kappcus.FieldDiff) kappcus.FieldDiff

func isHandled(diff kappcus.FieldDiff, reset resetFunc) bool {
diff = reset(diff)
return reflect.DeepEqual(diff.Old, diff.New)
}

func Enum(diff kappcus.FieldDiff) (bool, error) {
reset := func(diff kappcus.FieldDiff) kappcus.FieldDiff {
diff.Old.Enum = []apiextensionsv1.JSON{}
diff.New.Enum = []apiextensionsv1.JSON{}
return diff
}

oldEnums := sets.New[string]()
for _, json := range diff.Old.Enum {
oldEnums.Insert(string(json.Raw))
}

newEnums := sets.New[string]()
for _, json := range diff.New.Enum {
newEnums.Insert(string(json.Raw))
}
diffEnums := oldEnums.Difference(newEnums)
var err error

switch {
case oldEnums.Len() == 0 && newEnums.Len() > 0:
err = fmt.Errorf("enum constraints %v added when there were no restrictions previously", newEnums.UnsortedList())
case diffEnums.Len() > 0:
err = fmt.Errorf("enums %v removed from the set of previously allowed values", diffEnums.UnsortedList())
}

return isHandled(diff, reset), err
}

func Required(diff kappcus.FieldDiff) (bool, error) {
reset := func(diff kappcus.FieldDiff) kappcus.FieldDiff {
diff.Old.Required = []string{}
diff.New.Required = []string{}
return diff
}

oldRequired := sets.New(diff.Old.Required...)
newRequired := sets.New(diff.New.Required...)
diffRequired := newRequired.Difference(oldRequired)
var err error

if diffRequired.Len() > 0 {
err = fmt.Errorf("new required fields %v added", diffRequired.UnsortedList())
}

return isHandled(diff, reset), err
}

func maxVerification[T cmp.Ordered](older *T, newer *T) error {
var err error
switch {
case older == nil && newer != nil:
err = fmt.Errorf("constraint %v added when there were no restrictions previously", *newer)
case older != nil && newer != nil && *newer < *older:
err = fmt.Errorf("constraint decreased from %v to %v", *older, *newer)
}
return err
}

func Maximum(diff kappcus.FieldDiff) (bool, error) {
reset := func(diff kappcus.FieldDiff) kappcus.FieldDiff {
diff.Old.Maximum = nil
diff.New.Maximum = nil
return diff
}

err := maxVerification(diff.Old.Maximum, diff.New.Maximum)
if err != nil {
err = fmt.Errorf("maximum: %s", err.Error())
}

return isHandled(diff, reset), err
}

func MaxItems(diff kappcus.FieldDiff) (bool, error) {
reset := func(diff kappcus.FieldDiff) kappcus.FieldDiff {
diff.Old.MaxItems = nil
diff.New.MaxItems = nil
return diff
}

err := maxVerification(diff.Old.MaxItems, diff.New.MaxItems)
if err != nil {
err = fmt.Errorf("maxItems: %s", err.Error())
}

return isHandled(diff, reset), err
}

func MaxLength(diff kappcus.FieldDiff) (bool, error) {
reset := func(diff kappcus.FieldDiff) kappcus.FieldDiff {
diff.Old.MaxLength = nil
diff.New.MaxLength = nil
return diff
}

err := maxVerification(diff.Old.MaxLength, diff.New.MaxLength)
if err != nil {
err = fmt.Errorf("maxLength: %s", err.Error())
}

return isHandled(diff, reset), err
}

func MaxProperties(diff kappcus.FieldDiff) (bool, error) {
reset := func(diff kappcus.FieldDiff) kappcus.FieldDiff {
diff.Old.MaxProperties = nil
diff.New.MaxProperties = nil
return diff
}

err := maxVerification(diff.Old.MaxProperties, diff.New.MaxProperties)
if err != nil {
err = fmt.Errorf("maxProperties: %s", err.Error())
}

return isHandled(diff, reset), err
}

func minVerification[T cmp.Ordered](older *T, newer *T) error {
var err error
switch {
case older == nil && newer != nil:
err = fmt.Errorf("constraint %v added when there were no restrictions previously", *newer)
case older != nil && newer != nil && *newer > *older:
err = fmt.Errorf("constraint increased from %v to %v", *older, *newer)
}
return err
}

func Minimum(diff kappcus.FieldDiff) (bool, error) {
reset := func(diff kappcus.FieldDiff) kappcus.FieldDiff {
diff.Old.Minimum = nil
diff.New.Minimum = nil
return diff
}

err := minVerification(diff.Old.Minimum, diff.New.Minimum)
if err != nil {
err = fmt.Errorf("minimum: %s", err.Error())
}

return isHandled(diff, reset), err
}

func MinItems(diff kappcus.FieldDiff) (bool, error) {
reset := func(diff kappcus.FieldDiff) kappcus.FieldDiff {
diff.Old.MinItems = nil
diff.New.MinItems = nil
return diff
}

err := minVerification(diff.Old.MinItems, diff.New.MinItems)
if err != nil {
err = fmt.Errorf("minItems: %s", err.Error())
}

return isHandled(diff, reset), err
}

func MinLength(diff kappcus.FieldDiff) (bool, error) {
reset := func(diff kappcus.FieldDiff) kappcus.FieldDiff {
diff.Old.MinLength = nil
diff.New.MinLength = nil
return diff
}

err := minVerification(diff.Old.MinLength, diff.New.MinLength)
if err != nil {
err = fmt.Errorf("minLength: %s", err.Error())
}

return isHandled(diff, reset), err
}

func MinProperties(diff kappcus.FieldDiff) (bool, error) {
reset := func(diff kappcus.FieldDiff) kappcus.FieldDiff {
diff.Old.MinProperties = nil
diff.New.MinProperties = nil
return diff
}

err := minVerification(diff.Old.MinProperties, diff.New.MinProperties)
if err != nil {
err = fmt.Errorf("minProperties: %s", err.Error())
}

return isHandled(diff, reset), err
}

func Default(diff kappcus.FieldDiff) (bool, error) {
reset := func(diff kappcus.FieldDiff) kappcus.FieldDiff {
diff.Old.Default = nil
diff.New.Default = nil
return diff
}

var err error

switch {
case diff.Old.Default == nil && diff.New.Default != nil:
err = fmt.Errorf("default value %q added when there was no default previously", string(diff.New.Default.Raw))
case diff.Old.Default != nil && diff.New.Default == nil:
err = fmt.Errorf("default value %q removed", string(diff.Old.Default.Raw))
case diff.Old.Default != nil && diff.New.Default != nil && !bytes.Equal(diff.Old.Default.Raw, diff.New.Default.Raw):
err = fmt.Errorf("default value changed from %q to %q", string(diff.Old.Default.Raw), string(diff.New.Default.Raw))
}

return isHandled(diff, reset), err
}

func Type(diff kappcus.FieldDiff) (bool, error) {
reset := func(diff kappcus.FieldDiff) kappcus.FieldDiff {
diff.Old.Type = ""
diff.New.Type = ""
return diff
}

var err error
if diff.Old.Type != diff.New.Type {
err = fmt.Errorf("type changed from %q to %q", diff.Old.Type, diff.New.Type)
}

return isHandled(diff, reset), err
}
Loading

0 comments on commit ce86a65

Please sign in to comment.