[dashboard] refactor dashboard configuration (#1457)

- Refactor code for dashboard resources creation
- Move dashboard-config helm chart to dynamic dashboard controller

<!-- Thank you for making a contribution! Here are some tips for you:
- Start the PR title with the [label] of Cozystack component:
- For system components: [platform], [system], [linstor], [cilium],
[kube-ovn], [dashboard], [cluster-api], etc.
- For managed apps: [apps], [tenant], [kubernetes], [postgres],
[virtual-machine] etc.
- For development and maintenance: [tests], [ci], [docs], [maintenance].
- If it's a work in progress, consider creating this PR as a draft.
- Don't hesistate to ask for opinion and review in the community chats,
even if it's still a draft.
- Add the label `backport` if it's a bugfix that needs to be backported
to a previous version.
-->

## What this PR does


### Release note

<!--  Write a release note:
- Explain what has changed internally and for users.
- Start with the same [label] as in the PR title
- Follow the guidelines at
https://github.com/kubernetes/community/blob/master/contributors/guide/release-notes.md.
-->

```release-note
[]
```

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

* **New Features**
* Static dashboard resources (breadcrumbs, factories, forms, marketplace
panels, table mappings) are initialized at startup and materialized
automatically.

* **Improvements**
* Unified UI construction with consistent badges, headers and
deterministic IDs.
  * Automatic cleanup of stale/orphaned dashboard resources.
  * Increased controller client throughput for faster operations.

* **Refactor**
* Consolidated static dashboard resource generation into a unified,
config-driven flow.

* **Chores**
* Removed legacy dashboard-config templates; updated controller and
dashboard image digests.
  * Added dashboard ConfigMap and wired UI env vars to it.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Andrei Kvapil
2025-09-25 16:16:50 +02:00
committed by GitHub
90 changed files with 4878 additions and 6031 deletions

View File

@@ -153,7 +153,12 @@ func main() {
// this setup is not recommended for production.
}
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
// Configure rate limiting for the Kubernetes client
config := ctrl.GetConfigOrDie()
config.QPS = 50.0 // Increased from default 5.0
config.Burst = 100 // Increased from default 10
mgr, err := ctrl.NewManager(config, ctrl.Options{
Scheme: scheme,
Metrics: metricsServerOptions,
WebhookServer: webhookServer,

View File

@@ -23,8 +23,10 @@ import (
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/manager"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
@@ -39,6 +41,10 @@ type CozystackResourceDefinitionReconciler struct {
lastHandled time.Time
mem *crdmem.Memory
// Track static resources initialization
staticResourcesInitialized bool
staticResourcesMutex sync.Mutex
}
func (r *CozystackResourceDefinitionReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
@@ -70,15 +76,25 @@ func (r *CozystackResourceDefinitionReconciler) Reconcile(ctx context.Context, r
return res, derr
}
// After processing CRD, perform cleanup of orphaned resources
// This should be done after cache warming to ensure all current resources are known
if cleanupErr := mgr.CleanupOrphanedResources(ctx); cleanupErr != nil {
logger.Error(cleanupErr, "Failed to cleanup orphaned dashboard resources")
// Don't fail the reconciliation, just log the error
}
r.mu.Lock()
r.lastEvent = time.Now()
r.mu.Unlock()
return ctrl.Result{}, nil
}
if err != nil && !apierrors.IsNotFound(err) {
// Handle error cases (err is guaranteed to be non-nil here)
if !apierrors.IsNotFound(err) {
return ctrl.Result{}, err
}
if apierrors.IsNotFound(err) && r.mem != nil {
// If resource is not found, clean up from memory
if r.mem != nil {
r.mem.Delete(req.Name)
}
if req.Namespace == "cozy-system" && req.Name == "cozystack-api" {
@@ -87,6 +103,40 @@ func (r *CozystackResourceDefinitionReconciler) Reconcile(ctx context.Context, r
return ctrl.Result{}, nil
}
// initializeStaticResourcesOnce ensures static resources are created only once
func (r *CozystackResourceDefinitionReconciler) initializeStaticResourcesOnce(ctx context.Context) error {
r.staticResourcesMutex.Lock()
defer r.staticResourcesMutex.Unlock()
if r.staticResourcesInitialized {
return nil // Already initialized
}
// Create dashboard manager and initialize static resources
mgr := dashboard.NewManager(
r.Client,
r.Scheme,
dashboard.WithCRDListFunc(func(c context.Context) ([]cozyv1alpha1.CozystackResourceDefinition, error) {
if r.mem != nil {
return r.mem.ListFromCacheOrAPI(c, r.Client)
}
var list cozyv1alpha1.CozystackResourceDefinitionList
if err := r.Client.List(c, &list); err != nil {
return nil, err
}
return list.Items, nil
}),
)
if err := mgr.InitializeStaticResources(ctx); err != nil {
return err
}
r.staticResourcesInitialized = true
log.FromContext(ctx).Info("Static dashboard resources initialized successfully")
return nil
}
func (r *CozystackResourceDefinitionReconciler) SetupWithManager(mgr ctrl.Manager) error {
if r.Debounce == 0 {
r.Debounce = 5 * time.Second
@@ -97,6 +147,18 @@ func (r *CozystackResourceDefinitionReconciler) SetupWithManager(mgr ctrl.Manage
if err := r.mem.EnsurePrimingWithManager(mgr); err != nil {
return err
}
// Initialize static resources once during controller startup using manager.Runnable
if err := mgr.Add(manager.RunnableFunc(func(ctx context.Context) error {
if err := r.initializeStaticResourcesOnce(ctx); err != nil {
log.FromContext(ctx).Error(err, "Failed to initialize static resources")
return err
}
return nil
})); err != nil {
return err
}
return ctrl.NewControllerManagedBy(mgr).
Named("cozystackresource-controller").
For(&cozyv1alpha1.CozystackResourceDefinition{}, builder.WithPredicates()).
@@ -114,6 +176,9 @@ func (r *CozystackResourceDefinitionReconciler) SetupWithManager(mgr ctrl.Manage
}}
}),
).
WithOptions(controller.Options{
MaxConcurrentReconciles: 5, // Allow more concurrent reconciles with proper rate limiting
}).
Complete(r)
}

View File

@@ -0,0 +1,81 @@
package dashboard
import (
"context"
"encoding/json"
"fmt"
"strings"
dashv1alpha1 "github.com/cozystack/cozystack/api/dashboard/v1alpha1"
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)
// ensureBreadcrumb creates or updates a Breadcrumb resource for the given CRD
func (m *Manager) ensureBreadcrumb(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) error {
group, version, kind := pickGVK(crd)
lowerKind := strings.ToLower(kind)
detailID := fmt.Sprintf("stock-project-factory-%s-details", lowerKind)
obj := &dashv1alpha1.Breadcrumb{}
obj.SetName(detailID)
plural := pickPlural(kind, crd)
// Prefer dashboard.Plural for UI label if provided
labelPlural := titleFromKindPlural(kind, plural)
if crd != nil && crd.Spec.Dashboard != nil && crd.Spec.Dashboard.Plural != "" {
labelPlural = crd.Spec.Dashboard.Plural
}
key := plural // e.g., "virtualmachines"
label := labelPlural
link := fmt.Sprintf("/openapi-ui/{clusterName}/{namespace}/api-table/%s/%s/%s", strings.ToLower(group), strings.ToLower(version), plural)
// If Name is set, change the first breadcrumb item to "Tenant Modules"
// TODO add parameter to this
if crd.Spec.Dashboard != nil && strings.TrimSpace(crd.Spec.Dashboard.Name) != "" {
key = "tenantmodules"
label = "Tenant Modules"
link = "/openapi-ui/{clusterName}/{namespace}/api-table/core.cozystack.io/v1alpha1/tenantmodules"
}
items := []any{
map[string]any{
"key": key,
"label": label,
"link": link,
},
map[string]any{
"key": strings.ToLower(kind), // "etcd"
"label": "{6}", // literal, as in your example
},
}
spec := map[string]any{
"id": detailID,
"breadcrumbItems": items,
}
_, err := controllerutil.CreateOrUpdate(ctx, m.client, obj, func() error {
if err := controllerutil.SetOwnerReference(crd, obj, m.scheme); err != nil {
return err
}
// Add dashboard labels to dynamic resources
m.addDashboardLabels(obj, crd, ResourceTypeDynamic)
b, err := json.Marshal(spec)
if err != nil {
return err
}
// Only update spec if it's different to avoid unnecessary updates
newSpec := dashv1alpha1.ArbitrarySpec{JSON: apiextv1.JSON{Raw: b}}
if !compareArbitrarySpecs(obj.Spec, newSpec) {
obj.Spec = newSpec
}
return nil
})
return err
}

View File

@@ -2,8 +2,6 @@ package dashboard
import (
"context"
"crypto/sha1"
"encoding/hex"
"encoding/json"
"fmt"
"strings"
@@ -12,7 +10,6 @@ import (
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)
@@ -38,11 +35,6 @@ func (m *Manager) ensureCustomColumnsOverride(ctx context.Context, crd *cozyv1al
badgeColor := hexColorForKind(kind) // deterministic, dark enough for white text
obj := &dashv1alpha1.CustomColumnsOverride{}
obj.SetGroupVersionKind(schema.GroupVersionKind{
Group: "dashboard.cozystack.io",
Version: "v1alpha1",
Kind: "CustomColumnsOverride",
})
obj.SetName(name)
href := fmt.Sprintf("/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/%s/{reqsJsonPath[0]['.metadata.name']['-']}", detailsSegment)
@@ -153,146 +145,24 @@ func (m *Manager) ensureCustomColumnsOverride(ctx context.Context, crd *cozyv1al
},
}
// CreateOrUpdate using typed resource
_, err := controllerutil.CreateOrUpdate(ctx, m.client, obj, func() error {
if err := controllerutil.SetOwnerReference(crd, obj, m.scheme); err != nil {
return err
}
// Add dashboard labels to dynamic resources
m.addDashboardLabels(obj, crd, ResourceTypeDynamic)
b, err := json.Marshal(desired["spec"])
if err != nil {
return err
}
obj.Spec = dashv1alpha1.ArbitrarySpec{JSON: apiextv1.JSON{Raw: b}}
// Only update spec if it's different to avoid unnecessary updates
newSpec := dashv1alpha1.ArbitrarySpec{JSON: apiextv1.JSON{Raw: b}}
if !compareArbitrarySpecs(obj.Spec, newSpec) {
obj.Spec = newSpec
}
return nil
})
// Return OperationResultCreated/Updated is not available here with unstructured; we can mimic Updated when no error.
return controllerutil.OperationResultNone, err
}
// --- helpers ---
// pickGVK tries to read group/version/kind from the CRD. We prefer the "application" section,
// falling back to other likely fields if your schema differs.
func pickGVK(crd *cozyv1alpha1.CozystackResourceDefinition) (group, version, kind string) {
// Best guess based on your examples:
if crd.Spec.Application.Kind != "" {
kind = crd.Spec.Application.Kind
}
// Reasonable fallbacks if any are empty:
if group == "" {
group = "apps.cozystack.io"
}
if version == "" {
version = "v1alpha1"
}
if kind == "" {
kind = "Resource"
}
return
}
// pickPlural prefers a field on the CRD if you have it; otherwise do a simple lowercase + "s".
func pickPlural(kind string, crd *cozyv1alpha1.CozystackResourceDefinition) string {
// If you have crd.Spec.Application.Plural, prefer it. Example:
if crd.Spec.Application.Plural != "" {
return crd.Spec.Application.Plural
}
// naive pluralization
k := strings.ToLower(kind)
if strings.HasSuffix(k, "s") {
return k
}
return k + "s"
}
// initialsFromKind splits CamelCase and returns the first letters in upper case.
// "VirtualMachine" -> "VM"; "Bucket" -> "B".
func initialsFromKind(kind string) string {
parts := splitCamel(kind)
if len(parts) == 0 {
return strings.ToUpper(kind)
}
var b strings.Builder
for _, p := range parts {
if p == "" {
continue
}
b.WriteString(strings.ToUpper(string(p[0])))
// Limit to 3 chars to keep the badge compact (VM, PVC, etc.)
if b.Len() >= 3 {
break
}
}
return b.String()
}
// hexColorForKind returns a dark, saturated color (hex) derived from a stable hash of the kind.
// We map the hash to an HSL hue; fix S/L for consistent readability with white text.
func hexColorForKind(kind string) string {
// Stable short hash (sha1 → bytes → hue)
sum := sha1.Sum([]byte(kind))
// Use first two bytes for hue [0..359]
hue := int(sum[0])<<8 | int(sum[1])
hue = hue % 360
// Fixed S/L chosen to contrast with white text:
// S = 80%, L = 35% (dark enough so #fff is readable)
r, g, b := hslToRGB(float64(hue), 0.80, 0.35)
return fmt.Sprintf("#%02x%02x%02x", r, g, b)
}
// hslToRGB converts HSL (0..360, 0..1, 0..1) to sRGB (0..255).
func hslToRGB(h float64, s float64, l float64) (uint8, uint8, uint8) {
c := (1 - absFloat(2*l-1)) * s
hp := h / 60.0
x := c * (1 - absFloat(modFloat(hp, 2)-1))
var r1, g1, b1 float64
switch {
case 0 <= hp && hp < 1:
r1, g1, b1 = c, x, 0
case 1 <= hp && hp < 2:
r1, g1, b1 = x, c, 0
case 2 <= hp && hp < 3:
r1, g1, b1 = 0, c, x
case 3 <= hp && hp < 4:
r1, g1, b1 = 0, x, c
case 4 <= hp && hp < 5:
r1, g1, b1 = x, 0, c
default:
r1, g1, b1 = c, 0, x
}
m := l - c/2
r := uint8(clamp01(r1+m) * 255.0)
g := uint8(clamp01(g1+m) * 255.0)
b := uint8(clamp01(b1+m) * 255.0)
return r, g, b
}
func absFloat(v float64) float64 {
if v < 0 {
return -v
}
return v
}
func modFloat(a, b float64) float64 {
return a - b*float64(int(a/b))
}
func clamp01(v float64) float64 {
if v < 0 {
return 0
}
if v > 1 {
return 1
}
return v
}
// optional: tiny helper to expose the compact color hash (useful for debugging)
func shortHashHex(s string) string {
sum := sha1.Sum([]byte(s))
return hex.EncodeToString(sum[:4])
}

View File

@@ -0,0 +1,75 @@
package dashboard
import (
"context"
"encoding/json"
"fmt"
"strings"
dashv1alpha1 "github.com/cozystack/cozystack/api/dashboard/v1alpha1"
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)
// ensureCustomFormsOverride creates or updates a CustomFormsOverride resource for the given CRD
func (m *Manager) ensureCustomFormsOverride(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) error {
g, v, kind := pickGVK(crd)
plural := pickPlural(kind, crd)
name := fmt.Sprintf("%s.%s.%s", g, v, plural)
customizationID := fmt.Sprintf("default-/%s/%s/%s", g, v, plural)
obj := &dashv1alpha1.CustomFormsOverride{}
obj.SetName(name)
// Replicates your Helm includes (system metadata + api + status).
hidden := []any{}
hidden = append(hidden, hiddenMetadataSystem()...)
hidden = append(hidden, hiddenMetadataAPI()...)
hidden = append(hidden, hiddenStatus()...)
// If Name is set, hide metadata
if crd.Spec.Dashboard != nil && strings.TrimSpace(crd.Spec.Dashboard.Name) != "" {
hidden = append([]interface{}{
[]any{"metadata"},
}, hidden...)
}
var sort []any
if crd.Spec.Dashboard != nil && len(crd.Spec.Dashboard.KeysOrder) > 0 {
sort = make([]any, len(crd.Spec.Dashboard.KeysOrder))
for i, v := range crd.Spec.Dashboard.KeysOrder {
sort[i] = v
}
}
spec := map[string]any{
"customizationId": customizationID,
"hidden": hidden,
"sort": sort,
"schema": map[string]any{}, // {}
"strategy": "merge",
}
_, err := controllerutil.CreateOrUpdate(ctx, m.client, obj, func() error {
if err := controllerutil.SetOwnerReference(crd, obj, m.scheme); err != nil {
return err
}
// Add dashboard labels to dynamic resources
m.addDashboardLabels(obj, crd, ResourceTypeDynamic)
b, err := json.Marshal(spec)
if err != nil {
return err
}
// Only update spec if it's different to avoid unnecessary updates
newSpec := dashv1alpha1.ArbitrarySpec{JSON: apiextv1.JSON{Raw: b}}
if !compareArbitrarySpecs(obj.Spec, newSpec) {
obj.Spec = newSpec
}
return nil
})
return err
}

View File

@@ -0,0 +1,79 @@
package dashboard
import (
"context"
"encoding/json"
"fmt"
"strings"
dashv1alpha1 "github.com/cozystack/cozystack/api/dashboard/v1alpha1"
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
// ensureCustomFormsPrefill creates or updates a CustomFormsPrefill resource for the given CRD
func (m *Manager) ensureCustomFormsPrefill(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) (reconcile.Result, error) {
logger := log.FromContext(ctx)
app := crd.Spec.Application
group, version, kind := pickGVK(crd)
plural := pickPlural(kind, crd)
name := fmt.Sprintf("%s.%s.%s", group, version, plural)
customizationID := fmt.Sprintf("default-/%s/%s/%s", group, version, plural)
values, err := buildPrefillValues(app.OpenAPISchema)
if err != nil {
return reconcile.Result{}, err
}
// If Name is set, prefill metadata.name
if crd.Spec.Dashboard != nil && strings.TrimSpace(crd.Spec.Dashboard.Name) != "" {
values = append([]interface{}{
map[string]interface{}{
"path": toIfaceSlice([]string{"metadata", "name"}),
"value": crd.Spec.Dashboard.Name,
},
}, values...)
}
cfp := &dashv1alpha1.CustomFormsPrefill{}
cfp.Name = name // cluster-scoped
specMap := map[string]any{
"customizationId": customizationID,
"values": values,
}
// Use json.Marshal with sorted keys to ensure consistent output
specBytes, err := json.Marshal(specMap)
if err != nil {
return reconcile.Result{}, err
}
_, err = controllerutil.CreateOrUpdate(ctx, m.client, cfp, func() error {
if err := controllerutil.SetOwnerReference(crd, cfp, m.scheme); err != nil {
return err
}
// Add dashboard labels to dynamic resources
m.addDashboardLabels(cfp, crd, ResourceTypeDynamic)
// Only update spec if it's different to avoid unnecessary updates
newSpec := dashv1alpha1.ArbitrarySpec{
JSON: apiextv1.JSON{Raw: specBytes},
}
if !compareArbitrarySpecs(cfp.Spec, newSpec) {
cfp.Spec = newSpec
}
return nil
})
if err != nil {
return reconcile.Result{}, err
}
logger.Info("Applied CustomFormsPrefill", "name", cfp.Name)
return reconcile.Result{}, nil
}

View File

@@ -11,20 +11,10 @@ import (
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)
// ---------------- Types used by OpenAPI parsing ----------------
type fieldInfo struct {
JSONPathSpec string // dotted path under .spec (e.g., "systemDisk.image")
Label string // "System Disk / Image" or "systemDisk.image"
Description string
}
// ---------------- Public entry: ensure Factory ------------------
// ensureFactory creates or updates a Factory resource for the given CRD
func (m *Manager) ensureFactory(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) error {
g, v, kind := pickGVK(crd)
plural := pickPlural(kind, crd)
@@ -56,85 +46,37 @@ func (m *Manager) ensureFactory(ctx context.Context, crd *cozyv1alpha1.Cozystack
}
tabs = append(tabs, yamlTab(plural))
badgeText := initialsFromKind(kind)
badgeColor := hexColorForKind(kind)
header := map[string]any{
"type": "antdFlex",
"data": map[string]any{
"id": "header-row",
"align": "center",
"gap": float64(6),
"style": map[string]any{"marginBottom": float64(24)},
},
"children": []any{
map[string]any{
"type": "antdText",
"data": map[string]any{
"id": "badge-" + lowerKind,
"text": badgeText,
"title": strings.ToLower(plural),
"style": map[string]any{
"backgroundColor": badgeColor,
"borderRadius": "20px",
"color": "#fff",
"display": "inline-block",
"fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif",
"fontSize": float64(20),
"fontWeight": float64(400),
"lineHeight": "24px",
"minWidth": float64(24),
"padding": "0 9px",
"textAlign": "center",
"whiteSpace": "nowrap",
},
},
},
map[string]any{
"type": "parsedText",
"data": map[string]any{
"id": lowerKind + "-name",
"text": "{reqsJsonPath[0]['.metadata.name']['-']}",
"style": map[string]any{
"fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif",
"fontSize": float64(20),
"lineHeight": "24px",
},
},
},
},
// Use unified factory creation
config := UnifiedResourceConfig{
Name: factoryName,
ResourceType: "factory",
Kind: kind,
Plural: plural,
Title: strings.ToLower(plural),
Size: BadgeSizeLarge,
}
spec := map[string]any{
"key": factoryName,
"sidebarTags": []any{fmt.Sprintf("%s-sidebar", lowerKind)},
"withScrollableMainContentCard": true,
"urlsToFetch": []any{resourceFetch},
"data": []any{
header,
map[string]any{
"type": "antdTabs",
"data": map[string]any{
"id": lowerKind + "-tabs",
"defaultActiveKey": "details",
"items": tabs,
},
},
},
}
spec := createUnifiedFactory(config, tabs, []any{resourceFetch})
obj := &dashv1alpha1.Factory{}
obj.SetGroupVersionKind(schema.GroupVersionKind{Group: "dashboard.cozystack.io", Version: "v1alpha1", Kind: "Factory"})
obj.SetName(factoryName)
_, err := controllerutil.CreateOrUpdate(ctx, m.client, obj, func() error {
if err := controllerutil.SetOwnerReference(crd, obj, m.scheme); err != nil {
return err
}
// Add dashboard labels to dynamic resources
m.addDashboardLabels(obj, crd, ResourceTypeDynamic)
b, err := json.Marshal(spec)
if err != nil {
return err
}
obj.Spec = dashv1alpha1.ArbitrarySpec{JSON: apiextv1.JSON{Raw: b}}
// Only update spec if it's different to avoid unnecessary updates
newSpec := dashv1alpha1.ArbitrarySpec{JSON: apiextv1.JSON{Raw: b}}
if !compareArbitrarySpecs(obj.Spec, newSpec) {
obj.Spec = newSpec
}
return nil
})
return err
@@ -173,30 +115,10 @@ func detailsTab(kind, endpoint, schemaJSON string, keysOrder [][]string) map[str
"gap": float64(6),
},
"children": []any{
map[string]any{
"type": "antdText",
"data": map[string]any{
"id": "ns-badge",
"text": "NS",
"style": map[string]any{
"backgroundColor": "#a25792ff",
"borderRadius": "20px",
"color": "#fff",
"display": "inline-block",
"fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif",
"fontSize": float64(15),
"fontWeight": float64(400),
"lineHeight": "24px",
"minWidth": float64(24),
"padding": "0 9px",
"textAlign": "center",
"whiteSpace": "nowrap",
},
},
},
createUnifiedBadgeFromKind("ns-badge", "Namespace", "namespace", BadgeSizeMedium),
antdLink("namespace-link",
"{reqsJsonPath[0]['.metadata.namespace']['-']}",
"/openapi-ui/{2}/factory/namespace-details/{reqsJsonPath[0]['.metadata.namespace']['-']}",
"/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/marketplace",
),
},
},
@@ -407,131 +329,6 @@ func yamlTab(plural string) map[string]any {
}
}
// ---------------- UI helpers (use float64 for numeric fields) ----------------
func contentCard(id string, style map[string]any, children []any) map[string]any {
return map[string]any{
"type": "ContentCard",
"data": map[string]any{
"id": id,
"style": style,
},
"children": children,
}
}
func antdText(id string, strong bool, text string, style map[string]any) map[string]any {
data := map[string]any{
"id": id,
"text": text,
"strong": strong,
}
if style != nil {
data["style"] = style
}
return map[string]any{"type": "antdText", "data": data}
}
func parsedText(id, text string, style map[string]any) map[string]any {
data := map[string]any{
"id": id,
"text": text,
}
if style != nil {
data["style"] = style
}
return map[string]any{"type": "parsedText", "data": data}
}
func parsedTextWithFormatter(id, text, formatter string) map[string]any {
return map[string]any{
"type": "parsedText",
"data": map[string]any{
"id": id,
"text": text,
"formatter": formatter,
},
}
}
func spacer(id string, space float64) map[string]any {
return map[string]any{
"type": "Spacer",
"data": map[string]any{
"id": id,
"$space": space,
},
}
}
func antdFlex(id string, gap float64, children []any) map[string]any {
return map[string]any{
"type": "antdFlex",
"data": map[string]any{
"id": id,
"align": "center",
"gap": gap,
},
"children": children,
}
}
func antdFlexVertical(id string, gap float64, children []any) map[string]any {
return map[string]any{
"type": "antdFlex",
"data": map[string]any{
"id": id,
"vertical": true,
"gap": gap,
},
"children": children,
}
}
func antdRow(id string, gutter []any, children []any) map[string]any {
return map[string]any{
"type": "antdRow",
"data": map[string]any{
"id": id,
"gutter": gutter,
},
"children": children,
}
}
func antdCol(id string, span float64, children []any) map[string]any {
return map[string]any{
"type": "antdCol",
"data": map[string]any{
"id": id,
"span": span,
},
"children": children,
}
}
func antdColWithStyle(id string, style map[string]any, children []any) map[string]any {
return map[string]any{
"type": "antdCol",
"data": map[string]any{
"id": id,
"style": style,
},
"children": children,
}
}
func antdLink(id, text, href string) map[string]any {
return map[string]any{
"type": "antdLink",
"data": map[string]any{
"id": id,
"text": text,
"href": href,
},
}
}
// ---------------- OpenAPI → Right column ----------------
func buildOpenAPIParamsBlocks(schemaJSON string, keysOrder [][]string) []any {
@@ -576,7 +373,7 @@ func sortFieldsByKeysOrder(fields []fieldInfo, keysOrder [][]string) []fieldInfo
sort.Slice(fields, func(i, j int) bool {
posI, existsI := orderMap[fields[i].JSONPathSpec]
posJ, existsJ := orderMap[fields[j].JSONPathSpec]
// If both exist in orderMap, sort by position
if existsI && existsJ {
return posI < posJ
@@ -666,7 +463,7 @@ func collectOpenAPILeafFields(schemaJSON string, maxDepth, maxFields int) []fiel
}
return
}
// Arrays: try to render item if its scalar and depth limit allows
// Arrays: try to render item if it's scalar and depth limit allows
if n["type"] == "array" {
if items, ok := n["items"].(node); ok && (isScalarType(items) || isIntOrString(items) || hasEnum(items)) {
addField(prefix, items)
@@ -693,74 +490,6 @@ func collectOpenAPILeafFields(schemaJSON string, maxDepth, maxFields int) []fiel
return out
}
// --- helpers for schema inspection ---
func isScalarType(n map[string]any) bool {
switch getString(n, "type") {
case "string", "integer", "number", "boolean":
return true
default:
return false
}
}
func isIntOrString(n map[string]any) bool {
// Kubernetes extension: x-kubernetes-int-or-string: true
if v, ok := n["x-kubernetes-int-or-string"]; ok {
if b, ok := v.(bool); ok && b {
return true
}
}
// anyOf: integer|string
if anyOf, ok := n["anyOf"].([]any); ok {
hasInt := false
hasStr := false
for _, it := range anyOf {
if m, ok := it.(map[string]any); ok {
switch getString(m, "type") {
case "integer":
hasInt = true
case "string":
hasStr = true
}
}
}
return hasInt && hasStr
}
return false
}
func hasEnum(n map[string]any) bool {
_, ok := n["enum"]
return ok
}
func getString(n map[string]any, key string) string {
if v, ok := n[key]; ok {
if s, ok := v.(string); ok {
return s
}
}
return ""
}
// shouldExcludeParamPath returns true if any part of the path contains
// backup / bootstrap / password (case-insensitive)
func shouldExcludeParamPath(parts []string) bool {
for _, p := range parts {
lp := strings.ToLower(p)
if strings.Contains(lp, "backup") || strings.Contains(lp, "bootstrap") || strings.Contains(lp, "password") || strings.Contains(lp, "cloudInit") {
return true
}
}
return false
}
func humanizePath(parts []string) string {
// "systemDisk.image" -> "System Disk / Image"
return strings.Join(parts, " / ")
}
// ---------------- Feature flags ----------------
type factoryFlags struct {

View File

@@ -0,0 +1,553 @@
package dashboard
import (
"crypto/sha1"
"encoding/hex"
"encoding/json"
"fmt"
"reflect"
"regexp"
"sort"
"strings"
dashv1alpha1 "github.com/cozystack/cozystack/api/dashboard/v1alpha1"
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
)
// ---------------- Types used by OpenAPI parsing ----------------
type fieldInfo struct {
JSONPathSpec string // dotted path under .spec (e.g., "systemDisk.image")
Label string // "System Disk / Image" or "systemDisk.image"
Description string
}
// ---------------- Public entry: ensure Factory ------------------
// pickGVK tries to read group/version/kind from the CRD. We prefer the "application" section,
// falling back to other likely fields if your schema differs.
func pickGVK(crd *cozyv1alpha1.CozystackResourceDefinition) (group, version, kind string) {
// Best guess based on your examples:
if crd.Spec.Application.Kind != "" {
kind = crd.Spec.Application.Kind
}
// Parse crd.APIVersion to get group and version (format: "group/version")
if crd.APIVersion != "" {
parts := strings.Split(crd.APIVersion, "/")
if len(parts) == 2 {
group = parts[0]
version = parts[1]
}
}
// Reasonable fallbacks if any are empty:
if group == "" {
group = "apps.cozystack.io"
}
if version == "" {
version = "v1alpha1"
}
if kind == "" {
kind = "Resource"
}
return
}
// pickPlural prefers a field on the CRD if you have it; otherwise do a simple lowercase + "s".
func pickPlural(kind string, crd *cozyv1alpha1.CozystackResourceDefinition) string {
// If you have crd.Spec.Application.Plural, prefer it. Example:
if crd.Spec.Application.Plural != "" {
return crd.Spec.Application.Plural
}
// naive pluralization
k := strings.ToLower(kind)
if strings.HasSuffix(k, "s") {
return k
}
return k + "s"
}
// initialsFromKind splits CamelCase and returns the first letters in upper case.
// "VirtualMachine" -> "VM"; "Bucket" -> "B".
func initialsFromKind(kind string) string {
parts := splitCamel(kind)
if len(parts) == 0 {
return strings.ToUpper(kind)
}
var b strings.Builder
for _, p := range parts {
if p == "" {
continue
}
b.WriteString(strings.ToUpper(string(p[0])))
// Limit to 3 chars to keep the badge compact (VM, PVC, etc.)
if b.Len() >= 3 {
break
}
}
return b.String()
}
// hexColorForKind returns a dark, saturated color (hex) derived from a stable hash of the kind.
// We map the hash to an HSL hue; fix S/L for consistent readability with white text.
func hexColorForKind(kind string) string {
// Stable short hash (sha1 → bytes → hue)
sum := sha1.Sum([]byte(kind))
// Use first two bytes for hue [0..359]
hue := int(sum[0])<<8 | int(sum[1])
hue = hue % 360
// Fixed S/L chosen to contrast with white text:
// S = 80%, L = 35% (dark enough so #fff is readable)
r, g, b := hslToRGB(float64(hue), 0.80, 0.35)
return fmt.Sprintf("#%02x%02x%02x", r, g, b)
}
// hslToRGB converts HSL (0..360, 0..1, 0..1) to sRGB (0..255).
func hslToRGB(h float64, s float64, l float64) (uint8, uint8, uint8) {
c := (1 - absFloat(2*l-1)) * s
hp := h / 60.0
x := c * (1 - absFloat(modFloat(hp, 2)-1))
var r1, g1, b1 float64
switch {
case 0 <= hp && hp < 1:
r1, g1, b1 = c, x, 0
case 1 <= hp && hp < 2:
r1, g1, b1 = x, c, 0
case 2 <= hp && hp < 3:
r1, g1, b1 = 0, c, x
case 3 <= hp && hp < 4:
r1, g1, b1 = 0, x, c
case 4 <= hp && hp < 5:
r1, g1, b1 = x, 0, c
default:
r1, g1, b1 = c, 0, x
}
m := l - c/2
r := uint8(clamp01(r1+m) * 255.0)
g := uint8(clamp01(g1+m) * 255.0)
b := uint8(clamp01(b1+m) * 255.0)
return r, g, b
}
func absFloat(v float64) float64 {
if v < 0 {
return -v
}
return v
}
func modFloat(a, b float64) float64 {
return a - b*float64(int(a/b))
}
func clamp01(v float64) float64 {
if v < 0 {
return 0
}
if v > 1 {
return 1
}
return v
}
// optional: tiny helper to expose the compact color hash (useful for debugging)
func shortHashHex(s string) string {
sum := sha1.Sum([]byte(s))
return hex.EncodeToString(sum[:4])
}
// ----------------------- Helpers (OpenAPI → values) -----------------------
// defaultOrZero returns the schema default if present; otherwise a reasonable zero value.
func defaultOrZero(sub map[string]interface{}) interface{} {
if v, ok := sub["default"]; ok {
return v
}
typ, _ := sub["type"].(string)
switch typ {
case "string":
return ""
case "boolean":
return false
case "array":
return []interface{}{}
case "integer", "number":
return 0
case "object":
return map[string]interface{}{}
default:
return nil
}
}
// toIfaceSlice converts []string -> []interface{}.
func toIfaceSlice(ss []string) []interface{} {
out := make([]interface{}, len(ss))
for i, s := range ss {
out[i] = s
}
return out
}
// buildPrefillValues converts an OpenAPI schema (JSON string) into a []interface{} "values" list
// suitable for CustomFormsPrefill.spec.values.
// Rules:
// - For top-level primitive/array fields: emit an entry, using default if present, otherwise zero.
// - For top-level objects: recursively process nested objects and emit entries for all default values
// found at any nesting level.
func buildPrefillValues(openAPISchema string) ([]interface{}, error) {
var root map[string]interface{}
if err := json.Unmarshal([]byte(openAPISchema), &root); err != nil {
return nil, fmt.Errorf("cannot parse openAPISchema: %w", err)
}
props, _ := root["properties"].(map[string]interface{})
if props == nil {
return []interface{}{}, nil
}
var values []interface{}
processSchemaProperties(props, []string{"spec"}, &values, true)
return values, nil
}
// processSchemaProperties recursively processes OpenAPI schema properties and extracts default values
func processSchemaProperties(props map[string]interface{}, path []string, values *[]interface{}, topLevel bool) {
for pname, raw := range props {
sub, _ := raw.(map[string]interface{})
if sub == nil {
continue
}
typ, _ := sub["type"].(string)
currentPath := append(path, pname)
switch typ {
case "object":
// Check if this object has a default value
if objDefault, ok := sub["default"].(map[string]interface{}); ok {
// Process the default object recursively
processDefaultObject(objDefault, currentPath, values)
}
// Also process child properties for their individual defaults
if childProps, ok := sub["properties"].(map[string]interface{}); ok {
processSchemaProperties(childProps, currentPath, values, false)
}
default:
// For primitive types, use default if present, otherwise zero value
val := defaultOrZero(sub)
// Only emit zero-value entries when at top level
if val != nil || topLevel {
entry := map[string]interface{}{
"path": toIfaceSlice(currentPath),
"value": val,
}
*values = append(*values, entry)
}
}
}
}
// processDefaultObject recursively processes a default object and creates entries for all nested values
func processDefaultObject(obj map[string]interface{}, path []string, values *[]interface{}) {
for key, value := range obj {
currentPath := append(path, key)
// If the value is a map, process it recursively
if nestedObj, ok := value.(map[string]interface{}); ok {
processDefaultObject(nestedObj, currentPath, values)
} else {
// For primitive values, create an entry
entry := map[string]interface{}{
"path": toIfaceSlice(currentPath),
"value": value,
}
*values = append(*values, entry)
}
}
}
// normalizeJSON makes maps/slices JSON-safe for k8s Unstructured:
// - converts all int/int32/... to float64
// - leaves strings, bools, nil as-is
func normalizeJSON(v any) any {
switch t := v.(type) {
case map[string]any:
out := make(map[string]any, len(t))
for k, val := range t {
out[k] = normalizeJSON(val)
}
return out
case []any:
out := make([]any, len(t))
for i := range t {
out[i] = normalizeJSON(t[i])
}
return out
case int:
return float64(t)
case int8:
return float64(t)
case int16:
return float64(t)
case int32:
return float64(t)
case int64:
return float64(t)
case uint, uint8, uint16, uint32, uint64:
return float64(reflect.ValueOf(t).Convert(reflect.TypeOf(uint64(0))).Uint())
case float32:
return float64(t)
default:
return v
}
}
var camelSplitter = regexp.MustCompile(`(?m)([A-Z]+[a-z0-9]*|[a-z0-9]+)`)
func splitCamel(s string) []string {
return camelSplitter.FindAllString(s, -1)
}
// --- helpers for schema inspection ---
func isScalarType(n map[string]any) bool {
switch getString(n, "type") {
case "string", "integer", "number", "boolean":
return true
default:
return false
}
}
func isIntOrString(n map[string]any) bool {
// Kubernetes extension: x-kubernetes-int-or-string: true
if v, ok := n["x-kubernetes-int-or-string"]; ok {
if b, ok := v.(bool); ok && b {
return true
}
}
// anyOf: integer|string
if anyOf, ok := n["anyOf"].([]any); ok {
hasInt := false
hasStr := false
for _, it := range anyOf {
if m, ok := it.(map[string]any); ok {
switch getString(m, "type") {
case "integer":
hasInt = true
case "string":
hasStr = true
}
}
}
return hasInt && hasStr
}
return false
}
func hasEnum(n map[string]any) bool {
_, ok := n["enum"]
return ok
}
func getString(n map[string]any, key string) string {
if v, ok := n[key]; ok {
if s, ok := v.(string); ok {
return s
}
}
return ""
}
// shouldExcludeParamPath returns true if any part of the path contains
// backup / bootstrap / password (case-insensitive)
func shouldExcludeParamPath(parts []string) bool {
for _, p := range parts {
lp := strings.ToLower(p)
if strings.Contains(lp, "backup") || strings.Contains(lp, "bootstrap") || strings.Contains(lp, "password") || strings.Contains(lp, "cloudinit") {
return true
}
}
return false
}
func humanizePath(parts []string) string {
// "systemDisk.image" -> "System Disk / Image"
return strings.Join(parts, " / ")
}
// titleFromKindPlural returns a presentable plural label, e.g.:
// kind="VirtualMachine", plural="virtualmachines" => "VirtualMachines"
func titleFromKindPlural(kind, plural string) string {
return kind + "s"
}
// The hidden lists below mirror the Helm templates you shared.
// Each entry is a path as nested string array, e.g. ["metadata","creationTimestamp"].
func hiddenMetadataSystem() []any {
return []any{
[]any{"metadata", "annotations"},
[]any{"metadata", "labels"},
[]any{"metadata", "namespace"},
[]any{"metadata", "creationTimestamp"},
[]any{"metadata", "deletionGracePeriodSeconds"},
[]any{"metadata", "deletionTimestamp"},
[]any{"metadata", "finalizers"},
[]any{"metadata", "generateName"},
[]any{"metadata", "generation"},
[]any{"metadata", "managedFields"},
[]any{"metadata", "ownerReferences"},
[]any{"metadata", "resourceVersion"},
[]any{"metadata", "selfLink"},
[]any{"metadata", "uid"},
}
}
func hiddenMetadataAPI() []any {
return []any{
[]any{"kind"},
[]any{"apiVersion"},
[]any{"appVersion"},
}
}
func hiddenStatus() []any {
return []any{
[]any{"status"},
}
}
// compareArbitrarySpecs compares two ArbitrarySpec objects by comparing their JSON content
func compareArbitrarySpecs(spec1, spec2 dashv1alpha1.ArbitrarySpec) bool {
// If both are empty, they're equal
if len(spec1.JSON.Raw) == 0 && len(spec2.JSON.Raw) == 0 {
return true
}
// If one is empty and the other is not, they're different
if len(spec1.JSON.Raw) == 0 || len(spec2.JSON.Raw) == 0 {
return false
}
// Parse and normalize both specs
norm1, err := normalizeJSONForComparison(spec1.JSON.Raw)
if err != nil {
return false
}
norm2, err := normalizeJSONForComparison(spec2.JSON.Raw)
if err != nil {
return false
}
// Compare normalized JSON
equal := string(norm1) == string(norm2)
return equal
}
// normalizeJSONForComparison normalizes JSON by sorting arrays and objects
func normalizeJSONForComparison(data []byte) ([]byte, error) {
var obj interface{}
if err := json.Unmarshal(data, &obj); err != nil {
return nil, err
}
// Recursively normalize the object
normalized := normalizeObject(obj)
// Re-marshal to get normalized JSON
return json.Marshal(normalized)
}
// normalizeObject recursively normalizes objects and arrays
func normalizeObject(obj interface{}) interface{} {
switch v := obj.(type) {
case map[string]interface{}:
// For maps, we don't need to sort keys as json.Marshal handles that
result := make(map[string]interface{})
for k, val := range v {
result[k] = normalizeObject(val)
}
return result
case []interface{}:
// For arrays, we need to sort them if they contain objects with comparable fields
if len(v) == 0 {
return v
}
// Check if this is an array of objects that can be sorted
if canSortArray(v) {
// Sort the array
sorted := make([]interface{}, len(v))
copy(sorted, v)
sortArray(sorted)
return sorted
}
// If we can't sort, just normalize each element
result := make([]interface{}, len(v))
for i, val := range v {
result[i] = normalizeObject(val)
}
return result
default:
return v
}
}
// canSortArray checks if an array can be sorted (contains objects with comparable fields)
func canSortArray(arr []interface{}) bool {
if len(arr) == 0 {
return false
}
// Check if all elements are objects
for _, item := range arr {
if _, ok := item.(map[string]interface{}); !ok {
return false
}
}
// Check if objects have comparable fields (like "path" for CustomFormsPrefill values)
firstObj, ok := arr[0].(map[string]interface{})
if !ok {
return false
}
// Look for "path" field which is used in CustomFormsPrefill values
if _, hasPath := firstObj["path"]; hasPath {
return true
}
return false
}
// sortArray sorts an array of objects by their "path" field
func sortArray(arr []interface{}) {
sort.Slice(arr, func(i, j int) bool {
objI, okI := arr[i].(map[string]interface{})
objJ, okJ := arr[j].(map[string]interface{})
if !okI || !okJ {
return false
}
pathI, hasPathI := objI["path"]
pathJ, hasPathJ := objJ["path"]
if !hasPathI || !hasPathJ {
return false
}
// Convert paths to strings for comparison
pathIStr := fmt.Sprintf("%v", pathI)
pathJStr := fmt.Sprintf("%v", pathJ)
return pathIStr < pathJStr
})
}

View File

@@ -2,25 +2,35 @@ package dashboard
import (
"context"
"encoding/json"
"fmt"
"reflect"
"regexp"
"strings"
dashv1alpha1 "github.com/cozystack/cozystack/api/dashboard/v1alpha1"
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
const (
// Label keys for dashboard resource management
LabelManagedBy = "dashboard.cozystack.io/managed-by"
LabelResourceType = "dashboard.cozystack.io/resource-type"
LabelCRDName = "dashboard.cozystack.io/crd-name"
LabelCRDGroup = "dashboard.cozystack.io/crd-group"
LabelCRDVersion = "dashboard.cozystack.io/crd-version"
LabelCRDKind = "dashboard.cozystack.io/crd-kind"
LabelCRDPlural = "dashboard.cozystack.io/crd-plural"
// Label values
ManagedByValue = "cozystack-dashboard-controller"
ResourceTypeStatic = "static"
ResourceTypeDynamic = "dynamic"
)
// AddToScheme exposes dashboard types registration for controller setup.
func AddToScheme(s *runtime.Scheme) error {
return dashv1alpha1.AddToScheme(s)
@@ -63,281 +73,376 @@ func NewManager(c client.Client, scheme *runtime.Scheme, opts ...Option) *Manage
// - ensureSidebar (implemented)
// - ensureTableUriMapping (implemented)
func (m *Manager) EnsureForCRD(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) (reconcile.Result, error) {
// Early return if crd.Spec.Dashboard is nil to prevent oscillation
if crd.Spec.Dashboard == nil {
return reconcile.Result{}, nil
}
// MarketplacePanel
if res, err := m.ensureMarketplacePanel(ctx, crd); err != nil || res.Requeue || res.RequeueAfter > 0 {
return res, err
}
// CustomFormsPrefill
if res, err := m.ensureCustomFormsPrefill(ctx, crd); err != nil || res.Requeue || res.RequeueAfter > 0 {
return res, err
}
// CustomColumnsOverride
if _, err := m.ensureCustomColumnsOverride(ctx, crd); err != nil {
return reconcile.Result{}, err
}
if err := m.ensureTableUriMapping(ctx, crd); err != nil {
return reconcile.Result{}, err
}
if err := m.ensureBreadcrumb(ctx, crd); err != nil {
return reconcile.Result{}, err
}
if err := m.ensureCustomFormsOverride(ctx, crd); err != nil {
return reconcile.Result{}, err
}
if err := m.ensureSidebar(ctx, crd); err != nil {
return reconcile.Result{}, err
}
if err := m.ensureFactory(ctx, crd); err != nil {
return reconcile.Result{}, err
}
return reconcile.Result{}, nil
}
// ----------------------- MarketplacePanel -----------------------
// InitializeStaticResources creates all static dashboard resources once during controller startup
func (m *Manager) InitializeStaticResources(ctx context.Context) error {
return m.ensureStaticResources(ctx)
}
func (m *Manager) ensureMarketplacePanel(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) (reconcile.Result, error) {
logger := log.FromContext(ctx)
// addDashboardLabels adds standard dashboard management labels to a resource
func (m *Manager) addDashboardLabels(obj client.Object, crd *cozyv1alpha1.CozystackResourceDefinition, resourceType string) {
labels := obj.GetLabels()
if labels == nil {
labels = make(map[string]string)
}
mp := &dashv1alpha1.MarketplacePanel{}
mp.Name = crd.Name // cluster-scoped resource, name mirrors CRD name
labels[LabelManagedBy] = ManagedByValue
labels[LabelResourceType] = resourceType
// If dashboard is not set, delete the panel if it exists.
if crd.Spec.Dashboard == nil {
err := m.client.Get(ctx, client.ObjectKey{Name: mp.Name}, mp)
if apierrors.IsNotFound(err) {
return reconcile.Result{}, nil
}
if crd != nil {
g, v, kind := pickGVK(crd)
plural := pickPlural(kind, crd)
labels[LabelCRDName] = crd.Name
labels[LabelCRDGroup] = g
labels[LabelCRDVersion] = v
labels[LabelCRDKind] = kind
labels[LabelCRDPlural] = plural
}
obj.SetLabels(labels)
}
// getDashboardResourceSelector returns a label selector for dashboard-managed resources
func (m *Manager) getDashboardResourceSelector() client.MatchingLabels {
return client.MatchingLabels{
LabelManagedBy: ManagedByValue,
}
}
// getDynamicResourceSelector returns a label selector for dynamic dashboard resources
func (m *Manager) getDynamicResourceSelector() client.MatchingLabels {
return client.MatchingLabels{
LabelManagedBy: ManagedByValue,
LabelResourceType: ResourceTypeDynamic,
}
}
// getStaticResourceSelector returns a label selector for static dashboard resources
func (m *Manager) getStaticResourceSelector() client.MatchingLabels {
return client.MatchingLabels{
LabelManagedBy: ManagedByValue,
LabelResourceType: ResourceTypeStatic,
}
}
// CleanupOrphanedResources removes dashboard resources that are no longer needed
// This should be called after cache warming to ensure all current resources are known
func (m *Manager) CleanupOrphanedResources(ctx context.Context) error {
// Get all current CRDs to determine which resources should exist
var allCRDs []cozyv1alpha1.CozystackResourceDefinition
if m.crdListFn != nil {
s, err := m.crdListFn(ctx)
if err != nil {
return reconcile.Result{}, err
}
if err := m.client.Delete(ctx, mp); err != nil && !apierrors.IsNotFound(err) {
return reconcile.Result{}, err
}
logger.Info("Deleted MarketplacePanel because dashboard is not set", "name", mp.Name)
return reconcile.Result{}, nil
}
// Skip resources with non-empty spec.dashboard.name
if strings.TrimSpace(crd.Spec.Dashboard.Name) != "" {
err := m.client.Get(ctx, client.ObjectKey{Name: mp.Name}, mp)
if apierrors.IsNotFound(err) {
return reconcile.Result{}, nil
}
if err != nil {
return reconcile.Result{}, err
}
if err := m.client.Delete(ctx, mp); err != nil && !apierrors.IsNotFound(err) {
return reconcile.Result{}, err
}
logger.Info("Deleted MarketplacePanel because spec.dashboard.name is set", "name", mp.Name)
return reconcile.Result{}, nil
}
// Build desired spec from CRD fields
d := crd.Spec.Dashboard
app := crd.Spec.Application
displayName := d.Singular
if displayName == "" {
displayName = app.Kind
}
tags := make([]any, len(d.Tags))
for i, t := range d.Tags {
tags[i] = t
}
specMap := map[string]any{
"description": d.Description,
"name": displayName,
"type": "nonCrd",
"apiGroup": "apps.cozystack.io",
"apiVersion": "v1alpha1",
"typeName": app.Plural, // e.g., "buckets"
"disabled": false,
"hidden": false,
"tags": tags,
"icon": d.Icon,
}
specBytes, err := json.Marshal(specMap)
if err != nil {
return reconcile.Result{}, err
}
mutate := func() error {
if err := controllerutil.SetOwnerReference(crd, mp, m.scheme); err != nil {
return err
}
// Inline JSON payload (the ArbitrarySpec type inlines apiextv1.JSON)
mp.Spec = dashv1alpha1.ArbitrarySpec{
JSON: apiextv1.JSON{Raw: specBytes},
allCRDs = s
} else {
var crdList cozyv1alpha1.CozystackResourceDefinitionList
if err := m.client.List(ctx, &crdList, &client.ListOptions{}); err != nil {
return err
}
return nil
allCRDs = crdList.Items
}
op, err := controllerutil.CreateOrUpdate(ctx, m.client, mp, mutate)
if err != nil {
return reconcile.Result{}, err
// Build a set of expected resource names for each type
expectedResources := m.buildExpectedResourceSet(allCRDs)
// Clean up each resource type
resourceTypes := []client.Object{
&dashv1alpha1.CustomColumnsOverride{},
&dashv1alpha1.CustomFormsOverride{},
&dashv1alpha1.CustomFormsPrefill{},
&dashv1alpha1.MarketplacePanel{},
&dashv1alpha1.Sidebar{},
&dashv1alpha1.TableUriMapping{},
&dashv1alpha1.Breadcrumb{},
&dashv1alpha1.Factory{},
}
switch op {
case controllerutil.OperationResultCreated:
logger.Info("Created MarketplacePanel", "name", mp.Name)
case controllerutil.OperationResultUpdated:
logger.Info("Updated MarketplacePanel", "name", mp.Name)
for _, resourceType := range resourceTypes {
if err := m.cleanupResourceType(ctx, resourceType, expectedResources); err != nil {
return err
}
}
return reconcile.Result{}, nil
return nil
}
// ----------------------- Helpers (OpenAPI → values) -----------------------
// buildExpectedResourceSet creates a map of expected resource names by type
func (m *Manager) buildExpectedResourceSet(crds []cozyv1alpha1.CozystackResourceDefinition) map[string]map[string]bool {
expected := make(map[string]map[string]bool)
// defaultOrZero returns the schema default if present; otherwise a reasonable zero value.
func defaultOrZero(sub map[string]interface{}) interface{} {
if v, ok := sub["default"]; ok {
return v
}
typ, _ := sub["type"].(string)
switch typ {
case "string":
return ""
case "boolean":
return false
case "array":
return []interface{}{}
case "integer", "number":
return 0
case "object":
return map[string]interface{}{}
default:
return nil
}
}
// toIfaceSlice converts []string -> []interface{}.
func toIfaceSlice(ss []string) []interface{} {
out := make([]interface{}, len(ss))
for i, s := range ss {
out[i] = s
}
return out
}
// buildPrefillValues converts an OpenAPI schema (JSON string) into a []interface{} "values" list
// suitable for CustomFormsPrefill.spec.values.
// Rules:
// - For top-level primitive/array fields: emit an entry, using default if present, otherwise zero.
// - For top-level objects: recursively process nested objects and emit entries for all default values
// found at any nesting level.
func buildPrefillValues(openAPISchema string) ([]interface{}, error) {
var root map[string]interface{}
if err := json.Unmarshal([]byte(openAPISchema), &root); err != nil {
return nil, fmt.Errorf("cannot parse openAPISchema: %w", err)
}
props, _ := root["properties"].(map[string]interface{})
if props == nil {
return []interface{}{}, nil
// Initialize maps for each resource type
resourceTypes := []string{
"CustomColumnsOverride",
"CustomFormsOverride",
"CustomFormsPrefill",
"MarketplacePanel",
"Sidebar",
"TableUriMapping",
"Breadcrumb",
"Factory",
}
var values []interface{}
processSchemaProperties(props, []string{"spec"}, &values)
return values, nil
}
for _, rt := range resourceTypes {
expected[rt] = make(map[string]bool)
}
// processSchemaProperties recursively processes OpenAPI schema properties and extracts default values
func processSchemaProperties(props map[string]interface{}, path []string, values *[]interface{}) {
for pname, raw := range props {
sub, _ := raw.(map[string]interface{})
if sub == nil {
// Add static resources (these should always exist)
staticResources := CreateAllStaticResources()
for _, resource := range staticResources {
resourceType := resource.GetObjectKind().GroupVersionKind().Kind
if expected[resourceType] != nil {
expected[resourceType][resource.GetName()] = true
}
}
// Add dynamic resources based on current CRDs
for _, crd := range crds {
if crd.Spec.Dashboard == nil {
continue
}
typ, _ := sub["type"].(string)
currentPath := append(path, pname)
switch typ {
case "object":
// Check if this object has a default value
if objDefault, ok := sub["default"].(map[string]interface{}); ok {
// Process the default object recursively
processDefaultObject(objDefault, currentPath, values)
}
// Also process child properties for their individual defaults
if childProps, ok := sub["properties"].(map[string]interface{}); ok {
processSchemaProperties(childProps, currentPath, values)
}
default:
// For primitive types, use default if present, otherwise zero value
val := defaultOrZero(sub)
if val != nil {
entry := map[string]interface{}{
"path": toIfaceSlice(currentPath),
"value": val,
}
*values = append(*values, entry)
}
// Skip resources with non-empty spec.dashboard.name (tenant modules)
if strings.TrimSpace(crd.Spec.Dashboard.Name) != "" {
continue
}
g, v, kind := pickGVK(&crd)
plural := pickPlural(kind, &crd)
// CustomColumnsOverride
name := fmt.Sprintf("stock-namespace-%s.%s.%s", g, v, plural)
expected["CustomColumnsOverride"][name] = true
// CustomFormsOverride
name = fmt.Sprintf("%s.%s.%s", g, v, plural)
expected["CustomFormsOverride"][name] = true
// CustomFormsPrefill
expected["CustomFormsPrefill"][name] = true
// MarketplacePanel (name matches CRD name)
expected["MarketplacePanel"][crd.Name] = true
// Sidebar resources (multiple per CRD)
lowerKind := strings.ToLower(kind)
detailsID := fmt.Sprintf("stock-project-factory-%s-details", lowerKind)
expected["Sidebar"][detailsID] = true
// Add other stock sidebars that are created for each CRD
stockSidebars := []string{
"stock-instance-api-form",
"stock-instance-api-table",
"stock-instance-builtin-form",
"stock-instance-builtin-table",
"stock-project-factory-marketplace",
"stock-project-factory-workloadmonitor-details",
"stock-project-api-form",
"stock-project-api-table",
"stock-project-builtin-form",
"stock-project-builtin-table",
"stock-project-crd-form",
"stock-project-crd-table",
}
for _, sidebarID := range stockSidebars {
expected["Sidebar"][sidebarID] = true
}
// TableUriMapping
name = fmt.Sprintf("stock-namespace-%s.%s.%s", g, v, plural)
expected["TableUriMapping"][name] = true
// Breadcrumb
detailID := fmt.Sprintf("stock-project-factory-%s-details", lowerKind)
expected["Breadcrumb"][detailID] = true
// Factory
factoryName := fmt.Sprintf("%s-details", lowerKind)
expected["Factory"][factoryName] = true
}
return expected
}
// processDefaultObject recursively processes a default object and creates entries for all nested values
func processDefaultObject(obj map[string]interface{}, path []string, values *[]interface{}) {
for key, value := range obj {
currentPath := append(path, key)
// If the value is a map, process it recursively
if nestedObj, ok := value.(map[string]interface{}); ok {
processDefaultObject(nestedObj, currentPath, values)
} else {
// For primitive values, create an entry
entry := map[string]interface{}{
"path": toIfaceSlice(currentPath),
"value": value,
}
*values = append(*values, entry)
}
}
}
// normalizeJSON makes maps/slices JSON-safe for k8s Unstructured:
// - converts all int/int32/... to float64
// - leaves strings, bools, nil as-is
func normalizeJSON(v any) any {
switch t := v.(type) {
case map[string]any:
out := make(map[string]any, len(t))
for k, val := range t {
out[k] = normalizeJSON(val)
}
return out
case []any:
out := make([]any, len(t))
for i := range t {
out[i] = normalizeJSON(t[i])
}
return out
case int:
return float64(t)
case int8:
return float64(t)
case int16:
return float64(t)
case int32:
return float64(t)
case int64:
return float64(t)
case uint, uint8, uint16, uint32, uint64:
return float64(reflect.ValueOf(t).Convert(reflect.TypeOf(uint64(0))).Uint())
case float32:
return float64(t)
// cleanupResourceType removes orphaned resources of a specific type
func (m *Manager) cleanupResourceType(ctx context.Context, resourceType client.Object, expectedResources map[string]map[string]bool) error {
var (
list client.ObjectList
resourceKind string
)
switch resourceType.(type) {
case *dashv1alpha1.CustomColumnsOverride:
list = &dashv1alpha1.CustomColumnsOverrideList{}
resourceKind = "CustomColumnsOverride"
case *dashv1alpha1.CustomFormsOverride:
list = &dashv1alpha1.CustomFormsOverrideList{}
resourceKind = "CustomFormsOverride"
case *dashv1alpha1.CustomFormsPrefill:
list = &dashv1alpha1.CustomFormsPrefillList{}
resourceKind = "CustomFormsPrefill"
case *dashv1alpha1.MarketplacePanel:
list = &dashv1alpha1.MarketplacePanelList{}
resourceKind = "MarketplacePanel"
case *dashv1alpha1.Sidebar:
list = &dashv1alpha1.SidebarList{}
resourceKind = "Sidebar"
case *dashv1alpha1.TableUriMapping:
list = &dashv1alpha1.TableUriMappingList{}
resourceKind = "TableUriMapping"
case *dashv1alpha1.Breadcrumb:
list = &dashv1alpha1.BreadcrumbList{}
resourceKind = "Breadcrumb"
case *dashv1alpha1.Factory:
list = &dashv1alpha1.FactoryList{}
resourceKind = "Factory"
default:
return v
return nil // Unknown type
}
}
var camelSplitter = regexp.MustCompile(`(?m)([A-Z]+[a-z0-9]*|[a-z0-9]+)`)
expected := expectedResources[resourceKind]
if expected == nil {
return nil // No expected resources for this type
}
func splitCamel(s string) []string {
return camelSplitter.FindAllString(s, -1)
// List with dashboard labels
if err := m.client.List(ctx, list, m.getDashboardResourceSelector()); err != nil {
return err
}
// Delete resources that are not in the expected set
switch l := list.(type) {
case *dashv1alpha1.CustomColumnsOverrideList:
for _, item := range l.Items {
if !expected[item.Name] {
if err := m.client.Delete(ctx, &item); err != nil {
if !apierrors.IsNotFound(err) {
return err
}
// Resource already deleted, continue
}
}
}
case *dashv1alpha1.CustomFormsOverrideList:
for _, item := range l.Items {
if !expected[item.Name] {
if err := m.client.Delete(ctx, &item); err != nil {
if !apierrors.IsNotFound(err) {
return err
}
// Resource already deleted, continue
}
}
}
case *dashv1alpha1.CustomFormsPrefillList:
for _, item := range l.Items {
if !expected[item.Name] {
if err := m.client.Delete(ctx, &item); err != nil {
if !apierrors.IsNotFound(err) {
return err
}
// Resource already deleted, continue
}
}
}
case *dashv1alpha1.MarketplacePanelList:
for _, item := range l.Items {
if !expected[item.Name] {
if err := m.client.Delete(ctx, &item); err != nil {
if !apierrors.IsNotFound(err) {
return err
}
// Resource already deleted, continue
}
}
}
case *dashv1alpha1.SidebarList:
for _, item := range l.Items {
if !expected[item.Name] {
if err := m.client.Delete(ctx, &item); err != nil {
if !apierrors.IsNotFound(err) {
return err
}
// Resource already deleted, continue
}
}
}
case *dashv1alpha1.TableUriMappingList:
for _, item := range l.Items {
if !expected[item.Name] {
if err := m.client.Delete(ctx, &item); err != nil {
if !apierrors.IsNotFound(err) {
return err
}
// Resource already deleted, continue
}
}
}
case *dashv1alpha1.BreadcrumbList:
for _, item := range l.Items {
if !expected[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] {
if err := m.client.Delete(ctx, &item); err != nil {
if !apierrors.IsNotFound(err) {
return err
}
// Resource already deleted, continue
}
}
}
}
return nil
}

View File

@@ -0,0 +1,112 @@
package dashboard
import (
"context"
"encoding/json"
"strings"
dashv1alpha1 "github.com/cozystack/cozystack/api/dashboard/v1alpha1"
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
// ensureMarketplacePanel creates or updates a MarketplacePanel resource for the given CRD
func (m *Manager) ensureMarketplacePanel(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) (reconcile.Result, error) {
logger := log.FromContext(ctx)
mp := &dashv1alpha1.MarketplacePanel{}
mp.Name = crd.Name // cluster-scoped resource, name mirrors CRD name
// If dashboard is not set, delete the panel if it exists.
if crd.Spec.Dashboard == nil {
err := m.client.Get(ctx, client.ObjectKey{Name: mp.Name}, mp)
if apierrors.IsNotFound(err) {
return reconcile.Result{}, nil
}
if err != nil {
return reconcile.Result{}, err
}
if err := m.client.Delete(ctx, mp); err != nil && !apierrors.IsNotFound(err) {
return reconcile.Result{}, err
}
logger.Info("Deleted MarketplacePanel because dashboard is not set", "name", mp.Name)
return reconcile.Result{}, nil
}
// Skip resources with non-empty spec.dashboard.name
if strings.TrimSpace(crd.Spec.Dashboard.Name) != "" {
err := m.client.Get(ctx, client.ObjectKey{Name: mp.Name}, mp)
if apierrors.IsNotFound(err) {
return reconcile.Result{}, nil
}
if err != nil {
return reconcile.Result{}, err
}
if err := m.client.Delete(ctx, mp); err != nil && !apierrors.IsNotFound(err) {
return reconcile.Result{}, err
}
logger.Info("Deleted MarketplacePanel because spec.dashboard.name is set", "name", mp.Name)
return reconcile.Result{}, nil
}
// Build desired spec from CRD fields
d := crd.Spec.Dashboard
app := crd.Spec.Application
displayName := d.Singular
if displayName == "" {
displayName = app.Kind
}
tags := make([]any, len(d.Tags))
for i, t := range d.Tags {
tags[i] = t
}
specMap := map[string]any{
"description": d.Description,
"name": displayName,
"type": "nonCrd",
"apiGroup": "apps.cozystack.io",
"apiVersion": "v1alpha1",
"typeName": app.Plural, // e.g., "buckets"
"disabled": false,
"hidden": false,
"tags": tags,
"icon": d.Icon,
}
specBytes, err := json.Marshal(specMap)
if err != nil {
return reconcile.Result{}, err
}
_, err = controllerutil.CreateOrUpdate(ctx, m.client, mp, func() error {
if err := controllerutil.SetOwnerReference(crd, mp, m.scheme); err != nil {
return err
}
// Add dashboard labels to dynamic resources
m.addDashboardLabels(mp, crd, ResourceTypeDynamic)
// Only update spec if it's different to avoid unnecessary updates
newSpec := dashv1alpha1.ArbitrarySpec{
JSON: apiextv1.JSON{Raw: specBytes},
}
if !compareArbitrarySpecs(mp.Spec, newSpec) {
mp.Spec = newSpec
}
return nil
})
if err != nil {
return reconcile.Result{}, err
}
logger.Info("Applied MarketplacePanel", "name", mp.Name)
return reconcile.Result{}, nil
}

View File

@@ -11,7 +11,6 @@ import (
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)
@@ -217,22 +216,24 @@ func (m *Manager) upsertMultipleSidebars(
}
obj := &dashv1alpha1.Sidebar{}
obj.SetGroupVersionKind(schema.GroupVersionKind{
Group: "dashboard.cozystack.io",
Version: "v1alpha1",
Kind: "Sidebar",
})
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
}
// Add dashboard labels to dynamic resources
m.addDashboardLabels(obj, crd, ResourceTypeDynamic)
b, err := json.Marshal(spec)
if err != nil {
return err
}
obj.Spec = dashv1alpha1.ArbitrarySpec{JSON: apiextv1.JSON{Raw: b}}
// Only update spec if it's different to avoid unnecessary updates
newSpec := dashv1alpha1.ArbitrarySpec{JSON: apiextv1.JSON{Raw: b}}
if !compareArbitrarySpecs(obj.Spec, newSpec) {
obj.Spec = newSpec
}
return nil
}); err != nil {
return err

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,59 @@
package dashboard
import (
"context"
dashv1alpha1 "github.com/cozystack/cozystack/api/dashboard/v1alpha1"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
)
// ensureStaticResources ensures all static dashboard resources are created
func (m *Manager) ensureStaticResources(ctx context.Context) error {
// Use refactored resources from static_refactored.go
// This replaces the old static variables with dynamic creation using helper functions
staticResources := CreateAllStaticResources()
// Create or update each static resource
for _, resource := range staticResources {
if err := m.ensureStaticResource(ctx, resource); err != nil {
return err
}
}
return nil
}
// ensureStaticResource creates or updates a single static resource
func (m *Manager) ensureStaticResource(ctx context.Context, obj client.Object) error {
// Create a copy to avoid modifying the original
resource := obj.DeepCopyObject().(client.Object)
// Add dashboard labels to static resources
m.addDashboardLabels(resource, nil, ResourceTypeStatic)
_, err := controllerutil.CreateOrUpdate(ctx, m.client, resource, func() error {
// For static resources, we don't need to set owner references
// as they are meant to be persistent across CRD changes
// Copy Spec from the original object to the live object
switch o := obj.(type) {
case *dashv1alpha1.CustomColumnsOverride:
resource.(*dashv1alpha1.CustomColumnsOverride).Spec = o.Spec
case *dashv1alpha1.Breadcrumb:
resource.(*dashv1alpha1.Breadcrumb).Spec = o.Spec
case *dashv1alpha1.CustomFormsOverride:
resource.(*dashv1alpha1.CustomFormsOverride).Spec = o.Spec
case *dashv1alpha1.Factory:
resource.(*dashv1alpha1.Factory).Spec = o.Spec
case *dashv1alpha1.Navigation:
resource.(*dashv1alpha1.Navigation).Spec = o.Spec
case *dashv1alpha1.TableUriMapping:
resource.(*dashv1alpha1.TableUriMapping).Spec = o.Spec
}
// Ensure labels are always set
m.addDashboardLabels(resource, nil, ResourceTypeStatic)
return nil
})
return err
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,13 @@
package dashboard
import (
"context"
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
)
// ensureTableUriMapping creates or updates a TableUriMapping resource for the given CRD
func (m *Manager) ensureTableUriMapping(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) error {
// Links are fully managed by the CustomColumnsOverride.
return nil
}

View File

@@ -0,0 +1,209 @@
package dashboard
import "strings"
// ---------------- UI helpers (use float64 for numeric fields) ----------------
func contentCard(id string, style map[string]any, children []any) map[string]any {
return contentCardWithTitle(id, "", style, children)
}
func contentCardWithTitle(id any, title string, style map[string]any, children []any) map[string]any {
data := map[string]any{
"id": id,
"style": style,
}
if title != "" {
data["title"] = title
}
return map[string]any{
"type": "ContentCard",
"data": data,
"children": children,
}
}
func antdText(id string, strong bool, text string, style map[string]any) map[string]any {
// Auto-generate ID if not provided
if id == "" {
id = generateTextID("auto", "antd")
}
data := map[string]any{
"id": id,
"text": text,
"strong": strong,
}
if style != nil {
data["style"] = style
}
return map[string]any{"type": "antdText", "data": data}
}
func parsedText(id, text string, style map[string]any) map[string]any {
// Auto-generate ID if not provided
if id == "" {
id = generateTextID("auto", "parsed")
}
data := map[string]any{
"id": id,
"text": text,
}
if style != nil {
data["style"] = style
}
return map[string]any{"type": "parsedText", "data": data}
}
func parsedTextWithFormatter(id, text, formatter string) map[string]any {
// Auto-generate ID if not provided
if id == "" {
id = generateTextID("auto", "formatted")
}
return map[string]any{
"type": "parsedText",
"data": map[string]any{
"id": id,
"text": text,
"formatter": formatter,
},
}
}
func spacer(id string, space float64) map[string]any {
// Auto-generate ID if not provided
if id == "" {
id = generateContainerID("auto", "spacer")
}
return map[string]any{
"type": "Spacer",
"data": map[string]any{
"id": id,
"$space": space,
},
}
}
func antdFlex(id string, gap float64, children []any) map[string]any {
// Auto-generate ID if not provided
if id == "" {
id = generateContainerID("auto", "flex")
}
return map[string]any{
"type": "antdFlex",
"data": map[string]any{
"id": id,
"align": "center",
"gap": gap,
},
"children": children,
}
}
func antdFlexVertical(id string, gap float64, children []any) map[string]any {
// Auto-generate ID if not provided
if id == "" {
id = generateContainerID("auto", "flex-vertical")
}
return map[string]any{
"type": "antdFlex",
"data": map[string]any{
"id": id,
"vertical": true,
"gap": gap,
},
"children": children,
}
}
func antdRow(id string, gutter []any, children []any) map[string]any {
// Auto-generate ID if not provided
if id == "" {
id = generateContainerID("auto", "row")
}
return map[string]any{
"type": "antdRow",
"data": map[string]any{
"id": id,
"gutter": gutter,
},
"children": children,
}
}
func antdCol(id string, span float64, children []any) map[string]any {
return map[string]any{
"type": "antdCol",
"data": map[string]any{
"id": id,
"span": span,
},
"children": children,
}
}
func antdColWithStyle(id string, style map[string]any, children []any) map[string]any {
return map[string]any{
"type": "antdCol",
"data": map[string]any{
"id": id,
"style": style,
},
"children": children,
}
}
func antdLink(id, text, href string) map[string]any {
return map[string]any{
"type": "antdLink",
"data": map[string]any{
"id": id,
"text": text,
"href": href,
},
}
}
// ---------------- Badge helpers ----------------
// createBadge creates a badge element with the given text, color, and title
func createBadge(id, text, color, title string) map[string]any {
return map[string]any{
"type": "antdText",
"data": map[string]any{
"id": id,
"text": text,
"title": title,
"style": map[string]any{
"whiteSpace": "nowrap",
"backgroundColor": color,
"fontWeight": 400,
"lineHeight": "24px",
"minWidth": 24,
"textAlign": "center",
"borderRadius": "20px",
"color": "#fff",
"display": "inline-block",
"fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif",
"fontSize": "15px",
"padding": "0 9px",
},
},
}
}
// createBadgeFromKind creates a badge using the existing badge generation functions
func createBadgeFromKind(id, kind, title string) map[string]any {
return createUnifiedBadgeFromKind(id, kind, title, BadgeSizeMedium)
}
// createHeaderBadge creates a badge specifically for headers with consistent styling
func createHeaderBadge(id, kind, plural string) map[string]any {
return createUnifiedBadgeFromKind(id, kind, strings.ToLower(plural), BadgeSizeLarge)
}

View File

@@ -0,0 +1,407 @@
package dashboard
import (
"crypto/sha1"
"fmt"
"strings"
)
// ---------------- Unified ID generation helpers ----------------
// generateID creates a unique ID based on the provided components
func generateID(components ...string) string {
if len(components) == 0 {
return ""
}
// Join components with hyphens and convert to lowercase
id := strings.ToLower(strings.Join(components, "-"))
// Remove any special characters that might cause issues
id = strings.ReplaceAll(id, ".", "-")
id = strings.ReplaceAll(id, "/", "-")
id = strings.ReplaceAll(id, " ", "-")
// Remove multiple consecutive hyphens
for strings.Contains(id, "--") {
id = strings.ReplaceAll(id, "--", "-")
}
// Remove leading/trailing hyphens
id = strings.Trim(id, "-")
return id
}
// generateSpecID creates a spec.id from metadata.name and other components
func generateSpecID(metadataName string, components ...string) string {
allComponents := append([]string{metadataName}, components...)
return generateID(allComponents...)
}
// generateMetadataName creates metadata.name from spec.id
func generateMetadataName(specID string) string {
// Convert ID format to metadata.name format
// Replace / with . for metadata.name
name := strings.ReplaceAll(specID, "/", ".")
// Clean up the name to be RFC 1123 compliant
// Remove any leading/trailing dots and ensure it starts/ends with alphanumeric
name = strings.Trim(name, ".")
// Replace multiple consecutive dots with single dot
for strings.Contains(name, "..") {
name = strings.ReplaceAll(name, "..", ".")
}
// Replace any remaining problematic patterns
// Handle cases like "stock-namespace-.v1" -> "stock-namespace-v1"
name = strings.ReplaceAll(name, "-.", "-")
name = strings.ReplaceAll(name, ".-", "-")
// Ensure it starts with alphanumeric character
if len(name) > 0 && !isAlphanumeric(name[0]) {
name = "a" + name
}
// Ensure it ends with alphanumeric character
if len(name) > 0 && !isAlphanumeric(name[len(name)-1]) {
name = name + "a"
}
return name
}
// isAlphanumeric checks if a character is alphanumeric
func isAlphanumeric(c byte) bool {
return (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')
}
// ---------------- Unified badge generation helpers ----------------
// BadgeConfig holds configuration for badge generation
type BadgeConfig struct {
Text string
Color string
Title string
Size BadgeSize
}
// BadgeSize represents the size of the badge
type BadgeSize int
const (
BadgeSizeSmall BadgeSize = iota
BadgeSizeMedium
BadgeSizeLarge
)
// generateBadgeConfig creates a BadgeConfig from kind and optional custom values
func generateBadgeConfig(kind string, customText, customColor, customTitle string) BadgeConfig {
config := BadgeConfig{
Text: initialsFromKind(kind),
Color: hexColorForKind(kind),
Title: strings.ToLower(kind),
Size: BadgeSizeMedium,
}
// Override with custom values if provided
if customText != "" {
config.Text = customText
}
if customColor != "" {
config.Color = customColor
}
if customTitle != "" {
config.Title = customTitle
}
return config
}
// createUnifiedBadge creates a badge using the unified BadgeConfig
func createUnifiedBadge(id string, config BadgeConfig) map[string]any {
fontSize := "15px"
if config.Size == BadgeSizeLarge {
fontSize = "20px"
} else if config.Size == BadgeSizeSmall {
fontSize = "12px"
}
return map[string]any{
"type": "antdText",
"data": map[string]any{
"id": id,
"text": config.Text,
"title": config.Title,
"style": map[string]any{
"backgroundColor": config.Color,
"borderRadius": "20px",
"color": "#fff",
"display": "inline-block",
"fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif",
"fontSize": fontSize,
"fontWeight": float64(400),
"lineHeight": "24px",
"minWidth": float64(24),
"padding": "0 9px",
"textAlign": "center",
"whiteSpace": "nowrap",
},
},
}
}
// createUnifiedBadgeFromKind creates a badge from kind with automatic color generation
func createUnifiedBadgeFromKind(id, kind, title string, size BadgeSize) map[string]any {
config := BadgeConfig{
Text: initialsFromKind(kind),
Color: hexColorForKind(kind),
Title: title,
Size: size,
}
return createUnifiedBadge(id, config)
}
// ---------------- Resource creation helpers with unified approach ----------------
// ResourceConfig holds configuration for resource creation
type ResourceConfig struct {
SpecID string
MetadataName string
Kind string
Title string
BadgeConfig BadgeConfig
}
// createResourceConfig creates a ResourceConfig from components
func createResourceConfig(components []string, kind, title string) ResourceConfig {
// Generate spec.id from components
specID := generateID(components...)
// Generate metadata.name from spec.id
metadataName := generateMetadataName(specID)
// Generate badge config
badgeConfig := generateBadgeConfig(kind, "", "", title)
return ResourceConfig{
SpecID: specID,
MetadataName: metadataName,
Kind: kind,
Title: title,
BadgeConfig: badgeConfig,
}
}
// ---------------- Enhanced color generation ----------------
// getColorForKind returns a color for a specific kind with improved distribution
func getColorForKind(kind string) string {
// Use existing hexColorForKind function
return hexColorForKind(kind)
}
// getColorForType returns a color for a specific type (like "namespace", "service", etc.)
func getColorForType(typeName string) string {
// Map common types to specific colors for consistency
colorMap := map[string]string{
"namespace": "#a25792ff",
"service": "#6ca100",
"pod": "#009596",
"node": "#8476d1",
"secret": "#c46100",
"configmap": "#b48c78ff",
"ingress": "#2e7dff",
"workloadmonitor": "#c46100",
"module": "#8b5cf6",
}
if color, exists := colorMap[strings.ToLower(typeName)]; exists {
return color
}
// Fall back to hash-based color generation
return hexColorForKind(typeName)
}
// ---------------- Automatic ID generation for UI elements ----------------
// generateElementID creates an ID for UI elements based on context and type
func generateElementID(elementType, context string, components ...string) string {
allComponents := append([]string{elementType, context}, components...)
return generateID(allComponents...)
}
// generateBadgeID creates an ID for badge elements
func generateBadgeID(context string, kind string) string {
return generateElementID("badge", context, kind)
}
// generateLinkID creates an ID for link elements
func generateLinkID(context string, linkType string) string {
return generateElementID("link", context, linkType)
}
// generateTextID creates an ID for text elements
func generateTextID(context string, textType string) string {
return generateElementID("text", context, textType)
}
// generateContainerID creates an ID for container elements
func generateContainerID(context string, containerType string) string {
return generateElementID("container", context, containerType)
}
// generateTableID creates an ID for table elements
func generateTableID(context string, tableType string) string {
return generateElementID("table", context, tableType)
}
// ---------------- Enhanced resource creation with automatic IDs ----------------
// createResourceWithAutoID creates a resource with automatically generated IDs
func createResourceWithAutoID(resourceType, name string, spec map[string]any) map[string]any {
// Generate spec.id from name
specID := generateSpecID(name)
// Add the spec.id to the spec
spec["id"] = specID
return spec
}
// ---------------- Unified resource creation helpers ----------------
// UnifiedResourceConfig holds configuration for unified resource creation
type UnifiedResourceConfig struct {
Name string
ResourceType string
Kind string
Plural string
Title string
Color string
BadgeText string
Size BadgeSize
}
// createUnifiedFactory creates a factory using unified approach
func createUnifiedFactory(config UnifiedResourceConfig, tabs []any, urlsToFetch []any) map[string]any {
// Generate spec.id from name
specID := generateSpecID(config.Name)
// Create header with unified badge
badgeConfig := BadgeConfig{
Text: config.BadgeText,
Color: config.Color,
Title: config.Title,
Size: config.Size,
}
if badgeConfig.Text == "" {
badgeConfig.Text = initialsFromKind(config.Kind)
}
if badgeConfig.Color == "" {
badgeConfig.Color = getColorForKind(config.Kind)
}
badge := createUnifiedBadge(generateBadgeID("header", config.Kind), badgeConfig)
nameText := parsedText(generateTextID("header", "name"), "{reqsJsonPath[0]['.metadata.name']['-']}", map[string]any{
"fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif",
"fontSize": float64(20),
"lineHeight": "24px",
})
header := antdFlex(generateContainerID("header", "row"), float64(6), []any{
badge,
nameText,
})
// Add marginBottom style to header
if headerData, ok := header["data"].(map[string]any); ok {
if headerData["style"] == nil {
headerData["style"] = map[string]any{}
}
if style, ok := headerData["style"].(map[string]any); ok {
style["marginBottom"] = float64(24)
}
}
return map[string]any{
"key": config.Name,
"id": specID,
"sidebarTags": []any{fmt.Sprintf("%s-sidebar", strings.ToLower(config.Kind))},
"withScrollableMainContentCard": true,
"urlsToFetch": urlsToFetch,
"data": []any{
header,
map[string]any{
"type": "antdTabs",
"data": map[string]any{
"id": generateContainerID("tabs", strings.ToLower(config.Kind)),
"defaultActiveKey": "details",
"items": tabs,
},
},
},
}
}
// createUnifiedCustomColumn creates a custom column using unified approach
func createUnifiedCustomColumn(name, jsonPath, kind, title, href string) map[string]any {
badgeConfig := generateBadgeConfig(kind, "", "", title)
badge := createUnifiedBadge(generateBadgeID("column", kind), badgeConfig)
linkID := generateLinkID("column", "name")
if jsonPath == ".metadata.namespace" {
linkID = generateLinkID("column", "namespace")
}
link := antdLink(linkID, "{reqsJsonPath[0]['"+jsonPath+"']['-']}", href)
return map[string]any{
"name": name,
"type": "factory",
"jsonPath": jsonPath,
"customProps": map[string]any{
"disableEventBubbling": true,
"items": []any{
map[string]any{
"type": "antdFlex",
"data": map[string]any{
"id": generateContainerID("column", "header"),
"align": "center",
"gap": float64(6),
},
"children": []any{badge, link},
},
},
},
}
}
// ---------------- Utility functions ----------------
// hashString creates a short hash from a string for ID generation
func hashString(s string) string {
hash := sha1.Sum([]byte(s))
return fmt.Sprintf("%x", hash[:4])
}
// sanitizeForID removes characters that shouldn't be in IDs
func sanitizeForID(s string) string {
// Replace problematic characters
s = strings.ReplaceAll(s, ".", "-")
s = strings.ReplaceAll(s, "/", "-")
s = strings.ReplaceAll(s, " ", "-")
s = strings.ReplaceAll(s, "_", "-")
// Remove multiple consecutive hyphens
for strings.Contains(s, "--") {
s = strings.ReplaceAll(s, "--", "-")
}
// Remove leading/trailing hyphens
s = strings.Trim(s, "-")
return strings.ToLower(s)
}

View File

@@ -1,266 +0,0 @@
package dashboard
import (
"context"
"encoding/json"
"fmt"
"strings"
dashv1alpha1 "github.com/cozystack/cozystack/api/dashboard/v1alpha1"
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)
// Ensure the three additional dashboard/frontend resources exist:
// - TableUriMapping (dashboard.cozystack.io/v1alpha1)
// - Breadcrumb (dashboard.cozystack.io/v1alpha1)
// - CustomFormsOverride (dashboard.cozystack.io/v1alpha1)
//
// Call these from Manager.EnsureForCRD() after ensureCustomColumnsOverride.
// --------------------------- TableUriMapping -----------------------------
func (m *Manager) ensureTableUriMapping(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) error {
// Links are fully managed by the CustomColumnsOverride.
return nil
}
// ------------------------------- Breadcrumb -----------------------------
func (m *Manager) ensureBreadcrumb(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) error {
_, _, kind := pickGVK(crd)
lowerKind := strings.ToLower(kind)
detailID := fmt.Sprintf("stock-project-factory-%s-details", lowerKind)
obj := &dashv1alpha1.Breadcrumb{}
obj.SetGroupVersionKind(schema.GroupVersionKind{
Group: "dashboard.cozystack.io",
Version: "v1alpha1",
Kind: "Breadcrumb",
})
obj.SetName(detailID)
plural := pickPlural(kind, crd)
// Prefer dashboard.Plural for UI label if provided
labelPlural := titleFromKindPlural(kind, plural)
if crd != nil && crd.Spec.Dashboard != nil && crd.Spec.Dashboard.Plural != "" {
labelPlural = crd.Spec.Dashboard.Plural
}
key := plural // e.g., "virtualmachines"
label := labelPlural
link := fmt.Sprintf("/openapi-ui/{clusterName}/{namespace}/api-table/apps.cozystack.io/v1alpha1/%s", plural)
// If Name is set, change the first breadcrumb item to "Tenant Modules"
// TODO add parameter to this
if crd.Spec.Dashboard.Name != "" {
key = "tenantmodules"
label = "Tenant Modules"
link = "/openapi-ui/{clusterName}/{namespace}/api-table/core.cozystack.io/v1alpha1/tenantmodules"
}
items := []any{
map[string]any{
"key": key,
"label": label,
"link": link,
},
map[string]any{
"key": strings.ToLower(kind), // "etcd"
"label": "{6}", // literal, as in your example
},
}
spec := map[string]any{
"id": detailID,
"breadcrumbItems": items,
}
_, err := controllerutil.CreateOrUpdate(ctx, m.client, obj, func() error {
if err := controllerutil.SetOwnerReference(crd, obj, m.scheme); err != nil {
return err
}
b, err := json.Marshal(spec)
if err != nil {
return err
}
obj.Spec = dashv1alpha1.ArbitrarySpec{JSON: apiextv1.JSON{Raw: b}}
return nil
})
return err
}
// --------------------------- CustomFormsOverride ------------------------
func (m *Manager) ensureCustomFormsOverride(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) error {
g, v, kind := pickGVK(crd)
plural := pickPlural(kind, crd)
name := fmt.Sprintf("%s.%s.%s", g, v, plural)
customizationID := fmt.Sprintf("default-/%s/%s/%s", g, v, plural)
obj := &dashv1alpha1.CustomFormsOverride{}
obj.SetGroupVersionKind(schema.GroupVersionKind{
Group: "dashboard.cozystack.io",
Version: "v1alpha1",
Kind: "CustomFormsOverride",
})
obj.SetName(name)
// Replicates your Helm includes (system metadata + api + status).
hidden := []any{}
hidden = append(hidden, hiddenMetadataSystem()...)
hidden = append(hidden, hiddenMetadataAPI()...)
hidden = append(hidden, hiddenStatus()...)
// If Name is set, hide metadata
if crd.Spec.Dashboard != nil && strings.TrimSpace(crd.Spec.Dashboard.Name) != "" {
hidden = append([]interface{}{
[]any{"metadata"},
}, hidden...)
}
sort := make([]any, len(crd.Spec.Dashboard.KeysOrder))
for i, v := range crd.Spec.Dashboard.KeysOrder {
sort[i] = v
}
spec := map[string]any{
"customizationId": customizationID,
"hidden": hidden,
"sort": sort,
"schema": map[string]any{}, // {}
"strategy": "merge",
}
_, err := controllerutil.CreateOrUpdate(ctx, m.client, obj, func() error {
if err := controllerutil.SetOwnerReference(crd, obj, m.scheme); err != nil {
return err
}
b, err := json.Marshal(spec)
if err != nil {
return err
}
obj.Spec = dashv1alpha1.ArbitrarySpec{JSON: apiextv1.JSON{Raw: b}}
return nil
})
return err
}
// ----------------------- CustomFormsPrefill -----------------------
func (m *Manager) ensureCustomFormsPrefill(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) (reconcile.Result, error) {
logger := log.FromContext(ctx)
app := crd.Spec.Application
group := "apps.cozystack.io"
version := "v1alpha1"
name := fmt.Sprintf("%s.%s.%s", group, version, app.Plural)
customizationID := fmt.Sprintf("default-/%s/%s/%s", group, version, app.Plural)
values, err := buildPrefillValues(app.OpenAPISchema)
if err != nil {
return reconcile.Result{}, err
}
// If Name is set, prefill metadata.name
if crd.Spec.Dashboard != nil && strings.TrimSpace(crd.Spec.Dashboard.Name) != "" {
values = append([]interface{}{
map[string]interface{}{
"path": toIfaceSlice([]string{"metadata", "name"}),
"value": crd.Spec.Dashboard.Name,
},
}, values...)
}
cfp := &dashv1alpha1.CustomFormsPrefill{}
cfp.Name = name // cluster-scoped
specMap := map[string]any{
"customizationId": customizationID,
"values": values,
}
specBytes, err := json.Marshal(specMap)
if err != nil {
return reconcile.Result{}, err
}
mutate := func() error {
if err := controllerutil.SetOwnerReference(crd, cfp, m.scheme); err != nil {
return err
}
cfp.Spec = dashv1alpha1.ArbitrarySpec{
JSON: apiextv1.JSON{Raw: specBytes},
}
return nil
}
op, err := controllerutil.CreateOrUpdate(ctx, m.client, cfp, mutate)
if err != nil {
return reconcile.Result{}, err
}
switch op {
case controllerutil.OperationResultCreated:
logger.Info("Created CustomFormsPrefill", "name", cfp.Name)
case controllerutil.OperationResultUpdated:
logger.Info("Updated CustomFormsPrefill", "name", cfp.Name)
}
return reconcile.Result{}, nil
}
// ------------------------------ Helpers ---------------------------------
// titleFromKindPlural returns a presentable plural label, e.g.:
// kind="VirtualMachine", plural="virtualmachines" => "VirtualMachines"
func titleFromKindPlural(kind, plural string) string {
label := kind
if !strings.HasSuffix(strings.ToLower(plural), "s") || !strings.HasSuffix(strings.ToLower(plural), "S") {
label += "s"
} else {
label += "s"
}
return label
}
// The hidden lists below mirror the Helm templates you shared.
// Each entry is a path as nested string array, e.g. ["metadata","creationTimestamp"].
func hiddenMetadataSystem() []any {
return []any{
[]any{"metadata", "annotations"},
[]any{"metadata", "labels"},
[]any{"metadata", "namespace"},
[]any{"metadata", "creationTimestamp"},
[]any{"metadata", "deletionGracePeriodSeconds"},
[]any{"metadata", "deletionTimestamp"},
[]any{"metadata", "finalizers"},
[]any{"metadata", "generateName"},
[]any{"metadata", "generation"},
[]any{"metadata", "managedFields"},
[]any{"metadata", "ownerReferences"},
[]any{"metadata", "resourceVersion"},
[]any{"metadata", "selfLink"},
[]any{"metadata", "uid"},
}
}
func hiddenMetadataAPI() []any {
return []any{
[]any{"kind"},
[]any{"apiVersion"},
[]any{"appVersion"},
}
}
func hiddenStatus() []any {
return []any{
[]any{"status"},
}
}

View File

@@ -297,24 +297,6 @@ releases:
- keycloak-configure
{{- end }}
- name: dashboard-config
releaseName: dashboard-config
chart: cozy-dashboard-config
namespace: cozy-dashboard
values:
{{- $dashboardKCconfig := lookup "v1" "ConfigMap" "cozy-dashboard" "kubeapps-auth-config" }}
{{- $dashboardKCValues := dig "data" "values.yaml" "" $dashboardKCconfig | fromYaml }}
{{- toYaml (deepCopy $dashboardKCValues | mergeOverwrite (fromYaml (include "cozystack.defaultDashboardValues" .))) | nindent 4 }}
dependsOn:
- cilium
- kubeovn
- dashboard
- cozystack-api
{{- if eq $oidcEnabled "true" }}
- keycloak-configure
{{- end }}
- name: kamaji
releaseName: kamaji
chart: cozy-kamaji

View File

@@ -182,22 +182,6 @@ releases:
dependsOn: []
{{- end }}
- name: dashboard-config
releaseName: dashboard-config
chart: cozy-dashboard-config
namespace: cozy-dashboard
values:
{{- $dashboardKCconfig := lookup "v1" "ConfigMap" "cozy-dashboard" "kubeapps-auth-config" }}
{{- $dashboardKCValues := dig "data" "values.yaml" "" $dashboardKCconfig | fromYaml }}
{{- toYaml (deepCopy $dashboardKCValues | mergeOverwrite (fromYaml (include "cozystack.defaultDashboardValues" .))) | nindent 4 }}
dependsOn:
- cilium
- kubeovn
- dashboard
{{- if eq $oidcEnabled "true" }}
- keycloak-configure
{{- end }}
{{- if $oidcEnabled }}
- name: keycloak
releaseName: keycloak

View File

@@ -1,5 +1,5 @@
cozystackController:
image: ghcr.io/cozystack/cozystack/cozystack-controller:latest@sha256:20483582c50ead02d76719d3acfe2bb5e0d5a62de7dce65ffebe4039f8f3c400
image: ghcr.io/cozystack/cozystack/cozystack-controller:latest@sha256:1c439f383e0a0ca5eed3b3c820c8caf00b4d5347579d2f78ce6897f031041cf6
debug: false
disableTelemetry: false
cozystackVersion: "latest"

View File

@@ -1,3 +0,0 @@
apiVersion: v2
version: 1.0.0
name: cozy-dashboard-config

View File

@@ -1,5 +0,0 @@
NAME := dashboard-config
NAMESPACE := cozy-dashboard
include ../../../scripts/common-envs.mk
include ../../../scripts/package.mk

View File

@@ -1,13 +0,0 @@
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: Breadcrumb
metadata:
name: stock-project-factory-configmap-details
spec:
id: "stock-project-factory-configmap-details"
breadcrumbItems:
- key: configmaps
label: "v1/configmaps"
link: "/openapi-ui/{clusterName}/{namespace}/builtin-table/configmaps"
- key: configmap
label: "{6}"

View File

@@ -1,13 +0,0 @@
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: Breadcrumb
metadata:
name: stock-cluster-factory-namespace-details
spec:
id: "stock-cluster-factory-namespace-details"
breadcrumbItems:
- key: namespaces
label: "v1/namespaces"
link: "/openapi-ui/{clusterName}/builtin-table/namespaces"
- key: namespace
label: "{5}"

View File

@@ -1,13 +0,0 @@
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: Breadcrumb
metadata:
name: stock-cluster-factory-node-details
spec:
id: "stock-cluster-factory-node-details"
breadcrumbItems:
- key: node
label: "v1/nodes"
link: "/openapi-ui/{clusterName}/builtin-table/nodes"
- key: node
label: "{5}"

View File

@@ -1,13 +0,0 @@
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: Breadcrumb
metadata:
name: stock-project-factory-pod-details
spec:
id: "stock-project-factory-pod-details"
breadcrumbItems:
- key: pods
label: "v1/pods"
link: "/openapi-ui/{clusterName}/{namespace}/builtin-table/pods"
- key: pod
label: "{6}"

View File

@@ -1,13 +0,0 @@
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: Breadcrumb
metadata:
name: stock-project-factory-secret-details
spec:
id: "stock-project-factory-secret-details"
breadcrumbItems:
- key: secrets
label: "v1/secrets"
link: "/openapi-ui/{clusterName}/{namespace}/builtin-table/secrets"
- key: secret
label: "{6}"

View File

@@ -1,13 +0,0 @@
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: Breadcrumb
metadata:
name: stock-project-factory-service-details
spec:
id: "stock-project-factory-service-details"
breadcrumbItems:
- key: services
label: "v1/services"
link: "/openapi-ui/{clusterName}/{namespace}/builtin-table/services"
- key: service
label: "{6}"

View File

@@ -1,38 +0,0 @@
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: Breadcrumb
metadata:
name: stock-cluster-api-table
spec:
id: "stock-cluster-api-table"
breadcrumbItems:
- key: api
label: "{apiGroup}/{apiVersion}/{typeName}"
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: Breadcrumb
metadata:
name: stock-cluster-api-form
spec:
id: "stock-cluster-api-form"
breadcrumbItems:
- key: create-api-res-namespaced-table
label: "{apiGroup}/{apiVersion}/{typeName}"
link: "/openapi-ui/{clusterName}/api-table/{apiGroup}/{apiVersion}/{typeName}"
- key: create-api-res-namespaced-typename
label: Create
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: Breadcrumb
metadata:
name: stock-cluster-api-form-edit
spec:
id: "stock-cluster-api-form-edit"
breadcrumbItems:
- key: create-api-res-namespaced-table
label: "{apiGroup}/{apiVersion}/{typeName}"
link: "/openapi-ui/{clusterName}/api-table/{apiGroup}/{apiVersion}/{typeName}"
- key: create-api-res-namespaced-typename
label: Update

View File

@@ -1,38 +0,0 @@
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: Breadcrumb
metadata:
name: stock-cluster-builtin-table
spec:
id: "stock-cluster-builtin-table"
breadcrumbItems:
- key: api
label: "v1/{typeName}"
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: Breadcrumb
metadata:
name: stock-cluster-builtin-form
spec:
id: "stock-cluster-builtin-form"
breadcrumbItems:
- key: create-api-res-namespaced-table
label: "v1/{typeName}"
link: "/openapi-ui/{clusterName}/builtin-table/{typeName}"
- key: create-api-res-namespaced-typename
label: Create
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: Breadcrumb
metadata:
name: stock-cluster-builtin-form-edit
spec:
id: "stock-cluster-builtin-form-edit"
breadcrumbItems:
- key: create-api-res-namespaced-table
label: "v1/{typeName}"
link: "/openapi-ui/{clusterName}/builtin-table/{typeName}"
- key: create-api-res-namespaced-typename
label: Update

View File

@@ -1,38 +0,0 @@
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: Breadcrumb
metadata:
name: stock-project-api-table
spec:
id: "stock-project-api-table"
breadcrumbItems:
- key: api
label: "{apiGroup}/{apiVersion}/{typeName}"
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: Breadcrumb
metadata:
name: stock-project-api-form
spec:
id: "stock-project-api-form"
breadcrumbItems:
- key: create-api-res-namespaced-table
label: "{apiGroup}/{apiVersion}/{typeName}"
link: "/openapi-ui/{clusterName}/{namespace}/api-table/{apiGroup}/{apiVersion}/{typeName}"
- key: create-api-res-namespaced-typename
label: Create
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: Breadcrumb
metadata:
name: stock-project-api-form-edit
spec:
id: "stock-project-api-form-edit"
breadcrumbItems:
- key: create-api-res-namespaced-table
label: "{apiGroup}/{apiVersion}/{typeName}"
link: "/openapi-ui/{clusterName}/{namespace}/api-table/{apiGroup}/{apiVersion}/{typeName}"
- key: create-api-res-namespaced-typename
label: Update

View File

@@ -1,38 +0,0 @@
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: Breadcrumb
metadata:
name: stock-project-builtin-table
spec:
id: "stock-project-builtin-table"
breadcrumbItems:
- key: api
label: "v1/{typeName}"
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: Breadcrumb
metadata:
name: stock-project-builtin-form
spec:
id: "stock-project-builtin-form"
breadcrumbItems:
- key: create-api-res-namespaced-table
label: "v1/{typeName}"
link: "/openapi-ui/{clusterName}/{namespace}/builtin-table/{typeName}"
- key: create-api-res-namespaced-typename
label: Create
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: Breadcrumb
metadata:
name: stock-project-builtin-form-edit
spec:
id: "stock-project-builtin-form-edit"
breadcrumbItems:
- key: create-api-res-namespaced-table
label: "v1/{typeName}"
link: "/openapi-ui/{clusterName}/{namespace}/builtin-table/{typeName}"
- key: create-api-res-namespaced-typename
label: Update

View File

@@ -1,126 +0,0 @@
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: CustomColumnsOverride
metadata:
name: factory-details-v1.services
spec:
id: factory-details-v1.services
additionalPrinterColumns:
- jsonPath: .metadata.name
name: Name
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.icon" (dict
"text" "S"
"title" "service"
"backgroundColor" "#6ca100"
)| nindent 8
}}
{{ include "incloud-web-resources.factory.linkblock" (dict
"reqIndex" 0
"type" "name"
"jsonPath" ".metadata.name"
"namespace" "{reqsJsonPath[0]['.metadata.namespace']['-']}"
"factory" "service-details"
) | nindent 12
}}
- jsonPath: .spec.clusterIP
name: ClusterIP
type: string
- jsonPath: .spec.loadBalancerIP
name: LoadbalancerIP
type: string
- jsonPath: .metadata.creationTimestamp
name: Created
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.factory.timeblock" (dict
"req" ".metadata.creationTimestamp"
) | nindent 8
}}
additionalPrinterColumnsUndefinedValues:
- key: ClusterIP
value: "-"
- key: LoadbalancerIP
value: "-"
additionalPrinterColumnsTrimLengths:
- key: Name
value: 64
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: CustomColumnsOverride
metadata:
name: stock-namespace-v1.services
spec:
additionalPrinterColumns:
- jsonPath: .metadata.name
name: Name
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.icon" (dict
"text" "S"
"title" "service"
"backgroundColor" "#6ca100"
)| nindent 8
}}
{{ include "incloud-web-resources.factory.linkblock" (dict
"reqIndex" 0
"type" "name"
"jsonPath" ".metadata.name"
"namespace" "{reqsJsonPath[0]['.metadata.namespace']['-']}"
"factory" "service-details"
) | nindent 12
}}
- jsonPath: .spec.clusterIP
name: ClusterIP
type: string
- jsonPath: .spec.loadBalancerIP
name: LoadbalancerIP
type: string
- jsonPath: .metadata.creationTimestamp
name: Created
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.factory.timeblock" (dict
"req" ".metadata.creationTimestamp"
) | nindent 8
}}
additionalPrinterColumnsUndefinedValues:
- key: ClusterIP
value: "-"
- key: LoadbalancerIP
value: "-"
additionalPrinterColumnsTrimLengths:
- key: Name
value: 64
id: stock-namespace-/v1/services
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: CustomColumnsOverride
metadata:
name: factory-service-details-port-mapping
spec:
additionalPrinterColumns:
- jsonPath: .name
name: Name
type: string
- jsonPath: .port
name: Port
type: string
- jsonPath: .protocol
name: Protocol
type: string
- jsonPath: .targetPort
name: Pod port or name
type: string
id: factory-service-details-port-mapping

View File

@@ -1,46 +0,0 @@
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: CustomColumnsOverride
metadata:
name: factory-details-v1alpha1.cozystack.io.workloadmonitors
spec:
id: factory-details-v1alpha1.cozystack.io.workloadmonitors
additionalPrinterColumns:
- jsonPath: .metadata.name
name: Name
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.icon" (dict
"text" "W"
"title" "workloadmonitor"
"backgroundColor" "#c46100"
)| nindent 8
}}
{{ include "incloud-web-resources.factory.linkblock" (dict
"reqIndex" 0
"type" "name"
"jsonPath" ".metadata.name"
"namespace" "{reqsJsonPath[0]['.metadata.namespace']['-']}"
"factory" "workloadmonitor-details"
) | nindent 12
}}
- jsonPath: .spec.type
name: TYPE
type: string
- jsonPath: .spec.version
name: VERSION
type: string
- jsonPath: .spec.replicas
name: REPLICAS
type: string
- jsonPath: .spec.minReplicas
name: MINREPLICAS
type: string
- jsonPath: .status.availableReplicas
name: AVAILABLE
type: string
- jsonPath: .status.observedReplicas
name: OBSERVED
type: string

View File

@@ -1,56 +0,0 @@
apiVersion: dashboard.cozystack.io/v1alpha1
kind: CustomColumnsOverride
metadata:
name: factory-details-v1alpha1.core.cozystack.io.tenantsecretstables
spec:
id: factory-details-v1alpha1.core.cozystack.io.tenantsecretstables
additionalPrinterColumns:
- jsonPath: .metadata.name
name: Name
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.icon" (dict
"text" "S"
"title" "secret"
"backgroundColor" "#c46100"
)| nindent 8
}}
{{ include "incloud-web-resources.factory.linkblock" (dict
"reqIndex" 0
"type" "name"
"jsonPath" ".metadata.name"
"namespace" "{reqsJsonPath[0]['.metadata.namespace']['-']}"
"factory" "secret-details"
) | nindent 12
}}
- jsonPath: .data.key
name: Key
type: string
- name: Value
type: factory
customProps:
disableEventBubbling: true
items:
- type: SecretBase64Plain
data:
id: example-secterbase64
plainTextValue: "hello"
base64Value: "{reqsJsonPath[0]['.data.value']['-']}"
- jsonPath: .metadata.creationTimestamp
name: Created
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.factory.timeblock" (dict
"req" ".metadata.creationTimestamp"
) | nindent 8
}}
additionalPrinterColumnsTrimLengths:
- key: Name
value: 64
additionalPrinterColumnsUndefinedValues:
- key: Namespace
value: '-'

View File

@@ -1,47 +0,0 @@
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: CustomColumnsOverride
metadata:
name: factory-ingress-details-rules
spec:
id: factory-ingress-details-rules
# Each item is a rule. We'll display the first HTTP path data where present
additionalPrinterColumns:
- jsonPath: .host
name: Host
type: string
- jsonPath: .http.paths[0].backend.service.name
name: Service
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.icon" (dict
"text" "S"
"title" "service"
"backgroundColor" "#6ca100"
)| nindent 8
}}
{{ include "incloud-web-resources.factory.linkblock" (dict
"reqIndex" 0
"type" "name"
"jsonPath" ".http.paths[0].backend.service.name"
"namespace" "{reqsJsonPath[0]['.metadata.namespace']['-']}"
"factory" "service-details"
) | nindent 12
}}
- jsonPath: .http.paths[0].backend.service.port.number
name: Port
type: string
- jsonPath: .http.paths[0].path
name: Path
type: string
additionalPrinterColumnsUndefinedValues:
- key: Service
value: "-"
- key: Port
value: "-"
- key: Path
value: "-"

View File

@@ -1,33 +0,0 @@
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: CustomColumnsOverride
metadata:
name: factory-node-images
spec:
additionalPrinterColumns:
- jsonPath: .names[0]
name: ImageID
type: string
- jsonPath: .sizeBytes
name: Size
type: factory
customProps:
disableEventBubbling: true
items:
- type: ConverterBytes
data:
id: example-converter-bytes
bytesValue: "{reqsJsonPath[0]['.sizeBytes']['-']}"
format: true
precision: 1
additionalPrinterColumnsUndefinedValues:
- key: Message
value: "-"
additionalPrinterColumnsTrimLengths:
- key: ImageID
value: 128
- key: Size
value: 63
id: "factory-node-images"

View File

@@ -1,11 +0,0 @@
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: CustomColumnsOverride
metadata:
name: factory-pod-details-volume-list
spec:
additionalPrinterColumns:
- jsonPath: .name
name: Name
type: string
id: "factory-pod-details-volume-list"

View File

@@ -1,40 +0,0 @@
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: CustomColumnsOverride
metadata:
name: factory-status-conditions
spec:
additionalPrinterColumns:
- jsonPath: .type
name: Type
type: string
- jsonPath: .status
name: Status
type: bool
- jsonPath: .lastTransitionTime
name: Updated
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.factory.timeblock" (dict
"req" ".lastTransitionTime"
) | nindent 8
}}
- jsonPath: .reason
name: Reason
type: string
- jsonPath: .message
name: Message
type: string
additionalPrinterColumnsUndefinedValues:
- key: Reason
value: "-"
- key: Message
value: "-"
additionalPrinterColumnsTrimLengths:
- key: Message
value: 63
id: "factory-status-conditions"

View File

@@ -1,162 +0,0 @@
{{- define "incloud-web-resources.customformoverride.hidden.metadata.system" -}}
- - metadata
- creationTimestamp
- - metadata
- deletionGracePeriodSeconds
- - metadata
- deletionTimestamp
- - metadata
- finalizers
- - metadata
- generateName
- - metadata
- generation
- - metadata
- managedFields
- - metadata
- ownerReferences
- - metadata
- resourceVersion
- - metadata
- selfLink
- - metadata
- uid
{{- end -}}
{{- define "incloud-web-resources.customformoverride.hidden.metadata.system-clusterscope" -}}
- - metadata
- creationTimestamp
- - metadata
- namespace
- - metadata
- deletionGracePeriodSeconds
- - metadata
- deletionTimestamp
- - metadata
- finalizers
- - metadata
- generateName
- - metadata
- generation
- - metadata
- managedFields
- - metadata
- ownerReferences
- - metadata
- resourceVersion
- - metadata
- selfLink
- - metadata
- uid
{{- end -}}
{{- define "incloud-web-resources.customformoverride.hidden.metadata.system.job-template" -}}
- - spec
- jobTemplate
- metadata
- creationTimestamp
- - spec
- jobTemplate
- metadata
- namespace
- - spec
- jobTemplate
- metadata
- deletionGracePeriodSeconds
- - spec
- jobTemplate
- metadata
- deletionTimestamp
- - spec
- jobTemplate
- metadata
- finalizers
- - spec
- jobTemplate
- metadata
- generateName
- - spec
- jobTemplate
- metadata
- generation
- - spec
- jobTemplate
- metadata
- managedFields
- - spec
- jobTemplate
- metadata
- ownerReferences
- - spec
- jobTemplate
- metadata
- resourceVersion
- - spec
- jobTemplate
- metadata
- selfLink
- - spec
- jobTemplate
- metadata
- uid
{{- end -}}
{{- define "incloud-web-resources.customformoverride.hidden.metadata.system.template" -}}
- - spec
- template
- metadata
- creationTimestamp
- - spec
- template
- metadata
- namespace
- - spec
- template
- metadata
- deletionGracePeriodSeconds
- - spec
- template
- metadata
- deletionTimestamp
- - spec
- template
- metadata
- finalizers
- - spec
- template
- metadata
- generateName
- - spec
- template
- metadata
- generation
- - spec
- template
- metadata
- managedFields
- - spec
- template
- metadata
- ownerReferences
- - spec
- template
- metadata
- resourceVersion
- - spec
- template
- metadata
- selfLink
- - spec
- template
- metadata
- uid
{{- end -}}
{{- define "incloud-web-resources.customformoverride.hidden.metadata.api" -}}
- - kind
- - apiVersion
{{- end -}}
{{- define "incloud-web-resources.customformoverride.hidden.status" -}}
- - status
{{- end -}}

View File

@@ -1,89 +0,0 @@
{{- define "incloud-web-resources.pod.icon" -}}
- type: antdFlex
data:
id: header-row
gap: 6
align: center
# style:
# marginBottom: 24px
children:
- type: antdText
data:
id: header-badge
text: P
title: Pods
style:
fontSize: 20px
lineHeight: 24px
padding: "0 9px"
borderRadius: "20px"
minWidth: 24
display: inline-block
textAlign: center
whiteSpace: nowrap
color: "#fff"
backgroundColor: "#009596"
fontFamily: RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif
fontWeight: 400
{{- end -}}
{{- define "incloud-web-resources.namespace.icon" -}}
- type: antdFlex
data:
id: header-row
gap: 6
align: center
# style:
# marginBottom: 24px
children:
- type: antdText
data:
id: header-badge
text: NS
title: Nanesoace
style:
fontSize: 20px
lineHeight: 24px
padding: "0 9px"
borderRadius: "20px"
minWidth: 24
display: inline-block
textAlign: center
whiteSpace: nowrap
color: "#fff"
backgroundColor: "#45a703ff"
fontFamily: RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif
fontWeight: 400
{{- end -}}
{{- define "incloud-web-resources.icon" -}}
{{- $text := (default "" .text) -}}
{{- $title := (default "" .title) -}}
{{- $backgroundColor := (default "#a25792ff" .backgroundColor) -}}
- type: antdFlex
data:
id: header-row
gap: 6
align: center
children:
# Badge with resource short name
- type: antdText
data:
id: header-badge
text: "{{ $text }}"
title: "{{ $title }}"
style:
fontSize: 15px
lineHeight: 24px
padding: "0 9px"
borderRadius: "20px"
minWidth: 24
display: inline-block
textAlign: center
whiteSpace: nowrap
color: "#fff"
backgroundColor: "{{ $backgroundColor }}"
fontFamily: RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif
fontWeight: 400
{{- end -}}

View File

@@ -1,154 +0,0 @@
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: CustomColumnsOverride
metadata:
name: container-status-init-containers-list
spec:
additionalPrinterColumns:
- jsonPath: .name
name: Name
type: string
- jsonPath: .imageID
name: Image
type: string
- jsonPath: .started
name: Started
type: bool
- jsonPath: .ready
name: Ready
type: bool
- jsonPath: .restartCount
name: RestartCount
type: string
- jsonPath: .state.waiting.reason
name: WaitingdReason
type: string
- jsonPath: .state.terminated.reason
name: TerminatedReason
type: string
additionalPrinterColumnsUndefinedValues:
- key: TerminatedReason
value: "-"
- key: WaitingdReason
value: "-"
additionalPrinterColumnsTrimLengths:
- key: Name
value: 63
- key: Image
value: 63
id: "container-status-init-containers-list"
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: CustomColumnsOverride
metadata:
name: container-status-containers-list
spec:
additionalPrinterColumns:
- jsonPath: .name
name: Name
type: string
- jsonPath: .imageID
name: Image
type: string
- jsonPath: .started
name: Started
type: bool
- jsonPath: .ready
name: Ready
type: bool
- jsonPath: .restartCount
name: RestartCount
type: string
- jsonPath: .state.waiting.reason
name: WaitingdReason
type: string
- jsonPath: .state.terminated.reason
name: TerminatedReason
type: string
additionalPrinterColumnsUndefinedValues:
- key: TerminatedReason
value: "-"
- key: WaitingdReason
value: "-"
additionalPrinterColumnsTrimLengths:
- key: Name
value: 63
- key: Image
value: 63
id: "container-status-containers-list"
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: CustomColumnsOverride
metadata:
name: container-spec-init-containers-list
spec:
additionalPrinterColumns:
- jsonPath: .name
name: Name
type: string
- jsonPath: .image
name: Image
type: string
- jsonPath: .resources.requests
name: Resources requests
type: array
- jsonPath: .resources.limits
name: Resources limits
type: array
additionalPrinterColumnsTrimLengths:
- key: Image
value: 64
additionalPrinterColumnsUndefinedValues:
- key: Name
value: "-"
- key: Image
value: "-"
- key: Resources limits
value: "-"
- key: Resources requests
value: "-"
id: container-spec-init-containers-list
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: CustomColumnsOverride
metadata:
name: container-spec-containers-list
spec:
additionalPrinterColumns:
- jsonPath: .name
name: Name
type: string
- jsonPath: .image
name: Image
type: string
- jsonPath: .resources.requests
name: Resources requests
type: array
- jsonPath: .resources.limits
name: Resources limits
type: array
- jsonPath: .ports[*].containerPort
name: Ports
type: array
additionalPrinterColumnsTrimLengths:
- key: Image
value: 64
additionalPrinterColumnsUndefinedValues:
- key: Name
value: "-"
- key: Image
value: "-"
- key: Resources limits
value: "-"
- key: Resources requests
value: "-"
- key: Ports
value: "-"
id: container-spec-containers-list

View File

@@ -1,117 +0,0 @@
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: CustomColumnsOverride
metadata:
name: factory-details-networking.k8s.io.v1.ingresses
spec:
id: factory-details-networking.k8s.io.v1.ingresses
additionalPrinterColumns:
- jsonPath: .metadata.name
name: Name
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.icon" (dict
"text" "I"
"title" "ingress"
"backgroundColor" "#2e7dff"
)| nindent 8
}}
{{ include "incloud-web-resources.factory.linkblock" (dict
"reqIndex" 0
"type" "name"
"jsonPath" ".metadata.name"
"namespace" "{reqsJsonPath[0]['.metadata.namespace']['-']}"
"factory" "ingress-details"
) | nindent 12
}}
- jsonPath: .spec.rules[*].host
name: Hosts
type: string
- jsonPath: .status.loadBalancer.ingress[0].ip
name: Address
type: string
- jsonPath: .spec.defaultBackend.service.port.number
name: Port
type: string
- jsonPath: .metadata.creationTimestamp
name: Created
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.factory.timeblock" (dict
"req" ".metadata.creationTimestamp"
) | nindent 8
}}
additionalPrinterColumnsUndefinedValues:
- key: Hosts
value: "-"
- key: Address
value: "-"
- key: Port
value: "-"
additionalPrinterColumnsTrimLengths:
- key: Name
value: 64
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: CustomColumnsOverride
metadata:
name: stock-namespace-networking.k8s.io.v1.ingresses
spec:
id: stock-namespace-/networking.k8s.io/v1/ingresses
additionalPrinterColumns:
- jsonPath: .metadata.name
name: Name
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.icon" (dict
"text" "I"
"title" "ingress"
"backgroundColor" "#2e7dff"
)| nindent 8
}}
{{ include "incloud-web-resources.factory.linkblock" (dict
"reqIndex" 0
"type" "name"
"jsonPath" ".metadata.name"
"namespace" "{reqsJsonPath[0]['.metadata.namespace']['-']}"
"factory" "ingress-details"
) | nindent 12
}}
- jsonPath: .spec.rules[*].host
name: Hosts
type: string
- jsonPath: .status.loadBalancer.ingress[0].ip
name: Address
type: string
- jsonPath: .spec.defaultBackend.service.port.number
name: Port
type: string
- jsonPath: .metadata.creationTimestamp
name: Created
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.factory.timeblock" (dict
"req" ".metadata.creationTimestamp"
) | nindent 8
}}
additionalPrinterColumnsUndefinedValues:
- key: Hosts
value: "-"
- key: Address
value: "-"
- key: Port
value: "-"
additionalPrinterColumnsTrimLengths:
- key: Name
value: 64

View File

@@ -1,169 +0,0 @@
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: CustomColumnsOverride
metadata:
name: "stock-cluster-v1.configmaps"
spec:
additionalPrinterColumns:
- jsonPath: .metadata.name
name: Name
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.icon" (dict
"text" "CM"
"title" "configmap"
"backgroundColor" "#b48c78ff"
)| nindent 8
}}
{{ include "incloud-web-resources.factory.linkblock" (dict
"reqIndex" 0
"type" "name"
"jsonPath" ".metadata.name"
"namespace" "{reqsJsonPath[0]['.metadata.namespace']['-']}"
"factory" "configmap-details"
) | nindent 12
}}
- jsonPath: .metadata.namespace
name: Namespace
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.icon" (dict
"text" "NS"
"title" "namespace"
"backgroundColor" "#a25792ff"
)| nindent 8
}}
{{ include "incloud-web-resources.factory.linkblock" (dict
"reqIndex" 0
"type" "namespace"
"jsonPath" ".metadata.namespace"
"factory" "namespace-details"
) | nindent 12
}}
- jsonPath: .metadata.creationTimestamp
name: Created
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.factory.timeblock" (dict
"req" ".metadata.creationTimestamp"
) | nindent 8
}}
additionalPrinterColumnsTrimLengths:
- key: Name
value: 64
additionalPrinterColumnsUndefinedValues:
- key: Namespace
value: "-"
id: "stock-cluster-/v1/configmaps"
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: CustomColumnsOverride
metadata:
name: "stock-namespace-v1.configmaps"
spec:
additionalPrinterColumns:
- jsonPath: .metadata.name
name: Name
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.icon" (dict
"text" "CM"
"title" "configmap"
"backgroundColor" "#b48c78ff"
)| nindent 8
}}
{{ include "incloud-web-resources.factory.linkblock" (dict
"reqIndex" 0
"type" "name"
"jsonPath" ".metadata.name"
"namespace" "{reqsJsonPath[0]['.metadata.namespace']['-']}"
"factory" "configmap-details"
) | nindent 12
}}
- jsonPath: .metadata.creationTimestamp
name: Created
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.factory.timeblock" (dict
"req" ".metadata.creationTimestamp"
) | nindent 8
}}
additionalPrinterColumnsTrimLengths:
- key: Name
value: 64
id: "stock-namespace-/v1/configmaps"
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: CustomColumnsOverride
metadata:
name: "cluster-v1.configmaps"
spec:
additionalPrinterColumns:
- jsonPath: .metadata.name
name: Name
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.icon" (dict
"text" "CM"
"title" "configmap"
"backgroundColor" "#b48c78ff"
)| nindent 8
}}
{{ include "incloud-web-resources.factory.linkblock" (dict
"reqIndex" 0
"type" "name"
"jsonPath" ".metadata.name"
"namespace" "{reqsJsonPath[0]['.metadata.namespace']['-']}"
"factory" "configmap-details"
) | nindent 12
}}
- jsonPath: .metadata.namespace
name: Namespace
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.icon" (dict
"text" "NS"
"title" "namespace"
"backgroundColor" "#a25792ff"
)| nindent 8
}}
{{ include "incloud-web-resources.factory.linkblock" (dict
"reqIndex" 0
"type" "namespace"
"jsonPath" ".metadata.namespace"
"factory" "namespace-details"
) | nindent 12
}}
- jsonPath: .metadata.creationTimestamp
name: Created
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.factory.timeblock" (dict
"req" ".metadata.creationTimestamp"
) | nindent 8
}}
additionalPrinterColumnsTrimLengths:
- key: Name
value: 64
additionalPrinterColumnsUndefinedValues:
- key: Namespace
value: "-"
id: "cluster-/v1/configmaps"

View File

@@ -1,46 +0,0 @@
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: CustomColumnsOverride
metadata:
name: stock-cluster-v1.nodes
spec:
additionalPrinterColumns:
- jsonPath: .metadata.name
name: Name
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.icon" (dict
"text" "N"
"title" "node"
"backgroundColor" "#8476d1"
)| nindent 8
}}
{{ include "incloud-web-resources.factory.linkblock" (dict
"reqIndex" 0
"type" "name"
"jsonPath" ".metadata.name"
"factory" "node-details"
) | nindent 12
}}
- name: Status
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.factory.statuses.node" . | nindent 8 }}
- jsonPath: .metadata.creationTimestamp
name: Created
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.factory.timeblock" (dict
"req" ".metadata.creationTimestamp"
) | nindent 8
}}
additionalPrinterColumnsTrimLengths:
- key: Name
value: 64
id: stock-cluster-/v1/nodes

View File

@@ -1,364 +0,0 @@
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: CustomColumnsOverride
metadata:
name: "factory-node-details-v1.pods"
spec:
additionalPrinterColumns:
- jsonPath: .metadata.name
name: Name
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.icon" (dict
"text" "P"
"title" "pod"
"backgroundColor" "#009596"
)| nindent 8
}}
{{ include "incloud-web-resources.factory.linkblock" (dict
"reqIndex" 0
"type" "name"
"jsonPath" ".metadata.name"
"namespace" "{reqsJsonPath[0]['.metadata.namespace']['-']}"
"factory" "pod-details"
) | nindent 12
}}
- jsonPath: .metadata.namespace
name: Namespace
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.icon" (dict
"text" "NS"
"title" "namespace"
"backgroundColor" "#a25792ff"
)| nindent 8
}}
{{ include "incloud-web-resources.factory.linkblock" (dict
"reqIndex" 0
"type" "namespace"
"jsonPath" ".metadata.namespace"
"factory" "namespace-details"
) | nindent 12
}}
- jsonPath: .spec.restartPolicy
name: Restart Policy
type: string
- jsonPath: .status.podIP
name: Pod IP
type: string
# - jsonPath: .status.conditions[?(@.type=="ContainersReady")].status
# name: Status Containers
# type: string
# - jsonPath: .status.conditions[?(@.type=="PodScheduled")].status
# name: Status Scheduled
# type: string
- jsonPath: .status.qosClass
name: QOS
type: string
- name: Status
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.factory.statuses.pod" . | nindent 8 }}
- jsonPath: .metadata.creationTimestamp
name: Created
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.factory.timeblock" (dict
"req" ".metadata.creationTimestamp"
) | nindent 8
}}
additionalPrinterColumnsUndefinedValues:
- key: Node
value: Unschedulable
- key: Pod IP
value: NotAllocated
additionalPrinterColumnsTrimLengths:
- key: Name
value: 64
id: "factory-node-details-/v1/pods"
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: CustomColumnsOverride
metadata:
name: "factory-v1.pods"
spec:
additionalPrinterColumns:
- jsonPath: .metadata.name
name: Name
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.icon" (dict
"text" "P"
"title" "pod"
"backgroundColor" "#009596"
)| nindent 8
}}
{{ include "incloud-web-resources.factory.linkblock" (dict
"reqIndex" 0
"type" "name"
"jsonPath" ".metadata.name"
"namespace" "{reqsJsonPath[0]['.metadata.namespace']['-']}"
"factory" "pod-details"
) | nindent 12
}}
- name: Node
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.icon" (dict
"text" "N"
"title" "node"
"backgroundColor" "#8476d1"
)| nindent 8
}}
{{ include "incloud-web-resources.factory.linkblock" (dict
"reqIndex" 0
"type" "name"
"jsonPath" ".spec.nodeName"
"factory" "node-details"
) | nindent 12
}}
- jsonPath: .spec.restartPolicy
name: Restart Policy
type: string
- jsonPath: .status.podIP
name: Pod IP
type: string
# - jsonPath: .status.conditions[?(@.type=="ContainersReady" || @.type=="PodReady")].status
# name: Status Containers
# type: string
# - jsonPath: .status.conditions[?(@.type=="PodScheduled")].status
# name: Status Scheduled
# type: string
- jsonPath: .status.qosClass
name: QOS
type: string
- name: Status
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.factory.statuses.pod" . | nindent 8 }}
- jsonPath: .metadata.creationTimestamp
name: Created
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.factory.timeblock" (dict
"req" ".metadata.creationTimestamp"
) | nindent 8
}}
additionalPrinterColumnsUndefinedValues:
- key: Node
value: Unschedulable
- key: Pod IP
value: NotAllocated
additionalPrinterColumnsTrimLengths:
- key: Name
value: 64
id: "factory-/v1/pods"
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: CustomColumnsOverride
metadata:
name: "stock-cluster-v1.pods"
spec:
additionalPrinterColumns:
- jsonPath: .metadata.name
name: Name
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.icon" (dict
"text" "P"
"title" "pod"
"backgroundColor" "#009596"
)| nindent 8
}}
{{ include "incloud-web-resources.factory.linkblock" (dict
"reqIndex" 0
"type" "name"
"jsonPath" ".metadata.name"
"namespace" "{reqsJsonPath[0]['.metadata.namespace']['-']}"
"factory" "pod-details"
) | nindent 12
}}
- jsonPath: .metadata.namespace
name: Namespace
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.icon" (dict
"text" "NS"
"title" "namespace"
"backgroundColor" "#a25792ff"
)| nindent 8
}}
{{ include "incloud-web-resources.factory.linkblock" (dict
"reqIndex" 0
"type" "namespace"
"jsonPath" ".metadata.namespace"
"factory" "namespace-details"
) | nindent 12
}}
- jsonPath: .spec.nodeName
name: Node
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.icon" (dict
"text" "N"
"title" "node"
"backgroundColor" "#8476d1"
)| nindent 8
}}
{{ include "incloud-web-resources.factory.linkblock" (dict
"reqIndex" 0
"type" "name"
"jsonPath" ".spec.nodeName"
"factory" "node-details"
) | nindent 12
}}
- jsonPath: .spec.restartPolicy
name: Restart Policy
type: string
- jsonPath: .status.podIP
name: Pod IP
type: string
# - jsonPath: .status.conditions[?(@.type=="ContainersReady")].status
# name: Status Containers
# type: string
# - jsonPath: .status.conditions[?(@.type=="PodScheduled")].status
# name: Status Scheduled
# type: string
- jsonPath: .status.qosClass
name: QOS
type: string
- name: Status
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.factory.statuses.pod" . | nindent 8 }}
- jsonPath: .metadata.creationTimestamp
name: Created
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.factory.timeblock" (dict
"req" ".metadata.creationTimestamp"
) | nindent 8
}}
additionalPrinterColumnsUndefinedValues:
- key: Node
value: Unschedulable
- key: Pod IP
value: NotAllocated
additionalPrinterColumnsTrimLengths:
- key: Name
value: 64
id: "stock-cluster-/v1/pods"
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: CustomColumnsOverride
metadata:
name: "stock-namespace-v1.pods"
spec:
additionalPrinterColumns:
- jsonPath: .metadata.name
name: Name
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.icon" (dict
"text" "P"
"title" "pod"
"backgroundColor" "#009596"
)| nindent 8
}}
{{ include "incloud-web-resources.factory.linkblock" (dict
"reqIndex" 0
"type" "name"
"jsonPath" ".metadata.name"
"namespace" "{reqsJsonPath[0]['.metadata.namespace']['-']}"
"factory" "pod-details"
) | nindent 12
}}
- name: Node
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.icon" (dict
"text" "N"
"title" "node"
"backgroundColor" "#8476d1"
)| nindent 8
}}
{{ include "incloud-web-resources.factory.linkblock" (dict
"reqIndex" 0
"type" "name"
"jsonPath" ".spec.nodeName"
"factory" "node-details"
) | nindent 12
}}
- jsonPath: .spec.restartPolicy
name: Restart Policy
type: string
- jsonPath: .status.podIP
name: Pod IP
type: string
# - jsonPath: .status.conditions[?(@.type=="ContainersReady")].status
# name: Status Containers
# type: string
# - jsonPath: .status.conditions[?(@.type=="PodScheduled")].status
# name: Status Scheduled
# type: string
- jsonPath: .status.qosClass
name: QOS
type: string
- name: Status
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.factory.statuses.pod" . | nindent 8 }}
- jsonPath: .metadata.creationTimestamp
name: Created
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.factory.timeblock" (dict
"req" ".metadata.creationTimestamp"
) | nindent 8
}}
additionalPrinterColumnsUndefinedValues:
- key: Node
value: Unschedulable
- key: Pod IP
value: NotAllocated
additionalPrinterColumnsTrimLengths:
- key: Name
value: 64
id: "stock-namespace-/v1/pods"

View File

@@ -1,111 +0,0 @@
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: CustomColumnsOverride
metadata:
name: "stock-cluster-v1.secrets"
spec:
additionalPrinterColumns:
- jsonPath: .metadata.name
name: Name
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.icon" (dict
"text" "S"
"title" "secret"
"backgroundColor" "#c46100"
)| nindent 8
}}
{{ include "incloud-web-resources.factory.linkblock" (dict
"reqIndex" 0
"type" "name"
"jsonPath" ".metadata.name"
"namespace" "{reqsJsonPath[0]['.metadata.namespace']['-']}"
"factory" "secret-details"
) | nindent 12
}}
- jsonPath: .metadata.namespace
name: Namespace
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.icon" (dict
"text" "NS"
"title" "namespace"
"backgroundColor" "#a25792ff"
)| nindent 8
}}
{{ include "incloud-web-resources.factory.linkblock" (dict
"reqIndex" 0
"type" "namespace"
"jsonPath" ".metadata.namespace"
"factory" "namespace-details"
) | nindent 12
}}
- jsonPath: .type
name: Type
type: string
- jsonPath: .metadata.creationTimestamp
name: Created
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.factory.timeblock" (dict
"req" ".metadata.creationTimestamp"
) | nindent 8
}}
additionalPrinterColumnsTrimLengths:
- key: Name
value: 64
id: "stock-cluster-/v1/secrets"
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: CustomColumnsOverride
metadata:
name: "stock-namespace-v1.secrets"
spec:
additionalPrinterColumns:
- jsonPath: .metadata.name
name: Name
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.icon" (dict
"text" "S"
"title" "secret"
"backgroundColor" "#c46100"
)| nindent 8
}}
{{ include "incloud-web-resources.factory.linkblock" (dict
"reqIndex" 0
"type" "name"
"jsonPath" ".metadata.name"
"namespace" "{reqsJsonPath[0]['.metadata.namespace']['-']}"
"factory" "secret-details"
) | nindent 12
}}
- jsonPath: .type
name: Type
type: string
- jsonPath: .metadata.creationTimestamp
name: Created
type: factory
customProps:
disableEventBubbling: true
items:
{{ include "incloud-web-resources.factory.timeblock" (dict
"req" ".metadata.creationTimestamp"
) | nindent 8
}}
additionalPrinterColumnsTrimLengths:
- key: Name
value: 64
additionalPrinterColumnsUndefinedValues:
- key: Namespace
value: "-"
id: "stock-namespace-/v1/secrets"

View File

@@ -1,42 +0,0 @@
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: CustomColumnsOverride
metadata:
name: factory-details-v1alpha1.cozystack.io.workloads
spec:
id: factory-details-v1alpha1.cozystack.io.workloads
additionalPrinterColumns:
- jsonPath: .metadata.name
name: Name
type: string
- jsonPath: .status.kind
name: Kind
type: string
- jsonPath: .status.type
name: Type
type: string
- jsonPath: .status.resources.cpu
name: CPU
type: string
- jsonPath: .status.resources.memory
name: Memory
type: string
- jsonPath: .status.operational
name: Operational
type: string
additionalPrinterColumnsTrimLengths:
- key: Name
value: 64
additionalPrinterColumnsUndefinedValues:
- key: Name
value: "-"
- key: Kind
value: "-"
- key: Type
value: "-"
- key: CPU
value: "-"
- key: Memory
value: "-"
- key: Operational
value: "-"

View File

@@ -1,13 +0,0 @@
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: CustomFormsOverride
metadata:
name: default-networking.k8s.io.v1.ingresses
spec:
customizationId: "default-/networking.k8s.io/v1/ingresses"
hidden:
{{ include "incloud-web-resources.customformoverride.hidden.metadata.system" . | nindent 4 }}
{{ include "incloud-web-resources.customformoverride.hidden.metadata.api" . | nindent 4 }}
{{ include "incloud-web-resources.customformoverride.hidden.status" . | nindent 4 }}
schema: {}
strategy: merge

View File

@@ -1,13 +0,0 @@
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: CustomFormsOverride
metadata:
name: default-storage.k8s.io.v1.storageclasses
spec:
customizationId: "default-/storage.k8s.io/v1/storageclasses"
hidden:
{{ include "incloud-web-resources.customformoverride.hidden.metadata.system" . | nindent 4 }}
{{ include "incloud-web-resources.customformoverride.hidden.metadata.api" . | nindent 4 }}
{{ include "incloud-web-resources.customformoverride.hidden.status" . | nindent 4 }}
schema: {}
strategy: merge

View File

@@ -1,13 +0,0 @@
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: CustomFormsOverride
metadata:
name: default-v1.configmaps
spec:
customizationId: "default-/v1/configmaps"
hidden:
{{ include "incloud-web-resources.customformoverride.hidden.metadata.system" . | nindent 4 }}
{{ include "incloud-web-resources.customformoverride.hidden.metadata.api" . | nindent 4 }}
{{ include "incloud-web-resources.customformoverride.hidden.status" . | nindent 4 }}
schema: {}
strategy: merge

View File

@@ -1,13 +0,0 @@
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: CustomFormsOverride
metadata:
name: default-v1.namespaces
spec:
customizationId: "default-/v1/namespaces"
hidden:
{{ include "incloud-web-resources.customformoverride.hidden.metadata.system-clusterscope" . | nindent 4 }}
{{ include "incloud-web-resources.customformoverride.hidden.metadata.api" . | nindent 4 }}
{{ include "incloud-web-resources.customformoverride.hidden.status" . | nindent 4 }}
schema: {}
strategy: merge

View File

@@ -1,13 +0,0 @@
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: CustomFormsOverride
metadata:
name: default-v1.nodes
spec:
customizationId: "default-/v1/nodes"
hidden:
{{ include "incloud-web-resources.customformoverride.hidden.metadata.system-clusterscope" . | nindent 4 }}
{{ include "incloud-web-resources.customformoverride.hidden.metadata.api" . | nindent 4 }}
{{ include "incloud-web-resources.customformoverride.hidden.status" . | nindent 4 }}
schema: {}
strategy: merge

View File

@@ -1,13 +0,0 @@
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: CustomFormsOverride
metadata:
name: default-v1.persistentvolumeclaims
spec:
customizationId: "default-/v1/persistentvolumeclaims"
hidden:
{{ include "incloud-web-resources.customformoverride.hidden.metadata.system" . | nindent 4 }}
{{ include "incloud-web-resources.customformoverride.hidden.metadata.api" . | nindent 4 }}
{{ include "incloud-web-resources.customformoverride.hidden.status" . | nindent 4 }}
schema: {}
strategy: merge

View File

@@ -1,13 +0,0 @@
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: CustomFormsOverride
metadata:
name: default-v1.persistentvolumes
spec:
customizationId: "default-/v1/persistentvolumes"
hidden:
{{ include "incloud-web-resources.customformoverride.hidden.metadata.system" . | nindent 4 }}
{{ include "incloud-web-resources.customformoverride.hidden.metadata.api" . | nindent 4 }}
{{ include "incloud-web-resources.customformoverride.hidden.status" . | nindent 4 }}
schema: {}
strategy: merge

View File

@@ -1,13 +0,0 @@
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: CustomFormsOverride
metadata:
name: default-v1.pods
spec:
customizationId: "default-/v1/pods"
hidden:
{{ include "incloud-web-resources.customformoverride.hidden.metadata.system" . | nindent 4 }}
{{ include "incloud-web-resources.customformoverride.hidden.metadata.api" . | nindent 4 }}
{{ include "incloud-web-resources.customformoverride.hidden.status" . | nindent 4 }}
schema: {}
strategy: merge

View File

@@ -1,13 +0,0 @@
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: CustomFormsOverride
metadata:
name: default-v1.secrets
spec:
customizationId: "default-/v1/secrets"
hidden:
{{ include "incloud-web-resources.customformoverride.hidden.metadata.system" . | nindent 4 }}
{{ include "incloud-web-resources.customformoverride.hidden.metadata.api" . | nindent 4 }}
{{ include "incloud-web-resources.customformoverride.hidden.status" . | nindent 4 }}
schema: {}
strategy: merge

View File

@@ -1,13 +0,0 @@
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: CustomFormsOverride
metadata:
name: default-v1.services
spec:
customizationId: "default-/v1/services"
hidden:
{{ include "incloud-web-resources.customformoverride.hidden.metadata.system" . | nindent 4 }}
{{ include "incloud-web-resources.customformoverride.hidden.metadata.api" . | nindent 4 }}
{{ include "incloud-web-resources.customformoverride.hidden.status" . | nindent 4 }}
schema: {}
strategy: merge

View File

@@ -1,29 +0,0 @@
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: Factory
metadata:
name: marketplace
spec:
key: marketplace
sidebarTags:
- marketplace-sidebar
withScrollableMainContentCard: true
urlsToFetch: []
data:
- type: ContentCard
data:
id: 31
title: "Marketplace"
style:
flexGrow: 1
children:
- type: MarketplaceCard
data:
id: 311
clusterNamePartOfUrl: "{2}"
namespacePartOfUrl: "{3}"
baseApiGroup: dashboard.cozystack.io
baseApiVersion: v1alpha1
mpResourceName: marketplacepanels
mpResourceKind: MarketplacePanel
baseprefix: openapi-ui

View File

@@ -1,30 +0,0 @@
{{- define "incloud-web-resources.factory.annotations.block" -}}
{{- $i := (default 0 .reqIndex) -}}
{{- $jsonPath := (default ".metadata.annotations" .jsonPath) -}}
{{- $endpoint := (default "" .endpoint) -}}
{{- $pathToValue := (default "/metadata/annotations" .pathToValue) -}}
- type: antdText
data:
id: annotations
strong: true
text: Annotations
- type: Annotations
data:
id: annotations
reqIndex: 0
jsonPathToObj: "{{ $jsonPath }}"
text: "~counter~ Annotations"
errorText: "0 Annotations"
notificationSuccessMessage: "Updated successfully"
notificationSuccessMessageDescription: "Annotations have been updated"
modalTitle: "Edit annotations"
modalDescriptionText: ""
inputLabel: ""
endpoint: "{{ $endpoint }}"
pathToValue: "{{ $pathToValue }}"
editModalWidth: "800px"
cols:
- 11
- 11
- 2
{{- end -}}

View File

@@ -1,35 +0,0 @@
{{- define "incloud-web-resources.factory.counters.fields" -}}
{{- $i := (default 0 .reqIndex) -}}
{{- $type := (default "counter" .type) -}}
{{- $title := (default "" .title) -}}
- type: antdText
data:
id: {{ printf "%s-label" $type }}
strong: true
text: "{{ $title }}"
- type: ItemCounter
data:
id: {{ printf "%s-counter" $type }}
text: "~counter~ {{ $type }}"
reqIndex: {{$i}}
jsonPathToArray: "{{ .jsonPath | default "" }}"
errorText: "Error"
{{- end -}}
{{- define "incloud-web-resources.factory.counters.object.fields" -}}
{{- $i := (default 0 .reqIndex) -}}
{{- $type := (default "counter" .type) -}}
{{- $title := (default "" .title) -}}
- type: antdText
data:
id: {{ printf "%s-label" $type }}
strong: true
text: "{{ $title }}"
- type: KeyCounter
data:
id: {{ printf "%s-counter" $type }}
text: "~counter~ {{ $type }}"
reqIndex: {{$i}}
jsonPathToObj: "{{ .jsonPath | default "" }}"
errorText: "Error"
{{- end -}}

View File

@@ -1,64 +0,0 @@
{{- define "incloud-web-resources.factory.labels" -}}
{{- $i := (default 0 .reqIndex) -}}
{{- $type := (default "labels" .type) -}}
{{- $title := (default "Labels" .title) -}}
{{- $jsonPath := (default ".metadata.labels" .jsonPath) -}}
{{- $endpoint := (default "" .endpoint) -}}
{{- $pathToValue := (default "/metadata/labels" .pathToValue) -}}
{{- $maxTagTextLength := (default 35 .maxTagTextLength) -}}
{{- $maxEditTagTextLength := (default 35 .maxEditTagTextLength) -}}
{{- $notificationSuccessMessage := (default "Updated successfully" .notificationSuccessMessage) -}}
{{- $notificationSuccessMessageDescription := (default "Labels have been updated" .notificationSuccessMessageDescription) -}}
{{- $modalTitle := (default "Edit labels" .modalTitle) -}}
{{- $modalDescriptionText := (default "" .modalDescriptionText) -}}
{{- $inputLabel := (default "" .inputLabel) -}}
{{- $containerMarginTop := (default "-30px" .containerMarginTop) -}}
- type: antdText
data:
id: {{ printf "%s-title" $type }}
text: "{{ $title }}"
strong: true
style:
fontSize: 14
- type: Labels
data:
id: {{ printf "%s-editor" $type }}
reqIndex: {{ $i }}
jsonPathToLabels: "{{ $jsonPath }}"
selectProps:
maxTagTextLength: {{ $maxTagTextLength }}
notificationSuccessMessage: "{{ $notificationSuccessMessage }}"
notificationSuccessMessageDescription: "{{ $notificationSuccessMessageDescription }}"
modalTitle: "{{ $modalTitle }}"
modalDescriptionText: "{{ $modalDescriptionText }}"
inputLabel: "{{ $inputLabel }}"
containerStyle:
marginTop: "{{ $containerMarginTop }}"
maxEditTagTextLength: {{ $maxEditTagTextLength }}
endpoint: "{{ $endpoint }}"
pathToValue: "{{ $pathToValue }}"
editModalWidth: 650
paddingContainerEnd: "24px"
{{- end -}}
{{- define "incloud-web-resources.factory.labels.base.selector" -}}
{{- $i := (default 0 .reqIndex) -}}
{{- $type := (default "pod-selector" .type) -}}
{{- $title := (default "Pod selector" .title) -}}
{{- $jsonPath := (default ".spec.template.metadata.labels" .jsonPath) -}}
- type: antdText
data:
id: {{ printf "%s-selector" $type }}
text: "{{ $title }}"
strong: true
style:
fontSize: 14
- type: LabelsToSearchParams
data:
id: {{ printf "%s-to-search-params" $type }}
reqIndex: {{$i}}
jsonPathToLabels: "{{ $jsonPath }}"
linkPrefix: "{{ .linkPrefix | default "/openapi-ui/{2}/search" }}"
errorText: "-"
{{- end -}}

View File

@@ -1,43 +0,0 @@
{{- define "incloud-web-resources.factory.links.details" -}}
{{- $i := (default 0 .reqIndex) -}}
{{- $type := (default "" .type) -}}
{{- $title := (default "" .title) -}}
{{- $jsonPath := (default "" .jsonPath) -}}
{{- $factory := (default "" .factory) -}}
{{- $ns := (default "" .namespace) -}}
{{- $nsPart := "" -}}
{{- if ne $ns "" }}
{{- $nsPart = printf "%s/" $ns -}}
{{- end }}
- type: parsedText
data:
id: {{ printf "%s-title" $type }}
strong: true
text: "{{ $title }}"
style:
fontWeight: bold
- type: antdLink
data:
id: {{ printf "%s-link" $type }}
text: "{reqsJsonPath[{{$i}}]['{{ $jsonPath }}']['-']}"
href: "/openapi-ui/{2}/{{$nsPart}}factory/{{ $factory }}/{reqsJsonPath[{{$i}}]['{{ $jsonPath }}']['-']}"
{{- end -}}
{{- define "incloud-web-resources.factory.linkblock" -}}
{{- $i := (default 0 .reqIndex) -}}
{{- $type := (default "" .type) -}}
{{- $jsonPath := (default "" .jsonPath) -}}
{{- $factory := (default "" .factory) -}}
{{- $ns := (default "" .namespace) -}}
{{- $nsPart := "" -}}
{{- if ne $ns "" }}
{{- $nsPart = printf "%s/" $ns -}}
{{- end }}
- type: antdLink
data:
id: {{ printf "%s-link" $type }}
text: "{reqsJsonPath[{{$i}}]['{{ $jsonPath }}']['-']}"
href: "/openapi-ui/{2}/{{$nsPart}}factory/{{ $factory }}/{reqsJsonPath[{{$i}}]['{{ $jsonPath }}']['-']}"
{{- end -}}

View File

@@ -1,189 +0,0 @@
{{- define "incloud-web-resources.factory.statuses.deployment" -}}
- type: StatusText
data:
id: header-status
# 1) Collect all possible Deployment conditions
values:
- "{reqsJsonPath[0]['.status.conditions[*].reason']['-']}"
# 2) Criteria: positive / negative; neutral goes to fallback
criteriaSuccess: equals
valueToCompareSuccess:
# Positive reasons
- "MinimumReplicasAvailable" # Available: all replicas are healthy
- "NewReplicaSetAvailable" # Progressing: new RS serves traffic
- "ReplicaSetUpdated" # Progressing: RS is updated/synced
- "Complete" # Update completed successfully
criteriaError: equals
valueToCompareError:
# Negative reasons
- "DeploymentReplicaFailure" # General replica failure
- "FailedCreate" # Failed to create Pod/RS
- "FailedDelete" # Failed to delete resource
- "FailedScaleUp" # Failed to scale up
- "FailedScaleDown" # Failed to scale down
# 3) Texts to display
successText: "Available"
errorText: "Error"
fallbackText: "Progressing"
# Notes on neutral/fallback cases:
# - ReplicaSetUpdated → neutral/positive (update in progress)
# - ScalingReplicaSet → neutral (normal scale up/down)
# - Paused / DeploymentPaused→ neutral (manually paused by admin)
# - NewReplicaSetCreated → neutral (new RS created, not yet serving)
# - FoundNewReplicaSet → neutral (RS found, syncing)
# - MinimumReplicasUnavailable → neutral (some replicas not ready yet)
# - ProgressDeadlineExceeded → error-like, stuck in progress
{{- end -}}
{{- define "incloud-web-resources.factory.statuses.pod" -}}
- type: StatusText
data:
id: pod-status
# --- Collected values from Pod status -----------------------------------
values:
# Init containers
- "{reqsJsonPath[0]['.status.initContainerStatuses[*].state.waiting.reason']}"
- "{reqsJsonPath[0]['.status.initContainerStatuses[*].state.terminated.reason']}"
- "{reqsJsonPath[0]['.status.initContainerStatuses[*].lastState.terminated.reason']}"
# Main containers
- "{reqsJsonPath[0]['.status.containerStatuses[*].state.waiting.reason']}"
- "{reqsJsonPath[0]['.status.containerStatuses[*].state.terminated.reason']}"
- "{reqsJsonPath[0]['.status.containerStatuses[*].lastState.terminated.reason']}"
# Pod phase and general reason
- "{reqsJsonPath[0]['.status.phase']}"
- "{reqsJsonPath[0]['.status.reason']}"
# Condition reasons (PodScheduled / Initialized / ContainersReady / Ready)
- "{reqsJsonPath[0]['.status.conditions[*].reason']}"
# --- Success criteria ---------------------------------------------------
criteriaSuccess: notEquals
stategySuccess: every
valueToCompareSuccess:
# Graceful or expected state transitions
- "Preempted"
- "Shutdown"
- "NodeShutdown"
- "DisruptionTarget"
# Transitional states (may require timeout)
- "Unschedulable"
- "SchedulingGated"
- "ContainersNotReady"
- "ContainersNotInitialized"
# Temporary failures
- "BackOff"
# Controlled shutdowns or benign errors
- "PreStopHookError"
- "KillError"
- "ContainerStatusUnknown"
# --- Error criteria -----------------------------------------------------
criteriaError: equals
strategyError: every
valueToCompareError:
# Pod-level fatal phases or errors
- "Failed"
- "Unknown"
- "Evicted"
- "NodeLost"
- "UnexpectedAdmissionError"
# Scheduler-related failures
- "SchedulerError"
- "FailedScheduling"
# Container-level fatal errors
- "CrashLoopBackOff"
- "ImagePullBackOff"
- "ErrImagePull"
- "ErrImageNeverPull"
- "InvalidImageName"
- "ImageInspectError"
- "CreateContainerConfigError"
- "CreateContainerError"
- "RunContainerError"
- "StartError"
- "PostStartHookError"
- "ContainerCannotRun"
- "OOMKilled"
- "Error"
- "DeadlineExceeded"
- "CreatePodSandboxError"
# --- Output text rendering ----------------------------------------------
successText: "{reqsJsonPath[0]['.status.phase']}"
errorText: "Error"
fallbackText: "Progressing"
{{- end -}}
{{- define "incloud-web-resources.factory.statuses.node" -}}
- type: StatusText
data:
id: node-status
# --- Collected values from Node status ----------------------------------
values:
# Node phase and conditions
- "{reqsJsonPath[0]['.status.conditions[?(@.status=='True')].reason']['-']}"
# --- Success criteria ---------------------------------------------------
criteriaSuccess: equals
stategySuccess: every
valueToCompareSuccess:
"KubeletReady"
# --- Error criteria -----------------------------------------------------
criteriaError: equals
strategyError: every
valueToCompareError:
# Node condition failures
- "KernelDeadlock"
- "ReadonlyFilesystem"
- "NetworkUnavailable"
- "MemoryPressure"
- "DiskPressure"
- "PIDPressure"
# --- Output text rendering ----------------------------------------------
successText: "Available"
errorText: "Unavailable"
fallbackText: "Progressing"
{{- end -}}
{{- define "incloud-web-resources.factory.statuses.job" -}}
- type: StatusText
data:
id: header-status
# --- Collected values from Job conditions -------------------------------
values:
# Extracts the type of any condition where type == 'Complete' or 'Failed'
- "{reqsJsonPath[0]['.status.conditions[?(@.type=='Complete' || @.type=='Failed')].type']['-']}"
# --- Success criteria ---------------------------------------------------
criteriaSuccess: equals
stategySuccess: every
valueToCompareSuccess:
- "Complete" # Job succeeded
# --- Error criteria -----------------------------------------------------
criteriaError: equals
strategyError: every # ← likely meant to be `strategyError`
valueToCompareError:
- "Failed" # Job failed
# --- Output text rendering ----------------------------------------------
successText: "Available"
errorText: "Unavailable"
fallbackText: "Progressing"
{{- end -}}

View File

@@ -1,40 +0,0 @@
{{- define "incloud-web-resources.factory.containers.table" -}}
{{- $i := (default 0 .reqIndex) -}}
{{- $type := (default "" .type) -}}
{{- $title := (default "Init containers" .title) -}}
{{- $jsonPath := (default "" .jsonPath) -}}
{{- $pathToItems := (default "" .pathToItems) -}}
{{- $apiGroup := (default "" .apiGroup) -}}
{{- $kind := (default "" .kind) -}}
{{- $resourceName := (default "" .resourceName) -}}
{{- $namespace := (default "" .namespace) -}}
{{- $namespacePart := "" -}}
{{- if ne $namespace "" }}
{{- $namespacePart = printf "namespaces/%s/" $namespace -}}
{{- end }}
- type: VisibilityContainer
data:
id: {{ printf "%s-container" $type }}
value: "{reqsJsonPath[{{$i}}]['{{ $jsonPath }}']['-']}"
style:
margin: 0
padding: 0
children:
- type: antdText
data:
id: {{ printf "%s-title" $type }}
text: "{{ $title }}"
strong: true
style:
fontSize: 22
marginBottom: 32px
- type: EnrichedTable
data:
id: {{ printf "%s-table" $type }}
fetchUrl: "/api/clusters/{2}/k8s/{{ $apiGroup }}/{{$namespacePart}}{{ $kind }}/{{$resourceName}}"
clusterNamePartOfUrl: "{2}"
customizationId: {{ .customizationId | default ("") }}
baseprefix: "/openapi-ui"
withoutControls: {{ default true .withoutControls }}
pathToItems: {{ $pathToItems }}
{{- end -}}

View File

@@ -1,30 +0,0 @@
{{- define "incloud-web-resources.factory.taints.block" -}}
{{- $i := (default 0 .reqIndex) -}}
{{- $jsonPathToArray := (default "" .jsonPathToArray) -}}
{{- $endpoint := (default "" .endpoint) -}}
{{- $pathToValue := (default "" .pathToValue) -}}
- type: antdText
data:
id: taints
strong: true
text: Taints
- type: Taints
data:
id: taints
reqIndex: {{ $i }}
jsonPathToArray: "{{ $jsonPathToArray }}"
text: "~counter~ Taints"
errorText: "0 Taints"
notificationSuccessMessage: "Updated successfully"
notificationSuccessMessageDescription: "Taints have been updated"
modalTitle: "Edit taints"
modalDescriptionText: ""
inputLabel: ""
endpoint: "{{ $endpoint }}"
pathToValue: "{{ $pathToValue }}"
editModalWidth: "800px"
cols:
- 8
- 8
- 6
{{- end -}}

View File

@@ -1,42 +0,0 @@
{{- define "incloud-web-resources.factory.time.create" -}}
{{- $i := (default 0 .reqIndex) -}}
- type: antdText
data:
id: {{ .labelId | default "time-label" }}
strong: true
text: "{{ .text | default "Created" }}"
- type: antdFlex
data:
id: {{ .id | default "time-block" }}
align: center
gap: 6
children:
- type: antdText
data:
id: {{ .iconId | default "time-icon" }}
text: "🌐"
- type: parsedText
data:
id: {{ .valueId | default "time-value" }}
text: "{reqsJsonPath[{{$i}}]['{{ .req }}']['-']}"
formatter: timestamp
{{- end -}}
{{- define "incloud-web-resources.factory.timeblock" -}}
{{- $i := (default 0 .reqIndex) -}}
- type: antdFlex
data:
id: {{ .id | default "time-block" }}
align: center
gap: 6
children:
- type: antdText
data:
id: {{ .iconId | default "time-icon" }}
text: "🌐"
- type: parsedText
data:
id: {{ .valueId | default "time-value" }}
text: "{reqsJsonPath[{{$i}}]['{{ .req }}']['-']}"
formatter: timestamp
{{- end -}}

View File

@@ -1,32 +0,0 @@
{{- define "incloud-web-resources.factory.tolerations.block" -}}
{{- $i := (default 0 .reqIndex) -}}
{{- $jsonPathToArray := (default "" .jsonPathToArray) -}}
{{- $endpoint := (default "" .endpoint) -}}
{{- $pathToValue := (default "" .pathToValue) -}}
- type: antdText
data:
id: tolerations
strong: true
text: Tolerations
- type: Tolerations
data:
id: tolerations
reqIndex: {{ $i }}
jsonPathToArray: "{{ $jsonPathToArray }}"
text: "~counter~ Tolerations"
errorText: "0 Tolerations"
notificationSuccessMessage: "Updated successfully"
notificationSuccessMessageDescription: "Tolerations have been updated"
modalTitle: "Edit tolerations"
modalDescriptionText: ""
inputLabel: ""
endpoint: "{{ $endpoint }}"
pathToValue: "{{ $pathToValue }}"
editModalWidth: "1000px"
cols:
- 8
- 3
- 8
- 4
- 1
{{- end -}}

View File

@@ -1,232 +0,0 @@
apiVersion: dashboard.cozystack.io/v1alpha1
kind: Factory
metadata:
name: namespace-details
spec:
# Unique key for this factory configuration
key: namespace-details
# Sidebar category tags
sidebarTags:
- namespace-sidebar
# Enable scrollable content card for main section
withScrollableMainContentCard: true
# API endpoint for fetching Namespace details
urlsToFetch:
- "/api/clusters/{2}/k8s/api/v1/namespaces/{5}"
data:
# === HEADER ROW ===
- type: antdFlex
data:
id: header-row
gap: 6
align: center
style:
marginBottom: 24px
children:
# Badge with resource short name
- type: antdText
data:
id: header-badge
text: "NS"
title: Namespace
style:
fontSize: 20px
lineHeight: 24px
padding: "0 9px"
borderRadius: "20px"
minWidth: 24
display: inline-block
textAlign: center
whiteSpace: nowrap
color: "#fff"
backgroundColor: "#a25792ff"
fontFamily: RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif
fontWeight: 400
# Namespace name
- type: parsedText
data:
id: header-name
text: "{reqsJsonPath[0]['.metadata.name']['-']}"
style:
fontSize: 20px
lineHeight: 24px
fontFamily: RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif
# === MAIN TABS ===
- type: antdTabs
data:
id: tabs-root
defaultActiveKey: "details"
items:
# ------ DETAILS TAB ------
- key: "details"
label: "Details"
children:
# Main card container for details section
- type: ContentCard
data:
id: details-card
style:
marginBottom: 24px
children:
# Section title
- type: antdText
data:
id: details-title
text: "Namespace details"
strong: true
style:
fontSize: 20
marginBottom: 12px
# Spacer for visual separation
- type: Spacer
data:
id: details-spacer
"$space": 16
# Grid layout: left and right columns
- type: antdRow
data:
id: details-grid
gutter: [48, 12]
children:
# LEFT COLUMN: metadata
- type: antdCol
data:
id: col-left
span: 12
children:
- type: antdFlex
data:
id: left-stack
vertical: true
gap: 24
children:
# Namespace name block
- type: antdFlex
data:
id: name-block
vertical: true
gap: 4
children:
- type: antdText
data:
id: name-label
strong: true
text: "Name"
- type: parsedText
data:
id: name-value
text: "{reqsJsonPath[0]['.metadata.name']['-']}"
# Labels display block
- type: antdFlex
data:
id: labels-block
vertical: true
gap: 8
children:
{{ include "incloud-web-resources.factory.labels" (dict
"endpoint" "/api/clusters/{2}/k8s/api/v1/namespaces/{5}"
) | nindent 34
}}
# Annotations counter block
- type: antdFlex
data:
id: ds-annotations
vertical: true
gap: 4
children:
{{ include "incloud-web-resources.factory.annotations.block" (dict
"endpoint" "/api/clusters/{2}/k8s/api/v1/namespaces/{5}"
) | nindent 34
}}
# Creation time block
- type: antdFlex
data:
id: created-time-block
vertical: true
gap: 4
children:
{{ include "incloud-web-resources.factory.time.create" (dict
"req" ".metadata.creationTimestamp"
"text" "Created"
) | nindent 38
}}
# Owner information block
# - type: antdFlex
# data:
# id: owner-block
# vertical: true
# gap: 4
# children:
# - type: antdText
# data:
# id: owner-label
# strong: true
# text: "Owner"
# - type: antdFlex
# data:
# id: owner-value-container
# gap: 6
# align: center
# children:
# - type: antdText
# data:
# id: owner-value
# text: "No owner"
# style:
# color: "#FF0000"
# RIGHT COLUMN: status
- type: antdCol
data:
id: col-right
span: 12
children:
- type: antdFlex
data:
id: right-stack
vertical: true
gap: 24
children:
# Namespace status phase
- type: antdFlex
data:
id: status-block
vertical: true
gap: 4
children:
- type: antdText
data:
id: status-label
strong: true
text: "Status"
- type: parsedText
data:
id: status-value
text: "{reqsJsonPath[0]['.status.phase']['-']}"
# ------ YAML TAB ------
- key: "yaml"
label: "YAML"
children:
# YAML editor for Namespace manifest
- type: YamlEditorSingleton
data:
id: yaml-editor
cluster: "{2}"
isNameSpaced: false
type: "builtin"
typeName: namespaces
prefillValuesRequestIndex: 0
substractHeight: 400

View File

@@ -1,511 +0,0 @@
apiVersion: dashboard.cozystack.io/v1alpha1
kind: Factory
metadata:
name: node-details
spec:
# Unique key for this factory configuration
key: node-details
# Sidebar category tags
sidebarTags:
- node-sidebar
# Enable scrollable content card for main section
withScrollableMainContentCard: true
# API endpoint for fetching Node details
urlsToFetch:
- "/api/clusters/{2}/k8s/api/v1/nodes/{5}"
data:
# === HEADER ROW ===
- type: antdFlex
data:
id: header-row
gap: 6
align: center
style:
marginBottom: 24px
children:
# Badge with resource short name
- type: antdText
data:
id: header-badge
text: "N"
title: nodes
style:
fontSize: 20px
lineHeight: 24px
padding: "0 9px"
borderRadius: "20px"
minWidth: 24
display: inline-block
textAlign: center
whiteSpace: nowrap
color: "#fff"
backgroundColor: "#8476d1"
fontFamily: RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif
fontWeight: 400
# Node name
- type: parsedText
data:
id: header-name
text: "{reqsJsonPath[0]['.metadata.name']['-']}"
style:
fontSize: 20px
lineHeight: 24px
fontFamily: RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif
- type: antdFlex
data:
id: status-header-block
vertical: true
gap: 4
children:
{{ include "incloud-web-resources.factory.statuses.node" . | nindent 12 }}
# === MAIN TABS ===
- type: antdTabs
data:
id: tabs-root
defaultActiveKey: "details"
items:
# ------ DETAILS TAB ------
- key: "details"
label: "Details"
children:
# Main card container for details section
- type: ContentCard
data:
id: details-card
style:
marginBottom: 24px
children:
# Section title
- type: antdText
data:
id: details-title
text: "Node details"
strong: true
style:
fontSize: 20
marginBottom: 12px
# Spacer for visual separation
- type: Spacer
data:
id: details-spacer
"$space": 16
# Grid layout: left and right columns
- type: antdRow
data:
id: details-grid
gutter: [48, 12]
children:
# LEFT COLUMN: general metadata
- type: antdCol
data:
id: col-left
span: 12
children:
- type: antdFlex
data:
id: left-stack
vertical: true
gap: 24
children:
# Node name block
- type: antdFlex
data:
id: name-block
vertical: true
gap: 4
children:
- type: antdText
data:
id: name-label
strong: true
text: "Name"
- type: parsedText
data:
id: name-value
text: "{reqsJsonPath[0]['.metadata.name']['-']}"
# External ID block
- type: antdFlex
data:
id: external-id-block
vertical: true
gap: 4
children:
- type: antdText
data:
id: external-id-label
strong: true
text: "External ID"
- type: parsedText
data:
id: external-id-value
text: "{reqsJsonPath[0]['.spec.externalID']['-']}"
# Uptime block (placeholder for future calculated value)
# TODO To be done
# - type: antdFlex
# data:
# id: uptime-block
# vertical: true
# gap: 4
# children:
# - type: antdText
# data:
# id: uptime-label
# strong: true
# text: "Uptime"
# - type: parsedText
# data:
# id: uptime-value
# text: "Function needed to calculate uptime"
# Node address block
- type: antdFlex
data:
id: node-address-block
vertical: true
gap: 4
children:
- type: antdText
data:
id: node-address-label
strong: true
text: "Node address"
- type: antdFlex
data:
id: node-address-stack
vertical: true
gap: 2
children:
- type: ArrayOfObjectsToKeyValues
data:
id: node-address
reqIndex: 0
jsonPathToArray: ".status.addresses"
keyFieldName: type
valueFieldName: address
keyFieldStyle:
color: "#aaabac"
# Labels block
- type: antdFlex
data:
id: labels-block
vertical: true
gap: 8
children:
{{ include "incloud-web-resources.factory.labels" (dict
"endpoint" "/api/clusters/{2}/k8s/api/v1/nodes/{5}"
) | nindent 34
}}
# Taints counter block
- type: antdFlex
data:
id: taints-counter-block
vertical: true
gap: 4
children:
{{ include "incloud-web-resources.factory.taints.block" (dict
"endpoint" "/api/clusters/{2}/k8s/api/v1/nodes/{5}"
"jsonPathToArray" ".spec.taints"
"pathToValue" "/spec/taints"
) | nindent 34
}}
# Annotations counter block
- type: antdFlex
data:
id: ds-annotations
vertical: true
gap: 4
children:
{{ include "incloud-web-resources.factory.annotations.block" (dict
"endpoint" "/api/clusters/{2}/k8s/api/v1/nodes/{5}"
) | nindent 34
}}
# Provider ID block
- type: antdFlex
data:
id: provider-id-block
vertical: true
gap: 4
children:
- type: antdText
data:
id: provider-id-label
strong: true
text: "Provider ID"
- type: parsedText
data:
id: provider-id-value
text: "{reqsJsonPath[0]['.spec.providerID']['-']}"
# Creation time block
- type: antdFlex
data:
id: created-time-block
vertical: true
gap: 4
children:
{{ include "incloud-web-resources.factory.time.create" (dict
"req" ".metadata.creationTimestamp"
"text" "Created"
) | nindent 38
}}
# RIGHT COLUMN: node info and status
- type: antdCol
data:
id: col-right
span: 12
children:
- type: antdFlex
data:
id: right-stack
vertical: true
gap: 24
children:
# Status block
- type: antdFlex
data:
id: status-block
vertical: true
gap: 4
children:
- type: antdText
data:
id: status-label
strong: true
text: "Status"
- type: antdFlex
data:
id: status-header-block
vertical: true
gap: 4
children:
{{ include "incloud-web-resources.factory.statuses.node" . | nindent 38 }}
# Operating system block
- type: antdFlex
data:
id: operating-system-block
vertical: true
gap: 4
children:
- type: antdText
data:
id: operating-system-label
strong: true
text: "Operating system"
- type: parsedText
data:
id: operating-system-value
text: "{reqsJsonPath[0]['.status.nodeInfo.operatingSystem']['-']}"
# OS image block
- type: antdFlex
data:
id: os-image-block
vertical: true
gap: 4
children:
- type: antdText
data:
id: os-image-label
strong: true
text: "OS image"
- type: parsedText
data:
id: os-image-value
text: "{reqsJsonPath[0]['.status.nodeInfo.osImage']['-']}"
# Architecture block
- type: antdFlex
data:
id: architecture-block
vertical: true
gap: 4
children:
- type: antdText
data:
id: architecture-label
strong: true
text: "Architecture"
- type: parsedText
data:
id: architecture-value
text: "{reqsJsonPath[0]['.status.nodeInfo.architecture']['-']}"
# Kernel version block
- type: antdFlex
data:
id: kernel-version-block
vertical: true
gap: 4
children:
- type: antdText
data:
id: kernel-version-label
strong: true
text: "Kernel versions"
- type: parsedText
data:
id: kernel-version-value
text: "{reqsJsonPath[0]['.status.nodeInfo.kernelVersion']['-']}"
# Boot ID block
- type: antdFlex
data:
id: boot-id-block
vertical: true
gap: 4
children:
- type: antdText
data:
id: boot-id-label
strong: true
text: "Boot ID"
- type: parsedText
data:
id: boot-id-value
text: "{reqsJsonPath[0]['.status.nodeInfo.bootID']['-']}"
# Container runtime block
- type: antdFlex
data:
id: container-runtime-block
vertical: true
gap: 4
children:
- type: antdText
data:
id: container-runtime-label
strong: true
text: "Container runtime"
- type: parsedText
data:
id: container-runtime-value
text: "{reqsJsonPath[0]['.status.nodeInfo.containerRuntimeVersion']['-']}"
# Kubelet version block
- type: antdFlex
data:
id: kubelet-version-block
vertical: true
gap: 4
children:
- type: antdText
data:
id: kubelet-version-label
strong: true
text: "Kubelet version"
- type: parsedText
data:
id: kubelet-version-value
text: "{reqsJsonPath[0]['.status.nodeInfo.kubeletVersion']['-']}"
# CONDITIONS SECTION
- type: antdCol
data:
id: conditions-col
style:
marginTop: 32px
padding: 16px
children:
- type: antdText
data:
id: conditions-title
text: "Conditions"
strong: true
style:
fontSize: 22
- type: EnrichedTable
data:
id: conditions-table
fetchUrl: "/api/clusters/{2}/k8s/api/v1/nodes/{5}"
clusterNamePartOfUrl: "{2}"
customizationId: "factory-status-conditions"
baseprefix: "/openapi-ui"
withoutControls: true
pathToItems: ".status.conditions"
# IMAGES SECTION
- type: antdCol
data:
id: images-col
style:
marginTop: 32px
padding: 16px
children:
- type: antdText
data:
id: images-title
text: "Images"
strong: true
style:
fontSize: 22
- type: EnrichedTable
data:
id: images-table
fetchUrl: "/api/clusters/{2}/k8s/api/v1/nodes/{5}"
clusterNamePartOfUrl: "{2}"
customizationId: "factory-node-images"
baseprefix: "/openapi-ui"
withoutControls: true
pathToItems: ".status.images"
# ------ YAML TAB ------
- key: "yaml"
label: "YAML"
children:
# YAML editor for Node manifest
- type: YamlEditorSingleton
data:
id: yaml-editor
cluster: "{2}"
isNameSpaced: false
type: "builtin"
typeName: nodes
prefillValuesRequestIndex: 0
substractHeight: 400
# ------ PODS TAB ------
- key: "pods"
label: "Pods"
children:
# Table of Pods scheduled on this Node
- type: EnrichedTable
data:
id: pods-table
fetchUrl: "/api/clusters/{2}/k8s/api/v1/pods"
clusterNamePartOfUrl: "{2}"
customizationId: "factory-node-details-/v1/pods"
baseprefix: "/openapi-ui"
withoutControls: true
fieldSelector:
fieldName: "spec.nodeName"
parsedText: "{reqsJsonPath[0]['.metadata.name']['-']}"
pathToItems: ".items"
# ------ TERMINAL TAB ------
- key: "terminal"
label: "Terminal"
children:
# Interactive node terminal
- type: NodeTerminal
data:
id: terminal
cluster: "{2}"
nodeName: "{reqsJsonPath[0]['.metadata.name']['-']}"
substractHeight: 400

View File

@@ -1,533 +0,0 @@
apiVersion: dashboard.cozystack.io/v1alpha1
kind: Factory
metadata:
name: pod-details
spec:
key: pod-details
sidebarTags:
- pods-sidebar
withScrollableMainContentCard: true
urlsToFetch:
- "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/pods/{6}"
# Header row with badge, pod name, and status
data:
- type: antdFlex
data:
id: header-row
gap: 6
align: center
style:
marginBottom: 24px
children:
# Pod badge
- type: antdText
data:
id: header-badge
text: P
title: Pods
style:
fontSize: 20px
lineHeight: 24px
padding: "0 9px"
borderRadius: "20px"
minWidth: 24
display: inline-block
textAlign: center
whiteSpace: nowrap
color: "#fff"
backgroundColor: "#009596"
fontFamily: RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif
fontWeight: 400
# Pod name next to badge
- type: parsedText
data:
id: header-pod-name
text: "{reqsJsonPath[0]['.metadata.name']['-']}"
style:
fontSize: 20px
lineHeight: 24px
fontFamily: RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif
- type: antdFlex
data:
id: status-header-block
vertical: true
gap: 4
children:
{{ include "incloud-web-resources.factory.statuses.pod" . | nindent 38 }}
# Tabs with Details, YAML, Logs, and Terminal views
- type: antdTabs
data:
id: pod-tabs
defaultActiveKey: "details"
items:
# Details tab with metadata and spec
- key: "details"
label: "Details"
children:
- type: ContentCard
data:
id: details-card
style:
marginBottom: 24px
children:
# Title of the details section
- type: antdText
data:
id: details-title
text: "Pod details"
strong: true
style:
fontSize: 20
marginBottom: 12px
# Spacer before grid
- type: Spacer
data:
id: details-spacer
"$space": 16
# Two-column layout for metadata (left) and status/spec (right)
- type: antdRow
data:
id: details-grid
gutter: [48, 12]
children:
# Left column: metadata, links, labels
- type: antdCol
data:
id: col-left
span: 12
children:
- type: antdFlex
data:
id: col-left-stack
vertical: true
gap: 24
children:
# Display name label/value
- type: antdFlex
data:
id: meta-name-block
vertical: true
gap: 4
children:
- type: antdText
data:
id: meta-name-label
strong: true
text: "Name"
- type: parsedText
data:
id: meta-name-value
text: "{reqsJsonPath[0]['.metadata.name']['-']}"
# Namespace link (kept as include)
- type: antdFlex
data:
id: meta-namespace-block
vertical: true
gap: 8
children:
- type: antdText
data:
id: meta-name-label
text: Namespace
strong: true
{{ include "incloud-web-resources.icon" (dict
"text" "NS"
"title" "namespace"
"backgroundColor" "#a25792ff"
)| nindent 34
}}
{{ include "incloud-web-resources.factory.linkblock" (dict
"reqIndex" 0
"type" "namespace"
"jsonPath" ".metadata.namespace"
"factory" "namespace-details"
) | nindent 38
}}
# Labels list (kept as include)
- type: antdFlex
data:
id: meta-labels-block
vertical: true
gap: 8
children:
{{ include "incloud-web-resources.factory.labels" (dict
"endpoint" "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/pods/{6}"
) | nindent 34
}}
# Node selector chips (kept as include)
- type: antdFlex
data:
id: meta-node-selector-block
vertical: true
gap: 4
children:
{{ include "incloud-web-resources.factory.labels.base.selector" (dict
"type" "node"
"title" "Node selector"
"jsonPath" ".spec.nodeSelector"
) | nindent 34
}}
# Tolerations counter (kept as include)
- type: antdFlex
data:
id: meta-tolerations-block
vertical: true
gap: 4
children:
{{ include "incloud-web-resources.factory.tolerations.block" (dict
"endpoint" "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/pods/{6}"
"jsonPathToArray" ".spec.tolerations"
"pathToValue" "/spec/tolerations"
) | nindent 34
}}
# Annotations counter block
- type: antdFlex
data:
id: ds-annotations
vertical: true
gap: 4
children:
{{ include "incloud-web-resources.factory.annotations.block" (dict
"endpoint" "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/pods/{6}"
) | nindent 34
}}
# Created timestamp (kept as include)
- type: antdFlex
data:
id: meta-created-block
vertical: true
gap: 4
children:
{{ include "incloud-web-resources.factory.time.create" (dict
"req" ".metadata.creationTimestamp"
"text" "Created"
) | nindent 38
}}
# Owner information (fallback when absent)
# - type: antdFlex
# data:
# id: meta-owner-block
# vertical: true
# gap: 4
# children:
# - type: antdText
# data:
# id: meta-owner-label
# strong: true
# text: "Owner"
# - type: parsedText
# data:
# id: meta-owner-fallback
# strong: true
# text: "No owner"
# style:
# color: red
# Right column: status and runtime info
- type: antdCol
data:
id: col-right
span: 12
children:
- type: antdFlex
data:
id: col-right-stack
vertical: true
gap: 24
children:
# Status block with readiness reason mapping
- type: antdFlex
data:
id: status-block
vertical: true
gap: 4
children:
- type: antdText
data:
id: status-label
strong: true
text: "Status"
# Pod readiness/status indicator
- type: antdFlex
data:
id: status-label-block
vertical: true
gap: 4
children:
{{ include "incloud-web-resources.factory.statuses.pod" . | nindent 38 }}
# Restart policy
- type: antdFlex
data:
id: restart-policy-block
vertical: true
gap: 4
children:
- type: antdText
data:
id: restart-policy-label
strong: true
text: "Restart policy"
- type: parsedText
data:
id: restart-policy-value
text: "{reqsJsonPath[0]['.spec.restartPolicy']['-']} restart"
# Active deadline seconds
- type: antdFlex
data:
id: active-deadline-block
vertical: true
gap: 4
children:
- type: antdText
data:
id: active-deadline-label
strong: true
text: "Active deadline seconds"
- type: parsedText
data:
id: active-deadline-value
text: "{reqsJsonPath[0]['.spec.activeDeadlineSeconds']['-']}"
# Pod IP
- type: antdFlex
data:
id: pod-ip-block
vertical: true
gap: 4
children:
- type: antdText
data:
id: pod-ip-label
strong: true
text: "Pod IP"
- type: parsedText
data:
id: pod-ip-value
text: "{reqsJsonPath[0]['.status.podIP']['-']}"
# Host IP
- type: antdFlex
data:
id: host-ip-block
vertical: true
gap: 4
children:
- type: antdText
data:
id: host-ip-label
strong: true
text: "Host IP"
- type: parsedText
data:
id: host-ip-value
text: "{reqsJsonPath[0]['.status.hostIP']['-']}"
# Node details link (kept as include)
- type: antdFlex
data:
id: node-link-block
vertical: true
gap: 8
children:
- type: antdText
data:
id: meta-node
text: Node
strong: true
{{ include "incloud-web-resources.icon" (dict
"text" "N"
"title" "node"
"backgroundColor" "#8476d1"
)| nindent 34
}}
{{ include "incloud-web-resources.factory.linkblock" (dict
"reqIndex" 0
"type" "name"
"jsonPath" ".spec.nodeName"
"factory" "node-details"
) | nindent 38
}}
# QoS class
- type: antdFlex
data:
id: qos-class-block
vertical: true
gap: 4
children:
- type: antdText
data:
id: qos-class-label
strong: true
text: "QOS class"
- type: parsedText
data:
id: qos-class-value
text: "{reqsJsonPath[0]['.status.qosClass']['-']}"
# Volumes section (table is hidden if no volumes)
# TODO to be done
# - type: antdCol
# data:
# id: volumes-col
# style:
# marginTop: 10
# padding: 10
# children:
# - type: VisibilityContainer
# data:
# id: volumes-visibility
# value: "{reqsJsonPath[0]['.spec.volumes']['-']}"
# style:
# margin: 0
# padding: 0
# children:
# # Volumes title
# - type: antdText
# data:
# id: volumes-title
# text: "Volumes"
# strong: true
# style:
# fontSize: 22
# marginBottom: 32px
# # Volumes table
# - type: EnrichedTable
# data:
# id: volumes-table
# fetchUrl: "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/pods/{6}"
# clusterNamePartOfUrl: "{2}"
# customizationId: factory-pod-details-volume-list
# baseprefix: "/openapi-ui"
# withoutControls: true
# pathToItems: ".spec.volumes"
# Init containers section (hidden if none)
- type: antdCol
data:
id: init-containers-col
style:
marginTop: 10
padding: 10
children:
{{ include "incloud-web-resources.factory.containers.table" (dict
"title" "Init containers"
"customizationId" "container-status-init-containers-list"
"type" "init-containers"
"apiGroup" "api/v1"
"kind" "pods"
"resourceName" "{6}"
"namespace" "{3}"
"jsonPath" ".status.initContainerStatuses"
"pathToItems" "['status','initContainerStatuses']"
) | nindent 22
}}
# Containers section (hidden if none)
- type: antdCol
data:
id: containers-col
style:
marginTop: 10
padding: 10
children:
{{ include "incloud-web-resources.factory.containers.table" (dict
"title" "Containers"
"customizationId" "container-status-containers-list"
"type" "containers"
"apiGroup" "api/v1"
"kind" "pods"
"resourceName" "{6}"
"namespace" "{3}"
"jsonPath" ".status.containerStatuses"
"pathToItems" "['status','containerStatuses']"
) | nindent 22
}}
# Conditions section (hidden if none)
- type: antdCol
data:
id: conditions-col
style:
marginTop: 10
padding: 10
children:
- type: VisibilityContainer
data:
id: conditions-visibility
value: "{reqsJsonPath[0]['.status.conditions']['-']}"
style:
margin: 0
padding: 0
children:
# Conditions title
- type: antdText
data:
id: conditions-title
text: "Conditions"
strong: true
style:
fontSize: 22
# Conditions table
- type: EnrichedTable
data:
id: conditions-table
fetchUrl: "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/pods/{6}"
clusterNamePartOfUrl: "{2}"
customizationId: factory-status-conditions
baseprefix: "/openapi-ui"
withoutControls: true
pathToItems: ".status.conditions"
# YAML tab with inline editor
- key: "yaml"
label: "YAML"
children:
- type: YamlEditorSingleton
data:
id: yaml-editor
cluster: "{2}"
isNameSpaced: true
type: "builtin"
typeName: pods
prefillValuesRequestIndex: 0
substractHeight: 400
# Logs tab with live pod logs
- key: "logs"
label: "Logs"
children:
- type: PodLogs
data:
id: pod-logs
cluster: "{2}"
namespace: "{reqsJsonPath[0]['.metadata.namespace']['-']}"
podName: "{reqsJsonPath[0]['.metadata.name']['-']}"
substractHeight: 400
# Terminal tab with exec into pod
- key: "terminal"
label: "Terminal"
children:
- type: PodTerminal
data:
id: pod-terminal
cluster: "{2}"
namespace: "{reqsJsonPath[0]['.metadata.namespace']['-']}"
podName: "{reqsJsonPath[0]['.metadata.name']['-']}"
substractHeight: 400

View File

@@ -1,268 +0,0 @@
apiVersion: dashboard.cozystack.io/v1alpha1
kind: Factory
metadata:
name: secret-details
spec:
key: secret-details
sidebarTags:
- secret-sidebar
withScrollableMainContentCard: true
urlsToFetch:
- "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/secrets/{6}"
# Header row with badge and Secret name
data:
- type: antdFlex
data:
id: header-row
gap: 6
align: center
style:
marginBottom: 24px
children:
# Secret badge
- type: antdText
data:
id: badge-secret
text: S
title: secret
style:
fontSize: 20px
lineHeight: 24px
padding: "0 9px"
borderRadius: "20px"
minWidth: 24
display: inline-block
textAlign: center
whiteSpace: nowrap
color: "#fff"
backgroundColor: "#c46100"
fontFamily: RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif
fontWeight: 400
# Secret name
- type: parsedText
data:
id: header-secret-name
text: "{reqsJsonPath[0]['.metadata.name']['-']}"
style:
fontSize: 20px
lineHeight: 24px
fontFamily: RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif
# Tabs with Details and YAML
- type: antdTabs
data:
id: secret-tabs
defaultActiveKey: "details"
items:
# Details tab with metadata and spec info
- key: "details"
label: "Details"
children:
- type: ContentCard
data:
id: details-card
style:
marginBottom: 24px
children:
# Title
- type: antdText
data:
id: details-title
text: "Secret details"
strong: true
style:
fontSize: 20
marginBottom: 12px
# Spacer
- type: Spacer
data:
id: details-spacer
"$space": 16
# Two-column layout
- type: antdRow
data:
id: details-grid
gutter: [48, 12]
children:
# Left column: metadata
- type: antdCol
data:
id: col-left
span: 12
children:
- type: antdFlex
data:
id: col-left-stack
vertical: true
gap: 24
children:
# Name block
- type: antdFlex
data:
id: meta-name-block
vertical: true
gap: 4
children:
- type: antdText
data:
id: meta-name-label
strong: true
text: "Name"
- type: parsedText
data:
id: meta-name-value
text: "{reqsJsonPath[0]['.metadata.name']['-']}"
# Namespace link
- type: antdFlex
data:
id: meta-namespace-block
vertical: true
gap: 8
children:
- type: antdText
data:
id: meta-name-label
text: Namespace
strong: true
{{ include "incloud-web-resources.icon" (dict
"text" "NS"
"title" "namespace"
"backgroundColor" "#a25792ff"
)| nindent 34
}}
{{ include "incloud-web-resources.factory.linkblock" (dict
"reqIndex" 0
"type" "namespace"
"jsonPath" ".metadata.namespace"
"factory" "namespace-details"
) | nindent 38
}}
# Labels
- type: antdFlex
data:
id: meta-labels-block
vertical: true
gap: 8
children:
{{ include "incloud-web-resources.factory.labels" (dict
"endpoint" "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/secrets/{6}"
) | nindent 34
}}
# Annotations counter block
- type: antdFlex
data:
id: ds-annotations
vertical: true
gap: 4
children:
{{ include "incloud-web-resources.factory.annotations.block" (dict
"endpoint" "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/secrets/{6}"
) | nindent 34
}}
# Created timestamp
- type: antdFlex
data:
id: meta-created-block
vertical: true
gap: 4
children:
{{ include "incloud-web-resources.factory.time.create" (dict
"req" ".metadata.creationTimestamp"
"text" "Created"
) | nindent 38
}}
# Owner block
# - type: antdFlex
# data:
# id: meta-owner-block
# vertical: true
# gap: 4
# children:
# - type: antdText
# data:
# id: meta-owner-label
# strong: true
# text: "Owner"
# - type: antdFlex
# data:
# id: meta-owner-flex
# gap: 6
# align: center
# children:
# - type: parsedText
# data:
# id: owner-value
# strong: true
# text: "No owner"
# style:
# color: red
# Right column: type info
- type: antdCol
data:
id: col-right
span: 12
children:
- type: antdFlex
data:
id: col-right-stack
vertical: true
gap: 24
children:
# Secret type
- type: antdFlex
data:
id: secret-type-block
vertical: true
gap: 4
children:
- type: antdText
data:
id: secret-type-label
strong: true
text: "Type"
- type: parsedText
data:
id: secret-type-value
text: "{reqsJsonPath[0]['.type']['-']}"
# Secret SA
- type: antdFlex
data:
id: secret-sa-block
vertical: true
gap: 4
children:
# SA Link
{{ include "incloud-web-resources.factory.links.details" (dict
"reqIndex" 0
"type" "serviceaccount"
"title" "ServiceAccount"
"jsonPath" ".metadata.annotations['kubernetes.io/service-account.name']"
"namespace" "{3}"
"factory" "serviceaccount-details"
) | nindent 34
}}
# YAML tab
- key: yaml
label: YAML
children:
- type: YamlEditorSingleton
data:
id: yaml-editor
cluster: "{2}"
isNameSpaced: true
type: builtin
typeName: secrets
prefillValuesRequestIndex: 0
substractHeight: 400

View File

@@ -1,403 +0,0 @@
apiVersion: dashboard.cozystack.io/v1alpha1
kind: Factory
metadata:
name: service-details
spec:
key: service-details
withScrollableMainContentCard: true
sidebarTags:
- service-sidebar
urlsToFetch:
- "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/services/{6}"
# Header row with badge and Service name
data:
- type: antdFlex
data:
id: header-row
gap: 6
align: center
style:
marginBottom: 24px
children:
# Service badge
- type: antdText
data:
id: badge-service
text: S
title: services
style:
fontSize: 20px
lineHeight: 24px
padding: "0 9px"
borderRadius: "20px"
minWidth: 24
display: inline-block
textAlign: center
whiteSpace: nowrap
color: "#fff"
backgroundColor: "#6ca100"
fontFamily: RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif
fontWeight: 400
# Service name
- type: parsedText
data:
id: service-name
text: "{reqsJsonPath[0]['.metadata.name']['-']}"
style:
fontSize: 20px
lineHeight: 24px
fontFamily: RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif
# Tabs with Details, YAML, and Pods
- type: antdTabs
data:
id: service-tabs
defaultActiveKey: "details"
items:
# Details tab
- key: "details"
label: "Details"
children:
- type: ContentCard
data:
id: details-card
style:
marginBottom: 24px
children:
- type: antdRow
data:
id: details-grid
gutter: [48, 12]
children:
# Left column: metadata and config
- type: antdCol
data:
id: col-left
span: 12
children:
- type: antdText
data:
id: details-title
text: "Service details"
strong: true
style:
fontSize: 20
marginBottom: 12px
- type: Spacer
data:
id: details-spacer
"$space": 16
- type: antdFlex
data:
id: col-left-stack
vertical: true
gap: 24
children:
# Name block
- type: antdFlex
data:
id: meta-name-block
vertical: true
gap: 4
children:
- type: antdText
data:
id: meta-name-label
strong: true
text: "Name"
- type: parsedText
data:
id: meta-name-value
text: "{reqsJsonPath[0]['.metadata.name']['-']}"
# Namespace link
- type: antdFlex
data:
id: meta-namespace-block
vertical: true
gap: 8
children:
- type: antdText
data:
id: meta-name-label
text: Namespace
strong: true
{{ include "incloud-web-resources.icon" (dict
"text" "NS"
"title" "namespace"
"backgroundColor" "#a25792ff"
)| nindent 34
}}
{{ include "incloud-web-resources.factory.linkblock" (dict
"reqIndex" 0
"type" "namespace"
"jsonPath" ".metadata.namespace"
"factory" "namespace-details"
) | nindent 38
}}
# Labels
- type: antdFlex
data:
id: meta-labels-block
vertical: true
gap: 8
children:
{{ include "incloud-web-resources.factory.labels" (dict
"endpoint" "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/services/{6}"
) | nindent 34
}}
# Pod selector
- type: antdFlex
data:
id: meta-pod-selector-block
vertical: true
gap: 4
children:
{{ include "incloud-web-resources.factory.labels.base.selector" (dict
"type" "pod"
"title" "Pod selector"
"jsonPath" ".spec.template.metadata.labels"
) | nindent 34
}}
# Annotations counter block
- type: antdFlex
data:
id: ds-annotations
vertical: true
gap: 4
children:
{{ include "incloud-web-resources.factory.annotations.block" (dict
"endpoint" "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/services/{6}"
) | nindent 34
}}
# Session affinity
- type: antdFlex
data:
id: meta-session-affinity-block
vertical: true
gap: 4
children:
- type: antdText
data:
id: meta-session-affinity-label
strong: true
text: "Session affinity"
- type: parsedText
data:
id: meta-session-affinity-value
text: "{reqsJsonPath[0]['.spec.sessionAffinity']['Not configured']}"
# Created timestamp
- type: antdFlex
data:
id: meta-created-block
vertical: true
gap: 4
children:
{{ include "incloud-web-resources.factory.time.create" (dict
"req" ".metadata.creationTimestamp"
"text" "Created"
) | nindent 34
}}
# Owner
# - type: antdFlex
# data:
# id: meta-owner-block
# vertical: true
# gap: 4
# children:
# - type: antdText
# data:
# id: meta-owner-label
# strong: true
# text: "Owner"
# - type: antdFlex
# data:
# id: meta-owner-flex
# gap: 6
# align: center
# children:
# - type: antdText
# data:
# id: meta-owner-fallback
# text: "No owner"
# style:
# color: "#FF0000"
# Right column: routing and ports
- type: antdCol
data:
id: col-right
span: 12
children:
- type: antdText
data:
id: routing-title
text: "Service routing"
strong: true
style:
fontSize: 20
marginBottom: 12px
- type: Spacer
data:
id: routing-spacer
"$space": 16
- type: antdFlex
data:
id: col-right-stack
vertical: true
gap: 24
children:
# Hostname
- type: antdFlex
data:
id: service-hostname-block
vertical: true
gap: 4
children:
- type: antdText
data:
id: service-hostname-label
strong: true
text: "Hostname"
- type: parsedText
data:
id: service-hostname-value
text: "{reqsJsonPath[0]['.metadata.name']['-']}.{reqsJsonPath[0]['.metadata.namespace']['-']}.svc.cluster.local"
# IP addresses block
- type: antdFlex
data:
id: service-ip-block
vertical: true
gap: 12
children:
# ClusterIP
- type: antdFlex
data:
id: clusterip-block
vertical: true
gap: 4
children:
- type: antdText
data:
id: clusterip-label
strong: true
text: "ClusterIP address"
- type: parsedText
data:
id: clusterip-value
text: "{reqsJsonPath[0]['.spec.clusterIP']['-']}"
# LoadBalancerIP
- type: antdFlex
data:
id: loadbalancerip-block
vertical: true
gap: 4
children:
- type: antdText
data:
id: loadbalancerip-label
strong: true
text: "LoadBalancerIP address"
- type: parsedText
data:
id: loadbalancerip-value
text: "{reqsJsonPath[0]['.status.loadBalancer.ingress[0].ip']['Not Configured']}"
# Service port mapping
- type: antdFlex
data:
id: service-port-mapping-block
vertical: true
gap: 4
children:
- type: antdText
data:
id: service-port-mapping-label
strong: true
text: "Service port mapping"
- type: EnrichedTable
data:
id: service-port-mapping-table
fetchUrl: "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/services/{6}"
clusterNamePartOfUrl: "{2}"
customizationId: "factory-service-details-port-mapping"
baseprefix: "/openapi-ui"
withoutControls: true
pathToItems: ".spec.ports"
# Pod serving
- type: VisibilityContainer
data:
id: service-pod-serving-vis
value: "{reqsJsonPath[0]['.spec.selector']['-']}"
style: { margin: 0, padding: 0 }
children:
- type: antdFlex
data:
id: service-pod-serving-block
vertical: true
gap: 4
children:
- type: antdText
data:
id: service-pod-serving-label
strong: true
text: "Pod serving"
- type: EnrichedTable
data:
id: service-pod-serving-table
fetchUrl: "/api/clusters/{2}/k8s/apis/discovery.k8s.io/v1/namespaces/{3}/endpointslices"
clusterNamePartOfUrl: "{2}"
customizationId: "factory-service-details-endpointslice"
baseprefix: "/openapi-ui"
withoutControls: true
labelsSelector:
kubernetes.io/service-name: "{reqsJsonPath[0]['.metadata.name']['-']}"
pathToItems: ".items[*].endpoints"
# YAML tab
- key: "yaml"
label: "YAML"
children:
- type: YamlEditorSingleton
data:
id: yaml-editor
cluster: "{2}"
isNameSpaced: true
type: "builtin"
typeName: services
prefillValuesRequestIndex: 0
substractHeight: 400
# Pods tab
- key: "pods"
label: "Pods"
children:
- type: VisibilityContainer
data:
id: service-pod-serving-vis
value: "{reqsJsonPath[0]['.spec.selector']['-']}"
style: { margin: 0, padding: 0 }
children:
- type: EnrichedTable
data:
id: pods-table
fetchUrl: "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/pods"
clusterNamePartOfUrl: "{2}"
customizationId: "factory-node-details-/v1/pods"
baseprefix: "/openapi-ui"
withoutControls: false
labelsSelectorFull:
reqIndex: 0
pathToLabels: ".spec.selector"
pathToItems: ".items"

View File

@@ -1,328 +0,0 @@
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: Factory
metadata:
name: workloadmonitor-details
spec:
key: workloadmonitor-details
sidebarTags:
- workloadmonitor-sidebar
withScrollableMainContentCard: true
urlsToFetch:
- "/api/clusters/{2}/k8s/apis/cozystack.io/v1alpha1/namespaces/{3}/workloadmonitors/{6}"
data:
- type: antdFlex
data:
id: header-row
gap: 6
align: center
style:
marginBottom: 24
children:
- type: antdText
data:
id: badge-workloadmonitor
text: "W"
title: workloadmonitors
style:
backgroundColor: "#c46100"
borderRadius: "20px"
color: "#fff"
display: inline-block
fontFamily: RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif
fontSize: 20
fontWeight: 400
lineHeight: "24px"
minWidth: 24
padding: "0 9px"
textAlign: center
whiteSpace: nowrap
- type: parsedText
data:
id: workloadmonitor-name
text: "{reqsJsonPath[0]['.metadata.name']['-']}"
style:
fontFamily: RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif
fontSize: 20
lineHeight: "24px"
- type: antdTabs
data:
id: workloadmonitor-tabs
defaultActiveKey: details
items:
- key: details
label: Details
children:
- type: ContentCard
data:
id: details-card
style:
marginBottom: 24
children:
- type: antdRow
data:
id: details-grid
gutter: [48, 12]
children:
- type: antdCol
data:
id: col-left
span: 12
children:
- type: antdFlex
data:
id: col-left-stack
vertical: true
gap: 24
children:
- type: antdFlex
data:
id: meta-name-block
vertical: true
gap: 4
children:
- type: antdText
data:
id: meta-name-label
text: Name
strong: true
- type: parsedText
data:
id: meta-name-value
text: "{reqsJsonPath[0]['.metadata.name']['-']}"
- type: antdFlex
data:
id: meta-namespace-block
vertical: true
gap: 8
children:
- type: antdText
data:
id: meta-namespace-label
text: Namespace
strong: true
- type: antdFlex
data:
id: namespace-row
align: center
gap: 6
children:
- type: antdText
data:
id: ns-badge
text: NS
style:
backgroundColor: "#a25792ff"
borderRadius: "20px"
color: "#fff"
display: inline-block
fontFamily: RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif
fontSize: 15
fontWeight: 400
lineHeight: "24px"
minWidth: 24
padding: "0 9px"
textAlign: center
whiteSpace: nowrap
- type: antdLink
data:
id: namespace-link
text: "{reqsJsonPath[0]['.metadata.namespace']['-']}"
href: "/openapi-ui/{2}/factory/namespace-details/{reqsJsonPath[0]['.metadata.namespace']['-']}"
- type: antdFlex
data:
id: meta-created-block
vertical: true
gap: 4
children:
{{ include "incloud-web-resources.factory.time.create" (dict
"req" ".metadata.creationTimestamp"
"text" "Created"
) | nindent 34
}}
- type: antdFlex
data:
id: meta-kind-block
vertical: true
gap: 4
children:
- type: antdText
data:
id: kind-label
text: Kind
strong: true
- type: parsedText
data:
id: kind-value
text: "{reqsJsonPath[0]['.spec.kind']['-']}"
- type: antdFlex
data:
id: meta-type-block
vertical: true
gap: 4
children:
- type: antdText
data:
id: type-label
text: Type
strong: true
- type: parsedText
data:
id: type-value
text: "{reqsJsonPath[0]['.spec.type']['-']}"
- type: antdCol
data:
id: col-right
span: 12
children:
- type: antdFlex
data:
id: col-right-stack
vertical: true
gap: 24
children:
- type: antdText
data:
id: params-title
text: Parameters
strong: true
style:
fontSize: 20
marginBottom: 12
- type: antdFlex
data:
id: params-list
vertical: true
gap: 24
children:
- type: antdFlex
data:
id: param-version
vertical: true
gap: 4
children:
- type: antdText
data:
id: param-version-label
text: Version
strong: true
- type: parsedText
data:
id: param-version-value
text: "{reqsJsonPath[0]['.spec.version']['-']}"
- type: antdFlex
data:
id: param-replicas
vertical: true
gap: 4
children:
- type: antdText
data:
id: param-replicas-label
text: Replicas
strong: true
- type: parsedText
data:
id: param-replicas-value
text: "{reqsJsonPath[0]['.spec.replicas']['-']}"
- type: antdFlex
data:
id: param-minreplicas
vertical: true
gap: 4
children:
- type: antdText
data:
id: param-minreplicas-label
text: MinReplicas
strong: true
- type: parsedText
data:
id: param-minreplicas-value
text: "{reqsJsonPath[0]['.spec.minReplicas']['-']}"
- type: antdFlex
data:
id: param-available
vertical: true
gap: 4
children:
- type: antdText
data:
id: param-available-label
text: AvailableReplicas
strong: true
- type: parsedText
data:
id: param-available-value
text: "{reqsJsonPath[0]['.status.availableReplicas']['-']}"
- type: antdFlex
data:
id: param-observed
vertical: true
gap: 4
children:
- type: antdText
data:
id: param-observed-label
text: ObservedReplicas
strong: true
- type: parsedText
data:
id: param-observed-value
text: "{reqsJsonPath[0]['.status.observedReplicas']['-']}"
- type: antdFlex
data:
id: param-operational
vertical: true
gap: 4
children:
- type: antdText
data:
id: param-operational-label
text: Operational
strong: true
- type: parsedText
data:
id: param-operational-value
text: "{reqsJsonPath[0]['.status.operational']['-']}"
- key: workloads
label: Workloads
children:
- type: EnrichedTable
data:
id: workloads-table
fetchUrl: "/api/clusters/{2}/k8s/apis/cozystack.io/v1alpha1/namespaces/{3}/workloads"
clusterNamePartOfUrl: "{2}"
baseprefix: "/openapi-ui"
customizationId: "factory-details-v1alpha1.cozystack.io.workloads"
pathToItems: ["items"]
labelsSelector:
workloads.cozystack.io/monitor: "{reqs[0]['metadata','name']}"
- key: yaml
label: YAML
children:
- type: YamlEditorSingleton
data:
id: yaml-editor
cluster: "{2}"
isNameSpaced: true
type: builtin
typeName: workloadmonitors
prefillValuesRequestIndex: 0
substractHeight: 400

View File

@@ -1,8 +0,0 @@
apiVersion: dashboard.cozystack.io/v1alpha1
kind: Navigation
metadata:
name: navigation
spec:
namespaces:
change: /openapi-ui/{selectedCluster}/{value}/factory/marketplace
clear: /openapi-ui/{selectedCluster}/api-table/core.cozystack.io/v1alpha1/tenantnamespaces

View File

@@ -1,26 +0,0 @@
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: TableUriMapping
metadata:
name: virtualmachine-details
name: stock-namespace-default-apps.cozystack.io.v1alpha1.virtualmachines.yaml
spec:
id: "stock-namespace-/apps.cozystack.io/v1alpha1/virtualmachines"
pathToNavigate: "/openapi-ui/{2}/{3}/factory/virtualmachine-details/~recordValue~"
keysToParse:
- metadata
- name
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: TableUriMapping
metadata:
name: stock-cluster-service-details
spec:
id: "vm-factory-services"
pathToNavigate: "/openapi-ui/default/~recordValueSecond~/factory/service-details/~recordValue~"
keysToParse:
- metadata
- name
keysToParseSecond:
- metadata
- namespace

View File

@@ -1,11 +0,0 @@
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: TableUriMapping
metadata:
name: namespaces
spec:
id: "stock-cluster-/core.cozystack.io/v1alpha1/tenantnamespaces"
pathToNavigate: "/openapi-ui/{clusterName}/~recordValue~/factory/marketplace"
keysToParse:
- metadata
- name

View File

@@ -1,23 +0,0 @@
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: TableUriMapping
metadata:
name: stock-cluster-networking.k8s.io.v1.ingress-details
spec:
keysToParse: ".metadata.name"
keysToParseSecond: ".metadata.namespace"
id: "stock-cluster-/networking.k8s.io/v1/ingresses"
pathToNavigate: "/openapi-ui/{clusterName}/~recordValueSecond~/factory/ingress-details/~recordValue~"
---
apiVersion: dashboard.cozystack.io/v1alpha1
kind: TableUriMapping
metadata:
name: stock-namespace-networking.k8s.io.v1.ingress-details
spec:
keysToParse: ".metadata.name"
keysToParseSecond: ".metadata.namespace"
id: "stock-namespace-/networking.k8s.io/v1/ingresses"
pathToNavigate: "/openapi-ui/{clusterName}/~recordValueSecond~/factory/ingress-details/~recordValue~"

View File

@@ -1,21 +0,0 @@
# ---
# apiVersion: dashboard.cozystack.io/v1alpha1
# kind: TableUriMapping
# metadata:
# name: stock-cluster-configmap-details
# spec:
# keysToParse: ".metadata.name"
# keysToParseSecond: ".metadata.namespace"
# id: "stock-cluster-/v1/configmaps"
# pathToNavigate: "/openapi-ui/{clusterName}/~recordValueSecond~/factory/configmap-details/~recordValue~"
# ---
# apiVersion: dashboard.cozystack.io/v1alpha1
# kind: TableUriMapping
# metadata:
# name: stock-namespace-configmap-details
# spec:
# keysToParse: ".metadata.name"
# keysToParseSecond: ".metadata.namespace"
# id: "stock-namespace-/v1/configmaps"
# pathToNavigate: "/openapi-ui/{clusterName}/~recordValueSecond~/factory/configmap-details/~recordValue~"

View File

@@ -1,9 +0,0 @@
# ---
# apiVersion: dashboard.cozystack.io/v1alpha1
# kind: TableUriMapping
# metadata:
# name: stock-cluster-namespace-details
# spec:
# keysToParse: ".metadata.name"
# id: "stock-cluster-/v1/namespaces"
# pathToNavigate: "/openapi-ui/{clusterName}/factory/namespace-details/~recordValue~"

View File

@@ -1,9 +0,0 @@
# ---
# apiVersion: dashboard.cozystack.io/v1alpha1
# kind: TableUriMapping
# metadata:
# name: stock-cluster-node-details
# spec:
# keysToParse: ".metadata.name"
# id: "stock-cluster-/v1/nodes"
# pathToNavigate: "/openapi-ui/{clusterName}/factory/node-details/~recordValue~"

View File

@@ -1,54 +0,0 @@
# ---
# apiVersion: dashboard.cozystack.io/v1alpha1
# kind: TableUriMapping
# metadata:
# name: stock-cluster-pod-details
# spec:
# keysToParse: ".metadata.name"
# keysToParseSecond: ".metadata.namespace"
# id: "stock-cluster-/v1/pods"
# pathToNavigate: "/openapi-ui/{clusterName}/~recordValueSecond~/factory/pod-details/~recordValue~"
# ---
# apiVersion: dashboard.cozystack.io/v1alpha1
# kind: TableUriMapping
# metadata:
# name: stock-namespace-pod-details
# spec:
# keysToParse: ".metadata.name"
# keysToParseSecond: ".metadata.namespace"
# id: "stock-namespace-/v1/pods"
# pathToNavigate: "/openapi-ui/{clusterName}/~recordValueSecond~/factory/pod-details/~recordValue~"
# ---
# apiVersion: dashboard.cozystack.io/v1alpha1
# kind: TableUriMapping
# metadata:
# name: factory-node-details-table-pods
# spec:
# keysToParse: ".metadata.name"
# keysToParseSecond: ".metadata.namespace"
# id: "factory-node-details-/v1/pods"
# pathToNavigate: "/openapi-ui/{2}/~recordValueSecond~/factory/pod-details/~recordValue~"
# ---
# apiVersion: dashboard.cozystack.io/v1alpha1
# kind: TableUriMapping
# metadata:
# name: factory-service-details-endpointslice
# spec:
# keysToParse: ".targetRef.name"
# keysToParseSecond: ".targetRef.namespace"
# id: "factory-service-details-endpointslice"
# pathToNavigate: "/openapi-ui/{2}/~recordValueSecond~/factory/pod-details/~recordValue~"
# ---
# apiVersion: dashboard.cozystack.io/v1alpha1
# kind: TableUriMapping
# metadata:
# name: "factory-v1.pods"
# spec:
# keysToParse: ".metadata.name"
# keysToParseSecond: ".metadata.namespace"
# id: "factory-/v1/pods"
# pathToNavigate: "/openapi-ui/{2}/~recordValueSecond~/factory/pod-details/~recordValue~"

View File

@@ -1,21 +0,0 @@
# ---
# apiVersion: dashboard.cozystack.io/v1alpha1
# kind: TableUriMapping
# metadata:
# name: stock-cluster-secret-details
# spec:
# keysToParse: ".metadata.name"
# keysToParseSecond: ".metadata.namespace"
# id: "stock-cluster-/v1/secrets"
# pathToNavigate: "/openapi-ui/{clusterName}/~recordValueSecond~/factory/secret-details/~recordValue~"
# ---
# apiVersion: dashboard.cozystack.io/v1alpha1
# kind: TableUriMapping
# metadata:
# name: stock-namespace-secret-details
# spec:
# keysToParse: ".metadata.name"
# keysToParseSecond: ".metadata.namespace"
# id: "stock-namespace-/v1/secrets"
# pathToNavigate: "/openapi-ui/{clusterName}/~recordValueSecond~/factory/secret-details/~recordValue~"

View File

@@ -1,21 +0,0 @@
# ---
# apiVersion: dashboard.cozystack.io/v1alpha1
# kind: TableUriMapping
# metadata:
# name: stock-cluster-service-details
# spec:
# keysToParse: ".metadata.name"
# keysToParseSecond: ".metadata.namespace"
# id: "stock-cluster-/v1/services"
# pathToNavigate: "/openapi-ui/{clusterName}/~recordValueSecond~/factory/service-details/~recordValue~"
# ---
# apiVersion: dashboard.cozystack.io/v1alpha1
# kind: TableUriMapping
# metadata:
# name: stock-namespace-service-details
# spec:
# keysToParse: ".metadata.name"
# keysToParseSecond: ".metadata.namespace"
# id: "stock-namespace-/v1/services"
# pathToNavigate: "/openapi-ui/{clusterName}/~recordValueSecond~/factory/service-details/~recordValue~"

View File

@@ -1,62 +0,0 @@
{{/*
Expand the name of the chart.
*/}}
{{- define "incloud-web-resources.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "incloud-web-resources.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
{{/*
Create chart name and version as used by the chart label.
*/}}
{{- define "incloud-web-resources.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
{{- end }}
{{/*
Common labels
*/}}
{{- define "incloud-web-resources.labels" -}}
helm.sh/chart: {{ include "incloud-web-resources.chart" . }}
{{ include "incloud-web-resources.selectorLabels" . }}
{{- if .Chart.AppVersion }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
{{- end }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end }}
{{/*
Selector labels
*/}}
{{- define "incloud-web-resources.selectorLabels" -}}
app.kubernetes.io/name: {{ include "incloud-web-resources.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end }}
{{/*
Create the name of the service account to use
*/}}
{{- define "incloud-web-resources.serviceAccountName" -}}
{{- if .Values.serviceAccount.create }}
{{- default (include "incloud-web-resources.fullname" .) .Values.serviceAccount.name }}
{{- else }}
{{- default "default" .Values.serviceAccount.name }}
{{- end }}
{{- end }}

View File

@@ -5,7 +5,7 @@ include ../../../scripts/common-envs.mk
include ../../../scripts/package.mk
update: update-crd update-dockerfiles
image: image-openapi-ui image-openapi-ui-k8s-bff image-token-proxy
image: image-openapi-ui image-openapi-ui-k8s-bff image-token-proxy update-tenant-text
update-dockerfiles:
@@ -65,3 +65,6 @@ image-token-proxy:
IMAGE="$(REGISTRY)/token-proxy:$(call settag,$(TAG))@$$(yq e '."containerimage.digest"' images/token-proxy.json -r)" \
yq -i '.tokenProxy.image = strenv(IMAGE)' values.yaml
rm -f images/token-proxy.json
update-tenant-text:
yq -i '.data.TENANT_TEXT = "$(TAG)"' ./templates/configmap.yaml

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,6 +1,6 @@
openapiUI:
image: ghcr.io/cozystack/cozystack/openapi-ui:latest@sha256:a8cd0ce7ba55a59df39c23d930bc3bf5b6bb390a24dce157d36c3828e9052365
image: ghcr.io/cozystack/cozystack/openapi-ui:latest@sha256:db5cc174e221fe74df7379cbed2b7ab8f6cb17760168790307649ff63ef37b12
openapiUIK8sBff:
image: ghcr.io/cozystack/cozystack/openapi-ui-k8s-bff:latest@sha256:9647ec88955e4749079e4f10c3a941dc57bd74bda67094d2ea0d45943d04a8e7
image: ghcr.io/cozystack/cozystack/openapi-ui-k8s-bff:latest@sha256:4ed5debc85d64c7fc09f719e1efb1a2e1515219a355728c0ae574d4989485850
tokenProxy:
image: ghcr.io/cozystack/cozystack/token-proxy:latest@sha256:fad27112617bb17816702571e1f39d0ac3fe5283468d25eb12f79906cdab566b