mirror of
https://github.com/outbackdingo/cozystack.git
synced 2026-01-27 18:18:41 +00:00
@@ -12,6 +12,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
)
|
||||
|
||||
@@ -245,29 +246,29 @@ func (m *Manager) buildExpectedResourceSet(crds []cozyv1alpha1.CozystackResource
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip resources with non-empty spec.dashboard.name (tenant modules)
|
||||
if strings.TrimSpace(crd.Spec.Dashboard.Name) != "" {
|
||||
continue
|
||||
}
|
||||
// Note: We include ALL resources with dashboard config, regardless of dashboard.name
|
||||
// because ensureFactory and ensureBreadcrumb create resources for all CRDs with dashboard config
|
||||
|
||||
g, v, kind := pickGVK(&crd)
|
||||
plural := pickPlural(kind, &crd)
|
||||
|
||||
// CustomColumnsOverride
|
||||
// CustomColumnsOverride - created for ALL CRDs with dashboard config
|
||||
name := fmt.Sprintf("stock-namespace-%s.%s.%s", g, v, plural)
|
||||
expected["CustomColumnsOverride"][name] = true
|
||||
|
||||
// CustomFormsOverride
|
||||
// CustomFormsOverride - created for ALL CRDs with dashboard config
|
||||
name = fmt.Sprintf("%s.%s.%s", g, v, plural)
|
||||
expected["CustomFormsOverride"][name] = true
|
||||
|
||||
// CustomFormsPrefill
|
||||
// CustomFormsPrefill - created for ALL CRDs with dashboard config
|
||||
expected["CustomFormsPrefill"][name] = true
|
||||
|
||||
// MarketplacePanel (name matches CRD name)
|
||||
expected["MarketplacePanel"][crd.Name] = true
|
||||
// MarketplacePanel - only created for CRDs WITHOUT dashboard.name
|
||||
if strings.TrimSpace(crd.Spec.Dashboard.Name) == "" {
|
||||
expected["MarketplacePanel"][crd.Name] = true
|
||||
}
|
||||
|
||||
// Sidebar resources (multiple per CRD)
|
||||
// Sidebar resources - created for ALL CRDs with dashboard config
|
||||
lowerKind := strings.ToLower(kind)
|
||||
detailsID := fmt.Sprintf("stock-project-factory-%s-details", lowerKind)
|
||||
expected["Sidebar"][detailsID] = true
|
||||
@@ -291,15 +292,15 @@ func (m *Manager) buildExpectedResourceSet(crds []cozyv1alpha1.CozystackResource
|
||||
expected["Sidebar"][sidebarID] = true
|
||||
}
|
||||
|
||||
// TableUriMapping
|
||||
// TableUriMapping - created for ALL CRDs with dashboard config
|
||||
name = fmt.Sprintf("stock-namespace-%s.%s.%s", g, v, plural)
|
||||
expected["TableUriMapping"][name] = true
|
||||
|
||||
// Breadcrumb
|
||||
// Breadcrumb - created for ALL CRDs with dashboard config
|
||||
detailID := fmt.Sprintf("stock-project-factory-%s-details", lowerKind)
|
||||
expected["Breadcrumb"][detailID] = true
|
||||
|
||||
// Factory
|
||||
// Factory - created for ALL CRDs with dashboard config
|
||||
factoryName := fmt.Sprintf("%s-details", lowerKind)
|
||||
expected["Factory"][factoryName] = true
|
||||
}
|
||||
@@ -423,22 +424,24 @@ func (m *Manager) cleanupResourceType(ctx context.Context, resourceType client.O
|
||||
case *dashv1alpha1.BreadcrumbList:
|
||||
for _, item := range l.Items {
|
||||
if !expected[item.Name] {
|
||||
logger := log.FromContext(ctx)
|
||||
logger.Info("Deleting orphaned Breadcrumb resource", "name", item.Name)
|
||||
if err := m.client.Delete(ctx, &item); err != nil {
|
||||
if !apierrors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
// Resource already deleted, continue
|
||||
}
|
||||
}
|
||||
}
|
||||
case *dashv1alpha1.FactoryList:
|
||||
for _, item := range l.Items {
|
||||
if !expected[item.Name] {
|
||||
logger := log.FromContext(ctx)
|
||||
logger.Info("Deleting orphaned Factory resource", "name", item.Name)
|
||||
if err := m.client.Delete(ctx, &item); err != nil {
|
||||
if !apierrors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
// Resource already deleted, continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +57,9 @@ func (m *Manager) ensureSidebar(ctx context.Context, crd *cozyv1alpha1.Cozystack
|
||||
categories := map[string][]item{} // category label -> children
|
||||
keysAndTags := map[string]any{} // plural -> []string{ "<lower(kind)>-sidebar" }
|
||||
|
||||
// Collect sidebar names for resources with dashboard.name
|
||||
var moduleSidebars []any
|
||||
|
||||
for i := range all {
|
||||
def := &all[i]
|
||||
|
||||
@@ -65,35 +68,46 @@ func (m *Manager) ensureSidebar(ctx context.Context, crd *cozyv1alpha1.Cozystack
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip resources with non-empty spec.dashboard.name
|
||||
if strings.TrimSpace(def.Spec.Dashboard.Name) != "" {
|
||||
continue
|
||||
}
|
||||
|
||||
g, v, kind := pickGVK(def)
|
||||
plural := pickPlural(kind, def)
|
||||
cat := safeCategory(def) // falls back to "Resources" if empty
|
||||
lowerKind := strings.ToLower(kind)
|
||||
|
||||
// Label: prefer dashboard.Plural if provided
|
||||
label := titleFromKindPlural(kind, plural)
|
||||
if def.Spec.Dashboard.Plural != "" {
|
||||
label = def.Spec.Dashboard.Plural
|
||||
// Check if this resource has dashboard.name set
|
||||
if strings.TrimSpace(def.Spec.Dashboard.Name) != "" {
|
||||
// Add to modules sidebar list
|
||||
moduleSidebars = append(moduleSidebars, fmt.Sprintf("%s-sidebar", lowerKind))
|
||||
} else {
|
||||
// Add to keysAndTags for resources without dashboard.name
|
||||
keysAndTags[plural] = []any{fmt.Sprintf("%s-sidebar", lowerKind)}
|
||||
}
|
||||
|
||||
// Weight (default 0)
|
||||
weight := def.Spec.Dashboard.Weight
|
||||
// Only add to menu categories if dashboard.name is empty
|
||||
if strings.TrimSpace(def.Spec.Dashboard.Name) == "" {
|
||||
cat := safeCategory(def) // falls back to "Resources" if empty
|
||||
|
||||
link := fmt.Sprintf("/openapi-ui/{clusterName}/{namespace}/api-table/%s/%s/%s", g, v, plural)
|
||||
// Label: prefer dashboard.Plural if provided
|
||||
label := titleFromKindPlural(kind, plural)
|
||||
if def.Spec.Dashboard.Plural != "" {
|
||||
label = def.Spec.Dashboard.Plural
|
||||
}
|
||||
|
||||
categories[cat] = append(categories[cat], item{
|
||||
Key: plural,
|
||||
Label: label,
|
||||
Link: link,
|
||||
Weight: weight,
|
||||
})
|
||||
// Weight (default 0)
|
||||
weight := def.Spec.Dashboard.Weight
|
||||
|
||||
// keysAndTags: plural -> [ "<lower(kind)>-sidebar" ]
|
||||
keysAndTags[plural] = []any{fmt.Sprintf("%s-sidebar", strings.ToLower(kind))}
|
||||
link := fmt.Sprintf("/openapi-ui/{clusterName}/{namespace}/api-table/%s/%s/%s", g, v, plural)
|
||||
|
||||
categories[cat] = append(categories[cat], item{
|
||||
Key: plural,
|
||||
Label: label,
|
||||
Link: link,
|
||||
Weight: weight,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Add modules to keysAndTags if we have any module sidebars
|
||||
if len(moduleSidebars) > 0 {
|
||||
keysAndTags["modules"] = moduleSidebars
|
||||
}
|
||||
|
||||
// 3) Sort items within each category by Weight (desc), then Label (A→Z)
|
||||
@@ -171,14 +185,8 @@ func (m *Manager) ensureSidebar(ctx context.Context, crd *cozyv1alpha1.Cozystack
|
||||
})
|
||||
|
||||
// 6) Prepare the list of Sidebar IDs to upsert with the SAME content
|
||||
_, _, thisKind := pickGVK(crd)
|
||||
lowerThisKind := strings.ToLower(thisKind)
|
||||
detailsID := fmt.Sprintf("stock-project-factory-%s-details", lowerThisKind)
|
||||
|
||||
// Create sidebars for ALL CRDs with dashboard config
|
||||
targetIDs := []string{
|
||||
// original details sidebar
|
||||
detailsID,
|
||||
|
||||
// stock-instance sidebars
|
||||
"stock-instance-api-form",
|
||||
"stock-instance-api-table",
|
||||
@@ -196,6 +204,18 @@ func (m *Manager) ensureSidebar(ctx context.Context, crd *cozyv1alpha1.Cozystack
|
||||
"stock-project-crd-table",
|
||||
}
|
||||
|
||||
// Add details sidebars for all CRDs with dashboard config
|
||||
for i := range all {
|
||||
def := &all[i]
|
||||
if def.Spec.Dashboard == nil {
|
||||
continue
|
||||
}
|
||||
_, _, kind := pickGVK(def)
|
||||
lowerKind := strings.ToLower(kind)
|
||||
detailsID := fmt.Sprintf("stock-project-factory-%s-details", lowerKind)
|
||||
targetIDs = append(targetIDs, detailsID)
|
||||
}
|
||||
|
||||
// 7) Upsert all target sidebars with identical menuItems and keysAndTags
|
||||
return m.upsertMultipleSidebars(ctx, crd, targetIDs, keysAndTags, menuItems)
|
||||
}
|
||||
@@ -219,11 +239,35 @@ func (m *Manager) upsertMultipleSidebars(
|
||||
obj.SetName(id)
|
||||
|
||||
if _, err := controllerutil.CreateOrUpdate(ctx, m.client, obj, func() error {
|
||||
if err := controllerutil.SetOwnerReference(crd, obj, m.scheme); err != nil {
|
||||
return err
|
||||
// Only set owner reference for dynamic sidebars (stock-project-factory-{kind}-details)
|
||||
// Static sidebars (stock-instance-*, stock-project-*) should not have owner references
|
||||
if strings.HasPrefix(id, "stock-project-factory-") && strings.HasSuffix(id, "-details") {
|
||||
// This is a dynamic sidebar, set owner reference only if it matches the current CRD
|
||||
_, _, kind := pickGVK(crd)
|
||||
lowerKind := strings.ToLower(kind)
|
||||
expectedID := fmt.Sprintf("stock-project-factory-%s-details", lowerKind)
|
||||
if id == expectedID {
|
||||
if err := controllerutil.SetOwnerReference(crd, obj, m.scheme); err != nil {
|
||||
return err
|
||||
}
|
||||
// Add dashboard labels to dynamic resources
|
||||
m.addDashboardLabels(obj, crd, ResourceTypeDynamic)
|
||||
} else {
|
||||
// This is a different CRD's sidebar, don't modify owner references or labels
|
||||
// Just update the spec
|
||||
}
|
||||
} else {
|
||||
// This is a static sidebar, don't set owner references
|
||||
// Add static labels
|
||||
labels := obj.GetLabels()
|
||||
if labels == nil {
|
||||
labels = make(map[string]string)
|
||||
}
|
||||
labels[LabelManagedBy] = ManagedByValue
|
||||
labels[LabelResourceType] = ResourceTypeStatic
|
||||
obj.SetLabels(labels)
|
||||
}
|
||||
// Add dashboard labels to dynamic resources
|
||||
m.addDashboardLabels(obj, crd, ResourceTypeDynamic)
|
||||
|
||||
b, err := json.Marshal(spec)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
Reference in New Issue
Block a user