[apps] Refactor apiserver to use typed objects and fix UnstructuredList GVK
This commit refactors the apiserver REST handlers to use typed objects (appsv1alpha1.Application) instead of unstructured.Unstructured, eliminating the need for runtime conversions and simplifying the codebase. Additionally, it fixes an issue where UnstructuredList objects were using the first registered kind from typeToGVK instead of the kind from the object's field when multiple kinds are registered with the same Go type. This is a more comprehensive fix for the problem addressed in https://github.com/cozystack/cozystack/pull/1630, which was reverted in https://github.com/cozystack/cozystack/pull/1677. The fix includes the upstream fix from kubernetes/kubernetes#135537, which enables short-circuit path for UnstructuredList similar to regular Unstructured objects, using GVK from the object field instead of typeToGVK. Changes: - Refactored rest.go handlers to use typed Application objects - Removed unstructured.Unstructured conversions - Fixed UnstructuredList GVK handling - Updated dependencies in go.mod/go.sum - Added e2e test for OpenAPI validation - Updated Dockerfile Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
This commit is contained in:
parent
c7f478fc7d
commit
ca29fc855a
5 changed files with 95 additions and 218 deletions
11
go.mod
11
go.mod
|
|
@ -6,11 +6,15 @@ go 1.23.0
|
|||
|
||||
require (
|
||||
github.com/fluxcd/helm-controller/api v1.1.0
|
||||
github.com/go-logr/logr v1.4.2
|
||||
github.com/go-logr/zapr v1.3.0
|
||||
github.com/google/gofuzz v1.2.0
|
||||
github.com/onsi/ginkgo/v2 v2.19.0
|
||||
github.com/onsi/gomega v1.33.1
|
||||
github.com/prometheus/client_golang v1.19.1
|
||||
github.com/spf13/cobra v1.8.1
|
||||
github.com/stretchr/testify v1.9.0
|
||||
go.uber.org/zap v1.27.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
k8s.io/api v0.31.2
|
||||
k8s.io/apiextensions-apiserver v0.31.2
|
||||
|
|
@ -44,9 +48,7 @@ require (
|
|||
github.com/fluxcd/pkg/apis/meta v1.6.1 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-logr/zapr v1.3.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
|
|
@ -74,7 +76,6 @@ require (
|
|||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_golang v1.19.1 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.55.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
|
|
@ -94,7 +95,6 @@ require (
|
|||
go.opentelemetry.io/otel/trace v1.28.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
golang.org/x/crypto v0.31.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
|
||||
golang.org/x/net v0.33.0 // indirect
|
||||
|
|
@ -119,3 +119,6 @@ require (
|
|||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
)
|
||||
|
||||
// See: issues.k8s.io/135537
|
||||
replace k8s.io/apimachinery => github.com/cozystack/apimachinery v0.0.0-20251201201312-18e522a87614
|
||||
|
|
|
|||
4
go.sum
4
go.sum
|
|
@ -18,6 +18,8 @@ github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr
|
|||
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/cozystack/apimachinery v0.0.0-20251201201312-18e522a87614 h1:jH9elECUvhiIs3IMv3oS5k1JgCLVsSK6oU4dmq5gyW8=
|
||||
github.com/cozystack/apimachinery v0.0.0-20251201201312-18e522a87614/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
|
@ -291,8 +293,6 @@ k8s.io/api v0.31.2 h1:3wLBbL5Uom/8Zy98GRPXpJ254nEFpl+hwndmk9RwmL0=
|
|||
k8s.io/api v0.31.2/go.mod h1:bWmGvrGPssSK1ljmLzd3pwCQ9MgoTsRCuK35u6SygUk=
|
||||
k8s.io/apiextensions-apiserver v0.31.2 h1:W8EwUb8+WXBLu56ser5IudT2cOho0gAKeTOnywBLxd0=
|
||||
k8s.io/apiextensions-apiserver v0.31.2/go.mod h1:i+Geh+nGCJEGiCGR3MlBDkS7koHIIKWVfWeRFiOsUcM=
|
||||
k8s.io/apimachinery v0.31.2 h1:i4vUt2hPK56W6mlT7Ry+AO8eEsyxMD1U44NR22CLTYw=
|
||||
k8s.io/apimachinery v0.31.2/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
|
||||
k8s.io/apiserver v0.31.2 h1:VUzOEUGRCDi6kX1OyQ801m4A7AUPglpsmGvdsekmcI4=
|
||||
k8s.io/apiserver v0.31.2/go.mod h1:o3nKZR7lPlJqkU5I3Ove+Zx3JuoFjQobGX1Gctw6XuE=
|
||||
k8s.io/client-go v0.31.2 h1:Y2F4dxU5d3AQj+ybwSMqQnpZH9F30//1ObxOKlTI9yc=
|
||||
|
|
|
|||
|
|
@ -21,14 +21,33 @@
|
|||
}
|
||||
|
||||
@test "Test kinds" {
|
||||
val=$(kubectl get --raw /apis/apps.cozystack.io/v1alpha1/tenants | jq -r '.kind')
|
||||
if [ "$val" != "TenantList" ]; then
|
||||
echo "Expected kind to be TenantList, got $val"
|
||||
exit 1
|
||||
fi
|
||||
val=$(kubectl get --raw /apis/apps.cozystack.io/v1alpha1/tenants | jq -r '.items[0].kind')
|
||||
if [ "$val" != "Tenant" ]; then
|
||||
echo "Expected kind to be Tenant, got $val"
|
||||
exit 1
|
||||
fi
|
||||
val=$(kubectl get --raw /apis/apps.cozystack.io/v1alpha1/ingresses | jq -r '.kind')
|
||||
if [ "$val" != "IngressList" ]; then
|
||||
echo "Expected kind to be IngressList, got $val"
|
||||
exit 1
|
||||
fi
|
||||
val=$(kubectl get --raw /apis/apps.cozystack.io/v1alpha1/ingresses | jq -r '.items[0].kind')
|
||||
if [ "$val" != "Ingress" ]; then
|
||||
echo "Expected kind to be Ingress, got $val"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
@test "Create and delete namespace" {
|
||||
kubectl create ns cozy-test-create-and-delete-namespace --dry-run=client -o yaml | kubectl apply -f -
|
||||
if ! kubectl delete ns cozy-test-create-and-delete-namespace; then
|
||||
echo "Failed to delete namespace"
|
||||
kubectl describe ns cozy-test-create-and-delete-namespace
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ import (
|
|||
helmv2 "github.com/fluxcd/helm-controller/api/v2"
|
||||
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
fields "k8s.io/apimachinery/pkg/fields"
|
||||
labels "k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
|
@ -142,17 +141,9 @@ func (r *REST) GetSingularName() string {
|
|||
// Create handles the creation of a new Application by converting it to a HelmRelease
|
||||
func (r *REST) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
|
||||
// Assert the object is of type Application
|
||||
us, ok := obj.(*unstructured.Unstructured)
|
||||
app, ok := obj.(*appsv1alpha1.Application)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected unstructured.Unstructured object, got %T", obj)
|
||||
}
|
||||
|
||||
app := &appsv1alpha1.Application{}
|
||||
|
||||
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(us.Object, app); err != nil {
|
||||
errMsg := fmt.Sprintf("returned unstructured.Unstructured object was not an Application")
|
||||
klog.Errorf(errMsg)
|
||||
return nil, fmt.Errorf(errMsg)
|
||||
return nil, fmt.Errorf("expected *appsv1alpha1.Application object, got %T", obj)
|
||||
}
|
||||
|
||||
// Convert Application to HelmRelease
|
||||
|
|
@ -186,15 +177,8 @@ func (r *REST) Create(ctx context.Context, obj runtime.Object, createValidation
|
|||
|
||||
klog.V(6).Infof("Successfully created and converted HelmRelease %s to Application", helmRelease.GetName())
|
||||
|
||||
// Convert Application to unstructured format
|
||||
unstructuredApp, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&convertedApp)
|
||||
if err != nil {
|
||||
klog.Errorf("Failed to convert Application to unstructured for resource %s: %v", convertedApp.GetName(), err)
|
||||
return nil, fmt.Errorf("failed to convert Application to unstructured: %v", err)
|
||||
}
|
||||
|
||||
klog.V(6).Infof("Successfully retrieved and converted resource %s of type %s to unstructured", convertedApp.GetName(), r.gvr.Resource)
|
||||
return &unstructured.Unstructured{Object: unstructuredApp}, nil
|
||||
klog.V(6).Infof("Successfully retrieved and converted resource %s of type %s", convertedApp.GetName(), r.gvr.Resource)
|
||||
return &convertedApp, nil
|
||||
}
|
||||
|
||||
// Get retrieves an Application by converting the corresponding HelmRelease
|
||||
|
|
@ -238,25 +222,8 @@ func (r *REST) Get(ctx context.Context, name string, options *metav1.GetOptions)
|
|||
return nil, fmt.Errorf("conversion error: %v", err)
|
||||
}
|
||||
|
||||
// Explicitly set apiVersion and kind for Application
|
||||
convertedApp.TypeMeta = metav1.TypeMeta{
|
||||
APIVersion: "apps.cozystack.io/v1alpha1",
|
||||
Kind: r.kindName,
|
||||
}
|
||||
|
||||
// Convert Application to unstructured format
|
||||
unstructuredApp, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&convertedApp)
|
||||
if err != nil {
|
||||
klog.Errorf("Failed to convert Application to unstructured for resource %s: %v", name, err)
|
||||
return nil, fmt.Errorf("failed to convert Application to unstructured: %v", err)
|
||||
}
|
||||
|
||||
// Explicitly set apiVersion and kind in unstructured object
|
||||
unstructuredApp["apiVersion"] = "apps.cozystack.io/v1alpha1"
|
||||
unstructuredApp["kind"] = r.kindName
|
||||
|
||||
klog.V(6).Infof("Successfully retrieved and converted resource %s of kind %s to unstructured", name, r.gvr.Resource)
|
||||
return &unstructured.Unstructured{Object: unstructuredApp}, nil
|
||||
klog.V(6).Infof("Successfully retrieved and converted resource %s of kind %s", name, r.gvr.Resource)
|
||||
return &convertedApp, nil
|
||||
}
|
||||
|
||||
// List retrieves a list of Applications by converting HelmReleases
|
||||
|
|
@ -339,8 +306,8 @@ func (r *REST) List(ctx context.Context, options *metainternalversion.ListOption
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// Initialize unstructured items array
|
||||
items := make([]unstructured.Unstructured, 0)
|
||||
// Initialize Application items array
|
||||
items := make([]appsv1alpha1.Application, 0, len(hrList.Items))
|
||||
|
||||
// Iterate over HelmReleases and convert to Applications
|
||||
for i := range hrList.Items {
|
||||
|
|
@ -387,17 +354,11 @@ func (r *REST) List(ctx context.Context, options *metainternalversion.ListOption
|
|||
}
|
||||
}
|
||||
|
||||
// Convert Application to unstructured
|
||||
unstructuredApp, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&app)
|
||||
if err != nil {
|
||||
klog.Errorf("Error converting Application %s to unstructured: %v", app.Name, err)
|
||||
continue
|
||||
}
|
||||
items = append(items, unstructured.Unstructured{Object: unstructuredApp})
|
||||
items = append(items, app)
|
||||
}
|
||||
|
||||
// Explicitly set apiVersion and kind in unstructured object
|
||||
appList := r.NewList().(*unstructured.UnstructuredList)
|
||||
// Create ApplicationList with proper kind
|
||||
appList := r.NewList().(*appsv1alpha1.ApplicationList)
|
||||
appList.SetResourceVersion(hrList.GetResourceVersion())
|
||||
appList.Items = items
|
||||
|
||||
|
|
@ -447,16 +408,9 @@ func (r *REST) Update(ctx context.Context, name string, objInfo rest.UpdatedObje
|
|||
}
|
||||
|
||||
// Assert the new object is of type Application
|
||||
us, ok := newObj.(*unstructured.Unstructured)
|
||||
app, ok := newObj.(*appsv1alpha1.Application)
|
||||
if !ok {
|
||||
errMsg := fmt.Sprintf("expected unstructured.Unstructured object, got %T", newObj)
|
||||
klog.Errorf(errMsg)
|
||||
return nil, false, fmt.Errorf(errMsg)
|
||||
}
|
||||
app := &appsv1alpha1.Application{}
|
||||
|
||||
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(us.Object, app); err != nil {
|
||||
errMsg := fmt.Sprintf("returned unstructured.Unstructured object was not an Application")
|
||||
errMsg := fmt.Sprintf("expected *appsv1alpha1.Application object, got %T", newObj)
|
||||
klog.Errorf(errMsg)
|
||||
return nil, false, fmt.Errorf(errMsg)
|
||||
}
|
||||
|
|
@ -517,24 +471,9 @@ func (r *REST) Update(ctx context.Context, name string, objInfo rest.UpdatedObje
|
|||
|
||||
klog.V(6).Infof("Successfully updated and converted HelmRelease %s to Application", helmRelease.GetName())
|
||||
|
||||
// Explicitly set apiVersion and kind for Application
|
||||
convertedApp.TypeMeta = metav1.TypeMeta{
|
||||
APIVersion: "apps.cozystack.io/v1alpha1",
|
||||
Kind: r.kindName,
|
||||
}
|
||||
klog.V(6).Infof("Returning updated Application object: %+v", convertedApp)
|
||||
|
||||
// Convert Application to unstructured format
|
||||
unstructuredApp, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&convertedApp)
|
||||
if err != nil {
|
||||
klog.Errorf("Failed to convert Application to unstructured for resource %s: %v", convertedApp.GetName(), err)
|
||||
return nil, false, fmt.Errorf("failed to convert Application to unstructured: %v", err)
|
||||
}
|
||||
obj := &unstructured.Unstructured{Object: unstructuredApp}
|
||||
obj.SetGroupVersionKind(r.gvk)
|
||||
|
||||
klog.V(6).Infof("Returning patched Application object: %+v", unstructuredApp)
|
||||
|
||||
return obj, false, nil
|
||||
return &convertedApp, false, nil
|
||||
}
|
||||
|
||||
// Delete removes an Application by deleting the corresponding HelmRelease
|
||||
|
|
@ -728,19 +667,10 @@ func (r *REST) Watch(ctx context.Context, options *metainternalversion.ListOptio
|
|||
}
|
||||
}
|
||||
|
||||
// Convert Application to unstructured
|
||||
unstructuredApp, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&app)
|
||||
if err != nil {
|
||||
klog.Errorf("Failed to convert Application to unstructured: %v", err)
|
||||
continue
|
||||
}
|
||||
obj := &unstructured.Unstructured{Object: unstructuredApp}
|
||||
obj.SetGroupVersionKind(r.gvk)
|
||||
|
||||
// Create watch event with Application object
|
||||
appEvent := watch.Event{
|
||||
Type: event.Type,
|
||||
Object: obj,
|
||||
Object: &app,
|
||||
}
|
||||
|
||||
// Send event to custom watcher
|
||||
|
|
@ -766,8 +696,8 @@ func (r *REST) Watch(ctx context.Context, options *metainternalversion.ListOptio
|
|||
|
||||
// Helper function to get HelmRelease name from object
|
||||
func helmReleaseName(obj runtime.Object) string {
|
||||
if u, ok := obj.(*unstructured.Unstructured); ok {
|
||||
return u.GetName()
|
||||
if app, ok := obj.(*appsv1alpha1.Application); ok {
|
||||
return app.GetName()
|
||||
}
|
||||
return "<unknown>"
|
||||
}
|
||||
|
|
@ -1059,56 +989,6 @@ func (r *REST) ConvertToTable(ctx context.Context, object runtime.Object, tableO
|
|||
case *appsv1alpha1.Application:
|
||||
table = r.buildTableFromApplication(*obj)
|
||||
table.ListMeta.ResourceVersion = obj.GetResourceVersion()
|
||||
case *unstructured.UnstructuredList:
|
||||
apps := make([]appsv1alpha1.Application, 0, len(obj.Items))
|
||||
for _, u := range obj.Items {
|
||||
var a appsv1alpha1.Application
|
||||
err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &a)
|
||||
if err != nil {
|
||||
klog.Errorf("Failed to convert Unstructured to Application: %v", err)
|
||||
continue
|
||||
}
|
||||
apps = append(apps, a)
|
||||
}
|
||||
table = r.buildTableFromApplications(apps)
|
||||
table.ListMeta.ResourceVersion = obj.GetResourceVersion()
|
||||
case *unstructured.Unstructured:
|
||||
var apps []appsv1alpha1.Application
|
||||
for {
|
||||
var items interface{}
|
||||
var ok bool
|
||||
var objects []unstructured.Unstructured
|
||||
if items, ok = obj.Object["items"]; !ok {
|
||||
break
|
||||
}
|
||||
if objects, ok = items.([]unstructured.Unstructured); !ok {
|
||||
break
|
||||
}
|
||||
apps = make([]appsv1alpha1.Application, 0, len(objects))
|
||||
var a appsv1alpha1.Application
|
||||
for i := range objects {
|
||||
err := runtime.DefaultUnstructuredConverter.FromUnstructured(objects[i].Object, &a)
|
||||
if err != nil {
|
||||
klog.Errorf("Failed to convert Unstructured to Application: %v", err)
|
||||
continue
|
||||
}
|
||||
apps = append(apps, a)
|
||||
}
|
||||
break
|
||||
}
|
||||
if apps != nil {
|
||||
table = r.buildTableFromApplications(apps)
|
||||
table.ListMeta.ResourceVersion = obj.GetResourceVersion()
|
||||
break
|
||||
}
|
||||
var app appsv1alpha1.Application
|
||||
err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.UnstructuredContent(), &app)
|
||||
if err != nil {
|
||||
klog.Errorf("Failed to convert Unstructured to Application: %v", err)
|
||||
return nil, fmt.Errorf("failed to convert Unstructured to Application: %v", err)
|
||||
}
|
||||
table = r.buildTableFromApplication(app)
|
||||
table.ListMeta.ResourceVersion = obj.GetResourceVersion()
|
||||
default:
|
||||
resource := schema.GroupResource{}
|
||||
if info, ok := request.RequestInfoFrom(ctx); ok {
|
||||
|
|
@ -1147,10 +1027,11 @@ func (r *REST) buildTableFromApplications(apps []appsv1alpha1.Application) metav
|
|||
}
|
||||
now := time.Now()
|
||||
|
||||
for _, app := range apps {
|
||||
for i := range apps {
|
||||
app := &apps[i]
|
||||
row := metav1.TableRow{
|
||||
Cells: []interface{}{app.GetName(), getReadyStatus(app.Status.Conditions), computeAge(app.GetCreationTimestamp().Time, now), getVersion(app.Status.Version)},
|
||||
Object: runtime.RawExtension{Object: &app},
|
||||
Object: runtime.RawExtension{Object: app},
|
||||
}
|
||||
table.Rows = append(table.Rows, row)
|
||||
}
|
||||
|
|
@ -1171,9 +1052,10 @@ func (r *REST) buildTableFromApplication(app appsv1alpha1.Application) metav1.Ta
|
|||
}
|
||||
now := time.Now()
|
||||
|
||||
a := app
|
||||
row := metav1.TableRow{
|
||||
Cells: []interface{}{app.GetName(), getReadyStatus(app.Status.Conditions), computeAge(app.GetCreationTimestamp().Time, now), getVersion(app.Status.Version)},
|
||||
Object: runtime.RawExtension{Object: &app},
|
||||
Object: runtime.RawExtension{Object: &a},
|
||||
}
|
||||
table.Rows = append(table.Rows, row)
|
||||
|
||||
|
|
@ -1237,15 +1119,21 @@ func (r *REST) Destroy() {
|
|||
|
||||
// New creates a new instance of Application
|
||||
func (r *REST) New() runtime.Object {
|
||||
obj := &unstructured.Unstructured{}
|
||||
obj.SetGroupVersionKind(r.gvk)
|
||||
obj := &appsv1alpha1.Application{}
|
||||
obj.TypeMeta = metav1.TypeMeta{
|
||||
APIVersion: r.gvk.GroupVersion().String(),
|
||||
Kind: r.kindName,
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
// NewList returns an empty list of Application objects
|
||||
func (r *REST) NewList() runtime.Object {
|
||||
obj := &unstructured.UnstructuredList{}
|
||||
obj.SetGroupVersionKind(r.gvk.GroupVersion().WithKind(r.kindName + "List"))
|
||||
obj := &appsv1alpha1.ApplicationList{}
|
||||
obj.TypeMeta = metav1.TypeMeta{
|
||||
APIVersion: r.gvk.GroupVersion().String(),
|
||||
Kind: r.kindName + "List",
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ import (
|
|||
helmv2 "github.com/fluxcd/helm-controller/api/v2"
|
||||
metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
fields "k8s.io/apimachinery/pkg/fields"
|
||||
labels "k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
|
@ -147,25 +146,8 @@ func (r *REST) Get(ctx context.Context, name string, options *metav1.GetOptions)
|
|||
return nil, fmt.Errorf("conversion error: %v", err)
|
||||
}
|
||||
|
||||
// Explicitly set apiVersion and kind for TenantModule
|
||||
convertedModule.TypeMeta = metav1.TypeMeta{
|
||||
APIVersion: "core.cozystack.io/v1alpha1",
|
||||
Kind: r.kindName,
|
||||
}
|
||||
|
||||
// Convert TenantModule to unstructured format
|
||||
unstructuredModule, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&convertedModule)
|
||||
if err != nil {
|
||||
klog.Errorf("Failed to convert TenantModule to unstructured for resource %s: %v", name, err)
|
||||
return nil, fmt.Errorf("failed to convert TenantModule to unstructured: %v", err)
|
||||
}
|
||||
|
||||
// Explicitly set apiVersion and kind in unstructured object
|
||||
unstructuredModule["apiVersion"] = "core.cozystack.io/v1alpha1"
|
||||
unstructuredModule["kind"] = r.kindName
|
||||
|
||||
klog.V(6).Infof("Successfully retrieved and converted resource %s of kind %s to unstructured", name, r.gvr.Resource)
|
||||
return &unstructured.Unstructured{Object: unstructuredModule}, nil
|
||||
klog.V(6).Infof("Successfully retrieved and converted resource %s of kind %s", name, r.gvr.Resource)
|
||||
return &convertedModule, nil
|
||||
}
|
||||
|
||||
// List retrieves a list of TenantModules by converting HelmReleases
|
||||
|
|
@ -245,8 +227,8 @@ func (r *REST) List(ctx context.Context, options *metainternalversion.ListOption
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// Initialize unstructured items array
|
||||
items := make([]unstructured.Unstructured, 0)
|
||||
// Initialize TenantModule items array
|
||||
items := make([]corev1alpha1.TenantModule, 0, len(hrList.Items))
|
||||
|
||||
// Iterate over HelmReleases and convert to TenantModules
|
||||
for i := range hrList.Items {
|
||||
|
|
@ -294,19 +276,15 @@ func (r *REST) List(ctx context.Context, options *metainternalversion.ListOption
|
|||
}
|
||||
}
|
||||
|
||||
// Convert TenantModule to unstructured
|
||||
unstructuredModule, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&module)
|
||||
if err != nil {
|
||||
klog.Errorf("Error converting TenantModule %s to unstructured: %v", module.Name, err)
|
||||
continue
|
||||
}
|
||||
items = append(items, unstructured.Unstructured{Object: unstructuredModule})
|
||||
items = append(items, module)
|
||||
}
|
||||
|
||||
// Explicitly set apiVersion and kind in unstructured object
|
||||
moduleList := &unstructured.UnstructuredList{}
|
||||
moduleList.SetAPIVersion("core.cozystack.io/v1alpha1")
|
||||
moduleList.SetKind(r.kindName + "List")
|
||||
// Create TenantModuleList with proper kind
|
||||
moduleList := &corev1alpha1.TenantModuleList{}
|
||||
moduleList.TypeMeta = metav1.TypeMeta{
|
||||
APIVersion: "core.cozystack.io/v1alpha1",
|
||||
Kind: r.kindName + "List",
|
||||
}
|
||||
moduleList.SetResourceVersion(hrList.GetResourceVersion())
|
||||
moduleList.Items = items
|
||||
|
||||
|
|
@ -455,17 +433,10 @@ func (r *REST) Watch(ctx context.Context, options *metainternalversion.ListOptio
|
|||
}
|
||||
}
|
||||
|
||||
// Convert TenantModule to unstructured
|
||||
unstructuredModule, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&module)
|
||||
if err != nil {
|
||||
klog.Errorf("Failed to convert TenantModule to unstructured: %v", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Create watch event with TenantModule object
|
||||
moduleEvent := watch.Event{
|
||||
Type: event.Type,
|
||||
Object: &unstructured.Unstructured{Object: unstructuredModule},
|
||||
Object: &module,
|
||||
}
|
||||
|
||||
// Send event to custom watcher
|
||||
|
|
@ -620,27 +591,11 @@ func (r *REST) ConvertToTable(ctx context.Context, object runtime.Object, tableO
|
|||
var table metav1.Table
|
||||
|
||||
switch obj := object.(type) {
|
||||
case *unstructured.UnstructuredList:
|
||||
modules := make([]corev1alpha1.TenantModule, 0, len(obj.Items))
|
||||
for _, u := range obj.Items {
|
||||
var m corev1alpha1.TenantModule
|
||||
err := runtime.DefaultUnstructuredConverter.FromUnstructured(u.Object, &m)
|
||||
if err != nil {
|
||||
klog.Errorf("Failed to convert Unstructured to TenantModule: %v", err)
|
||||
continue
|
||||
}
|
||||
modules = append(modules, m)
|
||||
}
|
||||
table = r.buildTableFromTenantModules(modules)
|
||||
table.ListMeta.ResourceVersion = obj.GetResourceVersion()
|
||||
case *unstructured.Unstructured:
|
||||
var module corev1alpha1.TenantModule
|
||||
err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.UnstructuredContent(), &module)
|
||||
if err != nil {
|
||||
klog.Errorf("Failed to convert Unstructured to TenantModule: %v", err)
|
||||
return nil, fmt.Errorf("failed to convert Unstructured to TenantModule: %v", err)
|
||||
}
|
||||
table = r.buildTableFromTenantModule(module)
|
||||
case *corev1alpha1.TenantModuleList:
|
||||
table = r.buildTableFromTenantModules(obj.Items)
|
||||
table.ListMeta.ResourceVersion = obj.ListMeta.ResourceVersion
|
||||
case *corev1alpha1.TenantModule:
|
||||
table = r.buildTableFromTenantModule(*obj)
|
||||
table.ListMeta.ResourceVersion = obj.GetResourceVersion()
|
||||
default:
|
||||
resource := schema.GroupResource{}
|
||||
|
|
@ -680,10 +635,11 @@ func (r *REST) buildTableFromTenantModules(modules []corev1alpha1.TenantModule)
|
|||
}
|
||||
now := time.Now()
|
||||
|
||||
for _, module := range modules {
|
||||
for i := range modules {
|
||||
module := &modules[i]
|
||||
row := metav1.TableRow{
|
||||
Cells: []interface{}{module.GetName(), getReadyStatus(module.Status.Conditions), computeAge(module.GetCreationTimestamp().Time, now), getVersion(module.Status.Version)},
|
||||
Object: runtime.RawExtension{Object: &module},
|
||||
Object: runtime.RawExtension{Object: module},
|
||||
}
|
||||
table.Rows = append(table.Rows, row)
|
||||
}
|
||||
|
|
@ -704,9 +660,10 @@ func (r *REST) buildTableFromTenantModule(module corev1alpha1.TenantModule) meta
|
|||
}
|
||||
now := time.Now()
|
||||
|
||||
m := module
|
||||
row := metav1.TableRow{
|
||||
Cells: []interface{}{module.GetName(), getReadyStatus(module.Status.Conditions), computeAge(module.GetCreationTimestamp().Time, now), getVersion(module.Status.Version)},
|
||||
Object: runtime.RawExtension{Object: &module},
|
||||
Object: runtime.RawExtension{Object: &m},
|
||||
}
|
||||
table.Rows = append(table.Rows, row)
|
||||
|
||||
|
|
@ -751,12 +708,22 @@ func (r *REST) Destroy() {
|
|||
|
||||
// New creates a new instance of TenantModule
|
||||
func (r *REST) New() runtime.Object {
|
||||
return &corev1alpha1.TenantModule{}
|
||||
obj := &corev1alpha1.TenantModule{}
|
||||
obj.TypeMeta = metav1.TypeMeta{
|
||||
APIVersion: r.gvk.GroupVersion().String(),
|
||||
Kind: r.kindName,
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
// NewList returns an empty list of TenantModule objects
|
||||
func (r *REST) NewList() runtime.Object {
|
||||
return &corev1alpha1.TenantModuleList{}
|
||||
obj := &corev1alpha1.TenantModuleList{}
|
||||
obj.TypeMeta = metav1.TypeMeta{
|
||||
APIVersion: r.gvk.GroupVersion().String(),
|
||||
Kind: r.kindName + "List",
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
// Kind returns the resource kind used for API discovery
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue