feat: add fuzz test for sidecarset (#1713) (#2010)

Signed-off-by: yechun <yechun.yc@alibaba-inc.com>
This commit is contained in:
Slide 2025-05-19 09:39:55 +08:00 committed by GitHub
parent 0fbbe891a3
commit 9c3a79bf7e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 745 additions and 48 deletions

View File

@ -0,0 +1,63 @@
package sidecarcontrol
import (
"encoding/json"
"testing"
fuzz "github.com/AdaLogics/go-fuzz-headers"
appsv1alpha1 "github.com/openkruise/kruise/apis/apps/v1alpha1"
fuzzutils "github.com/openkruise/kruise/test/fuzz"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
func FuzzPatchPodMetadata(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
cf := fuzz.NewConsumer(data)
metadata := &metav1.ObjectMeta{}
if err := cf.GenerateStruct(metadata); err != nil {
return
}
jsonPatch, err := cf.GetBool()
if err != nil {
return
}
patch, err := fuzzutils.GeneratePatchPodMetadata(cf, jsonPatch)
if err != nil || patch == nil {
return
}
// Make sure there is a probability that the same key exists.
if exist, err := cf.GetBool(); exist && err == nil {
for key := range patch.Annotations {
if jsonPatch {
m := make(map[string]string)
if err := cf.FuzzMap(&m); err != nil {
return
}
bytes, _ := json.Marshal(m)
metadata.GetAnnotations()[key] = string(bytes)
} else {
val, err := cf.GetString()
if err != nil {
return
}
metadata.GetAnnotations()[key] = val
}
}
}
_, err = PatchPodMetadata(metadata, []appsv1alpha1.SidecarSetPatchPodMetadata{*patch})
// Because function can capture panic error, so here to deal with the errors due to panic,
// Meanwhile, the error of the failed Patch merge in JSON format needs to be ignored.
if err != nil {
if !jsonPatch && patch.PatchPolicy == appsv1alpha1.SidecarSetMergePatchJsonPatchPolicy {
t.Logf("Ignore patch merge in JSON format failed: %s", err)
return
}
// The panic error will be printed.
t.Errorf("Panic: %s", err)
}
})
}

View File

@ -47,7 +47,7 @@ func FuzzParseSubsetReplicas(f *testing.F) {
}
udReplicas := int32(udReplicasInt)
subsetReplicas, err := fuzzutils.GenerateSubsetReplicas(cf)
subsetReplicas, err := fuzzutils.GenerateIntOrString(cf)
if err != nil {
return
}

View File

@ -0,0 +1,106 @@
package mutating
import (
"context"
"testing"
fuzz "github.com/AdaLogics/go-fuzz-headers"
appsv1alpha1 "github.com/openkruise/kruise/apis/apps/v1alpha1"
"github.com/openkruise/kruise/pkg/util/fieldindex"
fuzzutils "github.com/openkruise/kruise/test/fuzz"
admissionv1 "k8s.io/api/admission/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)
var (
fakeScheme = runtime.NewScheme()
defaultPod = &corev1.Pod{
ObjectMeta: metav1.ObjectMeta{
Name: "test-pod",
Namespace: "default",
Labels: map[string]string{"app": "fuzz-test"},
},
Spec: corev1.PodSpec{
InitContainers: []corev1.Container{
{
Name: "init-0",
Image: "busybox:1.0.0",
},
},
Containers: []corev1.Container{
{
Name: "nginx",
Image: "nginx:1.15.1",
},
},
},
}
req = admission.Request{
AdmissionRequest: admissionv1.AdmissionRequest{
Operation: admissionv1.Create,
Object: runtime.RawExtension{},
OldObject: runtime.RawExtension{},
Resource: metav1.GroupVersionResource{Group: corev1.SchemeGroupVersion.Group, Version: corev1.SchemeGroupVersion.Version, Resource: "pods"},
SubResource: "",
},
}
)
func init() {
_ = clientgoscheme.AddToScheme(fakeScheme)
_ = appsv1alpha1.AddToScheme(fakeScheme)
_ = appsv1alpha1.AddToScheme(clientgoscheme.Scheme)
}
func FuzzSidecarSetMutatingPod(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
cf := fuzz.NewConsumer(data)
sidecarSet := &appsv1alpha1.SidecarSet{}
if err := cf.GenerateStruct(sidecarSet); err != nil {
return
}
if err := fuzzutils.GenerateSidecarSetSpec(cf, sidecarSet,
fuzzutils.GenerateSidecarSetUpdateStrategy,
fuzzutils.GenerateSidecarSetInjectionStrategy,
fuzzutils.GenerateSidecarSetInitContainer,
fuzzutils.GenerateSidecarSetContainer,
fuzzutils.GenerateSidecarSetPatchPodMetadata); err != nil {
return
}
matched, err := cf.GetBool()
if err != nil {
return
}
if matched {
// Make sure can select to defaultPod
sidecarSet.Spec.Selector.MatchLabels = defaultPod.GetLabels()
sidecarSet.Spec.Selector.MatchExpressions = nil
sidecarSet.Spec.Namespace = defaultPod.GetNamespace()
sidecarSet.Spec.NamespaceSelector = nil
sidecarSet.Spec.InjectionStrategy.Revision = nil
}
if sidecarSet.GetDeletionTimestamp() != nil && len(sidecarSet.GetFinalizers()) == 0 {
sidecarSet.SetDeletionTimestamp(nil)
}
c := fake.NewClientBuilder().WithObjects(sidecarSet).WithIndex(
&appsv1alpha1.SidecarSet{}, fieldindex.IndexNameForSidecarSetNamespace, fieldindex.IndexSidecarSet,
).WithScheme(fakeScheme).Build()
handler := &PodCreateHandler{
Decoder: admission.NewDecoder(fakeScheme),
Client: c,
}
_, _ = handler.sidecarsetMutatingPod(context.Background(), req, defaultPod)
})
}

View File

@ -0,0 +1,135 @@
/*
Copyright 2025 The Kruise Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package validating
import (
"encoding/json"
"testing"
fuzz "github.com/AdaLogics/go-fuzz-headers"
appsv1alpha1 "github.com/openkruise/kruise/apis/apps/v1alpha1"
"github.com/openkruise/kruise/pkg/control/sidecarcontrol"
"github.com/openkruise/kruise/pkg/util"
"github.com/openkruise/kruise/pkg/util/configuration"
webhookutil "github.com/openkruise/kruise/pkg/webhook/util"
fuzzutils "github.com/openkruise/kruise/test/fuzz"
apps "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
)
var (
fakeScheme = runtime.NewScheme()
)
func init() {
_ = clientgoscheme.AddToScheme(fakeScheme)
_ = appsv1alpha1.AddToScheme(fakeScheme)
_ = appsv1alpha1.AddToScheme(clientgoscheme.Scheme)
}
func FuzzValidateSidecarSetSpec(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
cf := fuzz.NewConsumer(data)
ss := &appsv1alpha1.SidecarSet{}
if err := cf.GenerateStruct(ss); err != nil {
return
}
if err := fuzzutils.GenerateSidecarSetSpec(cf, ss,
fuzzutils.GenerateSidecarSetSelector,
fuzzutils.GenerateSidecarSetNamespace,
fuzzutils.GenerateSidecarSetNamespaceSelector,
fuzzutils.GenerateSidecarSetInitContainer,
fuzzutils.GenerateSidecarSetContainer,
fuzzutils.GenerateSidecarSetUpdateStrategy,
fuzzutils.GenerateSidecarSetInjectionStrategy,
fuzzutils.GenerateSidecarSetPatchPodMetadata); err != nil {
return
}
h, err := newFakeSidecarSetCreateUpdateHandler(cf, ss)
if err != nil {
return
}
_ = h.validateSidecarSetSpec(ss, field.NewPath("spec"))
})
}
func newFakeSidecarSetCreateUpdateHandler(cf *fuzz.ConsumeFuzzer, ss *appsv1alpha1.SidecarSet) (*SidecarSetCreateUpdateHandler, error) {
name, hash := "", ""
if ss.Spec.InjectionStrategy.Revision != nil && ss.Spec.InjectionStrategy.Revision.RevisionName != nil {
name = *ss.Spec.InjectionStrategy.Revision.RevisionName
}
if ss.Spec.InjectionStrategy.Revision != nil && ss.Spec.InjectionStrategy.Revision.CustomVersion != nil {
hash = *ss.Spec.InjectionStrategy.Revision.CustomVersion
}
object := &apps.ControllerRevision{
ObjectMeta: metav1.ObjectMeta{
Namespace: webhookutil.GetNamespace(),
Name: name,
},
}
objectList := &apps.ControllerRevisionList{
Items: []apps.ControllerRevision{
{
ObjectMeta: metav1.ObjectMeta{
Namespace: webhookutil.GetNamespace(),
Name: "default",
Labels: map[string]string{
sidecarcontrol.SidecarSetKindName: ss.GetName(),
appsv1alpha1.SidecarSetCustomVersionLabel: hash,
},
},
},
},
}
whiteList := &configuration.SidecarSetPatchMetadataWhiteList{}
if err := fuzzutils.GenerateSidecarSetWhiteListRule(cf, whiteList); err != nil {
return nil, err
}
whiteListJson, err := json.Marshal(whiteList)
if err != nil {
return nil, err
}
config := &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: configuration.KruiseConfigurationName,
Namespace: util.GetKruiseNamespace(),
},
Data: map[string]string{
configuration.SidecarSetPatchPodMetadataWhiteListKey: string(whiteListJson),
},
}
return &SidecarSetCreateUpdateHandler{
Client: fake.NewClientBuilder().WithScheme(fakeScheme).WithObjects(object, config).WithLists(objectList).Build(),
Decoder: admission.NewDecoder(fakeScheme),
}, err
}

View File

@ -17,9 +17,14 @@ limitations under the License.
package validating
import (
"encoding/json"
"testing"
fuzz "github.com/AdaLogics/go-fuzz-headers"
appsv1alpha1 "github.com/openkruise/kruise/apis/apps/v1alpha1"
"github.com/openkruise/kruise/pkg/util"
"github.com/openkruise/kruise/pkg/util/configuration"
fuzzutils "github.com/openkruise/kruise/test/fuzz"
appsv1 "k8s.io/api/apps/v1"
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
@ -28,9 +33,6 @@ import (
"k8s.io/apimachinery/pkg/util/validation/field"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
appsv1alpha1 "github.com/openkruise/kruise/apis/apps/v1alpha1"
fuzzutils "github.com/openkruise/kruise/test/fuzz"
)
var (
@ -64,15 +66,13 @@ func FuzzValidateWorkloadSpreadSpec(f *testing.F) {
return
}
whiteList, err := cf.GetString()
if err != nil {
whiteList := &configuration.WSCustomWorkloadWhiteList{}
if err := fuzzutils.GenerateWorkloadSpreadWhiteList(cf, whiteList); err != nil {
return
}
if validWhiteList, err := cf.GetBool(); err == nil && validWhiteList {
whiteList = "{\"workloads\":[{\"Group\":\"test\",\"Kind\":\"TFJob\"}]}"
if matched, err := cf.GetBool(); err == nil && matched {
whiteList = "{\"workloads\":[{\"Group\":\"training.kubedl.io\",\"Kind\":\"TFJob\"}]}"
}
whiteListJson, err := json.Marshal(whiteList)
if err != nil {
return
}
fakeClient := fake.NewClientBuilder().
@ -82,8 +82,8 @@ func FuzzValidateWorkloadSpreadSpec(f *testing.F) {
&appsv1.StatefulSet{ObjectMeta: metav1.ObjectMeta{Name: "valid-target", Namespace: "default"}},
&batchv1.Job{ObjectMeta: metav1.ObjectMeta{Name: "valid-target", Namespace: "default"}},
&appsv1.ReplicaSet{ObjectMeta: metav1.ObjectMeta{Name: "valid-target", Namespace: "default"}},
&corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: "kruise-configuration", Namespace: "kruise-system"},
Data: map[string]string{"WorkloadSpread_Watch_Custom_Workload_WhiteList": whiteList}},
&corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: configuration.KruiseConfigurationName, Namespace: util.GetKruiseNamespace()},
Data: map[string]string{configuration.WSWatchCustomWorkloadWhiteList: string(whiteListJson)}},
).Build()
h := &WorkloadSpreadCreateUpdateHandler{Client: fakeClient}

View File

@ -64,7 +64,7 @@ func FuzzParseYAML(f *testing.F) {
- `f.Add` 用来添加种子输入。
- `f.Fuzz` 自动生成随机输入(基于种子)并调用目标函数。
- 通过 `t.Logf` 记录异常情况(如解析失败)。
- 通过 `t.Errorf 可以中断执行并记录异常情况。
- 通过 `t.Errorf` 可以中断执行并记录异常情况。
- 同时也可以忽略错误以检查目标函数是否会崩溃。
@ -110,7 +110,7 @@ func FuzzHandlePodEvent(f *testing.F) {
### **3.1 目录结构规范**
在 OpenKruise 项目中,编写好的模糊测试需添加到以下脚本与 [oss-fuzz](https://github.com/google/oss-fuzz) 集成:
在 OpenKruise 项目中,编写好的模糊测试需添加到以下脚本与 [OSS-Fuzz](https://github.com/google/oss-fuzz) 集成:
```
openkruise/test/fuzz/oss_fuzz_build.sh

View File

@ -19,6 +19,11 @@ package fuzz
import (
"encoding/json"
"fmt"
"math/rand"
"sort"
"strings"
"time"
fuzz "github.com/AdaLogics/go-fuzz-headers"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
@ -26,12 +31,36 @@ import (
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/intstr"
"math/rand"
"sort"
"strings"
)
func GenerateSubsetReplicas(cf *fuzz.ConsumeFuzzer) (intstr.IntOrString, error) {
const (
// collectionMaxElements defines the upper bound for generating random collection sizes.
// Used to create small but variable-length slices/maps in fuzzing logic.
// The value ensures generated collections contain 1 or 2 elements (r.Intn(2) + 1).
collectionMaxElements = 2
// stringSegmentMaxLength represents the maximum allowed length for string segments.
// Ensures generated strings fit within typical system constraints (e.g., 63 characters).
stringSegmentMaxLength = 63
// stringSegmentEmptyCap defines the maximum length for string segments that may be empty.
// Allows generation of empty strings (0-length) up to stringSegmentMaxLength (63) characters.
stringSegmentEmptyCap = 64
// probabilityBaseRange defines the base range [0, N) for probability calculations.
// A higher value increases precision (e.g., 100 for percentage-based probabilities).
probabilityBaseRange = 10
// probabilityTriggerThreshold defines the upper bound for event occurrence in probability checks.
// If a generated number is less than this value, the event occurs (0 ≤ threshold ≤ range).
probabilityTriggerThreshold = 5
)
var (
r = rand.New(rand.NewSource(time.Now().UnixNano()))
)
func GenerateIntOrString(cf *fuzz.ConsumeFuzzer) (intstr.IntOrString, error) {
isInt, err := cf.GetBool()
if err != nil {
return intstr.IntOrString{}, err
@ -79,10 +108,10 @@ func GenerateNodeSelectorTerm(cf *fuzz.ConsumeFuzzer, term *corev1.NodeSelectorT
}
if len(term.MatchExpressions) == 0 {
term.MatchExpressions = make([]corev1.NodeSelectorRequirement, rand.Intn(2)+1)
term.MatchExpressions = make([]corev1.NodeSelectorRequirement, r.Intn(collectionMaxElements)+1)
}
if len(term.MatchFields) == 0 {
term.MatchFields = make([]corev1.NodeSelectorRequirement, rand.Intn(2)+1)
term.MatchFields = make([]corev1.NodeSelectorRequirement, r.Intn(collectionMaxElements)+1)
}
validOperators := []corev1.NodeSelectorOperator{
corev1.NodeSelectorOpIn,
@ -103,7 +132,7 @@ func GenerateNodeSelectorTerm(cf *fuzz.ConsumeFuzzer, term *corev1.NodeSelectorT
req.Operator = validOperators[choice%len(validOperators)]
if req.Operator == corev1.NodeSelectorOpIn || req.Operator == corev1.NodeSelectorOpNotIn {
if len(req.Values) == 0 {
req.Values = make([]string, rand.Intn(2)+1)
req.Values = make([]string, r.Intn(collectionMaxElements)+1)
}
for i := range req.Values {
req.Values[i] = GenerateValidValue()
@ -143,10 +172,10 @@ func GenerateLabelSelector(cf *fuzz.ConsumeFuzzer, selector *metav1.LabelSelecto
}
if len(selector.MatchExpressions) == 0 {
selector.MatchExpressions = make([]metav1.LabelSelectorRequirement, rand.Intn(2)+1)
selector.MatchExpressions = make([]metav1.LabelSelectorRequirement, r.Intn(collectionMaxElements)+1)
}
if len(selector.MatchLabels) == 0 {
selector.MatchLabels = make(map[string]string, rand.Intn(2)+1)
selector.MatchLabels = make(map[string]string, r.Intn(collectionMaxElements)+1)
}
if selector.MatchLabels != nil {
@ -175,7 +204,7 @@ func GenerateLabelSelector(cf *fuzz.ConsumeFuzzer, selector *metav1.LabelSelecto
req.Operator = validOperators[choice%len(validOperators)]
if req.Operator == metav1.LabelSelectorOpIn || req.Operator == metav1.LabelSelectorOpNotIn {
if len(req.Values) == 0 {
req.Values = make([]string, rand.Intn(2)+1)
req.Values = make([]string, r.Intn(collectionMaxElements)+1)
}
for i := range req.Values {
req.Values[i] = GenerateValidValue()
@ -247,12 +276,12 @@ func GeneratePatch(cf *fuzz.ConsumeFuzzer, rawExtension *runtime.RawExtension) e
switch choice % 2 {
case 0: // patch metadata labels and annotations
fuzzedLabels := make(map[string]string)
for i := 0; i < rand.Intn(2)+1; i++ {
for i := 0; i < r.Intn(collectionMaxElements)+1; i++ {
fuzzedLabels[GenerateValidKey()] = GenerateValidValue()
}
fuzzedAnnotations := make(map[string]string)
for i := 0; i < rand.Intn(2)+1; i++ {
for i := 0; i < r.Intn(collectionMaxElements)+1; i++ {
fuzzedAnnotations[GenerateValidKey()] = GenerateValidValue()
}
@ -370,7 +399,7 @@ func GenerateInvalidNamespaceName() string {
validName := GenerateValidNamespaceName()
runes := []rune(validName)
// Make sure the first character is illegal
runes[0] = invalidChars[rand.Intn(len(invalidChars))]
runes[0] = invalidChars[r.Intn(len(invalidChars))]
return string(runes)
}
@ -389,8 +418,9 @@ func randomLabelKey() string {
namePart := randomLabelPart(false)
prefixPart := ""
if rand.Intn(10) < 5 {
prefixPartsLen := rand.Intn(2) + 1
// 50% chance to generate a prefixed label ke
if r.Intn(probabilityBaseRange) < probabilityTriggerThreshold {
prefixPartsLen := r.Intn(collectionMaxElements) + 1
prefixParts := make([]string, prefixPartsLen)
for i := range prefixParts {
prefixParts[i] = randomRFC1123()
@ -406,9 +436,9 @@ func randomLabelPart(canBeEmpty bool) string {
validMiddle := []charRange{{'0', '9'}, {'a', 'z'}, {'A', 'Z'},
{'.', '.'}, {'-', '-'}, {'_', '_'}}
partLen := rand.Intn(64) // len is [0, 63]
partLen := r.Intn(stringSegmentEmptyCap) // len is [0, 63]
if !canBeEmpty {
partLen = rand.Intn(63) + 1 // len is [1, 63]
partLen = r.Intn(stringSegmentMaxLength) + 1 // len is [1, 63]
}
runes := make([]rune, partLen)
@ -416,11 +446,11 @@ func randomLabelPart(canBeEmpty bool) string {
return string(runes)
}
runes[0] = validStartEnd[rand.Intn(len(validStartEnd))].choose()
runes[0] = validStartEnd[r.Intn(len(validStartEnd))].choose()
for i := range runes[1:] {
runes[i+1] = validMiddle[rand.Intn(len(validMiddle))].choose()
runes[i+1] = validMiddle[r.Intn(len(validMiddle))].choose()
}
runes[len(runes)-1] = validStartEnd[rand.Intn(len(validStartEnd))].choose()
runes[len(runes)-1] = validStartEnd[r.Intn(len(validStartEnd))].choose()
return string(runes)
}
@ -429,14 +459,14 @@ func randomRFC1123() string {
validStartEnd := []charRange{{'0', '9'}, {'a', 'z'}}
validMiddle := []charRange{{'0', '9'}, {'a', 'z'}, {'-', '-'}}
partLen := rand.Intn(63) + 1 // len is [1, 63]
partLen := r.Intn(stringSegmentMaxLength) + 1 // len is [1, 63]
runes := make([]rune, partLen)
runes[0] = validStartEnd[rand.Intn(len(validStartEnd))].choose()
runes[0] = validStartEnd[r.Intn(len(validStartEnd))].choose()
for i := range runes[1:] {
runes[i+1] = validMiddle[rand.Intn(len(validMiddle))].choose()
runes[i+1] = validMiddle[r.Intn(len(validMiddle))].choose()
}
runes[len(runes)-1] = validStartEnd[rand.Intn(len(validStartEnd))].choose()
runes[len(runes)-1] = validStartEnd[r.Intn(len(validStartEnd))].choose()
return string(runes)
}

View File

@ -47,3 +47,8 @@ compile_native_go_fuzzer $SRC/kruise/pkg/webhook/resourcedistribution/validating
compile_native_go_fuzzer $SRC/kruise/pkg/webhook/resourcedistribution/validating FuzzValidateResourceDistributionSpec fuzz_validate_resource_distribution_spec
compile_native_go_fuzzer $SRC/kruise/pkg/webhook/resourcedistribution/validating FuzzValidateResourceDistributionTargets fuzz_validate_resource_distribution_targets
compile_native_go_fuzzer $SRC/kruise/pkg/webhook/resourcedistribution/validating FuzzValidateResourceDistributionResource fuzz_validate_resource_distribution_resource
# Compile fuzzers for the SidecarSet component.
compile_native_go_fuzzer $SRC/kruise/pkg/control/sidecarcontrol FuzzPatchPodMetadata fuzz_patch_pod_metadata
compile_native_go_fuzzer $SRC/kruise/pkg/webhook/sidecarset/validating FuzzValidateSidecarSetSpec fuzz_validate_sidecarset_spec
compile_native_go_fuzzer $SRC/kruise/pkg/webhook/pod/mutating FuzzSidecarSetMutatingPod fuzz_sidecarset_mutating_pod

343
test/fuzz/sidecarset.go Normal file
View File

@ -0,0 +1,343 @@
package fuzz
import (
"encoding/json"
fuzz "github.com/AdaLogics/go-fuzz-headers"
appsv1alpha1 "github.com/openkruise/kruise/apis/apps/v1alpha1"
"github.com/openkruise/kruise/pkg/util/configuration"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
type SidecarSetGenerateSpecFunc = func(cf *fuzz.ConsumeFuzzer, subset *appsv1alpha1.SidecarSet) error
func GenerateSidecarSetSpec(cf *fuzz.ConsumeFuzzer, sidecarSet *appsv1alpha1.SidecarSet, fns ...SidecarSetGenerateSpecFunc) error {
if len(fns) == 0 {
return nil
}
for _, fn := range fns {
if err := fn(cf, sidecarSet); err != nil {
return err
}
}
return nil
}
func GenerateSidecarSetSelector(cf *fuzz.ConsumeFuzzer, sidecarSet *appsv1alpha1.SidecarSet) error {
selector := &metav1.LabelSelector{}
if err := GenerateLabelSelector(cf, selector); err != nil {
return err
}
sidecarSet.Spec.Selector = selector
return nil
}
func GenerateSidecarSetNamespace(cf *fuzz.ConsumeFuzzer, sidecarSet *appsv1alpha1.SidecarSet) error {
isStructured, err := cf.GetBool()
if err != nil {
return err
}
if !isStructured {
namespace, err := cf.GetString()
if err != nil {
return err
}
sidecarSet.Spec.Namespace = namespace
return nil
}
if valid, err := cf.GetBool(); valid && err == nil {
sidecarSet.Spec.Namespace = GenerateValidNamespaceName()
} else {
sidecarSet.Spec.Namespace = GenerateInvalidNamespaceName()
}
return nil
}
func GenerateSidecarSetNamespaceSelector(cf *fuzz.ConsumeFuzzer, sidecarSet *appsv1alpha1.SidecarSet) error {
selector := &metav1.LabelSelector{}
if err := GenerateLabelSelector(cf, selector); err != nil {
return err
}
sidecarSet.Spec.NamespaceSelector = selector
return nil
}
func GenerateSidecarSetInitContainer(cf *fuzz.ConsumeFuzzer, sidecarSet *appsv1alpha1.SidecarSet) error {
isStructured, err := cf.GetBool()
if err != nil {
return err
}
if !isStructured {
initContainers := make([]appsv1alpha1.SidecarContainer, 0)
if err := cf.CreateSlice(&initContainers); err != nil {
return err
}
sidecarSet.Spec.InitContainers = initContainers
return nil
}
initContainers := make([]appsv1alpha1.SidecarContainer, r.Intn(collectionMaxElements)+1)
for i := range initContainers {
if err := GenerateSidecarContainer(cf, &initContainers[i]); err != nil {
return err
}
}
sidecarSet.Spec.InitContainers = initContainers
return nil
}
func GenerateSidecarSetContainer(cf *fuzz.ConsumeFuzzer, sidecarSet *appsv1alpha1.SidecarSet) error {
isStructured, err := cf.GetBool()
if err != nil {
return err
}
if !isStructured {
containers := make([]appsv1alpha1.SidecarContainer, 0)
if err := cf.CreateSlice(&containers); err != nil {
return err
}
sidecarSet.Spec.Containers = containers
return nil
}
containers := make([]appsv1alpha1.SidecarContainer, r.Intn(collectionMaxElements)+1)
for i := range containers {
if err := GenerateSidecarContainer(cf, &containers[i]); err != nil {
return err
}
}
sidecarSet.Spec.Containers = containers
return nil
}
func GenerateSidecarContainer(cf *fuzz.ConsumeFuzzer, sidecarContainer *appsv1alpha1.SidecarContainer) error {
isStructured, err := cf.GetBool()
if err != nil {
return err
}
if err := cf.GenerateStruct(sidecarContainer); err != nil {
return err
}
if !isStructured {
return nil
}
choice, err := cf.GetInt()
if err != nil {
return err
}
validPodInjectPolicyType := []appsv1alpha1.PodInjectPolicyType{
appsv1alpha1.BeforeAppContainerType,
appsv1alpha1.AfterAppContainerType,
}
validSidecarContainerUpgradeType := []appsv1alpha1.SidecarContainerUpgradeType{
appsv1alpha1.SidecarContainerColdUpgrade,
appsv1alpha1.SidecarContainerHotUpgrade,
}
validShareVolumePolicy := []appsv1alpha1.ShareVolumePolicyType{
appsv1alpha1.ShareVolumePolicyEnabled,
appsv1alpha1.ShareVolumePolicyDisabled,
}
validRestartPolicy := []corev1.ContainerRestartPolicy{
corev1.ContainerRestartPolicyAlways,
}
sidecarContainer.PodInjectPolicy = validPodInjectPolicyType[choice%(len(validPodInjectPolicyType))]
sidecarContainer.UpgradeStrategy.UpgradeType = validSidecarContainerUpgradeType[choice%(len(validSidecarContainerUpgradeType))]
sidecarContainer.ShareVolumePolicy.Type = validShareVolumePolicy[choice%(len(validShareVolumePolicy))]
sidecarContainer.ShareVolumeDevicePolicy.Type = validShareVolumePolicy[choice%(len(validShareVolumePolicy))]
isValid, err := cf.GetBool()
if err != nil {
return err
}
if isValid {
sidecarContainer.Container.RestartPolicy = &validRestartPolicy[choice%(len(validRestartPolicy))]
}
return nil
}
func GenerateSidecarSetUpdateStrategy(cf *fuzz.ConsumeFuzzer, sidecarSet *appsv1alpha1.SidecarSet) error {
isStructured, err := cf.GetBool()
if err != nil {
return err
}
updateStrategy := appsv1alpha1.SidecarSetUpdateStrategy{}
if err := cf.GenerateStruct(&updateStrategy); err != nil {
return err
}
if !isStructured {
sidecarSet.Spec.UpdateStrategy = updateStrategy
return nil
}
validStrategyTypes := []appsv1alpha1.SidecarSetUpdateStrategyType{
appsv1alpha1.NotUpdateSidecarSetStrategyType,
appsv1alpha1.RollingUpdateSidecarSetStrategyType,
}
choice, err := cf.GetInt()
if err != nil {
return err
}
updateStrategy.Type = validStrategyTypes[choice%len(validStrategyTypes)]
selector := &metav1.LabelSelector{}
if err := GenerateLabelSelector(cf, selector); err != nil {
return err
}
updateStrategy.Selector = selector
if partition, err := GenerateIntOrString(cf); err == nil {
updateStrategy.Partition = &partition
}
if maxUnavailable, err := GenerateIntOrString(cf); err == nil {
updateStrategy.MaxUnavailable = &maxUnavailable
}
sidecarSet.Spec.UpdateStrategy = updateStrategy
return nil
}
func GenerateSidecarSetInjectionStrategy(cf *fuzz.ConsumeFuzzer, sidecarSet *appsv1alpha1.SidecarSet) error {
isStructured, err := cf.GetBool()
if err != nil {
return err
}
injectionStrategy := appsv1alpha1.SidecarSetInjectionStrategy{}
if err := cf.GenerateStruct(&injectionStrategy); err != nil {
return err
}
if !isStructured {
sidecarSet.Spec.InjectionStrategy = injectionStrategy
return nil
}
validPolicies := []appsv1alpha1.SidecarSetInjectRevisionPolicy{
appsv1alpha1.AlwaysSidecarSetInjectRevisionPolicy,
appsv1alpha1.PartialSidecarSetInjectRevisionPolicy,
}
choice, err := cf.GetInt()
if err != nil {
return err
}
injectRevision := &appsv1alpha1.SidecarSetInjectRevision{
RevisionName: func() *string { name := GenerateValidValue(); return &name }(),
CustomVersion: func() *string { version := GenerateValidValue(); return &version }(),
Policy: validPolicies[choice%len(validPolicies)],
}
injectionStrategy.Revision = injectRevision
sidecarSet.Spec.InjectionStrategy = injectionStrategy
return nil
}
func GenerateSidecarSetPatchPodMetadata(cf *fuzz.ConsumeFuzzer, sidecarSet *appsv1alpha1.SidecarSet) error {
sliceLen, err := cf.GetInt()
if err != nil {
return err
}
sidecarSetPatchSlice := make([]appsv1alpha1.SidecarSetPatchPodMetadata, sliceLen%3+1)
for i := range sidecarSetPatchSlice {
jsonPatch, err := cf.GetBool()
if err != nil {
return err
}
patch, err := GeneratePatchPodMetadata(cf, jsonPatch)
if err != nil || patch == nil {
return err
}
sidecarSetPatchSlice[i] = *patch
}
sidecarSet.Spec.PatchPodMetadata = sidecarSetPatchSlice
return nil
}
func GeneratePatchPodMetadata(cf *fuzz.ConsumeFuzzer, jsonPatch bool) (*appsv1alpha1.SidecarSetPatchPodMetadata, error) {
isStructured, err := cf.GetBool()
if err != nil {
return nil, err
}
sidecarSetPatch := &appsv1alpha1.SidecarSetPatchPodMetadata{}
if !isStructured {
if err := cf.GenerateStruct(sidecarSetPatch); err != nil {
return nil, err
}
return sidecarSetPatch, nil
}
validPatchPolicies := []appsv1alpha1.SidecarSetPatchPolicyType{
appsv1alpha1.SidecarSetMergePatchJsonPatchPolicy,
appsv1alpha1.SidecarSetRetainPatchPolicy,
appsv1alpha1.SidecarSetOverwritePatchPolicy,
}
annotations := make(map[string]string)
if jsonPatch {
m := make(map[string]string)
m[GenerateValidKey()] = GenerateValidValue()
bytes, _ := json.Marshal(m)
annotations[GenerateValidKey()] = string(bytes)
} else {
annotations[GenerateValidKey()] = GenerateValidValue()
}
sidecarSetPatch.Annotations = annotations
choice, err := cf.GetInt()
if err != nil {
return sidecarSetPatch, nil
}
sidecarSetPatch.PatchPolicy = validPatchPolicies[choice%len(validPatchPolicies)]
return sidecarSetPatch, nil
}
func GenerateSidecarSetWhiteListRule(cf *fuzz.ConsumeFuzzer, whiteList *configuration.SidecarSetPatchMetadataWhiteList) error {
sliceLen, err := cf.GetInt()
if err != nil {
return err
}
whiteListRuleSlice := make([]configuration.SidecarSetPatchMetadataWhiteRule, sliceLen%3+1)
for i := range whiteListRuleSlice {
allowedAnnotationKeyExprs := make([]string, sliceLen%3+1)
for j := range allowedAnnotationKeyExprs {
expr, err := cf.GetString()
if err != nil {
return err
}
allowedAnnotationKeyExprs[j] = expr
}
selector := &metav1.LabelSelector{}
if err := GenerateLabelSelector(cf, selector); err != nil {
return err
}
whiteListRuleSlice[i] = configuration.SidecarSetPatchMetadataWhiteRule{
Selector: selector,
AllowedAnnotationKeyExprs: allowedAnnotationKeyExprs,
}
}
whiteList.Rules = whiteListRuleSlice
return nil
}

View File

@ -17,8 +17,6 @@ limitations under the License.
package fuzz
import (
"math/rand"
fuzz "github.com/AdaLogics/go-fuzz-headers"
appsv1alpha1 "github.com/openkruise/kruise/apis/apps/v1alpha1"
corev1 "k8s.io/api/core/v1"
@ -174,7 +172,7 @@ func GenerateUnitedDeploymentSubsetName(cf *fuzz.ConsumeFuzzer, subset *appsv1al
func GenerateUnitedDeploymentSubSetReplicas(cf *fuzz.ConsumeFuzzer, subset *appsv1alpha1.Subset) error {
if setMin, err := cf.GetBool(); setMin && err == nil {
minVal, err := GenerateSubsetReplicas(cf)
minVal, err := GenerateIntOrString(cf)
if err != nil {
return err
}
@ -182,7 +180,7 @@ func GenerateUnitedDeploymentSubSetReplicas(cf *fuzz.ConsumeFuzzer, subset *apps
}
if setMax, err := cf.GetBool(); setMax && err == nil {
maxVal, err := GenerateSubsetReplicas(cf)
maxVal, err := GenerateIntOrString(cf)
if err != nil {
return err
}
@ -190,7 +188,7 @@ func GenerateUnitedDeploymentSubSetReplicas(cf *fuzz.ConsumeFuzzer, subset *apps
}
if setReplicas, err := cf.GetBool(); setReplicas && err == nil {
replicas, err := GenerateSubsetReplicas(cf)
replicas, err := GenerateIntOrString(cf)
if err != nil {
return err
}
@ -219,7 +217,7 @@ func GenerateUnitedDeploymentNodeSelectorTerm(cf *fuzz.ConsumeFuzzer, subset *ap
}
func GenerateUnitedDeploymentTolerations(cf *fuzz.ConsumeFuzzer, subset *appsv1alpha1.Subset) error {
tolerations := make([]corev1.Toleration, rand.Intn(2)+1)
tolerations := make([]corev1.Toleration, r.Intn(collectionMaxElements)+1)
for i := range tolerations {
if err := GenerateTolerations(cf, &tolerations[i]); err != nil {
return err

View File

@ -18,10 +18,10 @@ package fuzz
import (
"math"
"math/rand"
fuzz "github.com/AdaLogics/go-fuzz-headers"
appsv1alpha1 "github.com/openkruise/kruise/apis/apps/v1alpha1"
"github.com/openkruise/kruise/pkg/util/configuration"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
@ -165,7 +165,7 @@ func GenerateWorkloadSpreadSubsetPatch(cf *fuzz.ConsumeFuzzer, subset *appsv1alp
}
func GenerateWorkloadSpreadSubsetReplicas(cf *fuzz.ConsumeFuzzer, subset *appsv1alpha1.WorkloadSpreadSubset) error {
maxReplicas, err := GenerateSubsetReplicas(cf)
maxReplicas, err := GenerateIntOrString(cf)
if err != nil {
return err
}
@ -183,7 +183,7 @@ func GenerateWorkloadSpreadNodeSelectorTerm(cf *fuzz.ConsumeFuzzer, subset *apps
}
func GenerateWorkloadSpreadTolerations(cf *fuzz.ConsumeFuzzer, subset *appsv1alpha1.WorkloadSpreadSubset) error {
tolerations := make([]corev1.Toleration, rand.Intn(2)+1)
tolerations := make([]corev1.Toleration, r.Intn(collectionMaxElements)+1)
for i := range tolerations {
if err := GenerateTolerations(cf, &tolerations[i]); err != nil {
return err
@ -192,3 +192,20 @@ func GenerateWorkloadSpreadTolerations(cf *fuzz.ConsumeFuzzer, subset *appsv1alp
subset.Tolerations = tolerations
return nil
}
func GenerateWorkloadSpreadWhiteList(cf *fuzz.ConsumeFuzzer, whiteList *configuration.WSCustomWorkloadWhiteList) error {
sliceLen, err := cf.GetInt()
if err != nil {
return err
}
workloads := make([]configuration.CustomWorkload, sliceLen%3+1)
for i := range workloads {
workLoad := configuration.CustomWorkload{}
if err := cf.GenerateStruct(&workLoad); err != nil {
return err
}
workloads[i] = workLoad
}
whiteList.Workloads = workloads
return nil
}