Skip to content

Commit c8e45a3

Browse files
authored
Merge pull request kubernetes#136620 from yongruilin/master_vg-fix-fuzz
fix DeclarativeValidation fuzzing test panic and refactor subresource handlin
2 parents 4caf96e + fbefdc8 commit c8e45a3

File tree

2 files changed

+42
-5
lines changed

2 files changed

+42
-5
lines changed

pkg/api/testing/validation.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
utilfeature "k8s.io/apiserver/pkg/util/feature"
3333
featuregatetesting "k8s.io/component-base/featuregate/testing"
3434
"k8s.io/kubernetes/pkg/api/legacyscheme"
35+
"sigs.k8s.io/randfill"
3536
)
3637

3738
// ValidateFunc is a function that runs validation.
@@ -89,6 +90,13 @@ func VerifyVersionedValidationEquivalence(t *testing.T, obj, old runtime.Object,
8990
if internalObj == nil {
9091
return
9192
}
93+
// We do fuzzing on the internal version of the object.
94+
// This is because custom fuzzing function are only
95+
// supported for internal objects.
96+
// Fuzz the internal object if a fuzzer is provided.
97+
if opts.Fuzzer != nil {
98+
opts.Fuzzer.Fill(internalObj)
99+
}
92100
if old == nil {
93101
runtimetest.RunValidationForEachVersion(t, legacyscheme.Scheme, []string{}, internalObj, accumulate, opts.IgnoreObjectConversionErrors, opts.SubResources...)
94102
} else {
@@ -101,6 +109,10 @@ func VerifyVersionedValidationEquivalence(t *testing.T, obj, old runtime.Object,
101109
if internalOld == nil {
102110
return
103111
}
112+
// Fuzz the internal old object if a fuzzer is provided.
113+
if opts.Fuzzer != nil {
114+
opts.Fuzzer.Fill(internalOld)
115+
}
104116
runtimetest.RunUpdateValidationForEachVersion(t, legacyscheme.Scheme, []string{}, internalObj, internalOld, accumulate, opts.IgnoreObjectConversionErrors, opts.SubResources...)
105117
}
106118

@@ -206,6 +218,9 @@ type validationOption struct {
206218
// IgnoreObjectConversions skips the tests if the conversion from the internal object
207219
// to the versioned object fails.
208220
IgnoreObjectConversionErrors bool
221+
222+
// Fuzzer is the fuzzer to use for generating test objects.
223+
Fuzzer *randfill.Filler
209224
}
210225

211226
func WithSubResources(subResources ...string) ValidationTestConfig {
@@ -226,6 +241,12 @@ func WithIgnoreObjectConversionErrors() ValidationTestConfig {
226241
}
227242
}
228243

244+
func WithFuzzer(fuzzer *randfill.Filler) ValidationTestConfig {
245+
return func(o *validationOption) {
246+
o.Fuzzer = fuzzer
247+
}
248+
}
249+
229250
// VerifyValidationEquivalence provides a helper for testing the migration from
230251
// hand-written imperative validation to declarative validation. It ensures that
231252
// the validation logic remains consistent before and after the feature is enabled.

pkg/api/testing/validation_test.go

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,18 @@ func TestVersionedValidationByFuzzing(t *testing.T) {
6565
{Group: "admissionregistration.k8s.io", Version: "v1alpha1"},
6666
}
6767

68+
// subresourceOnly specifies the subresource path for types that can only be validated
69+
// as subresources (e.g. autoscaling/Scale) and do not support root-level validation.
70+
// For GVKs not in this map, the test defaults to fuzzing the root resource ("").
71+
// Other resources with subresources (e.g. Pod status, exec) share validation logic with
72+
// the root resource, so fuzzing the root is sufficient to verify validation equivalence.
73+
subresourceOnly := map[schema.GroupVersionKind]string{
74+
{Group: "autoscaling", Version: "v1", Kind: "Scale"}: "scale",
75+
{Group: "autoscaling", Version: "v1beta1", Kind: "Scale"}: "scale",
76+
{Group: "autoscaling", Version: "v1beta2", Kind: "Scale"}: "scale",
77+
{Group: "autoscaling", Version: "v2", Kind: "Scale"}: "scale",
78+
}
79+
6880
fuzzIters := *roundtrip.FuzzIters / 10 // TODO: Find a better way to manage test running time
6981
f := fuzzer.FuzzerFor(FuzzerFuncs, rand.NewSource(rand.Int63()), legacyscheme.Codecs)
7082

@@ -77,17 +89,21 @@ func TestVersionedValidationByFuzzing(t *testing.T) {
7789
if err != nil {
7890
t.Fatalf("could not create a %v: %s", kind, err)
7991
}
80-
f.Fill(obj)
92+
93+
subresource := ""
94+
if specific, ok := subresourceOnly[gvk]; ok {
95+
subresource = specific
96+
}
8197

8298
var opts []ValidationTestConfig
8399
// TODO(API group level configuration): Consider configuring normalization rules at the
84100
// API group level to avoid potential collisions when multiple rule sets are combined.
85101
// This would allow each API group to register its own normalization rules independently.
86102
allRules := append([]field.NormalizationRule{}, resourcevalidation.ResourceNormalizationRules...)
87103
allRules = append(allRules, nodevalidation.NodeNormalizationRules...)
88-
opts = append(opts, WithNormalizationRules(allRules...))
89-
if gv.Group == "autoscaling" {
90-
opts = append(opts, WithIgnoreObjectConversionErrors())
104+
opts = append(opts, WithNormalizationRules(allRules...), WithFuzzer(f))
105+
if subresource != "" {
106+
opts = append(opts, WithSubResources(subresource))
91107
}
92108

93109
VerifyVersionedValidationEquivalence(t, obj, nil, opts...)
@@ -96,7 +112,7 @@ func TestVersionedValidationByFuzzing(t *testing.T) {
96112
if err != nil {
97113
t.Fatalf("could not create a %v: %s", kind, err)
98114
}
99-
f.Fill(old)
115+
100116
VerifyVersionedValidationEquivalence(t, obj, old, opts...)
101117
}
102118
})

0 commit comments

Comments
 (0)