Fix OpenAPIv2 definitions for dynamic resources (#484)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **New Features**
	- Enhanced OpenAPI schema handling for the Apps API server.
- Introduced a method for deep copying schema structures to improve
resource definition management.

- **Bug Fixes**
- Improved error handling during server configuration to ensure proper
reporting of setup issues.

- **Refactor**
- Removed dynamic type registration for the `v1alpha1` API version to
simplify server initialization.

- **Chores**
	- Updated image tag for the CozyStack API to the latest version.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Andrei Kvapil
2024-11-25 15:18:43 +01:00
committed by GitHub
parent b7375f730f
commit 5a4c165020
4 changed files with 95 additions and 12 deletions

View File

@@ -17,17 +17,20 @@ limitations under the License.
package v1alpha1
import (
"log"
"github.com/aenix.io/cozystack/pkg/config"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/klog/v2"
)
// GroupName holds the API group name.
const GroupName = "apps.cozystack.io"
var (
RegisteredGVKs []schema.GroupVersionKind
)
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha1"}
@@ -69,8 +72,8 @@ func RegisterDynamicTypes(scheme *runtime.Scheme, cfg *config.ResourceConfig) er
scheme.AddKnownTypeWithName(gvk, &Application{})
scheme.AddKnownTypeWithName(gvk.GroupVersion().WithKind(kind+"List"), &ApplicationList{})
log.Printf("Registered kind: %s\n", kind)
klog.V(1).Infof("Registered kind: %s\n", kind)
RegisteredGVKs = append(RegisteredGVKs, gvk)
}
return nil

View File

@@ -31,7 +31,6 @@ import (
"github.com/aenix.io/cozystack/pkg/apis/apps"
"github.com/aenix.io/cozystack/pkg/apis/apps/install"
appsv1alpha1 "github.com/aenix.io/cozystack/pkg/apis/apps/v1alpha1"
"github.com/aenix.io/cozystack/pkg/config"
appsregistry "github.com/aenix.io/cozystack/pkg/registry"
applicationstorage "github.com/aenix.io/cozystack/pkg/registry/apps/application"
@@ -112,12 +111,6 @@ func (c completedConfig) New() (*AppsServer, error) {
apiGroupInfo := genericapiserver.NewDefaultAPIGroupInfo(apps.GroupName, Scheme, metav1.ParameterCodec, Codecs)
// Dynamically register types based on the configuration.
err = appsv1alpha1.RegisterDynamicTypes(Scheme, c.ResourceConfig)
if err != nil {
return nil, fmt.Errorf("failed to register dynamic types: %v", err)
}
// Create a dynamic client for HelmRelease using InClusterConfig.
inClusterConfig, err := restclient.InClusterConfig()
if err != nil {

View File

@@ -18,6 +18,7 @@ package server
import (
"context"
"encoding/json"
"fmt"
"io"
"net"
@@ -37,6 +38,8 @@ import (
utilversionpkg "k8s.io/apiserver/pkg/util/version"
"k8s.io/component-base/featuregate"
baseversion "k8s.io/component-base/version"
"k8s.io/klog/v2"
"k8s.io/kube-openapi/pkg/validation/spec"
netutils "k8s.io/utils/net"
)
@@ -156,6 +159,22 @@ func (o AppsServerOptions) Validate(args []string) error {
return utilerrors.NewAggregate(allErrors)
}
// DeepCopySchema делает глубокую копию структуры spec.Schema
func DeepCopySchema(schema *spec.Schema) (*spec.Schema, error) {
data, err := json.Marshal(schema)
if err != nil {
return nil, fmt.Errorf("failed to marshal schema: %w", err)
}
var newSchema spec.Schema
err = json.Unmarshal(data, &newSchema)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal schema: %w", err)
}
return &newSchema, nil
}
// Config returns the configuration for the API server based on AppsServerOptions
func (o *AppsServerOptions) Config() (*apiserver.Config, error) {
// TODO: set the "real" external address
@@ -165,6 +184,12 @@ func (o *AppsServerOptions) Config() (*apiserver.Config, error) {
return nil, fmt.Errorf("error creating self-signed certificates: %v", err)
}
// First, register the dynamic types
err := v1alpha1.RegisterDynamicTypes(apiserver.Scheme, o.ResourceConfig)
if err != nil {
return nil, fmt.Errorf("failed to register dynamic types: %v", err)
}
serverConfig := genericapiserver.NewRecommendedConfig(apiserver.Codecs)
serverConfig.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig(
@@ -173,6 +198,68 @@ func (o *AppsServerOptions) Config() (*apiserver.Config, error) {
serverConfig.OpenAPIConfig.Info.Title = "Apps"
serverConfig.OpenAPIConfig.Info.Version = "0.1"
serverConfig.OpenAPIConfig.PostProcessSpec = func(swagger *spec.Swagger) (*spec.Swagger, error) {
defs := swagger.Definitions
// Check basic Application definition
appDef, exists := defs["com.github.aenix.io.cozystack.pkg.apis.apps.v1alpha1.Application"]
if !exists {
return swagger, fmt.Errorf("Application definition not found")
}
// Check basic ApplicationList definition
listDef, exists := defs["com.github.aenix.io.cozystack.pkg.apis.apps.v1alpha1.ApplicationList"]
if !exists {
return swagger, fmt.Errorf("ApplicationList definition not found")
}
for _, gvk := range v1alpha1.RegisteredGVKs {
resourceName := fmt.Sprintf("com.github.aenix.io.cozystack.pkg.apis.apps.v1alpha1.%s", gvk.Kind)
newDef, err := DeepCopySchema(&appDef)
if err != nil {
return nil, fmt.Errorf("failed to deepcopy schema for %s: %w", gvk.Kind, err)
}
// Fix Extensions for resource
if newDef.Extensions == nil {
newDef.Extensions = map[string]interface{}{}
}
newDef.Extensions["x-kubernetes-group-version-kind"] = []map[string]interface{}{
{
"group": gvk.Group,
"version": gvk.Version,
"kind": gvk.Kind,
},
}
defs[resourceName] = *newDef
klog.V(6).Infof("PostProcessSpec: Added OpenAPI definition for %s\n", resourceName)
// List resource
listResourceName := fmt.Sprintf("com.github.aenix.io.cozystack.pkg.apis.apps.v1alpha1.%sList", gvk.Kind)
newListDef, err := DeepCopySchema(&listDef)
if err != nil {
return nil, fmt.Errorf("failed to deepcopy schema for %sList: %w", gvk.Kind, err)
}
// Fix Extensions for List resource
if newListDef.Extensions == nil {
newListDef.Extensions = map[string]interface{}{}
}
newListDef.Extensions["x-kubernetes-group-version-kind"] = []map[string]interface{}{
{
"group": gvk.Group,
"version": gvk.Version,
"kind": fmt.Sprintf("%sList", gvk.Kind),
},
}
defs[listResourceName] = *newListDef
klog.V(6).Infof("PostProcessSpec: Added OpenAPI definition for %s\n", listResourceName)
}
swagger.Definitions = defs
return swagger, nil
}
serverConfig.OpenAPIV3Config = genericapiserver.DefaultOpenAPIV3Config(
sampleopenapi.GetOpenAPIDefinitions, openapi.NewDefinitionNamer(apiserver.Scheme),
)