From a5dd2ec0ea35e6b462176bd1c56282a99b0a3d27 Mon Sep 17 00:00:00 2001
From: kaiyou <dev@kaiyou.fr>
Date: Sat, 7 Oct 2023 15:06:11 +0200
Subject: [PATCH] Fix CRD on 1.27

---
 k8s/storage.go        | 32 +++++++++++++++++---------------
 services/apiserver.go |  9 +++++++--
 2 files changed, 24 insertions(+), 17 deletions(-)

diff --git a/k8s/storage.go b/k8s/storage.go
index 6bbc8f8..1b885ec 100644
--- a/k8s/storage.go
+++ b/k8s/storage.go
@@ -4,9 +4,11 @@ import (
 	"fmt"
 	"time"
 
+	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/runtime/schema"
 	"k8s.io/apiserver/pkg/registry/generic"
+	"k8s.io/apiserver/pkg/server/options"
 	"k8s.io/apiserver/pkg/server/storage"
 	"k8s.io/apiserver/pkg/storage/storagebackend"
 	"k8s.io/apiserver/pkg/util/flowcontrol/request"
@@ -22,7 +24,7 @@ type RestOptionsFactory struct {
 func (f *RestOptionsFactory) GetRESTOptions(resource schema.GroupResource) (generic.RESTOptions, error) {
 	storageConfig, err := f.StorageFactory.NewConfig(resource)
 	if err != nil {
-		return generic.RESTOptions{}, fmt.Errorf("unable to find storage destination for %v, due to %v", resource, err.Error())
+		return generic.RESTOptions{}, fmt.Errorf("cannot find storage destination for %v, due to %v", resource, err.Error())
 	}
 	return generic.RESTOptions{
 		StorageConfig:             storageConfig,
@@ -39,26 +41,26 @@ func PrepareStorage(codecs runtime.StorageSerializer, scheme *runtime.Scheme, re
 	// Etcd backend supports being created with a specific codec, however later created storage
 	// factories override that codec at runtime thanks to using DefaultStorageFactory instead
 	// of vanilla SimpleStorageFactory, so we create the backend with no pre-configured codec
-	etcdConfig := storagebackend.NewDefaultConfig("/registry", nil)
+	etcdConfig := storagebackend.NewDefaultConfig("/registry", unstructured.UnstructuredJSONScheme)
 	etcdConfig.Transport.ServerList = []string{"http://[::1]:2379"}
 
-	// The rest options getter abstracts all storage for the server, by specifying the scheme and codecs
+	// The storage factory getter abstracts all storage for the server, by specifying the scheme and codecs
 	// on top of the storage backend. Vanilla code sets a special multi-group versionner to avoid issues
 	// with large objects (cee cmd/kube-apiserver/app/apiextensions.go), which we ignore here
-	return &RestOptionsFactory{
-		// TODO: be careful about the default factory using the resourcename as prefix, which might
-		// cause collisions in the future
-		StorageFactory: storage.NewDefaultStorageFactory(
-			*etcdConfig,             // copied as value, so the factory may alter some fields
-			runtime.ContentTypeJSON, // media type, somewhat inefficient but good enough
+	var storageFactory storage.StorageFactory
+	if codecs == nil {
+		storageFactory = &options.SimpleStorageFactory{StorageConfig: *etcdConfig}
+	} else {
+		storageFactory = storage.NewDefaultStorageFactory(
+			*etcdConfig,
+			runtime.ContentTypeJSON,
 			codecs,
 			storage.NewDefaultResourceEncodingConfig(scheme),
 			resources,
-			// We always use an empty list of prefix overrides: no prefix override, contrary to
-			// vanilla code which has many overrides for backward compatibility against a given storage target
-			// This means hepto is not compatible with vanilla when using the same etcd backend, and we might
-			// want to be careful when upgrading to avoid unexpected prefix changes
-			make(map[schema.GroupResource]string),
-		),
+			map[schema.GroupResource]string{},
+		)
+	}
+	return &RestOptionsFactory{
+		StorageFactory: storageFactory,
 	}
 }
diff --git a/services/apiserver.go b/services/apiserver.go
index 13ee71f..289ab5f 100644
--- a/services/apiserver.go
+++ b/services/apiserver.go
@@ -44,6 +44,7 @@ import (
 	controllersa "k8s.io/kubernetes/pkg/controller/serviceaccount"
 	"k8s.io/kubernetes/pkg/controlplane"
 	"k8s.io/kubernetes/pkg/controlplane/controller/clusterauthenticationtrust"
+	"k8s.io/kubernetes/pkg/controlplane/controller/crdregistration"
 	"k8s.io/kubernetes/pkg/controlplane/reconcilers"
 	generatedopenapi "k8s.io/kubernetes/pkg/generated/openapi"
 	kubeadmission "k8s.io/kubernetes/pkg/kubeapiserver/admission"
@@ -99,6 +100,10 @@ var kubeApiserver = &Unit{
 			return fmt.Errorf("could not initialize aggregator client: %w", err)
 		}
 		registrationController := autoregister.NewAutoRegisterController(aggregatorServer.APIRegistrationInformers.Apiregistration().V1().APIServices(), apiRegistrationClient)
+		crdController := crdregistration.NewCRDRegistrationController(
+			extensionServer.Informers.Apiextensions().V1().CustomResourceDefinitions(),
+			registrationController,
+		)
 		for _, resourcePath := range apiServer.GenericAPIServer.ListedPaths() {
 			match := pathRegex.FindStringSubmatch(resourcePath)
 			if match == nil {
@@ -115,6 +120,7 @@ var kubeApiserver = &Unit{
 			})
 		}
 		go registrationController.Run(1, ctx.Done())
+		go crdController.Run(1, ctx.Done())
 
 		// Finally start the apiserver
 		server := aggregatorServer.GenericAPIServer.PrepareRun()
@@ -249,8 +255,7 @@ func buildExtensionsConfig(config server.Config, clients *k8s.Clients) (*extensi
 			SharedInformerFactory: clients.Informer,
 		},
 		ExtraConfig: extensions.ExtraConfig{
-			// TODO This might be an issue, since vanilla sets a special unstructured encoding for CRD
-			CRDRESTOptionsGetter: generic.RESTOptionsGetter,
+			CRDRESTOptionsGetter: k8s.PrepareStorage(nil, nil, nil),
 		},
 	}, nil
 }
-- 
GitLab