Skip to content

Commit 006146f

Browse files
committed
Add integration test for per-resource storage encoding.
1 parent a28f140 commit 006146f

File tree

1 file changed

+144
-0
lines changed

1 file changed

+144
-0
lines changed

test/integration/apiserver/apiserver_test.go

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"net/http"
2727
"path"
2828
"reflect"
29+
"slices"
2930
"strconv"
3031
"strings"
3132
"sync"
@@ -48,22 +49,29 @@ import (
4849
"k8s.io/apimachinery/pkg/fields"
4950
"k8s.io/apimachinery/pkg/runtime"
5051
"k8s.io/apimachinery/pkg/runtime/schema"
52+
jsonserializer "k8s.io/apimachinery/pkg/runtime/serializer/json"
5153
"k8s.io/apimachinery/pkg/runtime/serializer/protobuf"
54+
"k8s.io/apimachinery/pkg/runtime/serializer/recognizer"
5255
"k8s.io/apimachinery/pkg/runtime/serializer/streaming"
5356
"k8s.io/apimachinery/pkg/types"
57+
utiljson "k8s.io/apimachinery/pkg/util/json"
5458
"k8s.io/apimachinery/pkg/util/uuid"
5559
"k8s.io/apimachinery/pkg/util/wait"
5660
"k8s.io/apimachinery/pkg/watch"
5761
"k8s.io/apiserver/pkg/endpoints/handlers"
5862
"k8s.io/apiserver/pkg/storage/storagebackend"
63+
utilfeature "k8s.io/apiserver/pkg/util/feature"
5964
"k8s.io/client-go/discovery/cached/memory"
6065
"k8s.io/client-go/dynamic"
66+
clientfeatures "k8s.io/client-go/features"
67+
clientfeaturestesting "k8s.io/client-go/features/testing"
6168
clientset "k8s.io/client-go/kubernetes"
6269
appsv1 "k8s.io/client-go/kubernetes/typed/apps/v1"
6370
"k8s.io/client-go/metadata"
6471
restclient "k8s.io/client-go/rest"
6572
"k8s.io/client-go/restmapper"
6673
"k8s.io/client-go/tools/pager"
74+
featuregatetesting "k8s.io/component-base/featuregate/testing"
6775
utilversion "k8s.io/component-base/version"
6876
"k8s.io/klog/v2"
6977
"k8s.io/kubernetes/cmd/kube-apiserver/app/options"
@@ -3352,3 +3360,139 @@ func assertManagedFields(t *testing.T, obj *unstructured.Unstructured) {
33523360
func int32Ptr(i int32) *int32 {
33533361
return &i
33543362
}
3363+
3364+
// TestDefaultStorageEncoding verifies that the storage encoding for all built-in resources is
3365+
// Protobuf.
3366+
func TestDefaultStorageEncoding(t *testing.T) {
3367+
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, "AllAlpha", true)
3368+
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, "AllBeta", true)
3369+
3370+
// TODO: Remove this override when the codecs used for serving all built-in APIs are wired to the apiserver feature gate.
3371+
clientfeaturestesting.SetFeatureDuringTest(t, clientfeatures.ClientsPreferCBOR, false)
3372+
3373+
protobufRecognizer := protobuf.NewSerializer(runtime.NewScheme(), runtime.NewScheme())
3374+
var recognizersByGroup map[string]recognizer.RecognizingDecoder
3375+
{
3376+
jsonRecognizer := jsonserializer.NewSerializerWithOptions(jsonserializer.DefaultMetaFactory, runtime.NewScheme(), runtime.NewScheme(), jsonserializer.SerializerOptions{})
3377+
recognizersByGroup = map[string]recognizer.RecognizingDecoder{
3378+
// No new exceptions should be added. Once these groups begin using Protobuf
3379+
// for storage, the exception list should be removed.
3380+
"apiextensions.k8s.io": jsonRecognizer,
3381+
"apiregistration.k8s.io": jsonRecognizer,
3382+
}
3383+
}
3384+
3385+
storageConfig := framework.SharedEtcd()
3386+
etcdPrefix := string(uuid.NewUUID())
3387+
storageConfig.Prefix = path.Join(etcdPrefix, "registry")
3388+
server := kubeapiservertesting.StartTestServerOrDie(
3389+
t,
3390+
kubeapiservertesting.NewDefaultTestServerOptions(),
3391+
[]string{
3392+
"--runtime-config=api/all=true",
3393+
"--disable-admission-plugins=ServiceAccount",
3394+
},
3395+
storageConfig,
3396+
)
3397+
t.Cleanup(server.TearDownFn)
3398+
3399+
client, err := clientset.NewForConfig(server.ClientConfig)
3400+
if err != nil {
3401+
t.Fatal(err)
3402+
}
3403+
3404+
dynamicClient, err := dynamic.NewForConfig(server.ClientConfig)
3405+
if err != nil {
3406+
t.Fatal(err)
3407+
}
3408+
3409+
etcdClient, kvClient, err := integration.GetEtcdClients(storageConfig.Transport)
3410+
if err != nil {
3411+
t.Fatal(err)
3412+
}
3413+
t.Cleanup(func() {
3414+
if err := etcdClient.Close(); err != nil {
3415+
t.Error(err)
3416+
}
3417+
})
3418+
3419+
const NamespaceName = "test-protobuf-default-storage-encoding"
3420+
if _, err := client.CoreV1().Namespaces().Create(context.TODO(), &v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: NamespaceName}}, metav1.CreateOptions{}); err != nil {
3421+
t.Fatal(err)
3422+
}
3423+
3424+
storageDataByResource := etcd.GetEtcdStorageDataForNamespace(NamespaceName)
3425+
3426+
_, lists, err := client.Discovery().ServerGroupsAndResources()
3427+
if err != nil {
3428+
t.Fatal(err)
3429+
}
3430+
for _, list := range lists {
3431+
for _, resource := range list.APIResources {
3432+
gv, err := schema.ParseGroupVersion(list.GroupVersion)
3433+
if err != nil {
3434+
t.Fatal(err)
3435+
}
3436+
if resource.Group != "" {
3437+
gv.Group = resource.Group
3438+
}
3439+
if resource.Version != "" {
3440+
gv.Version = resource.Version
3441+
}
3442+
3443+
if strings.Contains(resource.Name, "/") {
3444+
continue
3445+
}
3446+
3447+
if !slices.Contains(resource.Verbs, "create") {
3448+
continue
3449+
}
3450+
3451+
gvr := gv.WithResource(resource.Name)
3452+
3453+
storageData := storageDataByResource[gvr]
3454+
if storageData.Stub == "" {
3455+
continue
3456+
}
3457+
3458+
name := fmt.Sprintf("%s.%s.%s", gvr.Resource, gvr.Version, gvr.Group)
3459+
if gvr.Group == "" {
3460+
name = fmt.Sprintf("%s.%s", gvr.Resource, gvr.Version)
3461+
}
3462+
t.Run(name, func(t *testing.T) {
3463+
var o unstructured.Unstructured
3464+
if err := utiljson.Unmarshal([]byte(storageData.Stub), &o.Object); err != nil {
3465+
t.Fatal(err)
3466+
}
3467+
3468+
resourceClient := dynamicClient.Resource(gvr).Namespace(NamespaceName)
3469+
if !resource.Namespaced {
3470+
resourceClient = dynamicClient.Resource(gvr)
3471+
}
3472+
if _, err := resourceClient.Create(context.TODO(), &o, metav1.CreateOptions{}); err != nil {
3473+
t.Fatal(err)
3474+
}
3475+
3476+
response, err := kvClient.Get(context.TODO(), path.Join("/", etcdPrefix, storageData.ExpectedEtcdPath))
3477+
if err != nil {
3478+
t.Fatal(err)
3479+
}
3480+
3481+
if n := len(response.Kvs); n != 1 {
3482+
t.Fatalf("expected 1 kv, got %d", n)
3483+
}
3484+
recognizer, ok := recognizersByGroup[gvr.Group]
3485+
if !ok {
3486+
recognizer = protobufRecognizer
3487+
}
3488+
recognized, _, err := recognizer.RecognizesData(response.Kvs[0].Value)
3489+
if err != nil {
3490+
t.Fatal(err)
3491+
}
3492+
if !recognized {
3493+
t.Fatal("stored object encoding not recognized as protobuf")
3494+
}
3495+
})
3496+
}
3497+
}
3498+
}

0 commit comments

Comments
 (0)