Skip to content

Commit

Permalink
reduce use of carvel validators
Browse files Browse the repository at this point in the history
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]>
  • Loading branch information
everettraven committed Sep 13, 2024
1 parent 188858c commit 00f1f80
Show file tree
Hide file tree
Showing 4 changed files with 1,188 additions and 34 deletions.
246 changes: 246 additions & 0 deletions internal/rukpak/preflights/crdupgradesafety/checks.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package crdupgradesafety

import (
"bytes"
"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 +73,246 @@ 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 Maximum(diff kappcus.FieldDiff) (bool, error) {
reset := func(diff kappcus.FieldDiff) kappcus.FieldDiff {
diff.Old.Maximum = nil
diff.New.Maximum = nil
return diff
}

var err error

switch {
case diff.Old.Maximum == nil && diff.New.Maximum != nil:
err = fmt.Errorf("maximum constraint %v added when there were no restrictions previously", *diff.New.Maximum)
case diff.Old.Maximum != nil && diff.New.Maximum != nil && *diff.New.Maximum < *diff.Old.Maximum:
err = fmt.Errorf("maximum constraint decreased from %v to %v", *diff.Old.Maximum, *diff.New.Maximum)
}

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
}

var err error

switch {
case diff.Old.MaxItems == nil && diff.New.MaxItems != nil:
err = fmt.Errorf("maxItems constraint %v added when there were no restrictions previously", *diff.New.MaxItems)
case diff.Old.MaxItems != nil && diff.New.MaxItems != nil && *diff.New.MaxItems < *diff.Old.MaxItems:
err = fmt.Errorf("maxItems constraint decreased from %v to %v", *diff.Old.MaxItems, *diff.New.MaxItems)
}

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
}

var err error

switch {
case diff.Old.MaxLength == nil && diff.New.MaxLength != nil:
err = fmt.Errorf("maxLength constraint %v added when there were no restrictions previously", *diff.New.MaxLength)
case diff.Old.MaxLength != nil && diff.New.MaxLength != nil && *diff.New.MaxLength < *diff.Old.MaxLength:
err = fmt.Errorf("maxLength constraint decreased from %v to %v", *diff.Old.MaxLength, *diff.New.MaxLength)
}

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
}

var err error

switch {
case diff.Old.MaxProperties == nil && diff.New.MaxProperties != nil:
err = fmt.Errorf("maxProperties constraint %v added when there were no restrictions previously", *diff.New.MaxProperties)
case diff.Old.MaxProperties != nil && diff.New.MaxProperties != nil && *diff.New.MaxProperties < *diff.Old.MaxProperties:
err = fmt.Errorf("maxProperties constraint decreased from %v to %v", *diff.Old.MaxProperties, *diff.New.MaxProperties)
}

return isHandled(diff, reset), 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
}

var err error

switch {
case diff.Old.Minimum == nil && diff.New.Minimum != nil:
err = fmt.Errorf("minimum constraint %v added when there were no restrictions previously", *diff.New.Minimum)
case diff.Old.Minimum != nil && diff.New.Minimum != nil && *diff.New.Minimum > *diff.Old.Minimum:
err = fmt.Errorf("minimum constraint increased from %v to %v", *diff.Old.Minimum, *diff.New.Minimum)
}

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
}

var err error

switch {
case diff.Old.MinItems == nil && diff.New.MinItems != nil:
err = fmt.Errorf("minItems constraint %v added when there were no restrictions previously", *diff.New.MinItems)
case diff.Old.MinItems != nil && diff.New.MinItems != nil && *diff.New.MinItems > *diff.Old.MinItems:
err = fmt.Errorf("minItems constraint increased from %v to %v", *diff.Old.MinItems, *diff.New.MinItems)
}

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
}

var err error

switch {
case diff.Old.MinLength == nil && diff.New.MinLength != nil:
err = fmt.Errorf("minLength constraint %v added when there were no restrictions previously", *diff.New.MinLength)
case diff.Old.MinLength != nil && diff.New.MinLength != nil && *diff.New.MinLength > *diff.Old.MinLength:
err = fmt.Errorf("minLength constraint increased from %v to %v", *diff.Old.MinLength, *diff.New.MinLength)
}

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
}

var err error

switch {
case diff.Old.MinProperties == nil && diff.New.MinProperties != nil:
err = fmt.Errorf("minProperties constraint %v added when there were no restrictions previously", *diff.New.MinProperties)
case diff.Old.MinProperties != nil && diff.New.MinProperties != nil && *diff.New.MinProperties > *diff.Old.MinProperties:
err = fmt.Errorf("minProperties constraint increased from %v to %v", *diff.Old.MinProperties, *diff.New.MinProperties)
}

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 00f1f80

Please sign in to comment.