From 553c2d54829d61949115fd49a120c000c7e1d8ae Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Thu, 24 Jul 2025 02:01:13 +0200 Subject: [PATCH] [cozystack-api] show default values from openapi spec Signed-off-by: Andrei Kvapil --- pkg/registry/apps/application/rest.go | 46 +++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/pkg/registry/apps/application/rest.go b/pkg/registry/apps/application/rest.go index 4b80daa3..cdca3329 100644 --- a/pkg/registry/apps/application/rest.go +++ b/pkg/registry/apps/application/rest.go @@ -18,6 +18,7 @@ package application import ( "context" + "encoding/json" "fmt" "net/http" "strings" @@ -41,6 +42,10 @@ import ( appsv1alpha1 "github.com/cozystack/cozystack/pkg/apis/apps/v1alpha1" "github.com/cozystack/cozystack/pkg/config" + internalapiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" + apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + structuralschema "k8s.io/apiextensions-apiserver/pkg/apiserver/schema" + schemadefault "k8s.io/apiextensions-apiserver/pkg/apiserver/schema/defaulting" // Importing API errors package to construct appropriate error responses apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -78,10 +83,21 @@ type REST struct { kindName string singularName string releaseConfig config.ReleaseConfig + specSchema *structuralschema.Structural } // NewREST creates a new REST storage for Application with specific configuration func NewREST(dynamicClient dynamic.Interface, config *config.Resource) *REST { + var specSchema *structuralschema.Structural + if raw := strings.TrimSpace(config.Application.OpenAPISchema); raw != "" { + var js internalapiext.JSONSchemaProps + if err := json.Unmarshal([]byte(raw), &js); err != nil { + klog.Errorf("Failed to unmarshal OpenAPI schema: %v", err) + } else if specSchema, err = structuralschema.NewStructural(&js); err != nil { + klog.Errorf("Failed to create structural schema: %v", err) + } + } + return &REST{ dynamicClient: dynamicClient, gvr: schema.GroupVersionResource{ @@ -96,6 +112,7 @@ func NewREST(dynamicClient dynamic.Interface, config *config.Resource) *REST { kindName: config.Application.Kind, singularName: config.Application.Singular, releaseConfig: config.Release, + specSchema: specSchema, } } @@ -918,6 +935,10 @@ func (r *REST) ConvertHelmReleaseToApplication(hr *unstructured.Unstructured) (a return appsv1alpha1.Application{}, err } + if err := r.applySpecDefaults(&app); err != nil { + return app, fmt.Errorf("defaulting error: %w", err) + } + klog.V(6).Infof("Successfully converted HelmRelease %s to Application", hr.GetName()) return app, nil } @@ -1170,3 +1191,28 @@ func (e errNotAcceptable) Status() metav1.Status { Message: e.Error(), } } + +// applySpecDefaults applies default values to the Application spec based on the schema +func (r *REST) applySpecDefaults(app *appsv1alpha1.Application) error { + if r.specSchema == nil { + return nil + } + var m map[string]any + if app.Spec != nil && len(app.Spec.Raw) > 0 { + if err := json.Unmarshal(app.Spec.Raw, &m); err != nil { + return err + } + } + if m == nil { + m = map[string]any{} + } + + schemadefault.Default(m, r.specSchema) + + raw, err := json.Marshal(m) + if err != nil { + return err + } + app.Spec = &apiextv1.JSON{Raw: raw} + return nil +}