mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-28 22:17:14 +00:00
Merge pull request #108204 from kevindelgado/field-validation-crd-unit-tests
Field validation CRD benchmarks and decoder unit tests
This commit is contained in:
commit
b90bddfd9f
@ -17,8 +17,10 @@ limitations under the License.
|
||||
package apiserver
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
@ -33,8 +35,10 @@ import (
|
||||
|
||||
"sigs.k8s.io/yaml"
|
||||
|
||||
"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions"
|
||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
"k8s.io/apiextensions-apiserver/pkg/apiserver/conversion"
|
||||
structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema"
|
||||
"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/fake"
|
||||
informers "k8s.io/apiextensions-apiserver/pkg/client/informers/externalversions"
|
||||
listers "k8s.io/apiextensions-apiserver/pkg/client/listers/apiextensions/v1"
|
||||
@ -44,6 +48,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
serializerjson "k8s.io/apimachinery/pkg/runtime/serializer/json"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer/protobuf"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apiserver/pkg/admission"
|
||||
@ -642,6 +647,197 @@ func testHandlerConversion(t *testing.T, enableWatchCache bool) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecoder(t *testing.T) {
|
||||
multiVersionJSON := `
|
||||
{
|
||||
"apiVersion": "stable.example.com/v1beta1",
|
||||
"kind": "MultiVersion",
|
||||
"metadata": {
|
||||
"name": "my-mv"
|
||||
},
|
||||
"num": 1,
|
||||
"num": 2,
|
||||
"unknown": "foo"
|
||||
}
|
||||
`
|
||||
multiVersionYAML := `
|
||||
apiVersion: stable.example.com/v1beta1
|
||||
kind: MultiVersion
|
||||
metadata:
|
||||
name: my-mv
|
||||
num: 1
|
||||
num: 2
|
||||
unknown: foo`
|
||||
|
||||
expectedObjUnknownNotPreserved := &unstructured.Unstructured{}
|
||||
err := expectedObjUnknownNotPreserved.UnmarshalJSON([]byte(`
|
||||
{
|
||||
"apiVersion": "stable.example.com/v1beta1",
|
||||
"kind": "MultiVersion",
|
||||
"metadata": {
|
||||
"creationTimestamp": null,
|
||||
"generation": 1,
|
||||
"name": "my-mv"
|
||||
},
|
||||
"num": 2
|
||||
}
|
||||
`))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expectedObjUnknownPreserved := &unstructured.Unstructured{}
|
||||
err = expectedObjUnknownPreserved.UnmarshalJSON([]byte(`
|
||||
{
|
||||
"apiVersion": "stable.example.com/v1beta1",
|
||||
"kind": "MultiVersion",
|
||||
"metadata": {
|
||||
"creationTimestamp": null,
|
||||
"generation": 1,
|
||||
"name": "my-mv"
|
||||
},
|
||||
"num": 2,
|
||||
"unknown": "foo"
|
||||
}
|
||||
`))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
testcases := []struct {
|
||||
name string
|
||||
body string
|
||||
yaml bool
|
||||
strictDecoding bool
|
||||
preserveUnknownFields bool
|
||||
expectedObj *unstructured.Unstructured
|
||||
expectedErr error
|
||||
}{
|
||||
{
|
||||
name: "strict-decoding",
|
||||
body: multiVersionJSON,
|
||||
strictDecoding: true,
|
||||
expectedObj: expectedObjUnknownNotPreserved,
|
||||
expectedErr: errors.New(`strict decoding error: duplicate field "num", unknown field "unknown"`),
|
||||
},
|
||||
{
|
||||
name: "non-strict-decoding",
|
||||
body: multiVersionJSON,
|
||||
strictDecoding: false,
|
||||
expectedObj: expectedObjUnknownNotPreserved,
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "strict-decoding-preserve-unknown",
|
||||
body: multiVersionJSON,
|
||||
strictDecoding: true,
|
||||
preserveUnknownFields: true,
|
||||
expectedObj: expectedObjUnknownPreserved,
|
||||
expectedErr: errors.New(`strict decoding error: duplicate field "num"`),
|
||||
},
|
||||
{
|
||||
name: "non-strict-decoding-preserve-unknown",
|
||||
body: multiVersionJSON,
|
||||
strictDecoding: false,
|
||||
preserveUnknownFields: true,
|
||||
expectedObj: expectedObjUnknownPreserved,
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "strict-decoding-yaml",
|
||||
body: multiVersionYAML,
|
||||
yaml: true,
|
||||
strictDecoding: true,
|
||||
expectedObj: expectedObjUnknownNotPreserved,
|
||||
expectedErr: errors.New(`strict decoding error: yaml: unmarshal errors:
|
||||
line 7: key "num" already set in map, unknown field "unknown"`),
|
||||
},
|
||||
{
|
||||
name: "non-strict-decoding-yaml",
|
||||
body: multiVersionYAML,
|
||||
yaml: true,
|
||||
strictDecoding: false,
|
||||
expectedObj: expectedObjUnknownNotPreserved,
|
||||
expectedErr: nil,
|
||||
},
|
||||
{
|
||||
name: "strict-decoding-preserve-unknown-yaml",
|
||||
body: multiVersionYAML,
|
||||
yaml: true,
|
||||
strictDecoding: true,
|
||||
preserveUnknownFields: true,
|
||||
expectedObj: expectedObjUnknownPreserved,
|
||||
expectedErr: errors.New(`strict decoding error: yaml: unmarshal errors:
|
||||
line 7: key "num" already set in map`),
|
||||
},
|
||||
{
|
||||
name: "non-strict-decoding-preserve-unknown-yaml",
|
||||
body: multiVersionYAML,
|
||||
yaml: true,
|
||||
strictDecoding: false,
|
||||
preserveUnknownFields: true,
|
||||
expectedObj: expectedObjUnknownPreserved,
|
||||
expectedErr: nil,
|
||||
},
|
||||
}
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
v := "v1beta1"
|
||||
structuralSchemas := map[string]*structuralschema.Structural{}
|
||||
structuralSchema, err := structuralschema.NewStructural(&apiextensions.JSONSchemaProps{
|
||||
Type: "object",
|
||||
Properties: map[string]apiextensions.JSONSchemaProps{"num": {Type: "integer", Description: "v1beta1 num field"}},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
structuralSchemas[v] = structuralSchema
|
||||
delegate := serializerjson.NewSerializerWithOptions(serializerjson.DefaultMetaFactory, unstructuredCreator{}, nil, serializerjson.SerializerOptions{tc.yaml, false, tc.strictDecoding})
|
||||
decoder := &schemaCoercingDecoder{
|
||||
delegate: delegate,
|
||||
validator: unstructuredSchemaCoercer{
|
||||
dropInvalidMetadata: true,
|
||||
repairGeneration: true,
|
||||
structuralSchemas: structuralSchemas,
|
||||
structuralSchemaGK: schema.GroupKind{
|
||||
Group: "stable.example.com",
|
||||
Kind: "MultiVersion",
|
||||
},
|
||||
returnUnknownFieldPaths: tc.strictDecoding,
|
||||
preserveUnknownFields: tc.preserveUnknownFields,
|
||||
},
|
||||
}
|
||||
|
||||
obj, _, err := decoder.Decode([]byte(tc.body), nil, nil)
|
||||
if obj != nil {
|
||||
unstructured, ok := obj.(*unstructured.Unstructured)
|
||||
if !ok {
|
||||
t.Fatalf("obj is not an unstructured: %v", obj)
|
||||
}
|
||||
objBytes, err := unstructured.MarshalJSON()
|
||||
if err != nil {
|
||||
t.Fatalf("err marshaling json: %v", err)
|
||||
}
|
||||
expectedBytes, err := tc.expectedObj.MarshalJSON()
|
||||
if err != nil {
|
||||
t.Fatalf("err marshaling json: %v", err)
|
||||
}
|
||||
if bytes.Compare(objBytes, expectedBytes) != 0 {
|
||||
t.Fatalf("expected obj: \n%v\n got obj: \n%v\n", tc.expectedObj, obj)
|
||||
}
|
||||
}
|
||||
if err == nil || tc.expectedErr == nil {
|
||||
if err != nil || tc.expectedErr != nil {
|
||||
t.Fatalf("expected err: %v, got err: %v", tc.expectedErr, err)
|
||||
}
|
||||
} else if err.Error() != tc.expectedErr.Error() {
|
||||
t.Fatalf("expected err: \n%v\n got err: \n%v\n", tc.expectedErr, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type dummyAdmissionImpl struct{}
|
||||
|
||||
func (dummyAdmissionImpl) Handles(operation admission.Operation) bool { return false }
|
||||
|
@ -261,7 +261,8 @@ spec:
|
||||
"apiVersion": "%s",
|
||||
"kind": "%s",
|
||||
"metadata": {
|
||||
"name": "%s"
|
||||
"name": "%s",
|
||||
"resourceVersion": "%s"
|
||||
},
|
||||
"spec": {
|
||||
"knownField1": "val1",
|
||||
@ -295,6 +296,20 @@ spec:
|
||||
hostPort: 8082
|
||||
unknownNested: val`
|
||||
|
||||
crdValidBodyYAML = `
|
||||
apiVersion: "%s"
|
||||
kind: "%s"
|
||||
metadata:
|
||||
name: "%s"
|
||||
resourceVersion: "%s"
|
||||
spec:
|
||||
knownField1: val1
|
||||
ports:
|
||||
- name: portName
|
||||
containerPort: 8080
|
||||
protocol: TCP
|
||||
hostPort: 8081`
|
||||
|
||||
crdApplyInvalidBody = `
|
||||
{
|
||||
"apiVersion": "%s",
|
||||
@ -333,6 +348,24 @@ spec:
|
||||
}
|
||||
}`
|
||||
|
||||
crdApplyValidBody2 = `
|
||||
{
|
||||
"apiVersion": "%s",
|
||||
"kind": "%s",
|
||||
"metadata": {
|
||||
"name": "%s"
|
||||
},
|
||||
"spec": {
|
||||
"knownField1": "val2",
|
||||
"ports": [{
|
||||
"name": "portName",
|
||||
"containerPort": 8080,
|
||||
"protocol": "TCP",
|
||||
"hostPort": 8083
|
||||
}]
|
||||
}
|
||||
}`
|
||||
|
||||
patchYAMLBody = `
|
||||
apiVersion: %s
|
||||
kind: %s
|
||||
@ -2914,7 +2947,7 @@ func testFieldValidationApplyUpdateCRDSchemaless(t *testing.T, rest rest.Interfa
|
||||
}
|
||||
}
|
||||
|
||||
func setupCRD(t *testing.T, config *rest.Config, apiGroup string, schemaless bool) *apiextensionsv1.CustomResourceDefinition {
|
||||
func setupCRD(t testing.TB, config *rest.Config, apiGroup string, schemaless bool) *apiextensionsv1.CustomResourceDefinition {
|
||||
apiExtensionClient, err := apiextensionsclient.NewForConfig(config)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@ -2970,6 +3003,32 @@ func BenchmarkFieldValidation(b *testing.B) {
|
||||
|
||||
client := clientset.NewForConfigOrDie(config)
|
||||
|
||||
schemaCRD := setupCRD(b, config, "schema.example.com", false)
|
||||
schemaGVR := schema.GroupVersionResource{
|
||||
Group: schemaCRD.Spec.Group,
|
||||
Version: schemaCRD.Spec.Versions[0].Name,
|
||||
Resource: schemaCRD.Spec.Names.Plural,
|
||||
}
|
||||
schemaGVK := schema.GroupVersionKind{
|
||||
Group: schemaCRD.Spec.Group,
|
||||
Version: schemaCRD.Spec.Versions[0].Name,
|
||||
Kind: schemaCRD.Spec.Names.Kind,
|
||||
}
|
||||
|
||||
schemalessCRD := setupCRD(b, config, "schemaless.example.com", true)
|
||||
schemalessGVR := schema.GroupVersionResource{
|
||||
Group: schemalessCRD.Spec.Group,
|
||||
Version: schemalessCRD.Spec.Versions[0].Name,
|
||||
Resource: schemalessCRD.Spec.Names.Plural,
|
||||
}
|
||||
schemalessGVK := schema.GroupVersionKind{
|
||||
Group: schemalessCRD.Spec.Group,
|
||||
Version: schemalessCRD.Spec.Versions[0].Name,
|
||||
Kind: schemalessCRD.Spec.Names.Kind,
|
||||
}
|
||||
|
||||
rest := client.Discovery().RESTClient()
|
||||
|
||||
b.Run("Post", func(b *testing.B) { benchFieldValidationPost(b, client) })
|
||||
b.Run("Put", func(b *testing.B) { benchFieldValidationPut(b, client) })
|
||||
b.Run("PatchTyped", func(b *testing.B) { benchFieldValidationPatchTyped(b, client) })
|
||||
@ -2977,6 +3036,18 @@ func BenchmarkFieldValidation(b *testing.B) {
|
||||
b.Run("ApplyCreate", func(b *testing.B) { benchFieldValidationApplyCreate(b, client) })
|
||||
b.Run("ApplyUpdate", func(b *testing.B) { benchFieldValidationApplyUpdate(b, client) })
|
||||
|
||||
b.Run("PostCRD", func(b *testing.B) { benchFieldValidationPostCRD(b, rest, schemaGVK, schemaGVR) })
|
||||
b.Run("PutCRD", func(b *testing.B) { benchFieldValidationPutCRD(b, rest, schemaGVK, schemaGVR) })
|
||||
b.Run("PatchCRD", func(b *testing.B) { benchFieldValidationPatchCRD(b, rest, schemaGVK, schemaGVR) })
|
||||
b.Run("ApplyCreateCRD", func(b *testing.B) { benchFieldValidationApplyCreateCRD(b, rest, schemaGVK, schemaGVR) })
|
||||
b.Run("ApplyUpdateCRD", func(b *testing.B) { benchFieldValidationApplyUpdateCRD(b, rest, schemaGVK, schemaGVR) })
|
||||
|
||||
b.Run("PostCRDSchemaless", func(b *testing.B) { benchFieldValidationPostCRD(b, rest, schemalessGVK, schemalessGVR) })
|
||||
b.Run("PutCRDSchemaless", func(b *testing.B) { benchFieldValidationPutCRD(b, rest, schemalessGVK, schemalessGVR) })
|
||||
b.Run("PatchCRDSchemaless", func(b *testing.B) { benchFieldValidationPatchCRD(b, rest, schemalessGVK, schemalessGVR) })
|
||||
b.Run("ApplyCreateCRDSchemaless", func(b *testing.B) { benchFieldValidationApplyCreateCRD(b, rest, schemalessGVK, schemalessGVR) })
|
||||
b.Run("ApplyUpdateCRDSchemaless", func(b *testing.B) { benchFieldValidationApplyUpdateCRD(b, rest, schemalessGVK, schemalessGVR) })
|
||||
|
||||
}
|
||||
|
||||
func benchFieldValidationPost(b *testing.B, client clientset.Interface) {
|
||||
@ -3477,3 +3548,467 @@ func benchFieldValidationApplyUpdate(b *testing.B, client clientset.Interface) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func benchFieldValidationPostCRD(b *testing.B, rest rest.Interface, gvk schema.GroupVersionKind, gvr schema.GroupVersionResource) {
|
||||
var testcases = []struct {
|
||||
name string
|
||||
opts metav1.PatchOptions
|
||||
body string
|
||||
contentType string
|
||||
}{
|
||||
{
|
||||
name: "crd-post-strict-validation",
|
||||
opts: metav1.PatchOptions{
|
||||
FieldValidation: "Strict",
|
||||
},
|
||||
body: crdValidBody,
|
||||
},
|
||||
{
|
||||
name: "crd-post-warn-validation",
|
||||
opts: metav1.PatchOptions{
|
||||
FieldValidation: "Warn",
|
||||
},
|
||||
body: crdValidBody,
|
||||
},
|
||||
{
|
||||
name: "crd-post-ignore-validation",
|
||||
opts: metav1.PatchOptions{
|
||||
FieldValidation: "Ignore",
|
||||
},
|
||||
body: crdValidBody,
|
||||
},
|
||||
{
|
||||
name: "crd-post-no-validation",
|
||||
body: crdValidBody,
|
||||
},
|
||||
{
|
||||
name: "crd-post-strict-validation-yaml",
|
||||
opts: metav1.PatchOptions{
|
||||
FieldValidation: "Strict",
|
||||
},
|
||||
body: crdValidBodyYAML,
|
||||
contentType: "application/yaml",
|
||||
},
|
||||
{
|
||||
name: "crd-post-warn-validation-yaml",
|
||||
opts: metav1.PatchOptions{
|
||||
FieldValidation: "Warn",
|
||||
},
|
||||
body: crdValidBodyYAML,
|
||||
contentType: "application/yaml",
|
||||
},
|
||||
{
|
||||
name: "crd-post-ignore-validation-yaml",
|
||||
opts: metav1.PatchOptions{
|
||||
FieldValidation: "Ignore",
|
||||
},
|
||||
body: crdValidBodyYAML,
|
||||
contentType: "application/yaml",
|
||||
},
|
||||
{
|
||||
name: "crd-post-no-validation-yaml",
|
||||
body: crdValidBodyYAML,
|
||||
contentType: "application/yaml",
|
||||
},
|
||||
}
|
||||
for _, tc := range testcases {
|
||||
b.Run(tc.name, func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for n := 0; n < b.N; n++ {
|
||||
kind := gvk.Kind
|
||||
apiVersion := gvk.Group + "/" + gvk.Version
|
||||
|
||||
// create the CR as specified by the test case
|
||||
jsonBody := []byte(fmt.Sprintf(tc.body, apiVersion, kind, fmt.Sprintf("test-dep-%s-%d-%d-%d", tc.name, n, b.N, time.Now().UnixNano())))
|
||||
req := rest.Post().
|
||||
AbsPath("/apis", gvr.Group, gvr.Version, gvr.Resource).
|
||||
SetHeader("Content-Type", tc.contentType).
|
||||
VersionedParams(&tc.opts, metav1.ParameterCodec)
|
||||
result := req.Body([]byte(jsonBody)).Do(context.TODO())
|
||||
|
||||
if result.Error() != nil {
|
||||
b.Fatalf("unexpected post err: %v", result.Error())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func benchFieldValidationPutCRD(b *testing.B, rest rest.Interface, gvk schema.GroupVersionKind, gvr schema.GroupVersionResource) {
|
||||
var testcases = []struct {
|
||||
name string
|
||||
opts metav1.PatchOptions
|
||||
putBody string
|
||||
contentType string
|
||||
}{
|
||||
{
|
||||
name: "crd-put-strict-validation",
|
||||
opts: metav1.PatchOptions{
|
||||
FieldValidation: "Strict",
|
||||
},
|
||||
putBody: crdValidBody,
|
||||
},
|
||||
{
|
||||
name: "crd-put-warn-validation",
|
||||
opts: metav1.PatchOptions{
|
||||
FieldValidation: "Warn",
|
||||
},
|
||||
putBody: crdValidBody,
|
||||
},
|
||||
{
|
||||
name: "crd-put-ignore-validation",
|
||||
opts: metav1.PatchOptions{
|
||||
FieldValidation: "Ignore",
|
||||
},
|
||||
putBody: crdValidBody,
|
||||
},
|
||||
{
|
||||
name: "crd-put-no-validation",
|
||||
putBody: crdValidBody,
|
||||
},
|
||||
{
|
||||
name: "crd-put-strict-validation-yaml",
|
||||
opts: metav1.PatchOptions{
|
||||
FieldValidation: "Strict",
|
||||
},
|
||||
putBody: crdValidBodyYAML,
|
||||
contentType: "application/yaml",
|
||||
},
|
||||
{
|
||||
name: "crd-put-warn-validation-yaml",
|
||||
opts: metav1.PatchOptions{
|
||||
FieldValidation: "Warn",
|
||||
},
|
||||
putBody: crdValidBodyYAML,
|
||||
contentType: "application/yaml",
|
||||
},
|
||||
{
|
||||
name: "crd-put-ignore-validation-yaml",
|
||||
opts: metav1.PatchOptions{
|
||||
FieldValidation: "Ignore",
|
||||
},
|
||||
putBody: crdValidBodyYAML,
|
||||
contentType: "application/yaml",
|
||||
},
|
||||
{
|
||||
name: "crd-put-no-validation-yaml",
|
||||
putBody: crdValidBodyYAML,
|
||||
contentType: "application/yaml",
|
||||
},
|
||||
}
|
||||
for _, tc := range testcases {
|
||||
b.Run(tc.name, func(b *testing.B) {
|
||||
kind := gvk.Kind
|
||||
apiVersion := gvk.Group + "/" + gvk.Version
|
||||
names := make([]string, b.N)
|
||||
resourceVersions := make([]string, b.N)
|
||||
for n := 0; n < b.N; n++ {
|
||||
deployName := fmt.Sprintf("test-dep-%s-%d-%d-%d", tc.name, n, b.N, time.Now().UnixNano())
|
||||
names[n] = deployName
|
||||
|
||||
// create the CR as specified by the test case
|
||||
jsonPostBody := []byte(fmt.Sprintf(crdValidBody, apiVersion, kind, deployName))
|
||||
postReq := rest.Post().
|
||||
AbsPath("/apis", gvr.Group, gvr.Version, gvr.Resource).
|
||||
VersionedParams(&tc.opts, metav1.ParameterCodec)
|
||||
postResult, err := postReq.Body([]byte(jsonPostBody)).Do(context.TODO()).Raw()
|
||||
if err != nil {
|
||||
b.Fatalf("unexpeted error on CR creation: %v", err)
|
||||
}
|
||||
postUnstructured := &unstructured.Unstructured{}
|
||||
if err := postUnstructured.UnmarshalJSON(postResult); err != nil {
|
||||
b.Fatalf("unexpeted error unmarshalling created CR: %v", err)
|
||||
}
|
||||
resourceVersions[n] = postUnstructured.GetResourceVersion()
|
||||
}
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for n := 0; n < b.N; n++ {
|
||||
// update the CR as specified by the test case
|
||||
putBody := []byte(fmt.Sprintf(tc.putBody, apiVersion, kind, names[n], resourceVersions[n]))
|
||||
putReq := rest.Put().
|
||||
AbsPath("/apis", gvr.Group, gvr.Version, gvr.Resource).
|
||||
Name(names[n]).
|
||||
SetHeader("Content-Type", tc.contentType).
|
||||
VersionedParams(&tc.opts, metav1.ParameterCodec)
|
||||
result := putReq.Body([]byte(putBody)).Do(context.TODO())
|
||||
if result.Error() != nil {
|
||||
b.Fatalf("unexpected put err: %v", result.Error())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func benchFieldValidationPatchCRD(b *testing.B, rest rest.Interface, gvk schema.GroupVersionKind, gvr schema.GroupVersionResource) {
|
||||
patchYAMLBody := `
|
||||
apiVersion: %s
|
||||
kind: %s
|
||||
metadata:
|
||||
name: %s
|
||||
finalizers:
|
||||
- test-finalizer
|
||||
spec:
|
||||
cronSpec: "* * * * */5"
|
||||
ports:
|
||||
- name: x
|
||||
containerPort: 80
|
||||
protocol: TCP`
|
||||
|
||||
mergePatchBody := `
|
||||
{
|
||||
"spec": {
|
||||
"knownField1": "val1",
|
||||
"ports": [{
|
||||
"name": "portName",
|
||||
"containerPort": 8080,
|
||||
"protocol": "TCP",
|
||||
"hostPort": 8081
|
||||
}]
|
||||
}
|
||||
}
|
||||
`
|
||||
jsonPatchBody := `
|
||||
[
|
||||
{"op": "add", "path": "/spec/knownField1", "value": "val1"},
|
||||
{"op": "add", "path": "/spec/ports/0/name", "value": "portName"},
|
||||
{"op": "add", "path": "/spec/ports/0/containerPort", "value": 8080},
|
||||
{"op": "add", "path": "/spec/ports/0/protocol", "value": "TCP"},
|
||||
{"op": "add", "path": "/spec/ports/0/hostPort", "value": 8081}
|
||||
]
|
||||
`
|
||||
var testcases = []struct {
|
||||
name string
|
||||
patchType types.PatchType
|
||||
opts metav1.PatchOptions
|
||||
body string
|
||||
}{
|
||||
{
|
||||
name: "crd-merge-patch-strict-validation",
|
||||
patchType: types.MergePatchType,
|
||||
opts: metav1.PatchOptions{
|
||||
FieldValidation: "Strict",
|
||||
},
|
||||
body: mergePatchBody,
|
||||
},
|
||||
{
|
||||
name: "crd-merge-patch-warn-validation",
|
||||
patchType: types.MergePatchType,
|
||||
opts: metav1.PatchOptions{
|
||||
FieldValidation: "Warn",
|
||||
},
|
||||
body: mergePatchBody,
|
||||
},
|
||||
{
|
||||
name: "crd-merge-patch-ignore-validation",
|
||||
patchType: types.MergePatchType,
|
||||
opts: metav1.PatchOptions{
|
||||
FieldValidation: "Ignore",
|
||||
},
|
||||
body: mergePatchBody,
|
||||
},
|
||||
{
|
||||
name: "crd-merge-patch-no-validation",
|
||||
patchType: types.MergePatchType,
|
||||
body: mergePatchBody,
|
||||
},
|
||||
{
|
||||
name: "crd-json-patch-strict-validation",
|
||||
patchType: types.JSONPatchType,
|
||||
opts: metav1.PatchOptions{
|
||||
FieldValidation: "Strict",
|
||||
},
|
||||
body: jsonPatchBody,
|
||||
},
|
||||
{
|
||||
name: "crd-json-patch-warn-validation",
|
||||
patchType: types.JSONPatchType,
|
||||
opts: metav1.PatchOptions{
|
||||
FieldValidation: "Warn",
|
||||
},
|
||||
body: jsonPatchBody,
|
||||
},
|
||||
{
|
||||
name: "crd-json-patch-ignore-validation",
|
||||
patchType: types.JSONPatchType,
|
||||
opts: metav1.PatchOptions{
|
||||
FieldValidation: "Ignore",
|
||||
},
|
||||
body: jsonPatchBody,
|
||||
},
|
||||
{
|
||||
name: "crd-json-patch-no-validation",
|
||||
patchType: types.JSONPatchType,
|
||||
body: jsonPatchBody,
|
||||
},
|
||||
}
|
||||
for _, tc := range testcases {
|
||||
b.Run(tc.name, func(b *testing.B) {
|
||||
kind := gvk.Kind
|
||||
apiVersion := gvk.Group + "/" + gvk.Version
|
||||
names := make([]string, b.N)
|
||||
for n := 0; n < b.N; n++ {
|
||||
deployName := fmt.Sprintf("test-dep-%s-%d-%d-%d", tc.name, n, b.N, time.Now().UnixNano())
|
||||
names[n] = deployName
|
||||
|
||||
// create a CR
|
||||
yamlBody := []byte(fmt.Sprintf(string(patchYAMLBody), apiVersion, kind, deployName))
|
||||
createResult, err := rest.Patch(types.ApplyPatchType).
|
||||
AbsPath("/apis", gvr.Group, gvr.Version, gvr.Resource).
|
||||
Name(deployName).
|
||||
Param("fieldManager", "apply_test").
|
||||
Body(yamlBody).
|
||||
DoRaw(context.TODO())
|
||||
if err != nil {
|
||||
b.Fatalf("failed to create custom resource with apply: %v:\n%v", err, string(createResult))
|
||||
}
|
||||
}
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for n := 0; n < b.N; n++ {
|
||||
// patch the CR as specified by the test case
|
||||
req := rest.Patch(tc.patchType).
|
||||
AbsPath("/apis", gvr.Group, gvr.Version, gvr.Resource).
|
||||
Name(names[n]).
|
||||
VersionedParams(&tc.opts, metav1.ParameterCodec)
|
||||
result := req.Body([]byte(tc.body)).Do(context.TODO())
|
||||
if result.Error() != nil {
|
||||
b.Fatalf("unexpected patch err: %v", result.Error())
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func benchFieldValidationApplyCreateCRD(b *testing.B, rest rest.Interface, gvk schema.GroupVersionKind, gvr schema.GroupVersionResource) {
|
||||
var testcases = []struct {
|
||||
name string
|
||||
opts metav1.PatchOptions
|
||||
}{
|
||||
{
|
||||
name: "strict-validation",
|
||||
opts: metav1.PatchOptions{
|
||||
FieldValidation: "Strict",
|
||||
FieldManager: "mgr",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "warn-validation",
|
||||
opts: metav1.PatchOptions{
|
||||
FieldValidation: "Warn",
|
||||
FieldManager: "mgr",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ignore-validation",
|
||||
opts: metav1.PatchOptions{
|
||||
FieldValidation: "Ignore",
|
||||
FieldManager: "mgr",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no-validation",
|
||||
opts: metav1.PatchOptions{
|
||||
FieldManager: "mgr",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
b.Run(tc.name, func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for n := 0; n < b.N; n++ {
|
||||
kind := gvk.Kind
|
||||
apiVersion := gvk.Group + "/" + gvk.Version
|
||||
name := fmt.Sprintf("test-dep-%s-%d-%d-%d", tc.name, n, b.N, time.Now().UnixNano())
|
||||
|
||||
// create the CR as specified by the test case
|
||||
applyCreateBody := []byte(fmt.Sprintf(crdApplyValidBody, apiVersion, kind, name))
|
||||
|
||||
req := rest.Patch(types.ApplyPatchType).
|
||||
AbsPath("/apis", gvr.Group, gvr.Version, gvr.Resource).
|
||||
Name(name).
|
||||
VersionedParams(&tc.opts, metav1.ParameterCodec)
|
||||
result := req.Body(applyCreateBody).Do(context.TODO())
|
||||
if result.Error() != nil {
|
||||
b.Fatalf("unexpected apply err: %v", result.Error())
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func benchFieldValidationApplyUpdateCRD(b *testing.B, rest rest.Interface, gvk schema.GroupVersionKind, gvr schema.GroupVersionResource) {
|
||||
var testcases = []struct {
|
||||
name string
|
||||
opts metav1.PatchOptions
|
||||
}{
|
||||
{
|
||||
name: "strict-validation",
|
||||
opts: metav1.PatchOptions{
|
||||
FieldValidation: "Strict",
|
||||
FieldManager: "mgr",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "warn-validation",
|
||||
opts: metav1.PatchOptions{
|
||||
FieldValidation: "Warn",
|
||||
FieldManager: "mgr",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ignore-validation",
|
||||
opts: metav1.PatchOptions{
|
||||
FieldValidation: "Ignore",
|
||||
FieldManager: "mgr",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no-validation",
|
||||
opts: metav1.PatchOptions{
|
||||
FieldManager: "mgr",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
b.Run(tc.name, func(b *testing.B) {
|
||||
kind := gvk.Kind
|
||||
apiVersion := gvk.Group + "/" + gvk.Version
|
||||
names := make([]string, b.N)
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
names[n] = fmt.Sprintf("apply-update-crd-%s-%d-%d-%d", tc.name, n, b.N, time.Now().UnixNano())
|
||||
applyCreateBody := []byte(fmt.Sprintf(crdApplyValidBody, apiVersion, kind, names[n]))
|
||||
createReq := rest.Patch(types.ApplyPatchType).
|
||||
AbsPath("/apis", gvr.Group, gvr.Version, gvr.Resource).
|
||||
Name(names[n]).
|
||||
VersionedParams(&tc.opts, metav1.ParameterCodec)
|
||||
createResult := createReq.Body(applyCreateBody).Do(context.TODO())
|
||||
if createResult.Error() != nil {
|
||||
b.Fatalf("unexpected apply create err: %v", createResult.Error())
|
||||
}
|
||||
}
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for n := 0; n < b.N; n++ {
|
||||
applyUpdateBody := []byte(fmt.Sprintf(crdApplyValidBody2, apiVersion, kind, names[n]))
|
||||
updateReq := rest.Patch(types.ApplyPatchType).
|
||||
AbsPath("/apis", gvr.Group, gvr.Version, gvr.Resource).
|
||||
Name(names[n]).
|
||||
VersionedParams(&tc.opts, metav1.ParameterCodec)
|
||||
result := updateReq.Body(applyUpdateBody).Do(context.TODO())
|
||||
|
||||
if result.Error() != nil {
|
||||
b.Fatalf("unexpected apply err: %v", result.Error())
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user