mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 11:50:44 +00:00
FieldValidation tests for endpoints apiserver and benchmarks for integration tests (#107848)
* wip, working post-strict-yaml * wip, merge-patch and json-patch tests added * added SMP tests * cleanup * add benchmarks * more detailed test failure message * start adding field validation integration benchmarks * use valid input for benchmarking * fix remaining integration benchmarks * benchmarking feedback * fix endpoints benchmarking * remove unused vars
This commit is contained in:
parent
912c9c46f8
commit
df2768123d
@ -56,6 +56,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer/streaming"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/diff"
|
||||
"k8s.io/apimachinery/pkg/util/net"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
@ -214,6 +215,10 @@ type defaultAPIServer struct {
|
||||
container *restful.Container
|
||||
}
|
||||
|
||||
func handleWithWarnings(storage map[string]rest.Storage) http.Handler {
|
||||
return genericapifilters.WithWarningRecorder(handle(storage))
|
||||
}
|
||||
|
||||
// uses the default settings
|
||||
func handle(storage map[string]rest.Storage) http.Handler {
|
||||
return handleInternal(storage, admissionControl, nil)
|
||||
@ -3965,7 +3970,7 @@ func TestUpdateChecksAPIVersion(t *testing.T) {
|
||||
|
||||
// runRequest is used by TestDryRun since it runs the test twice in a
|
||||
// row with a slightly different URL (one has ?dryRun, one doesn't).
|
||||
func runRequest(t *testing.T, path, verb string, data []byte, contentType string) *http.Response {
|
||||
func runRequest(t testing.TB, path, verb string, data []byte, contentType string) *http.Response {
|
||||
request, err := http.NewRequest(verb, path, bytes.NewBuffer(data))
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
@ -4000,6 +4005,257 @@ func (storage *SimpleRESTStorageWithDeleteCollection) DeleteCollection(ctx conte
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// shared vars used by both TestFieldValidation and BenchmarkFieldValidation
|
||||
var (
|
||||
strictFieldValidation = "?fieldValidation=Strict"
|
||||
warnFieldValidation = "?fieldValidation=Warn"
|
||||
ignoreFieldValidation = "?fieldValidation=Ignore"
|
||||
)
|
||||
|
||||
// TestFieldValidation tests the create, update, and patch handlers for correctness when faced with field validation errors.
|
||||
func TestFieldValidation(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ServerSideFieldValidation, true)()
|
||||
var (
|
||||
strictDecodingErr = `strict decoding error: duplicate field \"other\", unknown field \"unknown\"`
|
||||
strictDecodingWarns = []string{`duplicate field "other"`, `unknown field "unknown"`}
|
||||
strictDecodingErrYAML = `strict decoding error: yaml: unmarshal errors:\n line 6: key \"other\" already set in map, unknown field \"unknown\"`
|
||||
strictDecodingWarnsYAML = []string{`line 6: key "other" already set in map`, `unknown field "unknown"`}
|
||||
strictDecodingErrYAMLPut = `strict decoding error: yaml: unmarshal errors:\n line 7: key \"other\" already set in map, unknown field \"unknown\"`
|
||||
strictDecodingWarnsYAMLPut = []string{`line 7: key "other" already set in map`, `unknown field "unknown"`}
|
||||
|
||||
invalidJSONDataPost = []byte(`{"kind":"Simple", "apiVersion":"test.group/version", "metadata":{"creationTimestamp":null}, "other":"foo","other":"bar","unknown":"baz"}`)
|
||||
invalidYAMLDataPost = []byte(`apiVersion: test.group/version
|
||||
kind: Simple
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
other: foo
|
||||
other: bar
|
||||
unknown: baz`)
|
||||
|
||||
invalidJSONDataPut = []byte(`{"kind":"Simple", "apiVersion":"test.group/version", "metadata":{"name":"id", "creationTimestamp":null}, "other":"foo","other":"bar","unknown":"baz"}`)
|
||||
invalidYAMLDataPut = []byte(`apiVersion: test.group/version
|
||||
kind: Simple
|
||||
metadata:
|
||||
name: id
|
||||
creationTimestamp: null
|
||||
other: foo
|
||||
other: bar
|
||||
unknown: baz`)
|
||||
|
||||
invalidMergePatch = []byte(`{"labels":{"foo":"bar"}, "unknown": "foo", "other": "foo", "other": "bar"}`)
|
||||
invalidJSONPatch = []byte(`
|
||||
[
|
||||
{"op": "add", "path": "/unknown", "value": "foo"},
|
||||
{"op": "add", "path": "/other", "value": "foo"},
|
||||
{"op": "add", "path": "/other", "value": "bar"}
|
||||
]
|
||||
`)
|
||||
// note: duplicate fields in the patch itself
|
||||
// are dropped by the
|
||||
// evanphx/json-patch library and is expected.
|
||||
jsonPatchStrictDecodingErr = `strict decoding error: unknown field \"unknown\"`
|
||||
jsonPatchStrictDecodingWarns = []string{`unknown field "unknown"`}
|
||||
|
||||
invalidSMP = []byte(`{"unknown": "foo", "other":"foo", "other": "bar"}`)
|
||||
|
||||
fieldValidationTests = []struct {
|
||||
name string
|
||||
path string
|
||||
verb string
|
||||
data []byte
|
||||
queryParams string
|
||||
contentType string
|
||||
expectedErr string
|
||||
expectedWarns []string
|
||||
expectedStatusCode int
|
||||
}{
|
||||
// Create
|
||||
{name: "post-strict-validation", path: "/namespaces/default/simples", verb: "POST", data: invalidJSONDataPost, queryParams: strictFieldValidation, expectedStatusCode: http.StatusBadRequest, expectedErr: strictDecodingErr},
|
||||
{name: "post-warn-validation", path: "/namespaces/default/simples", verb: "POST", data: invalidJSONDataPost, queryParams: warnFieldValidation, expectedStatusCode: http.StatusCreated, expectedWarns: strictDecodingWarns},
|
||||
{name: "post-ignore-validation", path: "/namespaces/default/simples", verb: "POST", data: invalidJSONDataPost, queryParams: ignoreFieldValidation, expectedStatusCode: http.StatusCreated},
|
||||
|
||||
{name: "post-strict-validation-yaml", path: "/namespaces/default/simples", verb: "POST", data: invalidYAMLDataPost, queryParams: strictFieldValidation, contentType: "application/yaml", expectedStatusCode: http.StatusBadRequest, expectedErr: strictDecodingErrYAML},
|
||||
{name: "post-warn-validation-yaml", path: "/namespaces/default/simples", verb: "POST", data: invalidYAMLDataPost, queryParams: warnFieldValidation, contentType: "application/yaml", expectedStatusCode: http.StatusCreated, expectedWarns: strictDecodingWarnsYAML},
|
||||
{name: "post-ignore-validation-yaml", path: "/namespaces/default/simples", verb: "POST", data: invalidYAMLDataPost, queryParams: ignoreFieldValidation, contentType: "application/yaml", expectedStatusCode: http.StatusCreated},
|
||||
|
||||
// Update
|
||||
{name: "put-strict-validation", path: "/namespaces/default/simples/id", verb: "PUT", data: invalidJSONDataPut, queryParams: strictFieldValidation, expectedStatusCode: http.StatusBadRequest, expectedErr: strictDecodingErr},
|
||||
{name: "put-warn-validation", path: "/namespaces/default/simples/id", verb: "PUT", data: invalidJSONDataPut, queryParams: warnFieldValidation, expectedStatusCode: http.StatusOK, expectedWarns: strictDecodingWarns},
|
||||
{name: "put-ignore-validation", path: "/namespaces/default/simples/id", verb: "PUT", data: invalidJSONDataPut, queryParams: ignoreFieldValidation, expectedStatusCode: http.StatusOK},
|
||||
|
||||
{name: "put-strict-validation-yaml", path: "/namespaces/default/simples/id", verb: "PUT", data: invalidYAMLDataPut, queryParams: strictFieldValidation, contentType: "application/yaml", expectedStatusCode: http.StatusBadRequest, expectedErr: strictDecodingErrYAMLPut},
|
||||
{name: "put-warn-validation-yaml", path: "/namespaces/default/simples/id", verb: "PUT", data: invalidYAMLDataPut, queryParams: warnFieldValidation, contentType: "application/yaml", expectedStatusCode: http.StatusOK, expectedWarns: strictDecodingWarnsYAMLPut},
|
||||
{name: "put-ignore-validation-yaml", path: "/namespaces/default/simples/id", verb: "PUT", data: invalidYAMLDataPut, queryParams: ignoreFieldValidation, contentType: "application/yaml", expectedStatusCode: http.StatusOK},
|
||||
|
||||
// MergePatch
|
||||
{name: "merge-patch-strict-validation", path: "/namespaces/default/simples/id", verb: "PATCH", data: invalidMergePatch, queryParams: strictFieldValidation, contentType: "application/merge-patch+json; charset=UTF-8", expectedStatusCode: http.StatusUnprocessableEntity, expectedErr: strictDecodingErr},
|
||||
{name: "merge-patch-warn-validation", path: "/namespaces/default/simples/id", verb: "PATCH", data: invalidMergePatch, queryParams: warnFieldValidation, contentType: "application/merge-patch+json; charset=UTF-8", expectedStatusCode: http.StatusOK, expectedWarns: strictDecodingWarns},
|
||||
{name: "merge-patch-ignore-validation", path: "/namespaces/default/simples/id", verb: "PATCH", data: invalidMergePatch, queryParams: ignoreFieldValidation, contentType: "application/merge-patch+json; charset=UTF-8", expectedStatusCode: http.StatusOK},
|
||||
|
||||
// JSON Patch
|
||||
{name: "json-patch-strict-validation", path: "/namespaces/default/simples/id", verb: "PATCH", data: invalidJSONPatch, queryParams: strictFieldValidation, contentType: "application/json-patch+json; charset=UTF-8", expectedStatusCode: http.StatusUnprocessableEntity, expectedErr: jsonPatchStrictDecodingErr},
|
||||
{name: "json-patch-warn-validation", path: "/namespaces/default/simples/id", verb: "PATCH", data: invalidJSONPatch, queryParams: warnFieldValidation, contentType: "application/json-patch+json; charset=UTF-8", expectedStatusCode: http.StatusOK, expectedWarns: jsonPatchStrictDecodingWarns},
|
||||
{name: "json-patch-ignore-validation", path: "/namespaces/default/simples/id", verb: "PATCH", data: invalidJSONPatch, queryParams: ignoreFieldValidation, contentType: "application/json-patch+json; charset=UTF-8", expectedStatusCode: http.StatusOK},
|
||||
|
||||
// SMP
|
||||
{name: "strategic-merge-patch-strict-validation", path: "/namespaces/default/simples/id", verb: "PATCH", data: invalidSMP, queryParams: strictFieldValidation, contentType: "application/strategic-merge-patch+json; charset=UTF-8", expectedStatusCode: http.StatusUnprocessableEntity, expectedErr: strictDecodingErr},
|
||||
{name: "strategic-merge-patch-warn-validation", path: "/namespaces/default/simples/id", verb: "PATCH", data: invalidSMP, queryParams: warnFieldValidation, contentType: "application/strategic-merge-patch+json; charset=UTF-8", expectedStatusCode: http.StatusOK, expectedWarns: strictDecodingWarns},
|
||||
{name: "strategic-merge-patch-ignore-validation", path: "/namespaces/default/simples/id", verb: "PATCH", data: invalidSMP, queryParams: ignoreFieldValidation, contentType: "application/strategic-merge-patch+json; charset=UTF-8", expectedStatusCode: http.StatusOK},
|
||||
}
|
||||
)
|
||||
|
||||
server := httptest.NewServer(handleWithWarnings(map[string]rest.Storage{
|
||||
"simples": &SimpleRESTStorageWithDeleteCollection{
|
||||
SimpleRESTStorage{
|
||||
item: genericapitesting.Simple{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "id",
|
||||
Namespace: "",
|
||||
UID: "uid",
|
||||
},
|
||||
Other: "baz",
|
||||
},
|
||||
},
|
||||
},
|
||||
"simples/subsimple": &SimpleXGSubresourceRESTStorage{
|
||||
item: genericapitesting.SimpleXGSubresource{
|
||||
SubresourceInfo: "foo",
|
||||
},
|
||||
itemGVK: testGroup2Version.WithKind("SimpleXGSubresource"),
|
||||
},
|
||||
}))
|
||||
defer server.Close()
|
||||
for _, test := range fieldValidationTests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
baseURL := server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version
|
||||
response := runRequest(t, baseURL+test.path+test.queryParams, test.verb, test.data, test.contentType)
|
||||
buf := new(bytes.Buffer)
|
||||
buf.ReadFrom(response.Body)
|
||||
|
||||
if response.StatusCode != test.expectedStatusCode || !strings.Contains(buf.String(), test.expectedErr) {
|
||||
t.Fatalf("unexpected response: %#v, expected err: %#v", response, test.expectedErr)
|
||||
}
|
||||
|
||||
warnings, _ := net.ParseWarningHeaders(response.Header["Warning"])
|
||||
if len(warnings) != len(test.expectedWarns) {
|
||||
t.Fatalf("unexpected number of warnings. Got count %d, expected %d. Got warnings %#v, expected %#v", len(warnings), len(test.expectedWarns), warnings, test.expectedWarns)
|
||||
|
||||
}
|
||||
for i, warn := range warnings {
|
||||
if warn.Text != test.expectedWarns[i] {
|
||||
t.Fatalf("unexpected warning: %#v, expected warning: %#v", warn.Text, test.expectedWarns[i])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// BenchmarkFieldValidation benchmarks the create, update, and patch handlers for performance distinctions between
|
||||
// strict, warn, and ignore field validation handling.
|
||||
func BenchmarkFieldValidation(b *testing.B) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(b, utilfeature.DefaultFeatureGate, features.ServerSideFieldValidation, true)()
|
||||
var (
|
||||
validJSONDataPost = []byte(`{"kind":"Simple", "apiVersion":"test.group/version", "metadata":{"creationTimestamp":null}, "other":"foo"}`)
|
||||
validYAMLDataPost = []byte(`apiVersion: test.group/version
|
||||
kind: Simple
|
||||
metadata:
|
||||
creationTimestamp: null
|
||||
other: foo`)
|
||||
|
||||
validJSONDataPut = []byte(`{"kind":"Simple", "apiVersion":"test.group/version", "metadata":{"name":"id", "creationTimestamp":null}, "other":"bar"}`)
|
||||
validYAMLDataPut = []byte(`apiVersion: test.group/version
|
||||
kind: Simple
|
||||
metadata:
|
||||
name: id
|
||||
creationTimestamp: null
|
||||
other: bar`)
|
||||
|
||||
validMergePatch = []byte(`{"labels":{"foo":"bar"}, "other": "bar"}`)
|
||||
validJSONPatch = []byte(`
|
||||
[
|
||||
{"op": "add", "path": "/other", "value": "bar"}
|
||||
]
|
||||
`)
|
||||
validSMP = []byte(`{"other": "bar"}`)
|
||||
|
||||
fieldValidationBenchmarks = []struct {
|
||||
name string
|
||||
path string
|
||||
verb string
|
||||
data []byte
|
||||
queryParams string
|
||||
contentType string
|
||||
expectedStatusCode int
|
||||
}{
|
||||
// Create
|
||||
{name: "post-strict-validation", path: "/namespaces/default/simples", verb: "POST", data: validJSONDataPost, queryParams: strictFieldValidation, expectedStatusCode: http.StatusCreated},
|
||||
{name: "post-warn-validation", path: "/namespaces/default/simples", verb: "POST", data: validJSONDataPost, queryParams: warnFieldValidation, expectedStatusCode: http.StatusCreated},
|
||||
{name: "post-ignore-validation", path: "/namespaces/default/simples", verb: "POST", data: validJSONDataPost, queryParams: ignoreFieldValidation, expectedStatusCode: http.StatusCreated},
|
||||
|
||||
{name: "post-strict-validation-yaml", path: "/namespaces/default/simples", verb: "POST", data: validYAMLDataPost, queryParams: strictFieldValidation, contentType: "application/yaml", expectedStatusCode: http.StatusCreated},
|
||||
{name: "post-warn-validation-yaml", path: "/namespaces/default/simples", verb: "POST", data: validYAMLDataPost, queryParams: warnFieldValidation, contentType: "application/yaml", expectedStatusCode: http.StatusCreated},
|
||||
{name: "post-ignore-validation-yaml", path: "/namespaces/default/simples", verb: "POST", data: validYAMLDataPost, queryParams: ignoreFieldValidation, contentType: "application/yaml", expectedStatusCode: http.StatusCreated},
|
||||
|
||||
// Update
|
||||
{name: "put-strict-validation", path: "/namespaces/default/simples/id", verb: "PUT", data: validJSONDataPut, queryParams: strictFieldValidation, expectedStatusCode: http.StatusOK},
|
||||
{name: "put-warn-validation", path: "/namespaces/default/simples/id", verb: "PUT", data: validJSONDataPut, queryParams: warnFieldValidation, expectedStatusCode: http.StatusOK},
|
||||
{name: "put-ignore-validation", path: "/namespaces/default/simples/id", verb: "PUT", data: validJSONDataPut, queryParams: ignoreFieldValidation, expectedStatusCode: http.StatusOK},
|
||||
|
||||
{name: "put-strict-validation-yaml", path: "/namespaces/default/simples/id", verb: "PUT", data: validYAMLDataPut, queryParams: strictFieldValidation, contentType: "application/yaml", expectedStatusCode: http.StatusOK},
|
||||
{name: "put-warn-validation-yaml", path: "/namespaces/default/simples/id", verb: "PUT", data: validYAMLDataPut, queryParams: warnFieldValidation, contentType: "application/yaml", expectedStatusCode: http.StatusOK},
|
||||
{name: "put-ignore-validation-yaml", path: "/namespaces/default/simples/id", verb: "PUT", data: validYAMLDataPut, queryParams: ignoreFieldValidation, contentType: "application/yaml", expectedStatusCode: http.StatusOK},
|
||||
|
||||
// MergePatch
|
||||
{name: "merge-patch-strict-validation", path: "/namespaces/default/simples/id", verb: "PATCH", data: validMergePatch, queryParams: strictFieldValidation, contentType: "application/merge-patch+json; charset=UTF-8", expectedStatusCode: http.StatusOK},
|
||||
{name: "merge-patch-warn-validation", path: "/namespaces/default/simples/id", verb: "PATCH", data: validMergePatch, queryParams: warnFieldValidation, contentType: "application/merge-patch+json; charset=UTF-8", expectedStatusCode: http.StatusOK},
|
||||
{name: "merge-patch-ignore-validation", path: "/namespaces/default/simples/id", verb: "PATCH", data: validMergePatch, queryParams: ignoreFieldValidation, contentType: "application/merge-patch+json; charset=UTF-8", expectedStatusCode: http.StatusOK},
|
||||
|
||||
// JSON Patch
|
||||
{name: "json-patch-strict-validation", path: "/namespaces/default/simples/id", verb: "PATCH", data: validJSONPatch, queryParams: strictFieldValidation, contentType: "application/json-patch+json; charset=UTF-8", expectedStatusCode: http.StatusOK},
|
||||
{name: "json-patch-warn-validation", path: "/namespaces/default/simples/id", verb: "PATCH", data: validJSONPatch, queryParams: warnFieldValidation, contentType: "application/json-patch+json; charset=UTF-8", expectedStatusCode: http.StatusOK},
|
||||
{name: "json-patch-ignore-validation", path: "/namespaces/default/simples/id", verb: "PATCH", data: validJSONPatch, queryParams: ignoreFieldValidation, contentType: "application/json-patch+json; charset=UTF-8", expectedStatusCode: http.StatusOK},
|
||||
|
||||
// SMP
|
||||
{name: "strategic-merge-patch-strict-validation", path: "/namespaces/default/simples/id", verb: "PATCH", data: validSMP, queryParams: strictFieldValidation, contentType: "application/strategic-merge-patch+json; charset=UTF-8", expectedStatusCode: http.StatusOK},
|
||||
{name: "strategic-merge-patch-warn-validation", path: "/namespaces/default/simples/id", verb: "PATCH", data: validSMP, queryParams: warnFieldValidation, contentType: "application/strategic-merge-patch+json; charset=UTF-8", expectedStatusCode: http.StatusOK},
|
||||
{name: "strategic-merge-patch-ignore-validation", path: "/namespaces/default/simples/id", verb: "PATCH", data: validSMP, queryParams: ignoreFieldValidation, contentType: "application/strategic-merge-patch+json; charset=UTF-8", expectedStatusCode: http.StatusOK},
|
||||
}
|
||||
)
|
||||
|
||||
server := httptest.NewServer(handleWithWarnings(map[string]rest.Storage{
|
||||
"simples": &SimpleRESTStorageWithDeleteCollection{
|
||||
SimpleRESTStorage{
|
||||
item: genericapitesting.Simple{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "id",
|
||||
Namespace: "",
|
||||
UID: "uid",
|
||||
},
|
||||
Other: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
"simples/subsimple": &SimpleXGSubresourceRESTStorage{
|
||||
item: genericapitesting.SimpleXGSubresource{
|
||||
SubresourceInfo: "foo",
|
||||
},
|
||||
itemGVK: testGroup2Version.WithKind("SimpleXGSubresource"),
|
||||
},
|
||||
}))
|
||||
defer server.Close()
|
||||
for _, test := range fieldValidationBenchmarks {
|
||||
b.Run(test.name, func(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
baseURL := server.URL + "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version
|
||||
response := runRequest(b, baseURL+test.path+test.queryParams, test.verb, test.data, test.contentType)
|
||||
if response.StatusCode != test.expectedStatusCode {
|
||||
b.Fatalf("unexpected status code: %d, expected: %d", response.StatusCode, test.expectedStatusCode)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDryRunDisabled(t *testing.T) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DryRun, false)()
|
||||
|
||||
|
@ -19,9 +19,11 @@ package apiserver
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
||||
apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
|
||||
@ -80,6 +82,38 @@ var (
|
||||
}
|
||||
}
|
||||
`
|
||||
validBodyJSON = `
|
||||
{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": {
|
||||
"name": "%s",
|
||||
"labels": {"app": "nginx"},
|
||||
"annotations": {"a1": "foo", "a2": "bar"}
|
||||
},
|
||||
"spec": {
|
||||
"selector": {
|
||||
"matchLabels": {
|
||||
"app": "nginx"
|
||||
}
|
||||
},
|
||||
"template": {
|
||||
"metadata": {
|
||||
"labels": {
|
||||
"app": "nginx"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"containers": [{
|
||||
"name": "nginx",
|
||||
"image": "nginx:latest",
|
||||
"imagePullPolicy": "Always"
|
||||
}]
|
||||
}
|
||||
},
|
||||
"replicas": 2
|
||||
}
|
||||
}`
|
||||
|
||||
invalidBodyYAML = `apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
@ -108,36 +142,31 @@ spec:
|
||||
imagePullPolicy: Always
|
||||
imagePullPolicy: Never`
|
||||
|
||||
validBodyJSON = `
|
||||
{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": {
|
||||
"name": "%s",
|
||||
"labels": {"app": "nginx"}
|
||||
},
|
||||
"spec": {
|
||||
"selector": {
|
||||
"matchLabels": {
|
||||
"app": "nginx"
|
||||
}
|
||||
},
|
||||
"template": {
|
||||
"metadata": {
|
||||
"labels": {
|
||||
"app": "nginx"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"containers": [{
|
||||
"name": "nginx",
|
||||
"image": "nginx:latest",
|
||||
"imagePullPolicy": "Always"
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}`
|
||||
validBodyYAML = `apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: %s
|
||||
labels:
|
||||
app: nginx
|
||||
annotations:
|
||||
a1: foo
|
||||
a2: bar
|
||||
spec:
|
||||
replicas: 2
|
||||
paused: true
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:latest
|
||||
imagePullPolicy: Always`
|
||||
|
||||
applyInvalidBody = `{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
@ -170,6 +199,38 @@ spec:
|
||||
}
|
||||
}
|
||||
}`
|
||||
applyValidBody = `
|
||||
{
|
||||
"apiVersion": "apps/v1",
|
||||
"kind": "Deployment",
|
||||
"metadata": {
|
||||
"name": "%s",
|
||||
"labels": {"app": "nginx"},
|
||||
"annotations": {"a1": "foo", "a2": "bar"}
|
||||
},
|
||||
"spec": {
|
||||
"selector": {
|
||||
"matchLabels": {
|
||||
"app": "nginx"
|
||||
}
|
||||
},
|
||||
"template": {
|
||||
"metadata": {
|
||||
"labels": {
|
||||
"app": "nginx"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"containers": [{
|
||||
"name": "nginx",
|
||||
"image": "nginx:latest",
|
||||
"imagePullPolicy": "Always"
|
||||
}]
|
||||
}
|
||||
},
|
||||
"replicas": 3
|
||||
}
|
||||
}`
|
||||
crdInvalidBody = `
|
||||
{
|
||||
"apiVersion": "%s",
|
||||
@ -458,7 +519,7 @@ func testFieldValidationPost(t *testing.T, client clientset.Interface) {
|
||||
bodyBase: invalidBodyJSON,
|
||||
},
|
||||
{
|
||||
name: "post-default-ignore-validation",
|
||||
name: "post-no-validation",
|
||||
bodyBase: invalidBodyJSON,
|
||||
strictDecodingWarnings: []string{
|
||||
`unknown field "spec.unknown1"`,
|
||||
@ -1005,6 +1066,36 @@ func testFieldValidationPatchTyped(t *testing.T, client clientset.Interface) {
|
||||
// with unknown fields errors out when fieldValidation is strict,
|
||||
// but succeeds when fieldValidation is ignored.
|
||||
func testFieldValidationSMP(t *testing.T, client clientset.Interface) {
|
||||
// non-conflicting SMP has issues with the patch (duplicate fields),
|
||||
// but doesn't conflict with the existing object it's being patched to
|
||||
nonconflictingSMPBody := `
|
||||
{
|
||||
"spec": {
|
||||
"paused": true,
|
||||
"paused": false,
|
||||
"selector": {
|
||||
"matchLabels": {
|
||||
"app": "nginx"
|
||||
}
|
||||
},
|
||||
"template": {
|
||||
"metadata": {
|
||||
"labels": {
|
||||
"app": "nginx"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"containers": [{
|
||||
"name": "nginx",
|
||||
"imagePullPolicy": "Always",
|
||||
"imagePullPolicy": "Never"
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
smpBody := `
|
||||
{
|
||||
"spec": {
|
||||
@ -1036,36 +1127,6 @@ func testFieldValidationSMP(t *testing.T, client clientset.Interface) {
|
||||
}
|
||||
}
|
||||
`
|
||||
// non-conflicting SMP has issues with the patch (duplicate fields),
|
||||
// but doesn't conflict with the existing object it's being patched to
|
||||
nonconflictingSMPBody := `
|
||||
{
|
||||
"spec": {
|
||||
"paused": true,
|
||||
"paused": false,
|
||||
"selector": {
|
||||
"matchLabels": {
|
||||
"app": "nginx"
|
||||
}
|
||||
},
|
||||
"template": {
|
||||
"metadata": {
|
||||
"labels": {
|
||||
"app": "nginx"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"containers": [{
|
||||
"name": "nginx",
|
||||
"imagePullPolicy": "Always",
|
||||
"imagePullPolicy": "Never"
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
var testcases = []struct {
|
||||
name string
|
||||
opts metav1.PatchOptions
|
||||
@ -2893,3 +2954,526 @@ func setupCRD(t *testing.T, config *rest.Config, apiGroup string, schemaless boo
|
||||
|
||||
return crd
|
||||
}
|
||||
|
||||
func BenchmarkFieldValidation(b *testing.B) {
|
||||
defer featuregatetesting.SetFeatureGateDuringTest(b, utilfeature.DefaultFeatureGate, features.ServerSideFieldValidation, true)()
|
||||
flag.Lookup("v").Value.Set("0")
|
||||
server, err := kubeapiservertesting.StartTestServer(b, kubeapiservertesting.NewDefaultTestServerOptions(), nil, framework.SharedEtcd())
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
config := server.ClientConfig
|
||||
defer server.TearDownFn()
|
||||
|
||||
// don't log warnings, tests inspect them in the responses directly
|
||||
config.WarningHandler = rest.NoWarnings{}
|
||||
|
||||
client := clientset.NewForConfigOrDie(config)
|
||||
|
||||
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) })
|
||||
b.Run("SMP", func(b *testing.B) { benchFieldValidationSMP(b, client) })
|
||||
b.Run("ApplyCreate", func(b *testing.B) { benchFieldValidationApplyCreate(b, client) })
|
||||
b.Run("ApplyUpdate", func(b *testing.B) { benchFieldValidationApplyUpdate(b, client) })
|
||||
|
||||
}
|
||||
|
||||
func benchFieldValidationPost(b *testing.B, client clientset.Interface) {
|
||||
var benchmarks = []struct {
|
||||
name string
|
||||
bodyBase string
|
||||
opts metav1.CreateOptions
|
||||
contentType string
|
||||
}{
|
||||
{
|
||||
name: "post-strict-validation",
|
||||
opts: metav1.CreateOptions{
|
||||
FieldValidation: "Strict",
|
||||
},
|
||||
bodyBase: validBodyJSON,
|
||||
},
|
||||
{
|
||||
name: "post-warn-validation",
|
||||
opts: metav1.CreateOptions{
|
||||
FieldValidation: "Warn",
|
||||
},
|
||||
bodyBase: validBodyJSON,
|
||||
},
|
||||
{
|
||||
name: "post-ignore-validation",
|
||||
opts: metav1.CreateOptions{
|
||||
FieldValidation: "Ignore",
|
||||
},
|
||||
bodyBase: validBodyJSON,
|
||||
},
|
||||
{
|
||||
name: "post-strict-validation-yaml",
|
||||
opts: metav1.CreateOptions{
|
||||
FieldValidation: "Strict",
|
||||
},
|
||||
bodyBase: validBodyYAML,
|
||||
contentType: "application/yaml",
|
||||
},
|
||||
{
|
||||
name: "post-warn-validation-yaml",
|
||||
opts: metav1.CreateOptions{
|
||||
FieldValidation: "Warn",
|
||||
},
|
||||
bodyBase: validBodyYAML,
|
||||
contentType: "application/yaml",
|
||||
},
|
||||
{
|
||||
name: "post-ignore-validation-yaml",
|
||||
opts: metav1.CreateOptions{
|
||||
FieldValidation: "Ignore",
|
||||
},
|
||||
bodyBase: validBodyYAML,
|
||||
contentType: "application/yaml",
|
||||
},
|
||||
}
|
||||
|
||||
for _, bm := range benchmarks {
|
||||
b.Run(bm.name, func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for n := 0; n < b.N; n++ {
|
||||
body := []byte(fmt.Sprintf(bm.bodyBase, fmt.Sprintf("test-deployment-%s-%d-%d-%d", bm.name, n, b.N, time.Now().UnixNano())))
|
||||
req := client.CoreV1().RESTClient().Post().
|
||||
AbsPath("/apis/apps/v1").
|
||||
Namespace("default").
|
||||
Resource("deployments").
|
||||
SetHeader("Content-Type", bm.contentType).
|
||||
VersionedParams(&bm.opts, metav1.ParameterCodec)
|
||||
result := req.Body(body).Do(context.TODO())
|
||||
if result.Error() != nil {
|
||||
b.Fatalf("unexpected request err: %v", result.Error())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func benchFieldValidationPut(b *testing.B, client clientset.Interface) {
|
||||
var testcases = []struct {
|
||||
name string
|
||||
opts metav1.UpdateOptions
|
||||
putBodyBase string
|
||||
contentType string
|
||||
}{
|
||||
{
|
||||
name: "put-strict-validation",
|
||||
opts: metav1.UpdateOptions{
|
||||
FieldValidation: "Strict",
|
||||
},
|
||||
putBodyBase: validBodyJSON,
|
||||
},
|
||||
{
|
||||
name: "put-warn-validation",
|
||||
opts: metav1.UpdateOptions{
|
||||
FieldValidation: "Warn",
|
||||
},
|
||||
putBodyBase: validBodyJSON,
|
||||
},
|
||||
{
|
||||
name: "put-ignore-validation",
|
||||
opts: metav1.UpdateOptions{
|
||||
FieldValidation: "Ignore",
|
||||
},
|
||||
putBodyBase: validBodyJSON,
|
||||
},
|
||||
{
|
||||
name: "put-strict-validation-yaml",
|
||||
opts: metav1.UpdateOptions{
|
||||
FieldValidation: "Strict",
|
||||
},
|
||||
putBodyBase: validBodyYAML,
|
||||
contentType: "application/yaml",
|
||||
},
|
||||
{
|
||||
name: "put-warn-validation-yaml",
|
||||
opts: metav1.UpdateOptions{
|
||||
FieldValidation: "Warn",
|
||||
},
|
||||
putBodyBase: validBodyYAML,
|
||||
contentType: "application/yaml",
|
||||
},
|
||||
{
|
||||
name: "put-ignore-validation-yaml",
|
||||
opts: metav1.UpdateOptions{
|
||||
FieldValidation: "Ignore",
|
||||
},
|
||||
putBodyBase: validBodyYAML,
|
||||
contentType: "application/yaml",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
b.Run(tc.name, func(b *testing.B) {
|
||||
names := make([]string, b.N)
|
||||
for n := 0; n < b.N; n++ {
|
||||
deployName := fmt.Sprintf("%s-%d-%d-%d", tc.name, n, b.N, time.Now().UnixNano())
|
||||
names[n] = deployName
|
||||
postBody := []byte(fmt.Sprintf(string(validBodyJSON), deployName))
|
||||
|
||||
if _, err := client.CoreV1().RESTClient().Post().
|
||||
AbsPath("/apis/apps/v1").
|
||||
Namespace("default").
|
||||
Resource("deployments").
|
||||
Body(postBody).
|
||||
DoRaw(context.TODO()); err != nil {
|
||||
b.Fatalf("failed to create initial deployment: %v", err)
|
||||
}
|
||||
|
||||
}
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for n := 0; n < b.N; n++ {
|
||||
deployName := names[n]
|
||||
putBody := []byte(fmt.Sprintf(string(tc.putBodyBase), deployName))
|
||||
req := client.CoreV1().RESTClient().Put().
|
||||
AbsPath("/apis/apps/v1").
|
||||
Namespace("default").
|
||||
Resource("deployments").
|
||||
SetHeader("Content-Type", tc.contentType).
|
||||
Name(deployName).
|
||||
VersionedParams(&tc.opts, metav1.ParameterCodec)
|
||||
result := req.Body([]byte(putBody)).Do(context.TODO())
|
||||
if result.Error() != nil {
|
||||
b.Fatalf("unexpected request err: %v", result.Error())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func benchFieldValidationPatchTyped(b *testing.B, client clientset.Interface) {
|
||||
mergePatchBodyValid := `
|
||||
{
|
||||
"spec": {
|
||||
"paused": false,
|
||||
"template": {
|
||||
"spec": {
|
||||
"containers": [{
|
||||
"name": "nginx",
|
||||
"image": "nginx:latest",
|
||||
"imagePullPolicy": "Always"
|
||||
}]
|
||||
}
|
||||
},
|
||||
"replicas": 2
|
||||
}
|
||||
}
|
||||
`
|
||||
|
||||
jsonPatchBodyValid := `
|
||||
[
|
||||
{"op": "add", "path": "/spec/paused", "value": true},
|
||||
{"op": "add", "path": "/spec/template/spec/containers/0/imagePullPolicy", "value": "Never"},
|
||||
{"op": "add", "path": "/spec/replicas", "value": 2}
|
||||
]
|
||||
`
|
||||
|
||||
var testcases = []struct {
|
||||
name string
|
||||
opts metav1.PatchOptions
|
||||
patchType types.PatchType
|
||||
body string
|
||||
}{
|
||||
{
|
||||
name: "merge-patch-strict-validation",
|
||||
opts: metav1.PatchOptions{
|
||||
FieldValidation: "Strict",
|
||||
},
|
||||
patchType: types.MergePatchType,
|
||||
body: mergePatchBodyValid,
|
||||
},
|
||||
{
|
||||
name: "merge-patch-warn-validation",
|
||||
opts: metav1.PatchOptions{
|
||||
FieldValidation: "Warn",
|
||||
},
|
||||
patchType: types.MergePatchType,
|
||||
body: mergePatchBodyValid,
|
||||
},
|
||||
{
|
||||
name: "merge-patch-ignore-validation",
|
||||
opts: metav1.PatchOptions{
|
||||
FieldValidation: "Ignore",
|
||||
},
|
||||
patchType: types.MergePatchType,
|
||||
body: mergePatchBodyValid,
|
||||
},
|
||||
{
|
||||
name: "json-patch-strict-validation",
|
||||
patchType: types.JSONPatchType,
|
||||
opts: metav1.PatchOptions{
|
||||
FieldValidation: "Strict",
|
||||
},
|
||||
body: jsonPatchBodyValid,
|
||||
},
|
||||
{
|
||||
name: "json-patch-warn-validation",
|
||||
patchType: types.JSONPatchType,
|
||||
opts: metav1.PatchOptions{
|
||||
FieldValidation: "Warn",
|
||||
},
|
||||
body: jsonPatchBodyValid,
|
||||
},
|
||||
{
|
||||
name: "json-patch-ignore-validation",
|
||||
patchType: types.JSONPatchType,
|
||||
opts: metav1.PatchOptions{
|
||||
FieldValidation: "Ignore",
|
||||
},
|
||||
body: jsonPatchBodyValid,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
b.Run(tc.name, func(b *testing.B) {
|
||||
names := make([]string, b.N)
|
||||
for n := 0; n < b.N; n++ {
|
||||
deployName := fmt.Sprintf("%s-%d-%d-%d", tc.name, n, b.N, time.Now().UnixNano())
|
||||
names[n] = deployName
|
||||
postBody := []byte(fmt.Sprintf(string(validBodyJSON), deployName))
|
||||
|
||||
if _, err := client.CoreV1().RESTClient().Post().
|
||||
AbsPath("/apis/apps/v1").
|
||||
Namespace("default").
|
||||
Resource("deployments").
|
||||
Body(postBody).
|
||||
DoRaw(context.TODO()); err != nil {
|
||||
b.Fatalf("failed to create initial deployment: %v", err)
|
||||
}
|
||||
}
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for n := 0; n < b.N; n++ {
|
||||
deployName := names[n]
|
||||
req := client.CoreV1().RESTClient().Patch(tc.patchType).
|
||||
AbsPath("/apis/apps/v1").
|
||||
Namespace("default").
|
||||
Resource("deployments").
|
||||
Name(deployName).
|
||||
VersionedParams(&tc.opts, metav1.ParameterCodec)
|
||||
result := req.Body([]byte(tc.body)).Do(context.TODO())
|
||||
if result.Error() != nil {
|
||||
b.Fatalf("unexpected request err: %v", result.Error())
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func benchFieldValidationSMP(b *testing.B, client clientset.Interface) {
|
||||
smpBodyValid := `
|
||||
{
|
||||
"spec": {
|
||||
"replicas": 3,
|
||||
"paused": false,
|
||||
"selector": {
|
||||
"matchLabels": {
|
||||
"app": "nginx"
|
||||
}
|
||||
},
|
||||
"template": {
|
||||
"metadata": {
|
||||
"labels": {
|
||||
"app": "nginx"
|
||||
}
|
||||
},
|
||||
"spec": {
|
||||
"containers": [{
|
||||
"name": "nginx",
|
||||
"imagePullPolicy": "Never"
|
||||
}]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
var testcases = []struct {
|
||||
name string
|
||||
opts metav1.PatchOptions
|
||||
body string
|
||||
}{
|
||||
{
|
||||
name: "smp-strict-validation",
|
||||
opts: metav1.PatchOptions{
|
||||
FieldValidation: "Strict",
|
||||
},
|
||||
body: smpBodyValid,
|
||||
},
|
||||
{
|
||||
name: "smp-warn-validation",
|
||||
opts: metav1.PatchOptions{
|
||||
FieldValidation: "Warn",
|
||||
},
|
||||
body: smpBodyValid,
|
||||
},
|
||||
{
|
||||
name: "smp-ignore-validation",
|
||||
opts: metav1.PatchOptions{
|
||||
FieldValidation: "Ignore",
|
||||
},
|
||||
body: smpBodyValid,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
b.Run(tc.name, func(b *testing.B) {
|
||||
names := make([]string, b.N)
|
||||
for n := 0; n < b.N; n++ {
|
||||
name := fmt.Sprintf("%s-%d-%d-%d", tc.name, n, b.N, time.Now().UnixNano())
|
||||
names[n] = name
|
||||
body := []byte(fmt.Sprintf(validBodyJSON, name))
|
||||
_, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
|
||||
AbsPath("/apis/apps/v1").
|
||||
Namespace("default").
|
||||
Resource("deployments").
|
||||
Name(name).
|
||||
Param("fieldManager", "apply_test").
|
||||
Body(body).
|
||||
Do(context.TODO()).
|
||||
Get()
|
||||
if err != nil {
|
||||
b.Fatalf("Failed to create object using Apply patch: %v", err)
|
||||
}
|
||||
}
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for n := 0; n < b.N; n++ {
|
||||
name := names[n]
|
||||
req := client.CoreV1().RESTClient().Patch(types.StrategicMergePatchType).
|
||||
AbsPath("/apis/apps/v1").
|
||||
Namespace("default").
|
||||
Resource("deployments").
|
||||
Name(name).
|
||||
VersionedParams(&tc.opts, metav1.ParameterCodec)
|
||||
result := req.Body([]byte(tc.body)).Do(context.TODO())
|
||||
if result.Error() != nil {
|
||||
b.Fatalf("unexpected request err: %v", result.Error())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func benchFieldValidationApplyCreate(b *testing.B, client clientset.Interface) {
|
||||
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",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
b.Run(tc.name, func(b *testing.B) {
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
for n := 0; n < b.N; n++ {
|
||||
name := fmt.Sprintf("apply-create-deployment-%s-%d-%d-%d", tc.name, n, b.N, time.Now().UnixNano())
|
||||
body := []byte(fmt.Sprintf(validBodyJSON, name))
|
||||
req := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
|
||||
AbsPath("/apis/apps/v1").
|
||||
Namespace("default").
|
||||
Resource("deployments").
|
||||
Name(name).
|
||||
VersionedParams(&tc.opts, metav1.ParameterCodec)
|
||||
result := req.Body(body).Do(context.TODO())
|
||||
if result.Error() != nil {
|
||||
b.Fatalf("unexpected request err: %v", result.Error())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func benchFieldValidationApplyUpdate(b *testing.B, client clientset.Interface) {
|
||||
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",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
b.Run(tc.name, func(b *testing.B) {
|
||||
names := make([]string, b.N)
|
||||
for n := 0; n < b.N; n++ {
|
||||
name := fmt.Sprintf("apply-update-deployment-%s-%d-%d-%d", tc.name, n, b.N, time.Now().UnixNano())
|
||||
names[n] = name
|
||||
createBody := []byte(fmt.Sprintf(validBodyJSON, name))
|
||||
createReq := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
|
||||
AbsPath("/apis/apps/v1").
|
||||
Namespace("default").
|
||||
Resource("deployments").
|
||||
Name(name).
|
||||
VersionedParams(&tc.opts, metav1.ParameterCodec)
|
||||
createResult := createReq.Body(createBody).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++ {
|
||||
name := names[n]
|
||||
updateBody := []byte(fmt.Sprintf(applyValidBody, name))
|
||||
updateReq := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
|
||||
AbsPath("/apis/apps/v1").
|
||||
Namespace("default").
|
||||
Resource("deployments").
|
||||
Name(name).
|
||||
VersionedParams(&tc.opts, metav1.ParameterCodec)
|
||||
result := updateReq.Body(updateBody).Do(context.TODO())
|
||||
if result.Error() != nil {
|
||||
b.Fatalf("unexpected request err: %v", result.Error())
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user