- Replace rename-only wording on spec.targetApplicationRef form labels so users discover that the field also supports restoring into a different application, not just renaming. - Drop VMInstance-specific keys from the spec.options form label; the restore options are driver-specific, so hardcoding one driver's keys is misleading once other drivers land. - Render a single "Same as backup" fallback for an omitted spec.targetApplicationRef on the details page instead of the noisy "-.-/-" produced by concatenating three missing path fallbacks. - De-duplicate the time-block / time-icon / time-value component IDs within the restorejob-details factory by prefixing them with created-, started-at-, and completed-at-. - Mark non-CRD-backed stock-project-factory-*-details sidebars (kube-*, plan, backupjob, backup, restorejob) as static in upsertMultipleSidebars so they pick up consistent managed-by labels instead of being left unmanaged. Signed-off-by: Myasnikov Daniil <myasnikovdaniil2001@gmail.com>
2725 lines
103 KiB
Go
2725 lines
103 KiB
Go
package dashboard
|
|
|
|
import (
|
|
"encoding/json"
|
|
"strings"
|
|
|
|
dashboardv1alpha1 "github.com/cozystack/cozystack/api/dashboard/v1alpha1"
|
|
v1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
)
|
|
|
|
// ---------------- Complete refactored static resources ----------------
|
|
|
|
// CreateAllBreadcrumbs creates all breadcrumb resources using helper functions
|
|
func CreateAllBreadcrumbs() []*dashboardv1alpha1.Breadcrumb {
|
|
return []*dashboardv1alpha1.Breadcrumb{
|
|
// Stock project factory configmap details
|
|
createBreadcrumb("stock-project-factory-configmap-details", []map[string]any{
|
|
createBreadcrumbItem("configmaps", "v1/configmaps", "/openapi-ui/{cluster}/{namespace}/builtin-table/configmaps"),
|
|
createBreadcrumbItem("configmap", "{6}"),
|
|
}),
|
|
|
|
// Stock cluster factory namespace details
|
|
createBreadcrumb("stock-cluster-factory-namespace-details", []map[string]any{
|
|
createBreadcrumbItem("namespaces", "v1/namespaces", "/openapi-ui/{cluster}/builtin-table/namespaces"),
|
|
createBreadcrumbItem("namespace", "{5}"),
|
|
}),
|
|
|
|
// Stock cluster factory node details
|
|
createBreadcrumb("stock-cluster-factory-node-details", []map[string]any{
|
|
createBreadcrumbItem("node", "v1/nodes", "/openapi-ui/{cluster}/builtin-table/nodes"),
|
|
createBreadcrumbItem("node", "{5}"),
|
|
}),
|
|
|
|
// Stock project factory pod details
|
|
createBreadcrumb("stock-project-factory-pod-details", []map[string]any{
|
|
createBreadcrumbItem("pods", "v1/pods", "/openapi-ui/{cluster}/{namespace}/builtin-table/pods"),
|
|
createBreadcrumbItem("pod", "{6}"),
|
|
}),
|
|
|
|
// Stock project factory secret details
|
|
createBreadcrumb("stock-project-factory-kube-secret-details", []map[string]any{
|
|
createBreadcrumbItem("secrets", "v1/secrets", "/openapi-ui/{cluster}/{namespace}/builtin-table/secrets"),
|
|
createBreadcrumbItem("secret", "{6}"),
|
|
}),
|
|
|
|
// Stock project factory service details
|
|
createBreadcrumb("stock-project-factory-kube-service-details", []map[string]any{
|
|
createBreadcrumbItem("services", "v1/services", "/openapi-ui/{cluster}/{namespace}/builtin-table/services"),
|
|
createBreadcrumbItem("service", "{6}"),
|
|
}),
|
|
|
|
// Stock project factory ingress details
|
|
createBreadcrumb("stock-project-factory-kube-ingress-details", []map[string]any{
|
|
createBreadcrumbItem("ingresses", "networking.k8s.io/v1/ingresses", "/openapi-ui/{cluster}/{namespace}/builtin-table/ingresses"),
|
|
createBreadcrumbItem("ingress", "{6}"),
|
|
}),
|
|
|
|
// Stock cluster api table
|
|
createBreadcrumb("stock-cluster-api-table", []map[string]any{
|
|
createBreadcrumbItem("api", "{apiGroup}/{apiVersion}/{plural}"),
|
|
}),
|
|
|
|
// Stock cluster api form
|
|
createBreadcrumb("stock-cluster-api-form", []map[string]any{
|
|
createBreadcrumbItem("create-api-res-namespaced-table", "{apiGroup}/{apiVersion}/{plural}", "/openapi-ui/{cluster}/api-table/{apiGroup}/{apiVersion}/{plural}"),
|
|
createBreadcrumbItem("create-api-res-namespaced-typename", "Create"),
|
|
}),
|
|
|
|
// Stock cluster api form edit
|
|
createBreadcrumb("stock-cluster-api-form-edit", []map[string]any{
|
|
createBreadcrumbItem("create-api-res-namespaced-table", "{apiGroup}/{apiVersion}/{plural}", "/openapi-ui/{cluster}/api-table/{apiGroup}/{apiVersion}/{plural}"),
|
|
createBreadcrumbItem("create-api-res-namespaced-typename", "Update"),
|
|
}),
|
|
|
|
// Stock cluster builtin table
|
|
createBreadcrumb("stock-cluster-builtin-table", []map[string]any{
|
|
createBreadcrumbItem("api", "v1/{plural}"),
|
|
}),
|
|
|
|
// Stock cluster builtin form
|
|
createBreadcrumb("stock-cluster-builtin-form", []map[string]any{
|
|
createBreadcrumbItem("create-api-res-namespaced-table", "v1/{plural}", "/openapi-ui/{cluster}/builtin-table/{plural}"),
|
|
createBreadcrumbItem("create-api-res-namespaced-typename", "Create"),
|
|
}),
|
|
|
|
// Stock cluster builtin form edit
|
|
createBreadcrumb("stock-cluster-builtin-form-edit", []map[string]any{
|
|
createBreadcrumbItem("create-api-res-namespaced-table", "v1/{plural}", "/openapi-ui/{cluster}/builtin-table/{plural}"),
|
|
createBreadcrumbItem("create-api-res-namespaced-typename", "Update"),
|
|
}),
|
|
|
|
// Stock project api table
|
|
createBreadcrumb("stock-project-api-table", []map[string]any{
|
|
createBreadcrumbItem("api", "{apiGroup}/{apiVersion}/{plural}"),
|
|
}),
|
|
|
|
// Stock project api form
|
|
createBreadcrumb("stock-project-api-form", []map[string]any{
|
|
createBreadcrumbItem("create-api-res-namespaced-table", "{apiGroup}/{apiVersion}/{plural}", "/openapi-ui/{cluster}/{namespace}/api-table/{apiGroup}/{apiVersion}/{plural}"),
|
|
createBreadcrumbItem("create-api-res-namespaced-typename", "Create"),
|
|
}),
|
|
|
|
// Stock project api form edit
|
|
createBreadcrumb("stock-project-api-form-edit", []map[string]any{
|
|
createBreadcrumbItem("create-api-res-namespaced-table", "{apiGroup}/{apiVersion}/{plural}", "/openapi-ui/{cluster}/{namespace}/api-table/{apiGroup}/{apiVersion}/{plural}"),
|
|
createBreadcrumbItem("create-api-res-namespaced-typename", "Update"),
|
|
}),
|
|
|
|
// Stock project builtin table
|
|
createBreadcrumb("stock-project-builtin-table", []map[string]any{
|
|
createBreadcrumbItem("api", "v1/{plural}"),
|
|
}),
|
|
|
|
// Stock project builtin form
|
|
createBreadcrumb("stock-project-builtin-form", []map[string]any{
|
|
createBreadcrumbItem("create-api-res-namespaced-table", "v1/{plural}", "/openapi-ui/{cluster}/{namespace}/builtin-table/{plural}"),
|
|
createBreadcrumbItem("create-api-res-namespaced-typename", "Create"),
|
|
}),
|
|
|
|
// Stock project builtin form edit
|
|
createBreadcrumb("stock-project-builtin-form-edit", []map[string]any{
|
|
createBreadcrumbItem("create-api-res-namespaced-table", "v1/{plural}", "/openapi-ui/{cluster}/{namespace}/builtin-table/{plural}"),
|
|
createBreadcrumbItem("create-api-res-namespaced-typename", "Update"),
|
|
}),
|
|
}
|
|
}
|
|
|
|
// CreateAllCustomColumnsOverrides creates all custom column override resources using helper functions
|
|
func CreateAllCustomColumnsOverrides() []*dashboardv1alpha1.CustomColumnsOverride {
|
|
return []*dashboardv1alpha1.CustomColumnsOverride{
|
|
// Factory details v1 services
|
|
createCustomColumnsOverride("factory-details-v1.services", []any{
|
|
createCustomColumnWithSpecificColor("Name", "Service", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/kube-service-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
|
createStringColumn("ClusterIP", ".spec.clusterIP"),
|
|
createStringColumn("LoadbalancerIP", ".status.loadBalancer.ingress[0].ip"),
|
|
createTimestampColumn("Created", ".metadata.creationTimestamp"),
|
|
}),
|
|
|
|
// Stock namespace v1 services
|
|
createCustomColumnsOverride("stock-namespace-/v1/services", []any{
|
|
createCustomColumnWithJsonPath("Name", ".metadata.name", "Service", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/kube-service-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
|
createStringColumn("ClusterIP", ".spec.clusterIP"),
|
|
createStringColumn("LoadbalancerIP", ".status.loadBalancer.ingress[0].ip"),
|
|
createTimestampColumn("Created", ".metadata.creationTimestamp"),
|
|
}),
|
|
|
|
// Stock namespace core cozystack io v1alpha1 tenantmodules
|
|
createCustomColumnsOverride("stock-namespace-/core.cozystack.io/v1alpha1/tenantmodules", []any{
|
|
createCustomColumnWithJsonPath("Name", ".metadata.name", "Module", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/{reqsJsonPath[0]['.metadata.name']['-']}-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
|
createReadyColumn(),
|
|
createTimestampColumn("Created", ".metadata.creationTimestamp"),
|
|
createStringColumn("Version", ".status.version"),
|
|
}),
|
|
|
|
// Factory service details endpointslice (Pod serving table)
|
|
createCustomColumnsOverride("factory-kube-service-details-endpointslice", []any{
|
|
createStringColumn("Pod", ".targetRef.name"),
|
|
createArrayColumn("Addresses", ".addresses"),
|
|
createBoolColumn("Ready", ".conditions.ready"),
|
|
createStringColumn("Node", ".nodeName"),
|
|
}),
|
|
|
|
// Factory service details port mapping
|
|
createCustomColumnsOverride("factory-kube-service-details-port-mapping", []any{
|
|
createStringColumn("Name", ".name"),
|
|
createStringColumn("Port", ".port"),
|
|
createStringColumn("Protocol", ".protocol"),
|
|
createStringColumn("Pod port or name", ".targetPort"),
|
|
}),
|
|
|
|
// Factory details v1alpha1 cozystack io workloadmonitors
|
|
createCustomColumnsOverride("factory-details-v1alpha1.cozystack.io.workloadmonitors", []any{
|
|
createCustomColumnWithJsonPath("Name", ".metadata.name", "WorkloadMonitor", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/workloadmonitor-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
|
createStringColumn("TYPE", ".spec.type"),
|
|
createStringColumn("VERSION", ".spec.version"),
|
|
createStringColumn("REPLICAS", ".spec.replicas"),
|
|
createStringColumn("MINREPLICAS", ".spec.minReplicas"),
|
|
createStringColumn("AVAILABLE", ".status.availableReplicas"),
|
|
createStringColumn("OBSERVED", ".status.observedReplicas"),
|
|
}),
|
|
|
|
// Factory details v1alpha1 core cozystack io tenantsecrets
|
|
createCustomColumnsOverride("factory-details-v1alpha1.core.cozystack.io.tenantsecrets", []any{
|
|
createCustomColumnWithJsonPath("Name", ".metadata.name", "Secret", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/kube-secret-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
|
createFlatMapColumn("Data", ".data"),
|
|
createStringColumn("Key", "_flatMapData_Key"),
|
|
createSecretBase64Column("Value", "._flatMapData_Value"),
|
|
createTimestampColumn("Created", ".metadata.creationTimestamp"),
|
|
}),
|
|
|
|
// Virtual private cloud subnets
|
|
createCustomColumnsOverride("virtualprivatecloud-subnets", []any{
|
|
createFlatMapColumn("Data", ".data"),
|
|
createStringColumn("Subnet Parameters", "_flatMapData_Key"),
|
|
createStringColumn("Values", "_flatMapData_Value"),
|
|
}),
|
|
|
|
// Factory resource quotas
|
|
createCustomColumnsOverride("factory-resource-quotas", []any{
|
|
createFlatMapColumn("Data", ".spec.hard"),
|
|
createStringColumn("Resource", "_flatMapData_Key"),
|
|
createStringColumn("Hard", "_flatMapData_Value"),
|
|
createStringColumn("Used", ".status.used[_flatMapData_Key]"),
|
|
}),
|
|
|
|
// Factory ingress details rules
|
|
createCustomColumnsOverride("factory-kube-ingress-details-rules", []any{
|
|
createStringColumn("Host", ".host"),
|
|
createCustomColumnWithJsonPath("Service", ".http.paths[0].backend.service.name", "Service", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/kube-service-details/{reqsJsonPath[0]['.http.paths[0].backend.service.name']['-']}"),
|
|
createStringColumn("Port", ".http.paths[0].backend.service.port.number"),
|
|
createStringColumn("Path", ".http.paths[0].path"),
|
|
}),
|
|
|
|
// Factory node images
|
|
createCustomColumnsOverride("factory-node-images", []any{
|
|
createStringColumn("ImageID", ".names[0]"),
|
|
createConverterBytesColumn("Size", ".sizeBytes"),
|
|
}),
|
|
|
|
// Factory pod details volume list
|
|
createCustomColumnsOverride("factory-pod-details-volume-list", []any{
|
|
createStringColumn("Name", ".name"),
|
|
}),
|
|
|
|
// Factory details events. Event Time falls back to `.firstTimestamp`
|
|
// because core/v1 Events (Helm, controller-runtime) populate only the
|
|
// legacy timestamps while events.k8s.io/v1 Events populate only
|
|
// `.eventTime`; taking the first non-null of the two covers both.
|
|
createCustomColumnsOverride("factory-details-events", []any{
|
|
createTimestampColumn("Last Seen", ".lastTimestamp", ".eventTime"),
|
|
createTimestampColumn("Event Time", ".eventTime", ".firstTimestamp"),
|
|
createStringColumn("Type", ".type"),
|
|
createStringColumn("Reason", ".reason"),
|
|
createStringColumn("Object", ".involvedObject.kind"),
|
|
createStringColumn("Name", ".involvedObject.name"),
|
|
createStringColumn("Message", ".message"),
|
|
}),
|
|
|
|
// Factory status conditions
|
|
createCustomColumnsOverride("factory-status-conditions", []any{
|
|
createStringColumn("Type", ".type"),
|
|
createBoolColumn("Status", ".status"),
|
|
createTimestampColumn("Updated", ".lastTransitionTime"),
|
|
createStringColumn("Reason", ".reason"),
|
|
createStringColumn("Message", ".message"),
|
|
}),
|
|
|
|
// Container status init containers list
|
|
createCustomColumnsOverride("container-status-init-containers-list", []any{
|
|
createStringColumn("Name", ".name"),
|
|
createStringColumn("Image", ".imageID"),
|
|
createBoolColumn("Started", ".started"),
|
|
createBoolColumn("Ready", ".ready"),
|
|
createStringColumn("RestartCount", ".restartCount"),
|
|
createStringColumn("WaitingReason", ".state.waiting.reason"),
|
|
createStringColumn("TerminatedReason", ".state.terminated.reason"),
|
|
}),
|
|
|
|
// Container status containers list
|
|
createCustomColumnsOverride("container-status-containers-list", []any{
|
|
createStringColumn("Name", ".name"),
|
|
createStringColumn("Image", ".imageID"),
|
|
createBoolColumn("Started", ".started"),
|
|
createBoolColumn("Ready", ".ready"),
|
|
createStringColumn("RestartCount", ".restartCount"),
|
|
createStringColumn("WaitingReason", ".state.waiting.reason"),
|
|
createStringColumn("TerminatedReason", ".state.terminated.reason"),
|
|
}),
|
|
|
|
// Container spec init containers list
|
|
createCustomColumnsOverride("container-spec-init-containers-list", []any{
|
|
createStringColumn("Name", ".name"),
|
|
createStringColumn("Image", ".image"),
|
|
createArrayColumn("Resources requests", ".resources.requests"),
|
|
createArrayColumn("Resources limits", ".resources.limits"),
|
|
}),
|
|
|
|
// Container spec containers list
|
|
createCustomColumnsOverride("container-spec-containers-list", []any{
|
|
createStringColumn("Name", ".name"),
|
|
createStringColumn("Image", ".image"),
|
|
createArrayColumn("Resources requests", ".resources.requests"),
|
|
createArrayColumn("Resources limits", ".resources.limits"),
|
|
createArrayColumn("Ports", ".ports[*].containerPort"),
|
|
}),
|
|
|
|
// Factory details networking k8s io v1 ingresses
|
|
createCustomColumnsOverride("factory-details-networking.k8s.io.v1.ingresses", []any{
|
|
createCustomColumnWithJsonPath("Name", ".metadata.name", "Ingress", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/kube-ingress-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
|
createStringColumn("Hosts", ".spec.rules[*].host"),
|
|
createStringColumn("Address", ".status.loadBalancer.ingress[0].ip"),
|
|
createStringColumn("Port", ".spec.defaultBackend.service.port.number"),
|
|
createTimestampColumn("Created", ".metadata.creationTimestamp"),
|
|
}),
|
|
|
|
// Stock namespace networking k8s io v1 ingresses
|
|
createCustomColumnsOverride("stock-namespace-/networking.k8s.io/v1/ingresses", []any{
|
|
createCustomColumnWithJsonPath("Name", ".metadata.name", "Ingress", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/kube-ingress-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
|
createStringColumn("Hosts", ".spec.rules[*].host"),
|
|
createStringColumn("Address", ".status.loadBalancer.ingress[0].ip"),
|
|
createStringColumn("Port", ".spec.defaultBackend.service.port.number"),
|
|
createTimestampColumn("Created", ".metadata.creationTimestamp"),
|
|
}),
|
|
|
|
// Stock cluster v1 configmaps
|
|
createCustomColumnsOverride("stock-cluster-/v1/configmaps", []any{
|
|
createCustomColumnWithJsonPath("Name", ".metadata.name", "ConfigMap", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/configmap-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
|
createCustomColumnWithJsonPath("Namespace", ".metadata.namespace", "Namespace", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/marketplace"),
|
|
createTimestampColumn("Created", ".metadata.creationTimestamp"),
|
|
}),
|
|
|
|
// Stock namespace v1 configmaps
|
|
createCustomColumnsOverride("stock-namespace-/v1/configmaps", []any{
|
|
createCustomColumnWithJsonPath("Name", ".metadata.name", "ConfigMap", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/configmap-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
|
createTimestampColumn("Created", ".metadata.creationTimestamp"),
|
|
}),
|
|
|
|
// Cluster v1 configmaps
|
|
createCustomColumnsOverride("cluster-/v1/configmaps", []any{
|
|
createCustomColumnWithJsonPath("Name", ".metadata.name", "ConfigMap", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/configmap-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
|
createCustomColumnWithJsonPath("Namespace", ".metadata.namespace", "Namespace", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/marketplace"),
|
|
createTimestampColumn("Created", ".metadata.creationTimestamp"),
|
|
}),
|
|
|
|
// Stock cluster v1 nodes
|
|
createCustomColumnsOverride("stock-cluster-/v1/nodes", []any{
|
|
createCustomColumnWithJsonPath("Name", ".metadata.name", "Node", "", "/openapi-ui/{2}/factory/node-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
|
createSimpleStatusColumn("Status", "node-status"),
|
|
}),
|
|
|
|
// Factory node details v1 pods
|
|
createCustomColumnsOverride("factory-node-details-v1.pods", []any{
|
|
createCustomColumnWithJsonPath("Name", ".metadata.name", "Pod", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/pod-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
|
createCustomColumnWithJsonPath("Namespace", ".metadata.namespace", "Namespace", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/marketplace"),
|
|
createStringColumn("Restart Policy", ".spec.restartPolicy"),
|
|
createStringColumn("Pod IP", ".status.podIP"),
|
|
createStringColumn("QOS", ".status.qosClass"),
|
|
createSimpleStatusColumn("Status", "pod-status"),
|
|
}),
|
|
|
|
// Factory v1 pods
|
|
createCustomColumnsOverride("factory-v1.pods", []any{
|
|
createCustomColumnWithJsonPath("Name", ".metadata.name", "Pod", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/pod-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
|
createCustomColumnWithoutJsonPath("Node", "Node", "", "/openapi-ui/{2}/factory/node-details/{reqsJsonPath[0]['.spec.nodeName']['-']}"),
|
|
createStringColumn("Restart Policy", ".spec.restartPolicy"),
|
|
createStringColumn("Pod IP", ".status.podIP"),
|
|
createStringColumn("QOS", ".status.qosClass"),
|
|
createSimpleStatusColumn("Status", "pod-status"),
|
|
}),
|
|
|
|
// Stock cluster v1 pods
|
|
createCustomColumnsOverride("stock-cluster-/v1/pods", []any{
|
|
createCustomColumnWithJsonPath("Name", ".metadata.name", "Pod", "#009596", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/pod-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
|
createCustomColumnWithJsonPath("Namespace", ".metadata.namespace", "Namespace", "#a25792ff", "/openapi-ui/{2}/factory/tenantnamespace/{reqsJsonPath[0]['.metadata.namespace']['-']}"),
|
|
createCustomColumnWithJsonPath("Node", ".spec.nodeName", "Node", "#8476d1", "/openapi-ui/{2}/factory/node-details/{reqsJsonPath[0]['.spec.nodeName']['-']}"),
|
|
createStringColumn("Restart Policy", ".spec.restartPolicy"),
|
|
createStringColumn("Pod IP", ".status.podIP"),
|
|
createStringColumn("QOS", ".status.qosClass"),
|
|
createSimpleStatusColumn("Status", "pod-status"),
|
|
}),
|
|
|
|
// Stock namespace v1 pods
|
|
createCustomColumnsOverride("stock-namespace-/v1/pods", []any{
|
|
createCustomColumnWithJsonPath("Name", ".metadata.name", "Pod", "#009596", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/pod-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
|
createCustomColumnWithoutJsonPath("Node", "Node", "#8476d1", "/openapi-ui/{2}/factory/node-details/{reqsJsonPath[0]['.spec.nodeName']['-']}"),
|
|
createStringColumn("Restart Policy", ".spec.restartPolicy"),
|
|
createStringColumn("Pod IP", ".status.podIP"),
|
|
createStringColumn("QOS", ".status.qosClass"),
|
|
createSimpleStatusColumn("Status", "pod-status"),
|
|
}),
|
|
|
|
// Stock cluster v1 secrets
|
|
createCustomColumnsOverride("stock-cluster-/v1/secrets", []any{
|
|
createCustomColumnWithJsonPath("Name", ".metadata.name", "Secret", "#c46100", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/kube-secret-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
|
createCustomColumnWithJsonPath("Namespace", ".metadata.namespace", "Namespace", "#a25792ff", "/openapi-ui/{2}/factory/tenantnamespace/{reqsJsonPath[0]['.metadata.namespace']['-']}"),
|
|
createStringColumn("Type", ".type"),
|
|
createTimestampColumn("Created", ".metadata.creationTimestamp"),
|
|
}),
|
|
|
|
// Stock namespace v1 secrets
|
|
createCustomColumnsOverride("stock-namespace-/v1/secrets", []any{
|
|
createCustomColumnWithJsonPath("Name", ".metadata.name", "Secret", "#c46100", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/kube-secret-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
|
createStringColumn("Type", ".type"),
|
|
createTimestampColumn("Created", ".metadata.creationTimestamp"),
|
|
}),
|
|
|
|
// Factory details v1alpha1 cozystack io workloads
|
|
createCustomColumnsOverride("factory-details-v1alpha1.cozystack.io.workloads", []any{
|
|
createStringColumn("Name", ".metadata.name"),
|
|
createStringColumn("Kind", ".status.kind"),
|
|
createStringColumn("Type", ".status.type"),
|
|
createStringColumn("CPU", ".status.resources.cpu"),
|
|
createStringColumn("Memory", ".status.resources.memory"),
|
|
createStringColumn("Operational", ".status.operational"),
|
|
}),
|
|
|
|
// Stock cluster core cozystack io v1alpha1 tenantnamespaces
|
|
createCustomColumnsOverride("stock-cluster-/core.cozystack.io/v1alpha1/tenantnamespaces", []any{
|
|
createCustomColumnWithJsonPath("Name", ".metadata.name", "TenantNamespace", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.name']['-']}/factory/marketplace"),
|
|
createTimestampColumn("Created", ".metadata.creationTimestamp"),
|
|
}),
|
|
|
|
// Stock namespace backups cozystack io v1alpha1 plans
|
|
createCustomColumnsOverride("stock-namespace-/backups.cozystack.io/v1alpha1/plans", []any{
|
|
createCustomColumnWithJsonPath("Name", ".metadata.name", "Plan", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/plan-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
|
createApplicationRefColumn("Application"),
|
|
createTimestampColumn("Created", ".metadata.creationTimestamp"),
|
|
}),
|
|
|
|
// Stock namespace backups cozystack io v1alpha1 backupjobs
|
|
createCustomColumnsOverride("stock-namespace-/backups.cozystack.io/v1alpha1/backupjobs", []any{
|
|
createCustomColumnWithJsonPath("Name", ".metadata.name", "BackupJob", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/backupjob-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
|
createStringColumn("Phase", ".status.phase"),
|
|
createApplicationRefColumn("Application"),
|
|
createTimestampColumn("Created", ".metadata.creationTimestamp"),
|
|
}),
|
|
|
|
// Stock namespace backups cozystack io v1alpha1 backups
|
|
createCustomColumnsOverride("stock-namespace-/backups.cozystack.io/v1alpha1/backups", []any{
|
|
createCustomColumnWithJsonPath("Name", ".metadata.name", "Backup", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/backup-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
|
createStringColumn("Phase", ".status.phase"),
|
|
createApplicationRefColumn("Application"),
|
|
createTimestampColumn("Taken At", ".spec.takenAt"),
|
|
createTimestampColumn("Created", ".metadata.creationTimestamp"),
|
|
}),
|
|
|
|
// Stock namespace backups cozystack io v1alpha1 restorejobs
|
|
createCustomColumnsOverride("stock-namespace-/backups.cozystack.io/v1alpha1/restorejobs", []any{
|
|
createCustomColumnWithJsonPath("Name", ".metadata.name", "RestoreJob", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/restorejob-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
|
createStringColumn("Phase", ".status.phase"),
|
|
createStringColumn("Backup", ".spec.backupRef.name"),
|
|
createTimestampColumn("Created", ".metadata.creationTimestamp"),
|
|
}),
|
|
}
|
|
}
|
|
|
|
// CreateAllCustomFormsOverrides creates all custom forms override resources using helper functions
|
|
func CreateAllCustomFormsOverrides() []*dashboardv1alpha1.CustomFormsOverride {
|
|
return []*dashboardv1alpha1.CustomFormsOverride{
|
|
// Default networking k8s io v1 ingresses
|
|
createCustomFormsOverride("default-/networking.k8s.io/v1/ingresses", map[string]any{
|
|
"formItems": []any{
|
|
createFormItem("metadata.name", "Name", "text"),
|
|
createFormItem("metadata.namespace", "Namespace", "text"),
|
|
createFormItem("spec.rules", "Rules", "array"),
|
|
},
|
|
}),
|
|
|
|
// Default storage k8s io v1 storageclasses
|
|
createCustomFormsOverride("default-/storage.k8s.io/v1/storageclasses", map[string]any{
|
|
"formItems": []any{
|
|
createFormItem("metadata.name", "Name", "text"),
|
|
createFormItem("provisioner", "Provisioner", "text"),
|
|
createFormItem("reclaimPolicy", "Reclaim Policy", "select"),
|
|
},
|
|
}),
|
|
|
|
// Default v1 configmaps
|
|
createCustomFormsOverride("default-/v1/configmaps", map[string]any{
|
|
"formItems": []any{
|
|
createFormItem("metadata.name", "Name", "text"),
|
|
createFormItem("metadata.namespace", "Namespace", "text"),
|
|
createFormItem("data", "Data", "object"),
|
|
},
|
|
}),
|
|
|
|
// Default v1 namespaces
|
|
createCustomFormsOverride("default-/v1/namespaces", map[string]any{
|
|
"formItems": []any{
|
|
createFormItem("metadata.name", "Name", "text"),
|
|
createFormItem("metadata.labels", "Labels", "object"),
|
|
},
|
|
}),
|
|
|
|
// Default v1 nodes
|
|
createCustomFormsOverride("default-/v1/nodes", map[string]any{
|
|
"formItems": []any{
|
|
createFormItem("metadata.name", "Name", "text"),
|
|
createFormItem("spec.podCIDR", "Pod CIDR", "text"),
|
|
},
|
|
}),
|
|
|
|
// Default v1 persistentvolumeclaims
|
|
createCustomFormsOverride("default-/v1/persistentvolumeclaims", map[string]any{
|
|
"formItems": []any{
|
|
createFormItem("metadata.name", "Name", "text"),
|
|
createFormItem("metadata.namespace", "Namespace", "text"),
|
|
createFormItem("spec.accessModes", "Access Modes", "array"),
|
|
},
|
|
}),
|
|
|
|
// Default v1 persistentvolumes
|
|
createCustomFormsOverride("default-/v1/persistentvolumes", map[string]any{
|
|
"formItems": []any{
|
|
createFormItem("metadata.name", "Name", "text"),
|
|
createFormItem("spec.capacity", "Capacity", "object"),
|
|
},
|
|
}),
|
|
|
|
// Default v1 pods
|
|
createCustomFormsOverride("default-/v1/pods", map[string]any{
|
|
"formItems": []any{
|
|
createFormItem("metadata.name", "Name", "text"),
|
|
createFormItem("metadata.namespace", "Namespace", "text"),
|
|
createFormItem("spec.containers", "Containers", "array"),
|
|
},
|
|
}),
|
|
|
|
// Default v1 secrets
|
|
createCustomFormsOverride("default-/v1/secrets", map[string]any{
|
|
"formItems": []any{
|
|
createFormItem("metadata.name", "Name", "text"),
|
|
createFormItem("metadata.namespace", "Namespace", "text"),
|
|
createFormItem("type", "Type", "text"),
|
|
},
|
|
}),
|
|
|
|
// Default v1 services
|
|
createCustomFormsOverride("default-/v1/services", map[string]any{
|
|
"formItems": []any{
|
|
createFormItem("metadata.name", "Name", "text"),
|
|
createFormItem("metadata.namespace", "Namespace", "text"),
|
|
createFormItem("spec.ports", "Ports", "array"),
|
|
},
|
|
}),
|
|
|
|
// Plans form override - backups.cozystack.io/v1alpha1
|
|
createCustomFormsOverride("default-/backups.cozystack.io/v1alpha1/plans", map[string]any{
|
|
"formItems": []any{
|
|
createFormItem("metadata.name", "Name", "text"),
|
|
createFormItem("metadata.namespace", "Namespace", "text"),
|
|
createFormItem("spec.applicationRef.kind", "Application Kind", "text"),
|
|
createFormItem("spec.applicationRef.name", "Application Name", "text"),
|
|
createFormItem("spec.schedule.type", "Schedule Type", "text"),
|
|
createFormItem("spec.schedule.cron", "Schedule Cron", "text"),
|
|
},
|
|
"schema": createSchema(map[string]any{
|
|
"backupClassName": listInputScemaItemBackupClass(),
|
|
}),
|
|
}),
|
|
|
|
// BackupJobs form override - backups.cozystack.io/v1alpha1
|
|
createCustomFormsOverride("default-/backups.cozystack.io/v1alpha1/backupjobs", map[string]any{
|
|
"formItems": []any{
|
|
createFormItem("metadata.name", "Name", "text"),
|
|
createFormItem("metadata.namespace", "Namespace", "text"),
|
|
createFormItem("spec.planRef.name", "Plan Name (optional)", "text"),
|
|
createFormItem("spec.applicationRef.apiGroup", "Application API Group", "text"),
|
|
createFormItem("spec.applicationRef.kind", "Application Kind", "text"),
|
|
createFormItem("spec.applicationRef.name", "Application Name", "text"),
|
|
},
|
|
"schema": createSchema(map[string]any{
|
|
"backupClassName": listInputScemaItemBackupClass(),
|
|
}),
|
|
}),
|
|
|
|
// RestoreJobs form override - backups.cozystack.io/v1alpha1
|
|
createCustomFormsOverride("default-/backups.cozystack.io/v1alpha1/restorejobs", map[string]any{
|
|
"formItems": []any{
|
|
createFormItem("metadata.name", "Name", "text"),
|
|
createFormItem("metadata.namespace", "Namespace", "text"),
|
|
createFormItem("spec.backupRef.name", "Backup", "text"),
|
|
// Target application: leave empty to restore into the same application
|
|
// as referenced by the selected Backup. Fill all three to restore into
|
|
// a different application (e.g. rename, or restore into a new target).
|
|
createFormItem("spec.targetApplicationRef.apiGroup", "Target Application API Group (optional, used to restore into a different application)", "text"),
|
|
createFormItem("spec.targetApplicationRef.kind", "Target Application Kind (optional, used to restore into a different application)", "text"),
|
|
createFormItem("spec.targetApplicationRef.name", "Target Application Name (optional, used to restore into a different application)", "text"),
|
|
// Driver-specific options (key-value editor). Refer to the backup driver
|
|
// documentation for supported keys.
|
|
createFormItem("spec.options", "Options (driver-specific key/value pairs)", "object"),
|
|
},
|
|
"schema": createSchema(map[string]any{
|
|
"backupRef": map[string]any{
|
|
"properties": map[string]any{
|
|
"name": listInputSchemaItemBackup(),
|
|
},
|
|
},
|
|
}),
|
|
}),
|
|
}
|
|
}
|
|
|
|
// CreateAllFactories creates all factory resources using helper functions
|
|
func CreateAllFactories() []*dashboardv1alpha1.Factory {
|
|
// Marketplace factory
|
|
marketplaceSpec := map[string]any{
|
|
"key": "marketplace",
|
|
"sidebarTags": []any{
|
|
"marketplace-sidebar",
|
|
},
|
|
"urlsToFetch": []any{},
|
|
"withScrollableMainContentCard": true,
|
|
"data": []any{
|
|
contentCardWithTitle(31, "Marketplace", map[string]any{
|
|
"flexGrow": 1,
|
|
}, []any{
|
|
map[string]any{
|
|
"data": map[string]any{
|
|
"baseApiVersion": "v1alpha1",
|
|
"baseprefix": "openapi-ui",
|
|
"cluster": "{2}",
|
|
"id": 311,
|
|
"marketplaceKind": "MarketplacePanel",
|
|
"marketplacePlural": "marketplacepanels",
|
|
"namespace": "{3}",
|
|
"baseApiGroup": "dashboard.cozystack.io",
|
|
},
|
|
"type": "MarketplaceCard",
|
|
},
|
|
}),
|
|
},
|
|
}
|
|
|
|
// Namespace details factory using unified approach
|
|
namespaceConfig := UnifiedResourceConfig{
|
|
Name: "namespace-details",
|
|
ResourceType: "factory",
|
|
Kind: "Namespace",
|
|
Plural: "namespaces",
|
|
Title: "namespace",
|
|
}
|
|
namespaceSpec := createUnifiedFactory(namespaceConfig, nil, []any{"/api/clusters/{2}/k8s/api/v1/namespaces/{5}"})
|
|
|
|
// Node details factory
|
|
nodeHeader := createNodeHeader()
|
|
// Create node spec with tabs containing items
|
|
nodeTabs := []any{
|
|
map[string]any{
|
|
"key": "details",
|
|
"label": "Details",
|
|
"children": []any{
|
|
map[string]any{
|
|
"type": "ContentCard",
|
|
"data": map[string]any{
|
|
"id": "details-card",
|
|
"style": map[string]any{
|
|
"marginBottom": "24px",
|
|
},
|
|
},
|
|
"children": []any{
|
|
map[string]any{
|
|
"type": "antdText",
|
|
"data": map[string]any{
|
|
"id": "details-title",
|
|
"text": "Node details",
|
|
"strong": true,
|
|
"style": map[string]any{
|
|
"fontSize": float64(20),
|
|
"marginBottom": "12px",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
nodeSpec := map[string]any{
|
|
"key": "node-details",
|
|
"sidebarTags": []any{"node-sidebar"},
|
|
"withScrollableMainContentCard": true,
|
|
"urlsToFetch": []any{"/api/clusters/{2}/k8s/api/v1/nodes/{5}"},
|
|
"data": []any{
|
|
nodeHeader,
|
|
map[string]any{
|
|
"type": "antdTabs",
|
|
"data": map[string]any{
|
|
"id": "tabs-root",
|
|
"defaultActiveKey": "details",
|
|
"items": nodeTabs,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
// Pod details factory
|
|
podHeader := createPodHeader()
|
|
// Create pod spec with empty tabs (items: nil)
|
|
podSpec := map[string]any{
|
|
"key": "pod-details",
|
|
"sidebarTags": []any{"pods-sidebar"},
|
|
"withScrollableMainContentCard": true,
|
|
"urlsToFetch": []any{"/api/clusters/{2}/k8s/api/v1/namespaces/{3}/pods/{6}"},
|
|
"data": []any{
|
|
podHeader,
|
|
map[string]any{
|
|
"type": "antdTabs",
|
|
"data": map[string]any{
|
|
"id": "tabs-root",
|
|
"defaultActiveKey": "details",
|
|
"items": nil,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
// Secret details factory
|
|
secretHeader := map[string]any{
|
|
"type": "antdFlex",
|
|
"data": map[string]any{
|
|
"id": "header-row",
|
|
"align": "center",
|
|
"gap": float64(6),
|
|
"style": map[string]any{
|
|
"marginBottom": "24px",
|
|
},
|
|
},
|
|
"children": []any{
|
|
map[string]any{
|
|
"type": "antdText",
|
|
"data": map[string]any{
|
|
"id": "badge-secret",
|
|
"text": "S",
|
|
"title": "secret",
|
|
"style": map[string]any{
|
|
"backgroundColor": "#c46100",
|
|
"borderRadius": "20px",
|
|
"color": "#fff",
|
|
"display": "inline-block",
|
|
"fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif",
|
|
"fontSize": "20px",
|
|
"fontWeight": 400,
|
|
"lineHeight": "24px",
|
|
"minWidth": 24,
|
|
"padding": "0 9px",
|
|
"textAlign": "center",
|
|
"whiteSpace": "nowrap",
|
|
},
|
|
},
|
|
},
|
|
map[string]any{
|
|
"type": "parsedText",
|
|
"data": map[string]any{
|
|
"id": "header-secret-name",
|
|
"text": "{reqsJsonPath[0]['.metadata.name']['-']}",
|
|
"style": map[string]any{
|
|
"fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif",
|
|
"fontSize": "20px",
|
|
"lineHeight": "24px",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
secretTabs := []any{
|
|
map[string]any{
|
|
"key": "details",
|
|
"label": "Details",
|
|
"children": []any{
|
|
contentCard("details-card", map[string]any{
|
|
"marginBottom": "24px",
|
|
}, []any{
|
|
antdText("details-title", true, "Secret details", map[string]any{
|
|
"fontSize": 20,
|
|
"marginBottom": "12px",
|
|
}),
|
|
spacer("details-spacer", 16),
|
|
antdRow("details-grid", []any{48, 12}, []any{
|
|
antdCol("col-left", 12, []any{
|
|
antdFlexVertical("col-left-stack", 24, []any{
|
|
antdFlexVertical("meta-name-block", 4, []any{
|
|
antdText("meta-name-label", true, "Name", nil),
|
|
parsedText("meta-name-value", "{reqsJsonPath[0]['.metadata.name']['-']}", nil),
|
|
}),
|
|
antdFlexVertical("meta-namespace-block", 8, []any{
|
|
antdText("meta-name-label", true, "Namespace", nil),
|
|
antdFlex("header-row", 6, []any{
|
|
map[string]any{
|
|
"type": "antdText",
|
|
"data": map[string]any{
|
|
"id": "header-badge",
|
|
"text": "NS",
|
|
"title": "namespace",
|
|
"style": map[string]any{
|
|
"backgroundColor": "#a25792ff",
|
|
"borderRadius": "20px",
|
|
"color": "#fff",
|
|
"display": "inline-block",
|
|
"fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif",
|
|
"fontSize": "15px",
|
|
"fontWeight": 400,
|
|
"lineHeight": "24px",
|
|
"minWidth": 24,
|
|
"padding": "0 9px",
|
|
"textAlign": "center",
|
|
"whiteSpace": "nowrap",
|
|
},
|
|
},
|
|
},
|
|
map[string]any{
|
|
"type": "antdLink",
|
|
"data": map[string]any{
|
|
"id": "namespace-link",
|
|
"text": "{reqsJsonPath[0]['.metadata.namespace']['-']}",
|
|
"href": "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/marketplace",
|
|
},
|
|
},
|
|
}),
|
|
}),
|
|
antdFlexVertical("meta-labels-block", 8, []any{
|
|
antdText("labels-title", true, "Labels", map[string]any{
|
|
"fontSize": 14,
|
|
}),
|
|
map[string]any{
|
|
"type": "Labels",
|
|
"data": map[string]any{
|
|
"id": "labels-editor",
|
|
"endpoint": "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/secrets/{6}",
|
|
"jsonPathToLabels": ".metadata.labels",
|
|
"pathToValue": "/metadata/labels",
|
|
"reqIndex": 0,
|
|
"modalTitle": "Edit labels",
|
|
"modalDescriptionText": "",
|
|
"inputLabel": "",
|
|
"notificationSuccessMessage": "Updated successfully",
|
|
"notificationSuccessMessageDescription": "Labels have been updated",
|
|
"editModalWidth": 650,
|
|
"maxEditTagTextLength": 35,
|
|
"paddingContainerEnd": "24px",
|
|
"containerStyle": map[string]any{
|
|
"marginTop": "-30px",
|
|
},
|
|
"selectProps": map[string]any{
|
|
"maxTagTextLength": 35,
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
antdFlexVertical("ds-annotations", 4, []any{
|
|
antdText("annotations", true, "Annotations", nil),
|
|
map[string]any{
|
|
"type": "Annotations",
|
|
"data": map[string]any{
|
|
"id": "annotations",
|
|
"endpoint": "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/secrets/{6}",
|
|
"jsonPathToObj": ".metadata.annotations",
|
|
"pathToValue": "/metadata/annotations",
|
|
"reqIndex": 0,
|
|
"modalTitle": "Edit annotations",
|
|
"modalDescriptionText": "",
|
|
"inputLabel": "",
|
|
"notificationSuccessMessage": "Updated successfully",
|
|
"notificationSuccessMessageDescription": "Annotations have been updated",
|
|
"editModalWidth": "800px",
|
|
"errorText": "0 Annotations",
|
|
"text": "~counter~ Annotations",
|
|
"cols": []any{11, 11, 2},
|
|
},
|
|
},
|
|
}),
|
|
antdFlexVertical("meta-created-block", 4, []any{
|
|
antdText("time-label", true, "Created", nil),
|
|
antdFlex("time-block", 6, []any{
|
|
map[string]any{
|
|
"type": "antdText",
|
|
"data": map[string]any{
|
|
"id": "time-icon",
|
|
"text": "🌐",
|
|
},
|
|
},
|
|
map[string]any{
|
|
"type": "parsedText",
|
|
"data": map[string]any{
|
|
"formatter": "timestamp",
|
|
"id": "time-value",
|
|
"text": "{reqsJsonPath[0]['.metadata.creationTimestamp']['-']}",
|
|
},
|
|
},
|
|
}),
|
|
}),
|
|
}),
|
|
}),
|
|
antdCol("col-right", 12, []any{
|
|
antdFlexVertical("col-right-stack", 24, []any{
|
|
antdFlexVertical("secret-type-block", 4, []any{
|
|
antdText("secret-type-label", true, "Type", nil),
|
|
parsedText("secret-type-value", "{reqsJsonPath[0]['.type']['-']}", nil),
|
|
}),
|
|
antdFlexVertical("secret-sa-block", 4, []any{
|
|
map[string]any{
|
|
"type": "parsedText",
|
|
"data": map[string]any{
|
|
"id": "serviceaccount-title",
|
|
"text": "ServiceAccount",
|
|
"strong": true,
|
|
"style": map[string]any{
|
|
"fontWeight": "bold",
|
|
},
|
|
},
|
|
},
|
|
map[string]any{
|
|
"type": "antdLink",
|
|
"data": map[string]any{
|
|
"id": "serviceaccount-link",
|
|
"text": "{reqsJsonPath[0]['.metadata.annotations[\"kubernetes.io/service-account.name\"]']['-']}",
|
|
"href": "/openapi-ui/{2}/{3}/factory/serviceaccount-details/{reqsJsonPath[0]['.metadata.annotations[\"kubernetes.io/service-account.name\"]']['-']}",
|
|
},
|
|
},
|
|
}),
|
|
}),
|
|
}),
|
|
}),
|
|
}),
|
|
},
|
|
},
|
|
map[string]any{
|
|
"key": "yaml",
|
|
"label": "YAML",
|
|
"children": []any{
|
|
map[string]any{
|
|
"type": "YamlEditorSingleton",
|
|
"data": map[string]any{
|
|
"id": "yaml-editor",
|
|
"cluster": "{2}",
|
|
"isNameSpaced": true,
|
|
"prefillValuesRequestIndex": 0,
|
|
"substractHeight": float64(400),
|
|
"type": "builtin",
|
|
"plural": "secrets",
|
|
"readOnly": true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
secretSpec := createFactorySpec("kube-secret-details", []any{"secret-sidebar"}, []any{"/api/clusters/{2}/k8s/api/v1/namespaces/{3}/secrets/{6}"}, secretHeader, secretTabs)
|
|
|
|
// Service details factory
|
|
serviceHeader := map[string]any{
|
|
"type": "antdFlex",
|
|
"data": map[string]any{
|
|
"id": "header-row",
|
|
"align": "center",
|
|
"gap": float64(6),
|
|
"style": map[string]any{
|
|
"marginBottom": "24px",
|
|
},
|
|
},
|
|
"children": []any{
|
|
map[string]any{
|
|
"type": "antdText",
|
|
"data": map[string]any{
|
|
"id": "badge-service",
|
|
"text": "S",
|
|
"title": "services",
|
|
"style": map[string]any{
|
|
"backgroundColor": "#6ca100",
|
|
"borderRadius": "20px",
|
|
"color": "#fff",
|
|
"display": "inline-block",
|
|
"fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif",
|
|
"fontSize": "20px",
|
|
"fontWeight": 400,
|
|
"lineHeight": "24px",
|
|
"minWidth": 24,
|
|
"padding": "0 9px",
|
|
"textAlign": "center",
|
|
"whiteSpace": "nowrap",
|
|
},
|
|
},
|
|
},
|
|
map[string]any{
|
|
"type": "parsedText",
|
|
"data": map[string]any{
|
|
"id": "service-name",
|
|
"text": "{reqsJsonPath[0]['.metadata.name']['-']}",
|
|
"style": map[string]any{
|
|
"fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif",
|
|
"fontSize": "20px",
|
|
"lineHeight": "24px",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
serviceTabs := []any{
|
|
map[string]any{
|
|
"key": "details",
|
|
"label": "Details",
|
|
"children": []any{
|
|
contentCard("details-card", map[string]any{
|
|
"marginBottom": "24px",
|
|
}, []any{
|
|
antdText("details-title", true, "Service details", map[string]any{
|
|
"fontSize": 20,
|
|
"marginBottom": "12px",
|
|
}),
|
|
spacer("details-spacer", 16),
|
|
antdRow("details-grid", []any{48, 12}, []any{
|
|
antdCol("col-left", 12, []any{
|
|
antdFlexVertical("col-left-stack", 24, []any{
|
|
antdFlexVertical("meta-name-block", 4, []any{
|
|
antdText("meta-name-label", true, "Name", nil),
|
|
parsedText("meta-name-value", "{reqsJsonPath[0]['.metadata.name']['-']}", nil),
|
|
}),
|
|
antdFlexVertical("meta-namespace-block", 8, []any{
|
|
antdText("meta-name-label", true, "Namespace", nil),
|
|
antdFlex("header-row", 6, []any{
|
|
map[string]any{
|
|
"type": "antdText",
|
|
"data": map[string]any{
|
|
"id": "header-badge",
|
|
"text": "NS",
|
|
"title": "namespace",
|
|
"style": map[string]any{
|
|
"backgroundColor": "#a25792ff",
|
|
"borderRadius": "20px",
|
|
"color": "#fff",
|
|
"display": "inline-block",
|
|
"fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif",
|
|
"fontSize": "15px",
|
|
"fontWeight": 400,
|
|
"lineHeight": "24px",
|
|
"minWidth": 24,
|
|
"padding": "0 9px",
|
|
"textAlign": "center",
|
|
"whiteSpace": "nowrap",
|
|
},
|
|
},
|
|
},
|
|
map[string]any{
|
|
"type": "antdLink",
|
|
"data": map[string]any{
|
|
"id": "namespace-link",
|
|
"text": "{reqsJsonPath[0]['.metadata.namespace']['-']}",
|
|
"href": "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/marketplace",
|
|
},
|
|
},
|
|
}),
|
|
}),
|
|
antdFlexVertical("meta-labels-block", 8, []any{
|
|
antdText("labels-title", true, "Labels", map[string]any{
|
|
"fontSize": 14,
|
|
}),
|
|
map[string]any{
|
|
"type": "Labels",
|
|
"data": map[string]any{
|
|
"id": "labels-editor",
|
|
"endpoint": "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/services/{6}",
|
|
"jsonPathToLabels": ".metadata.labels",
|
|
"pathToValue": "/metadata/labels",
|
|
"reqIndex": 0,
|
|
"modalTitle": "Edit labels",
|
|
"modalDescriptionText": "",
|
|
"inputLabel": "",
|
|
"notificationSuccessMessage": "Updated successfully",
|
|
"notificationSuccessMessageDescription": "Labels have been updated",
|
|
"editModalWidth": 650,
|
|
"maxEditTagTextLength": 35,
|
|
"paddingContainerEnd": "24px",
|
|
"containerStyle": map[string]any{
|
|
"marginTop": "-30px",
|
|
},
|
|
"selectProps": map[string]any{
|
|
"maxTagTextLength": 35,
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
antdFlexVertical("meta-pod-selector-block", 4, []any{
|
|
antdText("pod-selector", true, "Pod selector", map[string]any{
|
|
"fontSize": 14,
|
|
}),
|
|
map[string]any{
|
|
"type": "LabelsToSearchParams",
|
|
"data": map[string]any{
|
|
"id": "pod-to-search-params",
|
|
"jsonPathToLabels": ".spec.selector",
|
|
"linkPrefix": "/openapi-ui/{2}/search",
|
|
"reqIndex": 0,
|
|
"errorText": "-",
|
|
},
|
|
},
|
|
}),
|
|
antdFlexVertical("ds-annotations", 4, []any{
|
|
antdText("annotations", true, "Annotations", nil),
|
|
map[string]any{
|
|
"type": "Annotations",
|
|
"data": map[string]any{
|
|
"id": "annotations",
|
|
"endpoint": "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/services/{6}",
|
|
"jsonPathToObj": ".metadata.annotations",
|
|
"pathToValue": "/metadata/annotations",
|
|
"reqIndex": 0,
|
|
"modalTitle": "Edit annotations",
|
|
"modalDescriptionText": "",
|
|
"inputLabel": "",
|
|
"notificationSuccessMessage": "Updated successfully",
|
|
"notificationSuccessMessageDescription": "Annotations have been updated",
|
|
"editModalWidth": "800px",
|
|
"errorText": "0 Annotations",
|
|
"text": "~counter~ Annotations",
|
|
"cols": []any{11, 11, 2},
|
|
},
|
|
},
|
|
}),
|
|
antdFlexVertical("meta-session-affinity-block", 4, []any{
|
|
antdText("meta-session-affinity-label", true, "Session affinity", nil),
|
|
parsedText("meta-session-affinity-value", "{reqsJsonPath[0]['.spec.sessionAffinity']['Not configured']}", nil),
|
|
}),
|
|
antdFlexVertical("meta-created-block", 4, []any{
|
|
antdText("time-label", true, "Created", nil),
|
|
antdFlex("time-block", 6, []any{
|
|
map[string]any{
|
|
"type": "antdText",
|
|
"data": map[string]any{
|
|
"id": "time-icon",
|
|
"text": "🌐",
|
|
},
|
|
},
|
|
map[string]any{
|
|
"type": "parsedText",
|
|
"data": map[string]any{
|
|
"formatter": "timestamp",
|
|
"id": "time-value",
|
|
"text": "{reqsJsonPath[0]['.metadata.creationTimestamp']['-']}",
|
|
},
|
|
},
|
|
}),
|
|
}),
|
|
}),
|
|
}),
|
|
antdCol("col-right", 12, []any{
|
|
antdFlexVertical("col-right-stack", 24, []any{
|
|
antdText("routing-title", true, "Service routing", map[string]any{
|
|
"fontSize": 20,
|
|
"marginBottom": "12px",
|
|
}),
|
|
spacer("routing-spacer", 16),
|
|
antdFlexVertical("service-hostname-block", 4, []any{
|
|
antdText("service-hostname-label", true, "Hostname", nil),
|
|
parsedText("service-hostname-value", "{reqsJsonPath[0]['.metadata.name']['-']}.{reqsJsonPath[0]['.metadata.namespace']['-']}.svc.cluster.local", nil),
|
|
}),
|
|
antdFlexVertical("service-ip-block", 12, []any{
|
|
antdFlexVertical("clusterip-block", 4, []any{
|
|
antdText("clusterip-label", true, "ClusterIP address", nil),
|
|
parsedText("clusterip-value", "{reqsJsonPath[0]['.spec.clusterIP']['-']}", nil),
|
|
}),
|
|
antdFlexVertical("loadbalancerip-block", 4, []any{
|
|
antdText("loadbalancerip-label", true, "LoadBalancerIP address", nil),
|
|
parsedText("loadbalancerip-value", "{reqsJsonPath[0]['.status.loadBalancer.ingress[0].ip']['Not Configured']}", nil),
|
|
}),
|
|
}),
|
|
antdFlexVertical("service-port-mapping-block", 4, []any{
|
|
antdText("service-port-mapping-label", true, "Service port mapping", nil),
|
|
map[string]any{
|
|
"type": "EnrichedTable",
|
|
"data": map[string]any{
|
|
"id": "service-port-mapping-table",
|
|
"baseprefix": "/openapi-ui",
|
|
"cluster": "{2}",
|
|
"customizationId": "factory-kube-service-details-port-mapping",
|
|
"fetchUrl": "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/services/{6}",
|
|
"pathToItems": ".spec.ports",
|
|
"withoutControls": true,
|
|
},
|
|
},
|
|
}),
|
|
map[string]any{
|
|
"type": "VisibilityContainer",
|
|
"data": map[string]any{
|
|
"id": "service-pod-serving-vis",
|
|
"value": "{reqsJsonPath[0]['.spec.selector']['-']}",
|
|
"style": map[string]any{
|
|
"margin": 0,
|
|
"padding": 0,
|
|
},
|
|
},
|
|
"children": []any{
|
|
antdFlexVertical("service-pod-serving-block", 4, []any{
|
|
antdText("service-pod-serving-label", true, "Pod serving", nil),
|
|
map[string]any{
|
|
"type": "EnrichedTable",
|
|
"data": map[string]any{
|
|
"id": "service-pod-serving-table",
|
|
"baseprefix": "/openapi-ui",
|
|
"cluster": "{2}",
|
|
"customizationId": "factory-kube-service-details-endpointslice",
|
|
"fetchUrl": "/api/clusters/{2}/k8s/apis/discovery.k8s.io/v1/namespaces/{3}/endpointslices",
|
|
"labelSelector": map[string]any{
|
|
"kubernetes.io/service-name": "{reqsJsonPath[0]['.metadata.name']['-']}",
|
|
},
|
|
"pathToItems": ".items[*].endpoints",
|
|
"withoutControls": true,
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
}),
|
|
}),
|
|
}),
|
|
}),
|
|
},
|
|
},
|
|
map[string]any{
|
|
"key": "yaml",
|
|
"label": "YAML",
|
|
"children": []any{
|
|
map[string]any{
|
|
"type": "YamlEditorSingleton",
|
|
"data": map[string]any{
|
|
"id": "yaml-editor",
|
|
"cluster": "{2}",
|
|
"isNameSpaced": true,
|
|
"prefillValuesRequestIndex": 0,
|
|
"substractHeight": float64(400),
|
|
"type": "builtin",
|
|
"plural": "services",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
map[string]any{
|
|
"key": "pods",
|
|
"label": "Pods",
|
|
"children": []any{
|
|
map[string]any{
|
|
"type": "VisibilityContainer",
|
|
"data": map[string]any{
|
|
"id": "service-pod-serving-vis",
|
|
"value": "{reqsJsonPath[0]['.spec.selector']['-']}",
|
|
"style": map[string]any{
|
|
"margin": 0,
|
|
"padding": 0,
|
|
},
|
|
},
|
|
"children": []any{
|
|
map[string]any{
|
|
"type": "EnrichedTable",
|
|
"data": map[string]any{
|
|
"id": "pods-table",
|
|
"baseprefix": "/openapi-ui",
|
|
"cluster": "{2}",
|
|
"customizationId": "factory-node-details-/v1/pods",
|
|
"fetchUrl": "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/pods",
|
|
"labelSelectorFull": map[string]any{
|
|
"pathToLabels": ".spec.selector",
|
|
"reqIndex": 0,
|
|
},
|
|
"pathToItems": ".items",
|
|
"withoutControls": false,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
serviceSpec := createFactorySpec("kube-service-details", []any{"service-sidebar"}, []any{"/api/clusters/{2}/k8s/api/v1/namespaces/{3}/services/{6}"}, serviceHeader, serviceTabs)
|
|
|
|
// Ingress details factory
|
|
ingressHeader := map[string]any{
|
|
"type": "antdFlex",
|
|
"data": map[string]any{
|
|
"id": "header-row",
|
|
"align": "center",
|
|
"gap": 6,
|
|
"style": map[string]any{
|
|
"marginBottom": float64(24),
|
|
},
|
|
},
|
|
"children": []any{
|
|
map[string]any{
|
|
"type": "antdText",
|
|
"data": map[string]any{
|
|
"id": "badge-ingress",
|
|
"text": "I",
|
|
"title": "ingresses",
|
|
"style": map[string]any{
|
|
"backgroundColor": "#2e7dff",
|
|
"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": "ingress-name",
|
|
"text": "{reqsJsonPath[0]['.metadata.name']['-']}",
|
|
"style": map[string]any{
|
|
"fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif",
|
|
"fontSize": float64(20),
|
|
"lineHeight": "24px",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
ingressTabs := []any{
|
|
map[string]any{
|
|
"key": "details",
|
|
"label": "Details",
|
|
"children": []any{
|
|
contentCard("details-card", map[string]any{
|
|
"marginBottom": float64(24),
|
|
}, []any{
|
|
antdRow("details-grid", []any{48, 12}, []any{
|
|
antdCol("col-left", 12, []any{
|
|
antdFlexVertical("col-left-stack", 24, []any{
|
|
antdFlexVertical("meta-name-block", 4, []any{
|
|
antdText("meta-name-label", true, "Name", nil),
|
|
parsedText("meta-name-value", "{reqsJsonPath[0]['.metadata.name']['-']}", nil),
|
|
}),
|
|
antdFlexVertical("meta-namespace-block", 8, []any{
|
|
antdText("meta-namespace-label", true, "Namespace", nil),
|
|
map[string]any{
|
|
"type": "antdFlex",
|
|
"data": map[string]any{
|
|
"id": "namespace-row",
|
|
"align": "center",
|
|
"gap": 6,
|
|
},
|
|
"children": []any{
|
|
createUnifiedBadgeFromKind("ns-badge", "Namespace"),
|
|
antdLink("namespace-link",
|
|
"{reqsJsonPath[0]['.metadata.namespace']['-']}",
|
|
"/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/marketplace",
|
|
),
|
|
},
|
|
},
|
|
}),
|
|
antdFlexVertical("meta-created-block", 4, []any{
|
|
antdText("time-label", true, "Created", nil),
|
|
antdFlex("time-block", 6, []any{
|
|
antdText("time-icon", false, "🌐", nil),
|
|
parsedTextWithFormatter("time-value", "{reqsJsonPath[0]['.metadata.creationTimestamp']['-']}", "timestamp"),
|
|
}),
|
|
}),
|
|
}),
|
|
}),
|
|
antdCol("col-right", 12, []any{
|
|
antdFlexVertical("col-right-stack", 24, []any{
|
|
antdFlexVertical("status-ingress-ip", 4, []any{
|
|
antdText("status-ingress-ip-label", true, "LoadBalancer IP", nil),
|
|
parsedText("status-ingress-ip-value", "{reqsJsonPath[0]['.status.loadBalancer.ingress[0].ip']['-']}", nil),
|
|
}),
|
|
antdFlexVertical("status-ingress-hostname", 4, []any{
|
|
antdText("status-ingress-hostname-label", true, "LoadBalancer Hostname", nil),
|
|
parsedText("status-ingress-hostname-value", "{reqsJsonPath[0]['.status.loadBalancer.ingress[0].hostname']['-']}", nil),
|
|
}),
|
|
}),
|
|
}),
|
|
}),
|
|
spacer("rules-title-spacer", float64(16)),
|
|
antdText("rules-title", true, "Rules", map[string]any{
|
|
"fontSize": float64(20),
|
|
}),
|
|
spacer("rules-spacer", float64(8)),
|
|
map[string]any{
|
|
"type": "EnrichedTable",
|
|
"data": map[string]any{
|
|
"id": "rules-table",
|
|
"fetchUrl": "/api/clusters/{2}/k8s/apis/networking.k8s.io/v1/namespaces/{3}/ingresses/{6}",
|
|
"cluster": "{2}",
|
|
"customizationId": "factory-kube-ingress-details-rules",
|
|
"baseprefix": "/openapi-ui",
|
|
"withoutControls": true,
|
|
"pathToItems": []any{"spec", "rules"},
|
|
},
|
|
},
|
|
}),
|
|
},
|
|
},
|
|
map[string]any{
|
|
"key": "yaml",
|
|
"label": "YAML",
|
|
"children": []any{
|
|
map[string]any{
|
|
"type": "YamlEditorSingleton",
|
|
"data": map[string]any{
|
|
"id": "yaml-editor",
|
|
"cluster": "{2}",
|
|
"isNameSpaced": true,
|
|
"type": "apis",
|
|
"apiGroup": "networking.k8s.io",
|
|
"apiVersion": "v1",
|
|
"plural": "ingresses",
|
|
"prefillValuesRequestIndex": float64(0),
|
|
"substractHeight": float64(400),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
ingressSpec := createFactorySpec("kube-ingress-details", []any{"ingress-sidebar"}, []any{"/api/clusters/{2}/k8s/apis/networking.k8s.io/v1/namespaces/{3}/ingresses/{6}"}, ingressHeader, ingressTabs)
|
|
|
|
// Workloadmonitor details factory
|
|
workloadmonitorHeader := createWorkloadmonitorHeader()
|
|
workloadmonitorTabs := []any{
|
|
map[string]any{
|
|
"key": "details",
|
|
"label": "Details",
|
|
"children": []any{
|
|
contentCard("details-card", map[string]any{
|
|
"marginBottom": float64(24),
|
|
}, []any{
|
|
antdRow("details-grid", []any{48, 12}, []any{
|
|
antdCol("col-left", 12, []any{
|
|
antdFlexVertical("col-left-stack", 24, []any{
|
|
antdFlexVertical("meta-name-block", 4, []any{
|
|
antdText("meta-name-label", true, "Name", nil),
|
|
parsedText("meta-name-value", "{reqsJsonPath[0]['.metadata.name']['-']}", nil),
|
|
}),
|
|
antdFlexVertical("meta-namespace-block", 8, []any{
|
|
antdText("meta-namespace-label", true, "Namespace", nil),
|
|
antdFlex("namespace-row", 6, []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": 15,
|
|
"fontWeight": 400,
|
|
"lineHeight": "24px",
|
|
"minWidth": 24,
|
|
"padding": "0 9px",
|
|
"textAlign": "center",
|
|
"whiteSpace": "nowrap",
|
|
},
|
|
},
|
|
},
|
|
map[string]any{
|
|
"type": "antdLink",
|
|
"data": map[string]any{
|
|
"id": "namespace-link",
|
|
"text": "{reqsJsonPath[0]['.metadata.namespace']['-']}",
|
|
"href": "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/marketplace",
|
|
},
|
|
},
|
|
}),
|
|
}),
|
|
antdFlexVertical("meta-created-block", 4, []any{
|
|
antdText("time-label", true, "Created", nil),
|
|
antdFlex("time-block", 6, []any{
|
|
map[string]any{
|
|
"type": "antdText",
|
|
"data": map[string]any{
|
|
"id": "time-icon",
|
|
"text": "🌐",
|
|
},
|
|
},
|
|
parsedTextWithFormatter("time-value", "{reqsJsonPath[0]['.metadata.creationTimestamp']['-']}", "timestamp"),
|
|
}),
|
|
}),
|
|
antdFlexVertical("meta-kind-block", 4, []any{
|
|
antdText("kind-label", true, "Kind", nil),
|
|
parsedText("kind-value", "{reqsJsonPath[0]['.spec.kind']['-']}", nil),
|
|
}),
|
|
antdFlexVertical("meta-type-block", 4, []any{
|
|
antdText("type-label", true, "Type", nil),
|
|
parsedText("type-value", "{reqsJsonPath[0]['.spec.type']['-']}", nil),
|
|
}),
|
|
}),
|
|
}),
|
|
antdCol("col-right", 12, []any{
|
|
antdFlexVertical("col-right-stack", 24, []any{
|
|
antdText("params-title", true, "Parameters", map[string]any{
|
|
"fontSize": float64(20),
|
|
"marginBottom": float64(12),
|
|
}),
|
|
antdFlexVertical("params-list", 24, []any{
|
|
antdFlexVertical("param-version", 4, []any{
|
|
antdText("param-version-label", true, "Version", nil),
|
|
parsedText("param-version-value", "{reqsJsonPath[0]['.spec.version']['-']}", nil),
|
|
}),
|
|
antdFlexVertical("param-replicas", 4, []any{
|
|
antdText("param-replicas-label", true, "Replicas", nil),
|
|
parsedText("param-replicas-value", "{reqsJsonPath[0]['.spec.replicas']['-']}", nil),
|
|
}),
|
|
antdFlexVertical("param-minreplicas", 4, []any{
|
|
antdText("param-minreplicas-label", true, "MinReplicas", nil),
|
|
parsedText("param-minreplicas-value", "{reqsJsonPath[0]['.spec.minReplicas']['-']}", nil),
|
|
}),
|
|
antdFlexVertical("param-available", 4, []any{
|
|
antdText("param-available-label", true, "AvailableReplicas", nil),
|
|
parsedText("param-available-value", "{reqsJsonPath[0]['.status.availableReplicas']['-']}", nil),
|
|
}),
|
|
antdFlexVertical("param-observed", 4, []any{
|
|
antdText("param-observed-label", true, "ObservedReplicas", nil),
|
|
parsedText("param-observed-value", "{reqsJsonPath[0]['.status.observedReplicas']['-']}", nil),
|
|
}),
|
|
antdFlexVertical("param-operational", 4, []any{
|
|
antdText("param-operational-label", true, "Operational", nil),
|
|
parsedText("param-operational-value", "{reqsJsonPath[0]['.status.operational']['-']}", nil),
|
|
}),
|
|
}),
|
|
}),
|
|
}),
|
|
}),
|
|
}),
|
|
},
|
|
},
|
|
map[string]any{
|
|
"key": "workloads",
|
|
"label": "Workloads",
|
|
"children": []any{
|
|
map[string]any{
|
|
"type": "EnrichedTable",
|
|
"data": map[string]any{
|
|
"id": "workloads-table",
|
|
"baseprefix": "/openapi-ui",
|
|
"cluster": "{2}",
|
|
"customizationId": "factory-details-v1alpha1.cozystack.io.workloads",
|
|
"fetchUrl": "/api/clusters/{2}/k8s/apis/cozystack.io/v1alpha1/namespaces/{3}/workloads",
|
|
"labelSelector": map[string]any{
|
|
"workloads.cozystack.io/monitor": "{reqs[0]['metadata','name']}",
|
|
},
|
|
"pathToItems": []any{"items"},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
map[string]any{
|
|
"key": "yaml",
|
|
"label": "YAML",
|
|
"children": []any{
|
|
map[string]any{
|
|
"type": "YamlEditorSingleton",
|
|
"data": map[string]any{
|
|
"id": "yaml-editor",
|
|
"cluster": "{2}",
|
|
"isNameSpaced": true,
|
|
"prefillValuesRequestIndex": 0,
|
|
"substractHeight": float64(400),
|
|
"type": "apis",
|
|
"apiGroup": "cozystack.io",
|
|
"apiVersion": "v1alpha1",
|
|
"plural": "workloadmonitors",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
workloadmonitorSpec := createFactorySpec("workloadmonitor-details", []any{"workloadmonitor-sidebar"}, []any{"/api/clusters/{2}/k8s/apis/cozystack.io/v1alpha1/namespaces/{3}/workloadmonitors/{6}"}, workloadmonitorHeader, workloadmonitorTabs)
|
|
|
|
// Plan details factory using unified approach
|
|
planConfig := UnifiedResourceConfig{
|
|
Name: "plan-details",
|
|
ResourceType: "factory",
|
|
Kind: "Plan",
|
|
Plural: "plans",
|
|
Title: "plan",
|
|
}
|
|
planTabs := []any{
|
|
map[string]any{
|
|
"key": "details",
|
|
"label": "Details",
|
|
"children": []any{
|
|
contentCard("details-card", map[string]any{
|
|
"marginBottom": "24px",
|
|
}, []any{
|
|
antdText("details-title", true, "Plan details", map[string]any{
|
|
"fontSize": 20,
|
|
"marginBottom": "12px",
|
|
}),
|
|
spacer("details-spacer", 16),
|
|
antdRow("details-grid", []any{48, 12}, []any{
|
|
antdCol("col-left", 12, []any{
|
|
antdFlexVertical("col-left-stack", 24, []any{
|
|
antdFlexVertical("meta-name-block", 4, []any{
|
|
antdText("meta-name-label", true, "Name", nil),
|
|
parsedText("meta-name-value", "{reqsJsonPath[0]['.metadata.name']['-']}", nil),
|
|
}),
|
|
antdFlexVertical("meta-namespace-block", 8, []any{
|
|
antdText("meta-namespace-label", true, "Namespace", nil),
|
|
antdFlex("header-row", 6, []any{
|
|
map[string]any{
|
|
"type": "antdText",
|
|
"data": map[string]any{
|
|
"id": "header-badge",
|
|
"text": "NS",
|
|
"title": "namespace",
|
|
"style": map[string]any{
|
|
"backgroundColor": "#a25792ff",
|
|
"borderRadius": "20px",
|
|
"color": "#fff",
|
|
"display": "inline-block",
|
|
"fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif",
|
|
"fontSize": "15px",
|
|
"fontWeight": 400,
|
|
"lineHeight": "24px",
|
|
"minWidth": 24,
|
|
"padding": "0 9px",
|
|
"textAlign": "center",
|
|
"whiteSpace": "nowrap",
|
|
},
|
|
},
|
|
},
|
|
map[string]any{
|
|
"type": "antdLink",
|
|
"data": map[string]any{
|
|
"id": "namespace-link",
|
|
"text": "{reqsJsonPath[0]['.metadata.namespace']['-']}",
|
|
"href": "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/marketplace",
|
|
},
|
|
},
|
|
}),
|
|
}),
|
|
antdFlexVertical("meta-created-block", 4, []any{
|
|
antdText("time-label", true, "Created", nil),
|
|
antdFlex("time-block", 6, []any{
|
|
map[string]any{
|
|
"type": "antdText",
|
|
"data": map[string]any{
|
|
"id": "time-icon",
|
|
"text": "🌐",
|
|
},
|
|
},
|
|
map[string]any{
|
|
"type": "parsedText",
|
|
"data": map[string]any{
|
|
"formatter": "timestamp",
|
|
"id": "time-value",
|
|
"text": "{reqsJsonPath[0]['.metadata.creationTimestamp']['-']}",
|
|
},
|
|
},
|
|
}),
|
|
}),
|
|
}),
|
|
}),
|
|
antdCol("col-right", 12, []any{
|
|
antdFlexVertical("col-right-stack", 24, []any{
|
|
antdFlexVertical("spec-application-ref-block", 4, []any{
|
|
antdText("application-ref-label", true, "Application", nil),
|
|
parsedText("application-ref-value", "{reqsJsonPath[0]['.spec.applicationRef.kind']['-']}.{reqsJsonPath[0]['.spec.applicationRef.apiGroup']['-']}/{reqsJsonPath[0]['.spec.applicationRef.name']['-']}", nil),
|
|
}),
|
|
antdFlexVertical("spec-backup-class-name-block", 4, []any{
|
|
antdText("backup-class-name-label", true, "Backup Class", nil),
|
|
parsedText("backup-class-name-value", "{reqsJsonPath[0]['.spec.backupClassName']['-']}", nil),
|
|
}),
|
|
antdFlexVertical("spec-schedule-type-block", 4, []any{
|
|
antdText("schedule-type-label", true, "Schedule Type", nil),
|
|
parsedText("schedule-type-value", "{reqsJsonPath[0]['.spec.schedule.type']['-']}", nil),
|
|
}),
|
|
antdFlexVertical("spec-schedule-cron-block", 4, []any{
|
|
antdText("schedule-cron-label", true, "Schedule Cron", nil),
|
|
parsedText("schedule-cron-value", "{reqsJsonPath[0]['.spec.schedule.cron']['-']}", nil),
|
|
}),
|
|
}),
|
|
}),
|
|
}),
|
|
}),
|
|
},
|
|
},
|
|
}
|
|
planSpec := createUnifiedFactory(planConfig, planTabs, []any{"/api/clusters/{2}/k8s/apis/backups.cozystack.io/v1alpha1/namespaces/{3}/plans/{6}"})
|
|
|
|
// BackupJob details factory using unified approach
|
|
backupJobConfig := UnifiedResourceConfig{
|
|
Name: "backupjob-details",
|
|
ResourceType: "factory",
|
|
Kind: "BackupJob",
|
|
Plural: "backupjobs",
|
|
Title: "backupjob",
|
|
}
|
|
backupJobTabs := []any{
|
|
map[string]any{
|
|
"key": "details",
|
|
"label": "Details",
|
|
"children": []any{
|
|
contentCard("details-card", map[string]any{
|
|
"marginBottom": "24px",
|
|
}, []any{
|
|
antdText("details-title", true, "BackupJob details", map[string]any{
|
|
"fontSize": 20,
|
|
"marginBottom": "12px",
|
|
}),
|
|
spacer("details-spacer", 16),
|
|
antdRow("details-grid", []any{48, 12}, []any{
|
|
antdCol("col-left", 12, []any{
|
|
antdFlexVertical("col-left-stack", 24, []any{
|
|
antdFlexVertical("meta-name-block", 4, []any{
|
|
antdText("meta-name-label", true, "Name", nil),
|
|
parsedText("meta-name-value", "{reqsJsonPath[0]['.metadata.name']['-']}", nil),
|
|
}),
|
|
antdFlexVertical("meta-namespace-block", 8, []any{
|
|
antdText("meta-namespace-label", true, "Namespace", nil),
|
|
antdFlex("header-row", 6, []any{
|
|
map[string]any{
|
|
"type": "antdText",
|
|
"data": map[string]any{
|
|
"id": "header-badge",
|
|
"text": "NS",
|
|
"title": "namespace",
|
|
"style": map[string]any{
|
|
"backgroundColor": "#a25792ff",
|
|
"borderRadius": "20px",
|
|
"color": "#fff",
|
|
"display": "inline-block",
|
|
"fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif",
|
|
"fontSize": "15px",
|
|
"fontWeight": 400,
|
|
"lineHeight": "24px",
|
|
"minWidth": 24,
|
|
"padding": "0 9px",
|
|
"textAlign": "center",
|
|
"whiteSpace": "nowrap",
|
|
},
|
|
},
|
|
},
|
|
map[string]any{
|
|
"type": "antdLink",
|
|
"data": map[string]any{
|
|
"id": "namespace-link",
|
|
"text": "{reqsJsonPath[0]['.metadata.namespace']['-']}",
|
|
"href": "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/marketplace",
|
|
},
|
|
},
|
|
}),
|
|
}),
|
|
antdFlexVertical("meta-created-block", 4, []any{
|
|
antdText("time-label", true, "Created", nil),
|
|
antdFlex("time-block", 6, []any{
|
|
map[string]any{
|
|
"type": "antdText",
|
|
"data": map[string]any{
|
|
"id": "time-icon",
|
|
"text": "🌐",
|
|
},
|
|
},
|
|
map[string]any{
|
|
"type": "parsedText",
|
|
"data": map[string]any{
|
|
"formatter": "timestamp",
|
|
"id": "time-value",
|
|
"text": "{reqsJsonPath[0]['.metadata.creationTimestamp']['-']}",
|
|
},
|
|
},
|
|
}),
|
|
}),
|
|
}),
|
|
}),
|
|
antdCol("col-right", 12, []any{
|
|
antdFlexVertical("col-right-stack", 24, []any{
|
|
antdFlexVertical("status-phase-block", 4, []any{
|
|
antdText("phase-label", true, "Phase", nil),
|
|
parsedText("phase-value", "{reqsJsonPath[0]['.status.phase']['-']}", nil),
|
|
}),
|
|
antdFlexVertical("spec-plan-ref-block", 4, []any{
|
|
antdText("plan-ref-label", true, "Plan Ref", nil),
|
|
parsedText("plan-ref-value", "{reqsJsonPath[0]['.spec.planRef.name']['-']}", nil),
|
|
}),
|
|
antdFlexVertical("spec-application-ref-block", 4, []any{
|
|
antdText("application-ref-label", true, "Application", nil),
|
|
parsedText("application-ref-value", "{reqsJsonPath[0]['.spec.applicationRef.kind']['-']}.{reqsJsonPath[0]['.spec.applicationRef.apiGroup']['-']}/{reqsJsonPath[0]['.spec.applicationRef.name']['-']}", nil),
|
|
}),
|
|
antdFlexVertical("spec-backup-class-name-block", 4, []any{
|
|
antdText("backup-class-name-label", true, "Backup Class", nil),
|
|
parsedText("backup-class-name-value", "{reqsJsonPath[0]['.spec.backupClassName']['-']}", nil),
|
|
}),
|
|
antdFlexVertical("status-backup-ref-block", 4, []any{
|
|
antdText("backup-ref-label", true, "Backup Ref", nil),
|
|
parsedText("backup-ref-value", "{reqsJsonPath[0]['.status.backupRef.name']['-']}", nil),
|
|
}),
|
|
antdFlexVertical("status-started-at-block", 4, []any{
|
|
antdText("started-at-label", true, "Started At", nil),
|
|
antdFlex("time-block", 6, []any{
|
|
map[string]any{
|
|
"type": "antdText",
|
|
"data": map[string]any{
|
|
"id": "time-icon",
|
|
"text": "🌐",
|
|
},
|
|
},
|
|
map[string]any{
|
|
"type": "parsedText",
|
|
"data": map[string]any{
|
|
"formatter": "timestamp",
|
|
"id": "time-value",
|
|
"text": "{reqsJsonPath[0]['.status.startedAt']['-']}",
|
|
},
|
|
},
|
|
}),
|
|
}),
|
|
antdFlexVertical("status-completed-at-block", 4, []any{
|
|
antdText("completed-at-label", true, "Completed At", nil),
|
|
antdFlex("time-block", 6, []any{
|
|
map[string]any{
|
|
"type": "antdText",
|
|
"data": map[string]any{
|
|
"id": "time-icon",
|
|
"text": "🌐",
|
|
},
|
|
},
|
|
map[string]any{
|
|
"type": "parsedText",
|
|
"data": map[string]any{
|
|
"formatter": "timestamp",
|
|
"id": "time-value",
|
|
"text": "{reqsJsonPath[0]['.status.completedAt']['-']}",
|
|
},
|
|
},
|
|
}),
|
|
}),
|
|
antdFlexVertical("status-message-block", 4, []any{
|
|
antdText("message-label", true, "Message", nil),
|
|
parsedText("message-value", "{reqsJsonPath[0]['.status.message']['-']}", nil),
|
|
}),
|
|
}),
|
|
}),
|
|
}),
|
|
}),
|
|
},
|
|
},
|
|
}
|
|
backupJobSpec := createUnifiedFactory(backupJobConfig, backupJobTabs, []any{"/api/clusters/{2}/k8s/apis/backups.cozystack.io/v1alpha1/namespaces/{3}/backupjobs/{6}"})
|
|
|
|
// Backup details factory using unified approach
|
|
backupConfig := UnifiedResourceConfig{
|
|
Name: "backup-details",
|
|
ResourceType: "factory",
|
|
Kind: "Backup",
|
|
Plural: "backups",
|
|
Title: "backup",
|
|
}
|
|
backupTabs := []any{
|
|
map[string]any{
|
|
"key": "details",
|
|
"label": "Details",
|
|
"children": []any{
|
|
contentCard("details-card", map[string]any{
|
|
"marginBottom": "24px",
|
|
}, []any{
|
|
antdText("details-title", true, "Backup details", map[string]any{
|
|
"fontSize": 20,
|
|
"marginBottom": "12px",
|
|
}),
|
|
spacer("details-spacer", 16),
|
|
antdRow("details-grid", []any{48, 12}, []any{
|
|
antdCol("col-left", 12, []any{
|
|
antdFlexVertical("col-left-stack", 24, []any{
|
|
antdFlexVertical("meta-name-block", 4, []any{
|
|
antdText("meta-name-label", true, "Name", nil),
|
|
parsedText("meta-name-value", "{reqsJsonPath[0]['.metadata.name']['-']}", nil),
|
|
}),
|
|
antdFlexVertical("meta-namespace-block", 8, []any{
|
|
antdText("meta-namespace-label", true, "Namespace", nil),
|
|
antdFlex("header-row", 6, []any{
|
|
map[string]any{
|
|
"type": "antdText",
|
|
"data": map[string]any{
|
|
"id": "header-badge",
|
|
"text": "NS",
|
|
"title": "namespace",
|
|
"style": map[string]any{
|
|
"backgroundColor": "#a25792ff",
|
|
"borderRadius": "20px",
|
|
"color": "#fff",
|
|
"display": "inline-block",
|
|
"fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif",
|
|
"fontSize": "15px",
|
|
"fontWeight": 400,
|
|
"lineHeight": "24px",
|
|
"minWidth": 24,
|
|
"padding": "0 9px",
|
|
"textAlign": "center",
|
|
"whiteSpace": "nowrap",
|
|
},
|
|
},
|
|
},
|
|
map[string]any{
|
|
"type": "antdLink",
|
|
"data": map[string]any{
|
|
"id": "namespace-link",
|
|
"text": "{reqsJsonPath[0]['.metadata.namespace']['-']}",
|
|
"href": "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/marketplace",
|
|
},
|
|
},
|
|
}),
|
|
}),
|
|
antdFlexVertical("meta-created-block", 4, []any{
|
|
antdText("time-label", true, "Created", nil),
|
|
antdFlex("time-block", 6, []any{
|
|
map[string]any{
|
|
"type": "antdText",
|
|
"data": map[string]any{
|
|
"id": "time-icon",
|
|
"text": "🌐",
|
|
},
|
|
},
|
|
map[string]any{
|
|
"type": "parsedText",
|
|
"data": map[string]any{
|
|
"formatter": "timestamp",
|
|
"id": "time-value",
|
|
"text": "{reqsJsonPath[0]['.metadata.creationTimestamp']['-']}",
|
|
},
|
|
},
|
|
}),
|
|
}),
|
|
}),
|
|
}),
|
|
antdCol("col-right", 12, []any{
|
|
antdFlexVertical("col-right-stack", 24, []any{
|
|
antdFlexVertical("status-phase-block", 4, []any{
|
|
antdText("phase-label", true, "Phase", nil),
|
|
parsedText("phase-value", "{reqsJsonPath[0]['.status.phase']['-']}", nil),
|
|
}),
|
|
antdFlexVertical("spec-taken-at-block", 4, []any{
|
|
antdText("taken-at-label", true, "Taken At", nil),
|
|
antdFlex("time-block", 6, []any{
|
|
map[string]any{
|
|
"type": "antdText",
|
|
"data": map[string]any{
|
|
"id": "time-icon",
|
|
"text": "🌐",
|
|
},
|
|
},
|
|
map[string]any{
|
|
"type": "parsedText",
|
|
"data": map[string]any{
|
|
"formatter": "timestamp",
|
|
"id": "time-value",
|
|
"text": "{reqsJsonPath[0]['.spec.takenAt']['-']}",
|
|
},
|
|
},
|
|
}),
|
|
}),
|
|
antdFlexVertical("spec-plan-ref-block", 4, []any{
|
|
antdText("plan-ref-label", true, "Plan Ref", nil),
|
|
parsedText("plan-ref-value", "{reqsJsonPath[0]['.spec.planRef.name']['-']}", nil),
|
|
}),
|
|
antdFlexVertical("spec-application-ref-block", 4, []any{
|
|
antdText("application-ref-label", true, "Application", nil),
|
|
parsedText("application-ref-value", "{reqsJsonPath[0]['.spec.applicationRef.kind']['-']}.{reqsJsonPath[0]['.spec.applicationRef.apiGroup']['-']}/{reqsJsonPath[0]['.spec.applicationRef.name']['-']}", nil),
|
|
}),
|
|
antdFlexVertical("spec-backup-class-name-block", 4, []any{
|
|
antdText("backup-class-name-label", true, "Backup Class", nil),
|
|
parsedText("backup-class-name-value", "{reqsJsonPath[0]['.spec.backupClassName']['-']}", nil),
|
|
}),
|
|
antdFlexVertical("status-artifact-uri-block", 4, []any{
|
|
antdText("artifact-uri-label", true, "Artifact URI", nil),
|
|
parsedText("artifact-uri-value", "{reqsJsonPath[0]['.status.artifact.uri']['-']}", nil),
|
|
}),
|
|
antdFlexVertical("status-artifact-size-block", 4, []any{
|
|
antdText("artifact-size-label", true, "Artifact Size", nil),
|
|
parsedText("artifact-size-value", "{reqsJsonPath[0]['.status.artifact.sizeBytes']['-']}", nil),
|
|
}),
|
|
antdFlexVertical("status-artifact-checksum-block", 4, []any{
|
|
antdText("artifact-checksum-label", true, "Artifact Checksum", nil),
|
|
parsedText("artifact-checksum-value", "{reqsJsonPath[0]['.status.artifact.checksum']['-']}", nil),
|
|
}),
|
|
}),
|
|
}),
|
|
}),
|
|
}),
|
|
},
|
|
},
|
|
}
|
|
backupSpec := createUnifiedFactory(backupConfig, backupTabs, []any{"/api/clusters/{2}/k8s/apis/backups.cozystack.io/v1alpha1/namespaces/{3}/backups/{6}"})
|
|
|
|
// RestoreJob details factory using unified approach
|
|
restoreJobConfig := UnifiedResourceConfig{
|
|
Name: "restorejob-details",
|
|
ResourceType: "factory",
|
|
Kind: "RestoreJob",
|
|
Plural: "restorejobs",
|
|
Title: "restorejob",
|
|
}
|
|
restoreJobTabs := []any{
|
|
map[string]any{
|
|
"key": "details",
|
|
"label": "Details",
|
|
"children": []any{
|
|
contentCard("details-card", map[string]any{
|
|
"marginBottom": "24px",
|
|
}, []any{
|
|
antdText("details-title", true, "RestoreJob details", map[string]any{
|
|
"fontSize": 20,
|
|
"marginBottom": "12px",
|
|
}),
|
|
spacer("details-spacer", 16),
|
|
antdRow("details-grid", []any{48, 12}, []any{
|
|
antdCol("col-left", 12, []any{
|
|
antdFlexVertical("col-left-stack", 24, []any{
|
|
antdFlexVertical("meta-name-block", 4, []any{
|
|
antdText("meta-name-label", true, "Name", nil),
|
|
parsedText("meta-name-value", "{reqsJsonPath[0]['.metadata.name']['-']}", nil),
|
|
}),
|
|
antdFlexVertical("meta-namespace-block", 8, []any{
|
|
antdText("meta-namespace-label", true, "Namespace", nil),
|
|
antdFlex("header-row", 6, []any{
|
|
map[string]any{
|
|
"type": "antdText",
|
|
"data": map[string]any{
|
|
"id": "header-badge",
|
|
"text": "NS",
|
|
"title": "namespace",
|
|
"style": map[string]any{
|
|
"backgroundColor": "#a25792ff",
|
|
"borderRadius": "20px",
|
|
"color": "#fff",
|
|
"display": "inline-block",
|
|
"fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif",
|
|
"fontSize": "15px",
|
|
"fontWeight": 400,
|
|
"lineHeight": "24px",
|
|
"minWidth": 24,
|
|
"padding": "0 9px",
|
|
"textAlign": "center",
|
|
"whiteSpace": "nowrap",
|
|
},
|
|
},
|
|
},
|
|
map[string]any{
|
|
"type": "antdLink",
|
|
"data": map[string]any{
|
|
"id": "namespace-link",
|
|
"text": "{reqsJsonPath[0]['.metadata.namespace']['-']}",
|
|
"href": "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/marketplace",
|
|
},
|
|
},
|
|
}),
|
|
}),
|
|
antdFlexVertical("meta-created-block", 4, []any{
|
|
antdText("created-time-label", true, "Created", nil),
|
|
antdFlex("created-time-block", 6, []any{
|
|
map[string]any{
|
|
"type": "antdText",
|
|
"data": map[string]any{
|
|
"id": "created-time-icon",
|
|
"text": "🌐",
|
|
},
|
|
},
|
|
map[string]any{
|
|
"type": "parsedText",
|
|
"data": map[string]any{
|
|
"formatter": "timestamp",
|
|
"id": "created-time-value",
|
|
"text": "{reqsJsonPath[0]['.metadata.creationTimestamp']['-']}",
|
|
},
|
|
},
|
|
}),
|
|
}),
|
|
}),
|
|
}),
|
|
antdCol("col-right", 12, []any{
|
|
antdFlexVertical("col-right-stack", 24, []any{
|
|
antdFlexVertical("status-phase-block", 4, []any{
|
|
antdText("phase-label", true, "Phase", nil),
|
|
parsedText("phase-value", "{reqsJsonPath[0]['.status.phase']['-']}", nil),
|
|
}),
|
|
antdFlexVertical("spec-backup-ref-block", 4, []any{
|
|
antdText("backup-ref-label", true, "Backup Ref", nil),
|
|
parsedText("backup-ref-value", "{reqsJsonPath[0]['.spec.backupRef.name']['-']}", nil),
|
|
}),
|
|
antdFlexVertical("spec-target-application-ref-block", 4, []any{
|
|
antdText("target-application-ref-label", true, "Target Application", nil),
|
|
// targetApplicationRef is optional — when absent, the restore targets
|
|
// the same application as the selected Backup. Show a single friendly
|
|
// fallback in that case instead of rendering "-.-/-".
|
|
parsedText("target-application-ref-value", "{reqsJsonPath[0]['.spec.targetApplicationRef.name']['Same as backup']}", nil),
|
|
}),
|
|
antdFlexVertical("spec-options-target-namespace-block", 4, []any{
|
|
antdText("options-target-namespace-label", true, "Target Namespace", nil),
|
|
parsedText("options-target-namespace-value", "{reqsJsonPath[0]['.spec.options.targetNamespace']['-']}", nil),
|
|
}),
|
|
antdFlexVertical("spec-options-fail-if-target-exists-block", 4, []any{
|
|
antdText("options-fail-if-target-exists-label", true, "Fail If Target Exists", nil),
|
|
parsedText("options-fail-if-target-exists-value", "{reqsJsonPath[0]['.spec.options.failIfTargetExists']['-']}", nil),
|
|
}),
|
|
antdFlexVertical("spec-options-keep-original-pvc-block", 4, []any{
|
|
antdText("options-keep-original-pvc-label", true, "Keep Original PVC", nil),
|
|
parsedText("options-keep-original-pvc-value", "{reqsJsonPath[0]['.spec.options.keepOriginalPVC']['-']}", nil),
|
|
}),
|
|
antdFlexVertical("spec-options-keep-original-ip-mac-block", 4, []any{
|
|
antdText("options-keep-original-ip-mac-label", true, "Keep Original IP/MAC", nil),
|
|
parsedText("options-keep-original-ip-mac-value", "{reqsJsonPath[0]['.spec.options.keepOriginalIpAndMac']['-']}", nil),
|
|
}),
|
|
antdFlexVertical("status-started-at-block", 4, []any{
|
|
antdText("started-at-label", true, "Started At", nil),
|
|
antdFlex("started-at-time-block", 6, []any{
|
|
map[string]any{
|
|
"type": "antdText",
|
|
"data": map[string]any{
|
|
"id": "started-at-time-icon",
|
|
"text": "🌐",
|
|
},
|
|
},
|
|
map[string]any{
|
|
"type": "parsedText",
|
|
"data": map[string]any{
|
|
"formatter": "timestamp",
|
|
"id": "started-at-time-value",
|
|
"text": "{reqsJsonPath[0]['.status.startedAt']['-']}",
|
|
},
|
|
},
|
|
}),
|
|
}),
|
|
antdFlexVertical("status-completed-at-block", 4, []any{
|
|
antdText("completed-at-label", true, "Completed At", nil),
|
|
antdFlex("completed-at-time-block", 6, []any{
|
|
map[string]any{
|
|
"type": "antdText",
|
|
"data": map[string]any{
|
|
"id": "completed-at-time-icon",
|
|
"text": "🌐",
|
|
},
|
|
},
|
|
map[string]any{
|
|
"type": "parsedText",
|
|
"data": map[string]any{
|
|
"formatter": "timestamp",
|
|
"id": "completed-at-time-value",
|
|
"text": "{reqsJsonPath[0]['.status.completedAt']['-']}",
|
|
},
|
|
},
|
|
}),
|
|
}),
|
|
antdFlexVertical("status-message-block", 4, []any{
|
|
antdText("message-label", true, "Message", nil),
|
|
parsedText("message-value", "{reqsJsonPath[0]['.status.message']['-']}", nil),
|
|
}),
|
|
}),
|
|
}),
|
|
}),
|
|
}),
|
|
},
|
|
},
|
|
}
|
|
restoreJobSpec := createUnifiedFactory(restoreJobConfig, restoreJobTabs, []any{"/api/clusters/{2}/k8s/apis/backups.cozystack.io/v1alpha1/namespaces/{3}/restorejobs/{6}"})
|
|
|
|
// External IPs factory (filtered services)
|
|
externalIPsTabs := []any{
|
|
map[string]any{
|
|
"key": "services",
|
|
"label": "Services",
|
|
"children": []any{
|
|
map[string]any{
|
|
"type": "EnrichedTable",
|
|
"data": map[string]any{
|
|
"id": "external-ips-table",
|
|
"fetchUrl": "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/services",
|
|
"cluster": "{2}",
|
|
"baseprefix": "/openapi-ui",
|
|
"customizationId": "factory-details-v1.services",
|
|
"pathToItems": ".items",
|
|
"fieldSelector": map[string]any{
|
|
"spec.type": "LoadBalancer",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
externalIPsSpec := map[string]any{
|
|
"key": "external-ips",
|
|
"sidebarTags": []any{"external-ips-sidebar"},
|
|
"withScrollableMainContentCard": true,
|
|
"urlsToFetch": []any{},
|
|
"data": []any{
|
|
map[string]any{
|
|
"type": "antdTabs",
|
|
"data": map[string]any{
|
|
"id": "tabs-root",
|
|
"defaultActiveKey": "services",
|
|
"items": externalIPsTabs,
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
return []*dashboardv1alpha1.Factory{
|
|
createFactory("marketplace", marketplaceSpec),
|
|
createFactory("namespace-details", namespaceSpec),
|
|
createFactory("node-details", nodeSpec),
|
|
createFactory("pod-details", podSpec),
|
|
createFactory("kube-secret-details", secretSpec),
|
|
createFactory("kube-service-details", serviceSpec),
|
|
createFactory("kube-ingress-details", ingressSpec),
|
|
createFactory("workloadmonitor-details", workloadmonitorSpec),
|
|
createFactory("plan-details", planSpec),
|
|
createFactory("backupjob-details", backupJobSpec),
|
|
createFactory("backup-details", backupSpec),
|
|
createFactory("restorejob-details", restoreJobSpec),
|
|
createFactory("external-ips", externalIPsSpec),
|
|
}
|
|
}
|
|
|
|
// CreateAllNavigations creates all navigation resources using helper functions
|
|
func CreateAllNavigations() []*dashboardv1alpha1.Navigation {
|
|
// Build baseFactoriesMapping for static (built-in) factories
|
|
baseFactoriesMapping := map[string]string{
|
|
// Cluster-scoped builtin resources
|
|
"base-factory-clusterscoped-builtin-v1-namespaces": "namespace-details",
|
|
"base-factory-clusterscoped-builtin-v1-nodes": "node-details",
|
|
// Namespaced builtin resources
|
|
"base-factory-namespaced-builtin-v1-pods": "pod-details",
|
|
"base-factory-namespaced-builtin-v1-secrets": "kube-secret-details",
|
|
"base-factory-namespaced-builtin-v1-services": "kube-service-details",
|
|
// Namespaced API resources
|
|
"base-factory-namespaced-api-networking.k8s.io-v1-ingresses": "kube-ingress-details",
|
|
"base-factory-namespaced-api-cozystack.io-v1alpha1-workloadmonitors": "workloadmonitor-details",
|
|
// Backup resources (not ApplicationDefinitions, so ensureNavigation doesn't cover them)
|
|
"base-factory-namespaced-api-backups.cozystack.io-v1alpha1-plans": "plan-details",
|
|
"base-factory-namespaced-api-backups.cozystack.io-v1alpha1-backupjobs": "backupjob-details",
|
|
"base-factory-namespaced-api-backups.cozystack.io-v1alpha1-backups": "backup-details",
|
|
"base-factory-namespaced-api-backups.cozystack.io-v1alpha1-restorejobs": "restorejob-details",
|
|
}
|
|
|
|
return []*dashboardv1alpha1.Navigation{
|
|
createNavigation("navigation", map[string]any{
|
|
"namespaces": map[string]any{
|
|
"change": "/openapi-ui/{selectedCluster}/{value}/factory/marketplace",
|
|
"clear": "/openapi-ui/{selectedCluster}/api-table/core.cozystack.io/v1alpha1/tenantnamespaces",
|
|
},
|
|
"baseFactoriesMapping": baseFactoriesMapping,
|
|
}),
|
|
}
|
|
}
|
|
|
|
// CreateAllTableUriMappings creates all table URI mapping resources using helper functions
|
|
func CreateAllTableUriMappings() []*dashboardv1alpha1.TableUriMapping {
|
|
// links are now handled through CustomFormsPrefills
|
|
return []*dashboardv1alpha1.TableUriMapping{}
|
|
}
|
|
|
|
// ---------------- Additional helper functions for missing resource types ----------------
|
|
|
|
// createCustomFormsOverride creates a CustomFormsOverride resource
|
|
func createCustomFormsOverride(customizationId string, spec map[string]any) *dashboardv1alpha1.CustomFormsOverride {
|
|
// Generate name from customizationId
|
|
name := customizationId
|
|
if strings.Contains(customizationId, "default-/") {
|
|
// For default-/ resources, replace "default-/" with "default-" and slashes with dots
|
|
name = strings.ReplaceAll(customizationId, "default-/", "default-")
|
|
name = strings.ReplaceAll(name, "/", ".")
|
|
}
|
|
|
|
// Create hidden fields list
|
|
hidden := []any{
|
|
[]any{"metadata", "creationTimestamp"},
|
|
}
|
|
|
|
// Add namespace to hidden for specific resources in the correct order
|
|
if strings.Contains(name, "namespaces") || strings.Contains(name, "nodes") {
|
|
hidden = append(hidden, []any{"metadata", "namespace"})
|
|
}
|
|
|
|
// Add remaining hidden fields
|
|
hidden = append(hidden, []any{
|
|
[]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"},
|
|
[]any{"kind"},
|
|
[]any{"apiVersion"},
|
|
[]any{"status"},
|
|
}...)
|
|
|
|
// Create new spec with all required fields including formItems from the original spec
|
|
newSpec := map[string]any{
|
|
"customizationId": customizationId,
|
|
"hidden": hidden,
|
|
"schema": map[string]any{},
|
|
"strategy": "merge",
|
|
}
|
|
|
|
// Merge into newSpec caller-provided fields without: customizationId, hidden, strategy
|
|
for key, value := range spec {
|
|
if key != "customizationId" && key != "hidden" && key != "strategy" {
|
|
newSpec[key] = value
|
|
}
|
|
}
|
|
|
|
jsonData, _ := json.Marshal(newSpec)
|
|
|
|
return &dashboardv1alpha1.CustomFormsOverride{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: "dashboard.cozystack.io/v1alpha1",
|
|
Kind: "CustomFormsOverride",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: name,
|
|
Namespace: "",
|
|
},
|
|
Spec: dashboardv1alpha1.ArbitrarySpec{
|
|
JSON: v1.JSON{
|
|
Raw: jsonData,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// createNavigation creates a Navigation resource
|
|
func createNavigation(name string, spec map[string]any) *dashboardv1alpha1.Navigation {
|
|
jsonData, _ := json.Marshal(spec)
|
|
|
|
return &dashboardv1alpha1.Navigation{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: "dashboard.cozystack.io/v1alpha1",
|
|
Kind: "Navigation",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: name,
|
|
Namespace: "",
|
|
},
|
|
Spec: dashboardv1alpha1.ArbitrarySpec{
|
|
JSON: v1.JSON{
|
|
Raw: jsonData,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func listInputScemaItemBackupClass() map[string]any {
|
|
return map[string]any{
|
|
"type": "listInput",
|
|
"customProps": map[string]any{
|
|
"valueUri": "/api/clusters/{cluster}/k8s/apis/backups.cozystack.io/v1alpha1/backupclasses",
|
|
"keysToValue": []any{"metadata", "name"},
|
|
"keysToLabel": []any{"metadata", "name"},
|
|
},
|
|
}
|
|
}
|
|
|
|
// listInputSchemaItemBackup returns a listInput schema overlay for selecting a Backup
|
|
// from the current namespace (used by RestoreJob form for spec.backupRef.name).
|
|
func listInputSchemaItemBackup() map[string]any {
|
|
return map[string]any{
|
|
"type": "listInput",
|
|
"customProps": map[string]any{
|
|
"valueUri": "/api/clusters/{cluster}/k8s/apis/backups.cozystack.io/v1alpha1/namespaces/{namespace}/backups",
|
|
"keysToValue": []any{"metadata", "name"},
|
|
"keysToLabel": []any{"metadata", "name"},
|
|
},
|
|
}
|
|
}
|
|
|
|
// backupClassSchema returns the schema for spec.backupClassName as listInput (BackupJob/Plan).
|
|
func createSchema(customProps map[string]any) map[string]any {
|
|
return map[string]any{
|
|
"properties": map[string]any{
|
|
"spec": map[string]any{
|
|
"properties": customProps,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// createFormItem creates a form item for CustomFormsOverride
|
|
func createFormItem(path, label, fieldType string) map[string]any {
|
|
return map[string]any{
|
|
"path": path,
|
|
"label": label,
|
|
"type": fieldType,
|
|
}
|
|
}
|
|
|
|
// createFormItemWithAPI creates a form item with API endpoint for resource-based selects
|
|
func createFormItemWithAPI(path, label, fieldType string, apiConfig map[string]any) map[string]any {
|
|
item := map[string]any{
|
|
"path": path,
|
|
"label": label,
|
|
"type": fieldType,
|
|
}
|
|
// Merge API configuration into the form item
|
|
for key, value := range apiConfig {
|
|
item[key] = value
|
|
}
|
|
return item
|
|
}
|
|
|
|
// ---------------- Workloadmonitor specific functions ----------------
|
|
|
|
// createNamespaceHeader creates a header specifically for namespace with correct colors and text
|
|
func createNamespaceHeader() map[string]any {
|
|
badge := map[string]any{
|
|
"type": "antdText",
|
|
"data": map[string]any{
|
|
"id": "header-badge",
|
|
"text": "NS",
|
|
"title": "Namespace",
|
|
"style": map[string]any{
|
|
"backgroundColor": "#a25792ff",
|
|
"borderRadius": "20px",
|
|
"color": "#fff",
|
|
"display": "inline-block",
|
|
"fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif",
|
|
"fontSize": "20px",
|
|
"fontWeight": float64(400),
|
|
"lineHeight": "24px",
|
|
"minWidth": float64(24),
|
|
"padding": "0 9px",
|
|
"textAlign": "center",
|
|
"whiteSpace": "nowrap",
|
|
},
|
|
},
|
|
}
|
|
|
|
nameText := parsedText("header-name", "{reqsJsonPath[0]['.metadata.name']['-']}", map[string]any{
|
|
"fontSize": "20px",
|
|
"lineHeight": "24px",
|
|
"fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif",
|
|
})
|
|
|
|
return map[string]any{
|
|
"type": "antdFlex",
|
|
"data": map[string]any{
|
|
"id": "header-row",
|
|
"align": "center",
|
|
"gap": float64(6),
|
|
"style": map[string]any{
|
|
"marginBottom": "24px",
|
|
},
|
|
},
|
|
"children": []any{
|
|
badge,
|
|
nameText,
|
|
},
|
|
}
|
|
}
|
|
|
|
// createNodeHeader creates a header specifically for node with correct colors and text
|
|
func createNodeHeader() map[string]any {
|
|
badge := map[string]any{
|
|
"type": "antdText",
|
|
"data": map[string]any{
|
|
"id": "header-badge",
|
|
"text": "N",
|
|
"title": "nodes",
|
|
"style": map[string]any{
|
|
"backgroundColor": "#8476d1",
|
|
"borderRadius": "20px",
|
|
"color": "#fff",
|
|
"display": "inline-block",
|
|
"fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif",
|
|
"fontSize": "20px",
|
|
"fontWeight": float64(400),
|
|
"lineHeight": "24px",
|
|
"minWidth": float64(24),
|
|
"padding": "0 9px",
|
|
"textAlign": "center",
|
|
"whiteSpace": "nowrap",
|
|
},
|
|
},
|
|
}
|
|
|
|
nameText := parsedText("header-name", "{reqsJsonPath[0]['.metadata.name']['-']}", map[string]any{
|
|
"fontSize": "20px",
|
|
"lineHeight": "24px",
|
|
"fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif",
|
|
})
|
|
|
|
statusBlock := map[string]any{
|
|
"type": "antdFlex",
|
|
"data": map[string]any{
|
|
"id": "status-header-block",
|
|
"vertical": true,
|
|
"gap": float64(4),
|
|
},
|
|
"children": []any{
|
|
map[string]any{
|
|
"type": "StatusText",
|
|
"data": map[string]any{
|
|
"id": "node-status",
|
|
"values": []any{
|
|
"{reqsJsonPath[0]['.status.conditions[?(@.status=='True')].reason']['-']}",
|
|
},
|
|
"criteriaSuccess": "equals",
|
|
"strategySuccess": "every",
|
|
"valueToCompareSuccess": "KubeletReady",
|
|
"criteriaError": "equals",
|
|
"strategyError": "every",
|
|
"valueToCompareError": []any{
|
|
"KernelDeadlock",
|
|
"ReadonlyFilesystem",
|
|
"NetworkUnavailable",
|
|
"MemoryPressure",
|
|
"DiskPressure",
|
|
"PIDPressure",
|
|
},
|
|
"successText": "Available",
|
|
"errorText": "Unavailable",
|
|
"fallbackText": "Progressing",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
return map[string]any{
|
|
"type": "antdFlex",
|
|
"data": map[string]any{
|
|
"id": "header-row",
|
|
"align": "center",
|
|
"gap": float64(6),
|
|
"style": map[string]any{
|
|
"marginBottom": "24px",
|
|
},
|
|
},
|
|
"children": []any{
|
|
badge,
|
|
nameText,
|
|
statusBlock,
|
|
},
|
|
}
|
|
}
|
|
|
|
// createPodHeader creates a header specifically for pod with correct colors and text
|
|
func createPodHeader() map[string]any {
|
|
badge := map[string]any{
|
|
"type": "antdText",
|
|
"data": map[string]any{
|
|
"id": "header-badge",
|
|
"text": "P",
|
|
"title": "Pods",
|
|
"style": map[string]any{
|
|
"backgroundColor": "#009596",
|
|
"borderRadius": "20px",
|
|
"color": "#fff",
|
|
"display": "inline-block",
|
|
"fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif",
|
|
"fontSize": "20px",
|
|
"fontWeight": float64(400),
|
|
"lineHeight": "24px",
|
|
"minWidth": float64(24),
|
|
"padding": "0 9px",
|
|
"textAlign": "center",
|
|
"whiteSpace": "nowrap",
|
|
},
|
|
},
|
|
}
|
|
|
|
nameText := parsedText("header-pod-name", "{reqsJsonPath[0]['.metadata.name']['-']}", map[string]any{
|
|
"fontSize": "20px",
|
|
"lineHeight": "24px",
|
|
"fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif",
|
|
})
|
|
|
|
statusBlock := map[string]any{
|
|
"type": "antdFlex",
|
|
"data": map[string]any{
|
|
"id": "status-header-block",
|
|
"vertical": true,
|
|
"gap": float64(4),
|
|
},
|
|
"children": []any{
|
|
map[string]any{
|
|
"type": "StatusText",
|
|
"data": map[string]any{
|
|
"id": "pod-status",
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
return map[string]any{
|
|
"type": "antdFlex",
|
|
"data": map[string]any{
|
|
"id": "header-row",
|
|
"align": "center",
|
|
"gap": float64(6),
|
|
"style": map[string]any{
|
|
"marginBottom": "24px",
|
|
},
|
|
},
|
|
"children": []any{
|
|
badge,
|
|
nameText,
|
|
statusBlock,
|
|
},
|
|
}
|
|
}
|
|
|
|
// createWorkloadmonitorHeader creates a header specifically for workloadmonitor with correct colors and text
|
|
func createWorkloadmonitorHeader() map[string]any {
|
|
badge := map[string]any{
|
|
"type": "antdText",
|
|
"data": map[string]any{
|
|
"id": "badge-workloadmonitor",
|
|
"text": "W",
|
|
"title": "workloadmonitors",
|
|
"style": map[string]any{
|
|
"backgroundColor": "#c46100",
|
|
"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",
|
|
},
|
|
},
|
|
}
|
|
|
|
nameText := parsedText("workloadmonitor-name", "{reqsJsonPath[0]['.metadata.name']['-']}", map[string]any{
|
|
"fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif",
|
|
"fontSize": float64(20),
|
|
"lineHeight": "24px",
|
|
})
|
|
|
|
return 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{
|
|
badge,
|
|
nameText,
|
|
},
|
|
}
|
|
}
|
|
|
|
// CreateStaticCFOMapping creates the CFOMapping resource with mappings from static CustomFormsOverrides
|
|
func CreateStaticCFOMapping() *dashboardv1alpha1.CFOMapping {
|
|
// Build mappings from static CustomFormsOverrides
|
|
customFormsOverrides := CreateAllCustomFormsOverrides()
|
|
mappings := make(map[string]string, len(customFormsOverrides))
|
|
for _, cfo := range customFormsOverrides {
|
|
var spec map[string]any
|
|
if err := json.Unmarshal(cfo.Spec.JSON.Raw, &spec); err != nil {
|
|
continue
|
|
}
|
|
customizationID, ok := spec["customizationId"].(string)
|
|
if !ok {
|
|
continue
|
|
}
|
|
// Extract the resource path from customizationId (remove "default-" prefix)
|
|
resourcePath := strings.TrimPrefix(customizationID, "default-")
|
|
mappings[resourcePath] = customizationID
|
|
}
|
|
|
|
return createCFOMapping("cfomapping", mappings)
|
|
}
|
|
|
|
// createCFOMapping creates a CFOMapping resource
|
|
func createCFOMapping(name string, mappings map[string]string) *dashboardv1alpha1.CFOMapping {
|
|
spec := map[string]any{
|
|
"mappings": mappings,
|
|
}
|
|
jsonData, _ := json.Marshal(spec)
|
|
|
|
return &dashboardv1alpha1.CFOMapping{
|
|
TypeMeta: metav1.TypeMeta{
|
|
APIVersion: "dashboard.cozystack.io/v1alpha1",
|
|
Kind: "CFOMapping",
|
|
},
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
Name: name,
|
|
},
|
|
Spec: dashboardv1alpha1.ArbitrarySpec{
|
|
JSON: v1.JSON{
|
|
Raw: jsonData,
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
// ---------------- Complete resource creation function ----------------
|
|
|
|
// CreateAllStaticResources creates all static dashboard resources using helper functions
|
|
func CreateAllStaticResources() []client.Object {
|
|
var resources []client.Object
|
|
|
|
// Add all breadcrumbs
|
|
for _, breadcrumb := range CreateAllBreadcrumbs() {
|
|
resources = append(resources, breadcrumb)
|
|
}
|
|
|
|
// Add all custom column overrides
|
|
for _, customColumns := range CreateAllCustomColumnsOverrides() {
|
|
resources = append(resources, customColumns)
|
|
}
|
|
|
|
// Add all custom forms overrides
|
|
for _, customForms := range CreateAllCustomFormsOverrides() {
|
|
resources = append(resources, customForms)
|
|
}
|
|
|
|
// Add all factories
|
|
for _, factory := range CreateAllFactories() {
|
|
resources = append(resources, factory)
|
|
}
|
|
|
|
// Add all navigations
|
|
for _, navigation := range CreateAllNavigations() {
|
|
resources = append(resources, navigation)
|
|
}
|
|
|
|
// Add all table URI mappings
|
|
for _, tableUriMapping := range CreateAllTableUriMappings() {
|
|
resources = append(resources, tableUriMapping)
|
|
}
|
|
|
|
// Add CFOMapping
|
|
resources = append(resources, CreateStaticCFOMapping())
|
|
|
|
return resources
|
|
}
|