mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 19:56:01 +00:00
Make semantic deep equal public feature
* Use semantic deep equal when validating * More test cases for deep equal
This commit is contained in:
parent
7f49ba0dcf
commit
0d628b3bff
@ -18,6 +18,28 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/conversion"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Semantic can do semantic deep equality checks for api objects.
|
||||||
|
// Example: api.Semantic.DeepEqual(aPod, aPodWithNonNilButEmptyMaps) == true
|
||||||
|
var Semantic = conversion.EqualitiesOrDie(
|
||||||
|
func(a, b resource.Quantity) bool {
|
||||||
|
// Ignore formatting, only care that numeric value stayed the same.
|
||||||
|
// TODO: if we decide it's important, after we drop v1beta1/2, we
|
||||||
|
// could start comparing format.
|
||||||
|
//
|
||||||
|
// Uninitialized quantities are equivilent to 0 quantities.
|
||||||
|
if a.Amount == nil && b.MilliValue() == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if b.Amount == nil && a.MilliValue() == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return a.Amount.Cmp(b.Amount) == 0
|
||||||
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: Address these per #1502
|
// TODO: Address these per #1502
|
||||||
|
@ -30,7 +30,6 @@ import (
|
|||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta2"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta2"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/conversion"
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
|
|
||||||
@ -41,14 +40,6 @@ import (
|
|||||||
|
|
||||||
var fuzzIters = flag.Int("fuzz_iters", 40, "How many fuzzing iterations to do.")
|
var fuzzIters = flag.Int("fuzz_iters", 40, "How many fuzzing iterations to do.")
|
||||||
|
|
||||||
// apiObjectComparer can do semantic deep equality checks for api objects.
|
|
||||||
var apiObjectComparer = conversion.EqualitiesOrDie(
|
|
||||||
func(a, b resource.Quantity) bool {
|
|
||||||
// Ignore formatting, only care that numeric value stayed the same.
|
|
||||||
return a.Amount.Cmp(b.Amount) == 0
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
// apiObjectFuzzer can randomly populate api objects.
|
// apiObjectFuzzer can randomly populate api objects.
|
||||||
var apiObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 1).Funcs(
|
var apiObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 1).Funcs(
|
||||||
func(j *runtime.PluginBase, c fuzz.Continue) {
|
func(j *runtime.PluginBase, c fuzz.Continue) {
|
||||||
@ -181,7 +172,7 @@ func runTest(t *testing.T, codec runtime.Codec, source runtime.Object) {
|
|||||||
t.Errorf("0: %v: %v\nCodec: %v\nData: %s\nSource: %#v", name, err, codec, string(data), source)
|
t.Errorf("0: %v: %v\nCodec: %v\nData: %s\nSource: %#v", name, err, codec, string(data), source)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !apiObjectComparer.DeepEqual(source, obj2) {
|
if !api.Semantic.DeepEqual(source, obj2) {
|
||||||
t.Errorf("1: %v: diff: %v\nCodec: %v\nData: %s\nSource: %#v", name, util.ObjectGoPrintDiff(source, obj2), codec, string(data), source)
|
t.Errorf("1: %v: diff: %v\nCodec: %v\nData: %s\nSource: %#v", name, util.ObjectGoPrintDiff(source, obj2), codec, string(data), source)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -192,7 +183,7 @@ func runTest(t *testing.T, codec runtime.Codec, source runtime.Object) {
|
|||||||
t.Errorf("2: %v: %v", name, err)
|
t.Errorf("2: %v: %v", name, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !apiObjectComparer.DeepEqual(source, obj3) {
|
if !api.Semantic.DeepEqual(source, obj3) {
|
||||||
t.Errorf("3: %v: diff: %v\nCodec: %v", name, util.ObjectDiff(source, obj3), codec)
|
t.Errorf("3: %v: diff: %v\nCodec: %v", name, util.ObjectDiff(source, obj3), codec)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -266,7 +257,7 @@ func TestEncode_Ptr(t *testing.T) {
|
|||||||
if _, ok := obj2.(*api.Pod); !ok {
|
if _, ok := obj2.(*api.Pod); !ok {
|
||||||
t.Fatalf("Got wrong type")
|
t.Fatalf("Got wrong type")
|
||||||
}
|
}
|
||||||
if !apiObjectComparer.DeepEqual(obj2, pod) {
|
if !api.Semantic.DeepEqual(obj2, pod) {
|
||||||
t.Errorf("Expected:\n %#v,\n Got:\n %#v", &pod, obj2)
|
t.Errorf("Expected:\n %#v,\n Got:\n %#v", &pod, obj2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,6 @@ package validation
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
@ -429,7 +428,7 @@ func ValidatePodUpdate(newPod, oldPod *api.Pod) errs.ValidationErrorList {
|
|||||||
newContainers = append(newContainers, container)
|
newContainers = append(newContainers, container)
|
||||||
}
|
}
|
||||||
pod.Spec.Containers = newContainers
|
pod.Spec.Containers = newContainers
|
||||||
if !reflect.DeepEqual(pod.Spec, oldPod.Spec) {
|
if !api.Semantic.DeepEqual(pod.Spec, oldPod.Spec) {
|
||||||
// TODO: a better error would include all immutable fields explicitly.
|
// TODO: a better error would include all immutable fields explicitly.
|
||||||
allErrs = append(allErrs, errs.NewFieldInvalid("spec.containers", newPod.Spec.Containers, "some fields are immutable"))
|
allErrs = append(allErrs, errs.NewFieldInvalid("spec.containers", newPod.Spec.Containers, "some fields are immutable"))
|
||||||
}
|
}
|
||||||
@ -586,7 +585,7 @@ func ValidateMinion(minion *api.Node) errs.ValidationErrorList {
|
|||||||
func ValidateMinionUpdate(oldMinion *api.Node, minion *api.Node) errs.ValidationErrorList {
|
func ValidateMinionUpdate(oldMinion *api.Node, minion *api.Node) errs.ValidationErrorList {
|
||||||
allErrs := errs.ValidationErrorList{}
|
allErrs := errs.ValidationErrorList{}
|
||||||
|
|
||||||
if !reflect.DeepEqual(minion.Status, api.NodeStatus{}) {
|
if !api.Semantic.DeepEqual(minion.Status, api.NodeStatus{}) {
|
||||||
allErrs = append(allErrs, errs.NewFieldInvalid("status", minion.Status, "status must be empty"))
|
allErrs = append(allErrs, errs.NewFieldInvalid("status", minion.Status, "status must be empty"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -596,7 +595,7 @@ func ValidateMinionUpdate(oldMinion *api.Node, minion *api.Node) errs.Validation
|
|||||||
// Clear status
|
// Clear status
|
||||||
oldMinion.Status = minion.Status
|
oldMinion.Status = minion.Status
|
||||||
|
|
||||||
if !reflect.DeepEqual(oldMinion, minion) {
|
if !api.Semantic.DeepEqual(oldMinion, minion) {
|
||||||
glog.V(4).Infof("Update failed validation %#v vs %#v", oldMinion, minion)
|
glog.V(4).Infof("Update failed validation %#v vs %#v", oldMinion, minion)
|
||||||
allErrs = append(allErrs, fmt.Errorf("update contains more than labels or capacity changes"))
|
allErrs = append(allErrs, fmt.Errorf("update contains more than labels or capacity changes"))
|
||||||
}
|
}
|
||||||
|
@ -22,15 +22,28 @@ import (
|
|||||||
|
|
||||||
func TestEqualities(t *testing.T) {
|
func TestEqualities(t *testing.T) {
|
||||||
e := Equalities{}
|
e := Equalities{}
|
||||||
|
type Bar struct {
|
||||||
|
X int
|
||||||
|
}
|
||||||
|
type Baz struct {
|
||||||
|
Y Bar
|
||||||
|
}
|
||||||
err := e.AddFuncs(
|
err := e.AddFuncs(
|
||||||
func(a, b int) bool {
|
func(a, b int) bool {
|
||||||
return a+1 == b
|
return a+1 == b
|
||||||
},
|
},
|
||||||
|
func(a, b Bar) bool {
|
||||||
|
return a.X*10 == b.X
|
||||||
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("Unexpected: %v", err)
|
t.Fatalf("Unexpected: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Foo struct {
|
||||||
|
X int
|
||||||
|
}
|
||||||
|
|
||||||
table := []struct {
|
table := []struct {
|
||||||
a, b interface{}
|
a, b interface{}
|
||||||
equal bool
|
equal bool
|
||||||
@ -38,6 +51,10 @@ func TestEqualities(t *testing.T) {
|
|||||||
{1, 2, true},
|
{1, 2, true},
|
||||||
{2, 1, false},
|
{2, 1, false},
|
||||||
{"foo", "foo", true},
|
{"foo", "foo", true},
|
||||||
|
{Foo{1}, Foo{2}, true},
|
||||||
|
{Bar{1}, Bar{10}, true},
|
||||||
|
{&Bar{1}, &Bar{10}, true},
|
||||||
|
{Baz{Bar{1}}, Baz{Bar{10}}, true},
|
||||||
{map[string]int{"foo": 1}, map[string]int{"foo": 2}, true},
|
{map[string]int{"foo": 1}, map[string]int{"foo": 2}, true},
|
||||||
{map[string]int{}, map[string]int(nil), true},
|
{map[string]int{}, map[string]int(nil), true},
|
||||||
{[]int{}, []int(nil), true},
|
{[]int{}, []int(nil), true},
|
||||||
|
Loading…
Reference in New Issue
Block a user