Merge pull request #108204 from kevindelgado/field-validation-crd-unit-tests

Field validation CRD benchmarks and decoder unit tests
This commit is contained in:
Kubernetes Prow Robot 2022-03-09 20:01:35 -08:00 committed by GitHub
commit b90bddfd9f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 733 additions and 2 deletions

View File

@ -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 }

View File

@ -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())
}
}
})
}
}