From 42d3e9752c605f7ea99b21425f2f32fe44a6ade8 Mon Sep 17 00:00:00 2001 From: Ben Luddy Date: Thu, 7 Nov 2024 00:06:24 -0500 Subject: [PATCH] Add E2E test for CBOR client compatibility with older apiservers. Clients must be able to use CBOR without a guarantee that all apiservers support it. The apiserver aggregation layer avoids changing in any way that would require an aggregated apiservers to be updated. This end-to-end test verifies that a client's content negotiation behaviors continue to work over time when communicating with a 1.17 sample-apiserver. --- test/e2e/apimachinery/protocol.go | 77 ++++++++++++++++++++++++++++++- test/e2e/feature/feature.go | 4 ++ 2 files changed, 79 insertions(+), 2 deletions(-) diff --git a/test/e2e/apimachinery/protocol.go b/test/e2e/apimachinery/protocol.go index b40f52275a0..491f5c15b97 100644 --- a/test/e2e/apimachinery/protocol.go +++ b/test/e2e/apimachinery/protocol.go @@ -26,12 +26,21 @@ import ( v1 "k8s.io/api/core/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/dynamic" + clientfeatures "k8s.io/client-go/features" + clientfeaturestesting "k8s.io/client-go/features/testing" "k8s.io/client-go/kubernetes" - admissionapi "k8s.io/pod-security-admission/api" - + aggregatorclientset "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset" + "k8s.io/kubernetes/test/e2e/feature" "k8s.io/kubernetes/test/e2e/framework" + imageutils "k8s.io/kubernetes/test/utils/image" + admissionapi "k8s.io/pod-security-admission/api" + samplev1alpha1 "k8s.io/sample-apiserver/pkg/apis/wardle/v1alpha1" + samplev1alpha1client "k8s.io/sample-apiserver/pkg/generated/clientset/versioned/typed/wardle/v1alpha1" ) var _ = SIGDescribe("client-go should negotiate", func() { @@ -96,3 +105,67 @@ var _ = SIGDescribe("client-go should negotiate", func() { }) } }) + +var _ = SIGDescribe("CBOR", feature.CBOR, func() { + f := framework.NewDefaultFramework("cbor") + f.NamespacePodSecurityLevel = admissionapi.LevelBaseline + + // Must be serial to avoid conflict with other tests that set up a sample apiserver. + f.It("clients remain compatible with the 1.17 sample-apiserver", f.WithSerial(), func(ctx context.Context) { + clientfeaturestesting.SetFeatureDuringTest(g.GinkgoTB(), clientfeatures.ClientsAllowCBOR, true) + clientfeaturestesting.SetFeatureDuringTest(g.GinkgoTB(), clientfeatures.ClientsPreferCBOR, true) + + clientConfig, err := framework.LoadConfig() + framework.ExpectNoError(err) + + aggregatorClient, err := aggregatorclientset.NewForConfig(clientConfig) + framework.ExpectNoError(err) + + dynamicClient, err := dynamic.NewForConfig(clientConfig) + framework.ExpectNoError(err) + + objectNames := generateSampleAPIServerObjectNames(f.Namespace.Name) + g.DeferCleanup(func(ctx context.Context) { + cleanupSampleAPIServer(ctx, f.ClientSet, aggregatorClient, objectNames, samplev1alpha1.SchemeGroupVersion.Version+"."+samplev1alpha1.SchemeGroupVersion.Group) + }) + SetUpSampleAPIServer(ctx, f, aggregatorClient, imageutils.GetE2EImage(imageutils.APIServer), objectNames, samplev1alpha1.SchemeGroupVersion.Group, samplev1alpha1.SchemeGroupVersion.Version) + + flunder := samplev1alpha1.Flunder{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-flunder", + }, + } + + g.By("making requests with a generated client", func() { + sampleClient, err := samplev1alpha1client.NewForConfig(clientConfig) + framework.ExpectNoError(err) + + _, err = sampleClient.Flunders(f.Namespace.Name).List(ctx, metav1.ListOptions{LabelSelector: "a,!a"}) + framework.ExpectNoError(err, "Failed to list with generated client") + + _, err = sampleClient.Flunders(f.Namespace.Name).Create(ctx, &flunder, metav1.CreateOptions{DryRun: []string{metav1.DryRunAll}}) + o.Expect(err).To(o.MatchError(apierrors.IsUnsupportedMediaType, "Expected 415 (Unsupported Media Type) response on first write with generated client")) + + _, err = sampleClient.Flunders(f.Namespace.Name).Create(ctx, &flunder, metav1.CreateOptions{DryRun: []string{metav1.DryRunAll}}) + framework.ExpectNoError(err, "Expected subsequent writes to succeed with generated client") + }) + + g.By("making requests with a dynamic client", func() { + unstructuredFlunderContent, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&flunder) + framework.ExpectNoError(err) + unstructuredFlunder := &unstructured.Unstructured{Object: unstructuredFlunderContent} + + flunderDynamicClient := dynamicClient.Resource(samplev1alpha1.SchemeGroupVersion.WithResource("flunders")).Namespace(f.Namespace.Name) + + list, err := flunderDynamicClient.List(ctx, metav1.ListOptions{LabelSelector: "a,!a"}) + framework.ExpectNoError(err, "Failed to list with dynamic client") + o.Expect(list.GetObjectKind().GroupVersionKind()).To(o.Equal(samplev1alpha1.SchemeGroupVersion.WithKind("FlunderList"))) + + _, err = flunderDynamicClient.Create(ctx, unstructuredFlunder, metav1.CreateOptions{DryRun: []string{metav1.DryRunAll}}) + o.Expect(err).To(o.MatchError(apierrors.IsUnsupportedMediaType, "Expected 415 (Unsupported Media Type) response on first write with dynamic client")) + + _, err = flunderDynamicClient.Create(ctx, unstructuredFlunder, metav1.CreateOptions{DryRun: []string{metav1.DryRunAll}}) + framework.ExpectNoError(err, "Expected subsequent writes to succeed with dynamic client") + }) + }) +}) diff --git a/test/e2e/feature/feature.go b/test/e2e/feature/feature.go index 557d0930cee..d9cf9d47f46 100644 --- a/test/e2e/feature/feature.go +++ b/test/e2e/feature/feature.go @@ -34,6 +34,10 @@ var ( // TODO: document the feature (owning SIG, when to use this feature for a test) BoundServiceAccountTokenVolume = framework.WithFeature(framework.ValidFeatures.Add("BoundServiceAccountTokenVolume")) + // Owner: sig-api-machinery + // Marks tests that exercise the CBOR data format for serving or storage. + CBOR = framework.WithFeature(framework.ValidFeatures.Add("CBOR")) + // TODO: document the feature (owning SIG, when to use this feature for a test) CloudProvider = framework.WithFeature(framework.ValidFeatures.Add("CloudProvider"))