Skip to content

Commit 77401d7

Browse files
committed
Add CBOR variant of admission webhook integration test.
The existing admission webhook integration test provides good coverage of serving built-in resources and custom resources, including subresources. Serialization concerns, including roundtrippability, of built-in types have existing test coverage; the CBOR variant of the admission webhook integration test additionally exercises client and server codec wiring.
1 parent 3e1b6aa commit 77401d7

File tree

2 files changed

+89
-8
lines changed

2 files changed

+89
-8
lines changed

test/integration/apiserver/admissionwebhook/admission_test.go

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import (
2020
"context"
2121
"crypto/tls"
2222
"crypto/x509"
23-
"encoding/json"
2423
"fmt"
2524
"io"
2625
"net/http"
@@ -49,6 +48,7 @@ import (
4948
"k8s.io/apimachinery/pkg/runtime"
5049
"k8s.io/apimachinery/pkg/runtime/schema"
5150
"k8s.io/apimachinery/pkg/types"
51+
"k8s.io/apimachinery/pkg/util/json"
5252
"k8s.io/apimachinery/pkg/util/sets"
5353
"k8s.io/apimachinery/pkg/util/wait"
5454
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
@@ -446,16 +446,25 @@ func (w *warningHandler) HandleWarningHeader(code int, agent string, message str
446446

447447
// TestWebhookAdmissionWithWatchCache tests communication between API server and webhook process.
448448
func TestWebhookAdmissionWithWatchCache(t *testing.T) {
449-
testWebhookAdmission(t, true)
449+
testWebhookAdmission(t, true, func(testing.TB, *rest.Config) {})
450450
}
451451

452452
// TestWebhookAdmissionWithoutWatchCache tests communication between API server and webhook process.
453453
func TestWebhookAdmissionWithoutWatchCache(t *testing.T) {
454-
testWebhookAdmission(t, false)
454+
testWebhookAdmission(t, false, func(testing.TB, *rest.Config) {})
455+
}
456+
457+
func TestWebhookAdmissionWithCBOR(t *testing.T) {
458+
framework.EnableCBORServingAndStorageForTest(t)
459+
framework.SetTestOnlyCBORClientFeatureGatesForTest(t, true, true)
460+
testWebhookAdmission(t, false, func(t testing.TB, config *rest.Config) {
461+
config.Wrap(framework.AssertRequestResponseAsCBOR(t))
462+
})
455463
}
456464

457465
// testWebhookAdmission tests communication between API server and webhook process.
458-
func testWebhookAdmission(t *testing.T, watchCache bool) {
466+
func testWebhookAdmission(t *testing.T, watchCache bool, reconfigureClient func(testing.TB, *rest.Config)) {
467+
459468
// holder communicates expectations to webhooks, and results from webhooks
460469
holder := &holder{
461470
t: t,
@@ -528,10 +537,6 @@ func testWebhookAdmission(t *testing.T, watchCache bool) {
528537
}
529538

530539
// gather resources to test
531-
dynamicClient, err := dynamic.NewForConfig(clientConfig)
532-
if err != nil {
533-
t.Fatal(err)
534-
}
535540
_, resources, err := client.Discovery().ServerGroupsAndResources()
536541
if err != nil {
537542
t.Fatalf("Failed to get ServerGroupsAndResources with error: %+v", err)
@@ -640,6 +645,13 @@ func testWebhookAdmission(t *testing.T, watchCache bool) {
640645
for _, verb := range []string{"create", "update", "patch", "connect", "delete", "deletecollection"} {
641646
if shouldTestResourceVerb(gvr, resource, verb) {
642647
t.Run(verb, func(t *testing.T) {
648+
clientConfig := rest.CopyConfig(clientConfig)
649+
reconfigureClient(t, clientConfig)
650+
dynamicClient, err := dynamic.NewForConfig(clientConfig)
651+
if err != nil {
652+
t.Fatal(err)
653+
}
654+
643655
count++
644656
holder.reset(t)
645657
testFunc := getTestFunc(gvr, verb)

test/integration/framework/cbor.go

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,21 @@ limitations under the License.
1717
package framework
1818

1919
import (
20+
"bytes"
21+
"io"
22+
"net/http"
2023
"testing"
2124

2225
apiextensionsapiserver "k8s.io/apiextensions-apiserver/pkg/apiserver"
2326
metainternalscheme "k8s.io/apimachinery/pkg/apis/meta/internalversion/scheme"
2427
"k8s.io/apimachinery/pkg/runtime"
2528
"k8s.io/apimachinery/pkg/runtime/serializer"
2629
"k8s.io/apimachinery/pkg/runtime/serializer/cbor"
30+
"k8s.io/apimachinery/pkg/util/sets"
2731
"k8s.io/apiserver/pkg/features"
2832
utilfeature "k8s.io/apiserver/pkg/util/feature"
2933
clientfeatures "k8s.io/client-go/features"
34+
"k8s.io/client-go/transport"
3035
featuregatetesting "k8s.io/component-base/featuregate/testing"
3136
aggregatorscheme "k8s.io/kube-aggregator/pkg/apiserver/scheme"
3237
"k8s.io/kubernetes/pkg/api/legacyscheme"
@@ -102,3 +107,67 @@ func EnableCBORServingAndStorageForTest(tb testing.TB) {
102107
*codecs[scheme] = serializer.NewCodecFactory(scheme, serializer.WithSerializer(newCBORSerializerInfo))
103108
}
104109
}
110+
111+
// AssertRequestResponseAsCBOR returns a transport.WrapperFunc that will report a test error if a
112+
// non-empty request or response body contains data that does not appear to be CBOR-encoded.
113+
func AssertRequestResponseAsCBOR(t testing.TB) transport.WrapperFunc {
114+
recognizer := cbor.NewSerializer(runtime.NewScheme(), runtime.NewScheme())
115+
116+
unsupportedPatchContentTypes := sets.New(
117+
"application/json-patch+json",
118+
"application/merge-patch+json",
119+
"application/strategic-merge-patch+json",
120+
)
121+
122+
return func(rt http.RoundTripper) http.RoundTripper {
123+
return roundTripperFunc(func(request *http.Request) (*http.Response, error) {
124+
if request.Body != nil && !unsupportedPatchContentTypes.Has(request.Header.Get("Content-Type")) {
125+
requestbody, err := io.ReadAll(request.Body)
126+
if err != nil {
127+
t.Error(err)
128+
}
129+
recognized, _, err := recognizer.RecognizesData(requestbody)
130+
if err != nil {
131+
t.Error(err)
132+
}
133+
if len(requestbody) > 0 && !recognized {
134+
t.Errorf("non-cbor request: 0x%x", requestbody)
135+
}
136+
request.Body = io.NopCloser(bytes.NewReader(requestbody))
137+
}
138+
139+
response, rterr := rt.RoundTrip(request)
140+
if rterr != nil {
141+
return response, rterr
142+
}
143+
144+
// We can't synchronously inspect streaming responses, so tee to a buffer
145+
// and inspect it at the end of the test.
146+
var buf bytes.Buffer
147+
response.Body = struct {
148+
io.Reader
149+
io.Closer
150+
}{
151+
Reader: io.TeeReader(response.Body, &buf),
152+
Closer: response.Body,
153+
}
154+
t.Cleanup(func() {
155+
recognized, _, err := recognizer.RecognizesData(buf.Bytes())
156+
if err != nil {
157+
t.Error(err)
158+
}
159+
if buf.Len() > 0 && !recognized {
160+
t.Errorf("non-cbor response: 0x%x", buf.Bytes())
161+
}
162+
})
163+
164+
return response, rterr
165+
})
166+
}
167+
}
168+
169+
type roundTripperFunc func(*http.Request) (*http.Response, error)
170+
171+
func (f roundTripperFunc) RoundTrip(r *http.Request) (*http.Response, error) {
172+
return f(r)
173+
}

0 commit comments

Comments
 (0)