mirror of
https://github.com/cozystack/cozystack.git
synced 2026-03-04 14:08:52 +00:00
Compare commits
64 Commits
testing/ad
...
workloadmo
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
156c1e8524 | ||
|
|
19ed058897 | ||
|
|
6438ce98b1 | ||
|
|
523d8ea638 | ||
|
|
e89896fdba | ||
|
|
ab5101a713 | ||
|
|
af460f1c41 | ||
|
|
634649f9ec | ||
|
|
df782fec9c | ||
|
|
172774b6cd | ||
|
|
62119eb761 | ||
|
|
48c6e23ca0 | ||
|
|
9064a72c92 | ||
|
|
dc06b16d11 | ||
|
|
739a74dc28 | ||
|
|
723eefea66 | ||
|
|
1d10907168 | ||
|
|
c19cddf08e | ||
|
|
4c08caafe1 | ||
|
|
be58047aba | ||
|
|
f60e2555c9 | ||
|
|
6443a1264e | ||
|
|
52a23eacfc | ||
|
|
2634b01465 | ||
|
|
15a3636d5f | ||
|
|
ef43ef6753 | ||
|
|
ba804b7c52 | ||
|
|
9c5abf49ca | ||
|
|
10e79651ef | ||
|
|
965818efd4 | ||
|
|
b1ebc9cc85 | ||
|
|
667c778f27 | ||
|
|
77d95e3b91 | ||
|
|
a8d3cbce82 | ||
|
|
eea685065a | ||
|
|
480f8027d7 | ||
|
|
19b56414a6 | ||
|
|
0f9806e9b0 | ||
|
|
177073596c | ||
|
|
93a9241899 | ||
|
|
5401ae9734 | ||
|
|
b78d97f374 | ||
|
|
8b95db06ee | ||
|
|
5a2d4d7e66 | ||
|
|
42e6f0e3f2 | ||
|
|
e2eb1e267b | ||
|
|
2ac533f2f6 | ||
|
|
ae9f9c57b1 | ||
|
|
18f253f77a | ||
|
|
bd9dcb52a3 | ||
|
|
be473a12be | ||
|
|
8f5adcccf5 | ||
|
|
08bd918a10 | ||
|
|
023276ebab | ||
|
|
19c4674ebb | ||
|
|
202da193c0 | ||
|
|
cc9687707c | ||
|
|
ac10e35272 | ||
|
|
fc7d5ee71f | ||
|
|
9d90503fb7 | ||
|
|
4be1c257d6 | ||
|
|
f3ba8eca8e | ||
|
|
0f286ee7ba | ||
|
|
1e36722ab8 |
@@ -31,4 +31,5 @@ This list is sorted in chronological order, based on the submission date.
|
||||
| [gohost](https://gohost.kz/) | @karabass_off | 2024-02-01 | Our company has been working in the market of Kazakhstan for more than 15 years, providing clients with a standard set of services: VPS/VDC, IaaS, shared hosting, etc. Now we are expanding the lineup by introducing Bare Metal Kubenetes cluster under Cozystack management. |
|
||||
| [Urmanac](https://urmanac.com) | @kingdonb | 2024-12-04 | Urmanac is the future home of a hosting platform for the knowledge base of a community of personal server enthusiasts. We use Cozystack to provide support services for web sites hosted using both conventional deployments and on SpinKube, with WASM. |
|
||||
| [Hidora](https://hikube.cloud) | @matthieu-robin | 2025-09-17 | Hidora is a Swiss cloud provider delivering managed services and infrastructure solutions through datacenters located in Switzerland, ensuring data sovereignty and reliability. Its sovereign cloud platform, Hikube, is designed to run workloads with high availability across multiple datacenters, providing enterprises with a secure and scalable foundation for their applications based on Cozystack. |
|
||||
|
|
||||
| [QOSI](https://qosi.kz) | @tabu-a | 2025-10-04 | QOSI is a non-profit organization driving open-source adoption and digital sovereignty across Kazakhstan and Central Asia. We use Cozystack as a platform for deploying sovereign, GPU-enabled clouds and educational environments under the National AI Program. Our goal is to accelerate the region’s transition toward open, self-hosted cloud-native technologies |
|
||||
|
|
||||
@@ -59,6 +59,10 @@ type CozystackResourceDefinitionSpec struct {
|
||||
|
||||
// Dashboard configuration for this resource
|
||||
Dashboard *CozystackResourceDefinitionDashboard `json:"dashboard,omitempty"`
|
||||
|
||||
// WorkloadMonitors configuration for this resource
|
||||
// List of WorkloadMonitor templates to be created for each application instance
|
||||
WorkloadMonitors []WorkloadMonitorTemplate `json:"workloadMonitors,omitempty"`
|
||||
}
|
||||
|
||||
type CozystackResourceDefinitionChart struct {
|
||||
@@ -110,17 +114,18 @@ type CozystackResourceDefinitionRelease struct {
|
||||
// - {{ .namespace }}: The namespace of the resource being processed
|
||||
//
|
||||
// Example YAML:
|
||||
// secrets:
|
||||
// include:
|
||||
// - matchExpressions:
|
||||
// - key: badlabel
|
||||
// operator: DoesNotExist
|
||||
// matchLabels:
|
||||
// goodlabel: goodvalue
|
||||
// resourceNames:
|
||||
// - "{{ .name }}-secret"
|
||||
// - "{{ .kind }}-{{ .name }}-tls"
|
||||
// - "specificname"
|
||||
//
|
||||
// secrets:
|
||||
// include:
|
||||
// - matchExpressions:
|
||||
// - key: badlabel
|
||||
// operator: DoesNotExist
|
||||
// matchLabels:
|
||||
// goodlabel: goodvalue
|
||||
// resourceNames:
|
||||
// - "{{ .name }}-secret"
|
||||
// - "{{ .kind }}-{{ .name }}-tls"
|
||||
// - "specificname"
|
||||
type CozystackResourceDefinitionResourceSelector struct {
|
||||
metav1.LabelSelector `json:",inline"`
|
||||
// ResourceNames is a list of resource names to match
|
||||
@@ -191,3 +196,47 @@ type CozystackResourceDefinitionDashboard struct {
|
||||
// +optional
|
||||
Module bool `json:"module,omitempty"`
|
||||
}
|
||||
|
||||
// ---- WorkloadMonitor types ----
|
||||
|
||||
// WorkloadMonitorTemplate defines a template for creating WorkloadMonitor resources
|
||||
// for application instances. Fields support Go template syntax with the following variables:
|
||||
// - {{ .Release.Name }}: The name of the Helm release
|
||||
// - {{ .Release.Namespace }}: The namespace of the Helm release
|
||||
// - {{ .Chart.Version }}: The version of the Helm chart
|
||||
// - {{ .Values.<path> }}: Any value from the Helm values
|
||||
type WorkloadMonitorTemplate struct {
|
||||
// Name is the name of the WorkloadMonitor.
|
||||
// Supports Go template syntax (e.g., "{{ .Release.Name }}-keeper")
|
||||
// +required
|
||||
Name string `json:"name"`
|
||||
|
||||
// Kind specifies the kind of the workload (e.g., "postgres", "kafka")
|
||||
// +required
|
||||
Kind string `json:"kind"`
|
||||
|
||||
// Type specifies the type of the workload (e.g., "postgres", "zookeeper")
|
||||
// +required
|
||||
Type string `json:"type"`
|
||||
|
||||
// Selector is a map of label key-value pairs for matching workloads.
|
||||
// Supports Go template syntax in values (e.g., "app.kubernetes.io/instance: {{ .Release.Name }}")
|
||||
// +required
|
||||
Selector map[string]string `json:"selector"`
|
||||
|
||||
// Replicas is a Go template expression that evaluates to the desired number of replicas.
|
||||
// Example: "{{ .Values.replicas }}" or "{{ .Values.clickhouseKeeper.replicas }}"
|
||||
// +optional
|
||||
Replicas string `json:"replicas,omitempty"`
|
||||
|
||||
// MinReplicas is a Go template expression that evaluates to the minimum number of replicas.
|
||||
// Example: "1" or "{{ div .Values.replicas 2 | add1 }}"
|
||||
// +optional
|
||||
MinReplicas string `json:"minReplicas,omitempty"`
|
||||
|
||||
// Condition is a Go template expression that must evaluate to "true" for the monitor to be created.
|
||||
// Example: "{{ .Values.clickhouseKeeper.enabled }}"
|
||||
// If empty, the monitor is always created.
|
||||
// +optional
|
||||
Condition string `json:"condition,omitempty"`
|
||||
}
|
||||
|
||||
@@ -244,6 +244,13 @@ func (in *CozystackResourceDefinitionSpec) DeepCopyInto(out *CozystackResourceDe
|
||||
*out = new(CozystackResourceDefinitionDashboard)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.WorkloadMonitors != nil {
|
||||
in, out := &in.WorkloadMonitors, &out.WorkloadMonitors
|
||||
*out = make([]WorkloadMonitorTemplate, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionSpec.
|
||||
@@ -461,6 +468,28 @@ func (in *WorkloadMonitorStatus) DeepCopy() *WorkloadMonitorStatus {
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *WorkloadMonitorTemplate) DeepCopyInto(out *WorkloadMonitorTemplate) {
|
||||
*out = *in
|
||||
if in.Selector != nil {
|
||||
in, out := &in.Selector, &out.Selector
|
||||
*out = make(map[string]string, len(*in))
|
||||
for key, val := range *in {
|
||||
(*out)[key] = val
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkloadMonitorTemplate.
|
||||
func (in *WorkloadMonitorTemplate) DeepCopy() *WorkloadMonitorTemplate {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(WorkloadMonitorTemplate)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *WorkloadStatus) DeepCopyInto(out *WorkloadStatus) {
|
||||
*out = *in
|
||||
|
||||
@@ -192,6 +192,14 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err = (&controller.WorkloadMonitorFromCRDReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "WorkloadMonitorFromCRD")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err = (&controller.WorkloadReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
@@ -229,6 +237,15 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
dashboardManager := &dashboard.Manager{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
}
|
||||
if err = dashboardManager.SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "DashboardReconciler")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// +kubebuilder:scaffold:builder
|
||||
|
||||
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
|
||||
@@ -254,7 +271,9 @@ func main() {
|
||||
}
|
||||
|
||||
setupLog.Info("starting manager")
|
||||
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
|
||||
ctx := ctrl.SetupSignalHandler()
|
||||
dashboardManager.InitializeStaticResources(ctx)
|
||||
if err := mgr.Start(ctx); err != nil {
|
||||
setupLog.Error(err, "problem running manager")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@@ -87,14 +87,14 @@ EOF
|
||||
|
||||
|
||||
# Set up port forwarding to the Kubernetes API server for a 200 second timeout
|
||||
bash -c 'timeout 200s kubectl port-forward service/kubernetes-'"${test_name}"' -n tenant-test '"${port}"':6443 > /dev/null 2>&1 &'
|
||||
bash -c 'timeout 300s kubectl port-forward service/kubernetes-'"${test_name}"' -n tenant-test '"${port}"':6443 > /dev/null 2>&1 &'
|
||||
# Verify the Kubernetes version matches what we expect (retry for up to 20 seconds)
|
||||
timeout 20 sh -ec 'until kubectl --kubeconfig tenantkubeconfig version 2>/dev/null | grep -Fq "Server Version: ${k8s_version}"; do sleep 5; done'
|
||||
|
||||
# Wait for the nodes to be ready (timeout after 2 minutes)
|
||||
timeout 2m bash -c '
|
||||
timeout 3m bash -c '
|
||||
until [ "$(kubectl --kubeconfig tenantkubeconfig get nodes -o jsonpath="{.items[*].metadata.name}" | wc -w)" -eq 2 ]; do
|
||||
sleep 3
|
||||
sleep 2
|
||||
done
|
||||
'
|
||||
# Verify the nodes are ready
|
||||
|
||||
@@ -132,7 +132,6 @@ machine:
|
||||
- usermode_helper=disabled
|
||||
- name: zfs
|
||||
- name: spl
|
||||
- name: lldpd
|
||||
registries:
|
||||
mirrors:
|
||||
docker.io:
|
||||
|
||||
@@ -58,8 +58,8 @@ func (m *Manager) ensureBreadcrumb(ctx context.Context, crd *cozyv1alpha1.Cozyst
|
||||
"breadcrumbItems": items,
|
||||
}
|
||||
|
||||
_, err := controllerutil.CreateOrUpdate(ctx, m.client, obj, func() error {
|
||||
if err := controllerutil.SetOwnerReference(crd, obj, m.scheme); err != nil {
|
||||
_, err := controllerutil.CreateOrUpdate(ctx, m.Client, obj, func() error {
|
||||
if err := controllerutil.SetOwnerReference(crd, obj, m.Scheme); err != nil {
|
||||
return err
|
||||
}
|
||||
// Add dashboard labels to dynamic resources
|
||||
|
||||
@@ -30,10 +30,6 @@ func (m *Manager) ensureCustomColumnsOverride(ctx context.Context, crd *cozyv1al
|
||||
name := fmt.Sprintf("stock-namespace-%s.%s.%s", g, v, plural)
|
||||
id := fmt.Sprintf("stock-namespace-/%s/%s/%s", g, v, plural)
|
||||
|
||||
// Badge content & color derived from kind
|
||||
badgeText := initialsFromKind(kind) // e.g., "VirtualMachine" -> "VM", "Bucket" -> "B"
|
||||
badgeColor := hexColorForKind(kind) // deterministic, dark enough for white text
|
||||
|
||||
obj := &dashv1alpha1.CustomColumnsOverride{}
|
||||
obj.SetName(name)
|
||||
|
||||
@@ -62,25 +58,11 @@ func (m *Manager) ensureCustomColumnsOverride(ctx context.Context, crd *cozyv1al
|
||||
},
|
||||
"children": []any{
|
||||
map[string]any{
|
||||
"type": "antdText",
|
||||
"type": "ResourceBadge",
|
||||
"data": map[string]any{
|
||||
"id": "header-badge",
|
||||
"text": badgeText,
|
||||
"title": strings.ToLower(kind), // optional tooltip
|
||||
"style": map[string]any{
|
||||
"backgroundColor": badgeColor,
|
||||
"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",
|
||||
},
|
||||
"value": kind,
|
||||
// abbreviation auto-generated by ResourceBadge from value
|
||||
},
|
||||
},
|
||||
map[string]any{
|
||||
@@ -145,8 +127,8 @@ func (m *Manager) ensureCustomColumnsOverride(ctx context.Context, crd *cozyv1al
|
||||
},
|
||||
}
|
||||
|
||||
_, err := controllerutil.CreateOrUpdate(ctx, m.client, obj, func() error {
|
||||
if err := controllerutil.SetOwnerReference(crd, obj, m.scheme); err != nil {
|
||||
_, err := controllerutil.CreateOrUpdate(ctx, m.Client, obj, func() error {
|
||||
if err := controllerutil.SetOwnerReference(crd, obj, m.Scheme); err != nil {
|
||||
return err
|
||||
}
|
||||
// Add dashboard labels to dynamic resources
|
||||
|
||||
@@ -53,8 +53,8 @@ func (m *Manager) ensureCustomFormsOverride(ctx context.Context, crd *cozyv1alph
|
||||
"strategy": "merge",
|
||||
}
|
||||
|
||||
_, err := controllerutil.CreateOrUpdate(ctx, m.client, obj, func() error {
|
||||
if err := controllerutil.SetOwnerReference(crd, obj, m.scheme); err != nil {
|
||||
_, err := controllerutil.CreateOrUpdate(ctx, m.Client, obj, func() error {
|
||||
if err := controllerutil.SetOwnerReference(crd, obj, m.Scheme); err != nil {
|
||||
return err
|
||||
}
|
||||
// Add dashboard labels to dynamic resources
|
||||
|
||||
@@ -56,8 +56,8 @@ func (m *Manager) ensureCustomFormsPrefill(ctx context.Context, crd *cozyv1alpha
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
|
||||
_, err = controllerutil.CreateOrUpdate(ctx, m.client, cfp, func() error {
|
||||
if err := controllerutil.SetOwnerReference(crd, cfp, m.scheme); err != nil {
|
||||
_, err = controllerutil.CreateOrUpdate(ctx, m.Client, cfp, func() error {
|
||||
if err := controllerutil.SetOwnerReference(crd, cfp, m.Scheme); err != nil {
|
||||
return err
|
||||
}
|
||||
// Add dashboard labels to dynamic resources
|
||||
|
||||
@@ -53,7 +53,6 @@ func (m *Manager) ensureFactory(ctx context.Context, crd *cozyv1alpha1.Cozystack
|
||||
Kind: kind,
|
||||
Plural: plural,
|
||||
Title: strings.ToLower(plural),
|
||||
Size: BadgeSizeLarge,
|
||||
}
|
||||
|
||||
spec := createUnifiedFactory(config, tabs, []any{resourceFetch})
|
||||
@@ -61,8 +60,8 @@ func (m *Manager) ensureFactory(ctx context.Context, crd *cozyv1alpha1.Cozystack
|
||||
obj := &dashv1alpha1.Factory{}
|
||||
obj.SetName(factoryName)
|
||||
|
||||
_, err := controllerutil.CreateOrUpdate(ctx, m.client, obj, func() error {
|
||||
if err := controllerutil.SetOwnerReference(crd, obj, m.scheme); err != nil {
|
||||
_, err := controllerutil.CreateOrUpdate(ctx, m.Client, obj, func() error {
|
||||
if err := controllerutil.SetOwnerReference(crd, obj, m.Scheme); err != nil {
|
||||
return err
|
||||
}
|
||||
// Add dashboard labels to dynamic resources
|
||||
@@ -115,7 +114,7 @@ func detailsTab(kind, endpoint, schemaJSON string, keysOrder [][]string) map[str
|
||||
"gap": float64(6),
|
||||
},
|
||||
"children": []any{
|
||||
createUnifiedBadgeFromKind("ns-badge", "Namespace", "namespace", BadgeSizeMedium),
|
||||
createUnifiedBadgeFromKind("ns-badge", "Namespace"),
|
||||
antdLink("namespace-link",
|
||||
"{reqsJsonPath[0]['.metadata.namespace']['-']}",
|
||||
"/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/marketplace",
|
||||
@@ -324,6 +323,7 @@ func yamlTab(plural string) map[string]any {
|
||||
"type": "builtin",
|
||||
"typeName": plural,
|
||||
"prefillValuesRequestIndex": float64(0),
|
||||
"readOnly": true,
|
||||
"substractHeight": float64(400),
|
||||
},
|
||||
},
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
package dashboard
|
||||
|
||||
import (
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
@@ -57,97 +54,6 @@ func pickPlural(kind string, crd *cozyv1alpha1.CozystackResourceDefinition) stri
|
||||
return k + "s"
|
||||
}
|
||||
|
||||
// initialsFromKind splits CamelCase and returns the first letters in upper case.
|
||||
// "VirtualMachine" -> "VM"; "Bucket" -> "B".
|
||||
func initialsFromKind(kind string) string {
|
||||
parts := splitCamel(kind)
|
||||
if len(parts) == 0 {
|
||||
return strings.ToUpper(kind)
|
||||
}
|
||||
var b strings.Builder
|
||||
for _, p := range parts {
|
||||
if p == "" {
|
||||
continue
|
||||
}
|
||||
b.WriteString(strings.ToUpper(string(p[0])))
|
||||
// Limit to 3 chars to keep the badge compact (VM, PVC, etc.)
|
||||
if b.Len() >= 3 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// hexColorForKind returns a dark, saturated color (hex) derived from a stable hash of the kind.
|
||||
// We map the hash to an HSL hue; fix S/L for consistent readability with white text.
|
||||
func hexColorForKind(kind string) string {
|
||||
// Stable short hash (sha1 → bytes → hue)
|
||||
sum := sha1.Sum([]byte(kind))
|
||||
// Use first two bytes for hue [0..359]
|
||||
hue := int(sum[0])<<8 | int(sum[1])
|
||||
hue = hue % 360
|
||||
|
||||
// Fixed S/L chosen to contrast with white text:
|
||||
// S = 80%, L = 35% (dark enough so #fff is readable)
|
||||
r, g, b := hslToRGB(float64(hue), 0.80, 0.35)
|
||||
|
||||
return fmt.Sprintf("#%02x%02x%02x", r, g, b)
|
||||
}
|
||||
|
||||
// hslToRGB converts HSL (0..360, 0..1, 0..1) to sRGB (0..255).
|
||||
func hslToRGB(h float64, s float64, l float64) (uint8, uint8, uint8) {
|
||||
c := (1 - absFloat(2*l-1)) * s
|
||||
hp := h / 60.0
|
||||
x := c * (1 - absFloat(modFloat(hp, 2)-1))
|
||||
var r1, g1, b1 float64
|
||||
switch {
|
||||
case 0 <= hp && hp < 1:
|
||||
r1, g1, b1 = c, x, 0
|
||||
case 1 <= hp && hp < 2:
|
||||
r1, g1, b1 = x, c, 0
|
||||
case 2 <= hp && hp < 3:
|
||||
r1, g1, b1 = 0, c, x
|
||||
case 3 <= hp && hp < 4:
|
||||
r1, g1, b1 = 0, x, c
|
||||
case 4 <= hp && hp < 5:
|
||||
r1, g1, b1 = x, 0, c
|
||||
default:
|
||||
r1, g1, b1 = c, 0, x
|
||||
}
|
||||
m := l - c/2
|
||||
r := uint8(clamp01(r1+m) * 255.0)
|
||||
g := uint8(clamp01(g1+m) * 255.0)
|
||||
b := uint8(clamp01(b1+m) * 255.0)
|
||||
return r, g, b
|
||||
}
|
||||
|
||||
func absFloat(v float64) float64 {
|
||||
if v < 0 {
|
||||
return -v
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
func modFloat(a, b float64) float64 {
|
||||
return a - b*float64(int(a/b))
|
||||
}
|
||||
|
||||
func clamp01(v float64) float64 {
|
||||
if v < 0 {
|
||||
return 0
|
||||
}
|
||||
if v > 1 {
|
||||
return 1
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// optional: tiny helper to expose the compact color hash (useful for debugging)
|
||||
func shortHashHex(s string) string {
|
||||
sum := sha1.Sum([]byte(s))
|
||||
return hex.EncodeToString(sum[:4])
|
||||
}
|
||||
|
||||
// ----------------------- Helpers (OpenAPI → values) -----------------------
|
||||
|
||||
// defaultOrZero returns the schema default if present; otherwise a reasonable zero value.
|
||||
@@ -295,12 +201,6 @@ func normalizeJSON(v any) any {
|
||||
}
|
||||
}
|
||||
|
||||
var camelSplitter = regexp.MustCompile(`(?m)([A-Z]+[a-z0-9]*|[a-z0-9]+)`)
|
||||
|
||||
func splitCamel(s string) []string {
|
||||
return camelSplitter.FindAllString(s, -1)
|
||||
}
|
||||
|
||||
// --- helpers for schema inspection ---
|
||||
|
||||
func isScalarType(n map[string]any) bool {
|
||||
|
||||
@@ -10,7 +10,9 @@ import (
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
@@ -40,28 +42,42 @@ func AddToScheme(s *runtime.Scheme) error {
|
||||
// Manager owns logic for creating/updating dashboard resources derived from CRDs.
|
||||
// It’s easy to extend: add new ensure* methods and wire them into EnsureForCRD.
|
||||
type Manager struct {
|
||||
client client.Client
|
||||
scheme *runtime.Scheme
|
||||
crdListFn func(context.Context) ([]cozyv1alpha1.CozystackResourceDefinition, error)
|
||||
}
|
||||
|
||||
// Option pattern so callers can inject a custom lister.
|
||||
type Option func(*Manager)
|
||||
|
||||
// WithCRDListFunc overrides how Manager lists all CozystackResourceDefinitions.
|
||||
func WithCRDListFunc(fn func(context.Context) ([]cozyv1alpha1.CozystackResourceDefinition, error)) Option {
|
||||
return func(m *Manager) { m.crdListFn = fn }
|
||||
client.Client
|
||||
Scheme *runtime.Scheme
|
||||
}
|
||||
|
||||
// NewManager constructs a dashboard Manager.
|
||||
func NewManager(c client.Client, scheme *runtime.Scheme, opts ...Option) *Manager {
|
||||
m := &Manager{client: c, scheme: scheme}
|
||||
for _, o := range opts {
|
||||
o(m)
|
||||
}
|
||||
func NewManager(c client.Client, scheme *runtime.Scheme) *Manager {
|
||||
m := &Manager{Client: c, Scheme: scheme}
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *Manager) SetupWithManager(mgr ctrl.Manager) error {
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
Named("dashboard-reconciler").
|
||||
For(&cozyv1alpha1.CozystackResourceDefinition{}).
|
||||
Complete(m)
|
||||
}
|
||||
|
||||
func (m *Manager) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
l := log.FromContext(ctx)
|
||||
|
||||
crd := &cozyv1alpha1.CozystackResourceDefinition{}
|
||||
|
||||
err := m.Get(ctx, types.NamespacedName{Name: req.Name}, crd)
|
||||
if err != nil {
|
||||
if apierrors.IsNotFound(err) {
|
||||
if err := m.CleanupOrphanedResources(ctx); err != nil {
|
||||
l.Error(err, "Failed to cleanup orphaned dashboard resources")
|
||||
}
|
||||
return ctrl.Result{}, nil // no point in requeuing here
|
||||
}
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
return m.EnsureForCRD(ctx, crd)
|
||||
}
|
||||
|
||||
// EnsureForCRD is the single entry-point used by the controller.
|
||||
// Add more ensure* calls here as you implement support for other resources:
|
||||
//
|
||||
@@ -171,21 +187,11 @@ func (m *Manager) getStaticResourceSelector() client.MatchingLabels {
|
||||
// CleanupOrphanedResources removes dashboard resources that are no longer needed
|
||||
// This should be called after cache warming to ensure all current resources are known
|
||||
func (m *Manager) CleanupOrphanedResources(ctx context.Context) error {
|
||||
// Get all current CRDs to determine which resources should exist
|
||||
var allCRDs []cozyv1alpha1.CozystackResourceDefinition
|
||||
if m.crdListFn != nil {
|
||||
s, err := m.crdListFn(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
allCRDs = s
|
||||
} else {
|
||||
var crdList cozyv1alpha1.CozystackResourceDefinitionList
|
||||
if err := m.client.List(ctx, &crdList, &client.ListOptions{}); err != nil {
|
||||
return err
|
||||
}
|
||||
allCRDs = crdList.Items
|
||||
var crdList cozyv1alpha1.CozystackResourceDefinitionList
|
||||
if err := m.List(ctx, &crdList, &client.ListOptions{}); err != nil {
|
||||
return err
|
||||
}
|
||||
allCRDs := crdList.Items
|
||||
|
||||
// Build a set of expected resource names for each type
|
||||
expectedResources := m.buildExpectedResourceSet(allCRDs)
|
||||
@@ -349,7 +355,7 @@ func (m *Manager) cleanupResourceType(ctx context.Context, resourceType client.O
|
||||
}
|
||||
|
||||
// List with dashboard labels
|
||||
if err := m.client.List(ctx, list, m.getDashboardResourceSelector()); err != nil {
|
||||
if err := m.List(ctx, list, m.getDashboardResourceSelector()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -358,7 +364,7 @@ func (m *Manager) cleanupResourceType(ctx context.Context, resourceType client.O
|
||||
case *dashv1alpha1.CustomColumnsOverrideList:
|
||||
for _, item := range l.Items {
|
||||
if !expected[item.Name] {
|
||||
if err := m.client.Delete(ctx, &item); err != nil {
|
||||
if err := m.Delete(ctx, &item); err != nil {
|
||||
if !apierrors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
@@ -369,7 +375,7 @@ func (m *Manager) cleanupResourceType(ctx context.Context, resourceType client.O
|
||||
case *dashv1alpha1.CustomFormsOverrideList:
|
||||
for _, item := range l.Items {
|
||||
if !expected[item.Name] {
|
||||
if err := m.client.Delete(ctx, &item); err != nil {
|
||||
if err := m.Delete(ctx, &item); err != nil {
|
||||
if !apierrors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
@@ -380,7 +386,7 @@ func (m *Manager) cleanupResourceType(ctx context.Context, resourceType client.O
|
||||
case *dashv1alpha1.CustomFormsPrefillList:
|
||||
for _, item := range l.Items {
|
||||
if !expected[item.Name] {
|
||||
if err := m.client.Delete(ctx, &item); err != nil {
|
||||
if err := m.Delete(ctx, &item); err != nil {
|
||||
if !apierrors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
@@ -391,7 +397,7 @@ func (m *Manager) cleanupResourceType(ctx context.Context, resourceType client.O
|
||||
case *dashv1alpha1.MarketplacePanelList:
|
||||
for _, item := range l.Items {
|
||||
if !expected[item.Name] {
|
||||
if err := m.client.Delete(ctx, &item); err != nil {
|
||||
if err := m.Delete(ctx, &item); err != nil {
|
||||
if !apierrors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
@@ -402,7 +408,7 @@ func (m *Manager) cleanupResourceType(ctx context.Context, resourceType client.O
|
||||
case *dashv1alpha1.SidebarList:
|
||||
for _, item := range l.Items {
|
||||
if !expected[item.Name] {
|
||||
if err := m.client.Delete(ctx, &item); err != nil {
|
||||
if err := m.Delete(ctx, &item); err != nil {
|
||||
if !apierrors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
@@ -413,7 +419,7 @@ func (m *Manager) cleanupResourceType(ctx context.Context, resourceType client.O
|
||||
case *dashv1alpha1.TableUriMappingList:
|
||||
for _, item := range l.Items {
|
||||
if !expected[item.Name] {
|
||||
if err := m.client.Delete(ctx, &item); err != nil {
|
||||
if err := m.Delete(ctx, &item); err != nil {
|
||||
if !apierrors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
@@ -426,7 +432,7 @@ func (m *Manager) cleanupResourceType(ctx context.Context, resourceType client.O
|
||||
if !expected[item.Name] {
|
||||
logger := log.FromContext(ctx)
|
||||
logger.Info("Deleting orphaned Breadcrumb resource", "name", item.Name)
|
||||
if err := m.client.Delete(ctx, &item); err != nil {
|
||||
if err := m.Delete(ctx, &item); err != nil {
|
||||
if !apierrors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
@@ -438,7 +444,7 @@ func (m *Manager) cleanupResourceType(ctx context.Context, resourceType client.O
|
||||
if !expected[item.Name] {
|
||||
logger := log.FromContext(ctx)
|
||||
logger.Info("Deleting orphaned Factory resource", "name", item.Name)
|
||||
if err := m.client.Delete(ctx, &item); err != nil {
|
||||
if err := m.Delete(ctx, &item); err != nil {
|
||||
if !apierrors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -24,14 +24,14 @@ func (m *Manager) ensureMarketplacePanel(ctx context.Context, crd *cozyv1alpha1.
|
||||
|
||||
// If dashboard is not set, delete the panel if it exists.
|
||||
if crd.Spec.Dashboard == nil {
|
||||
err := m.client.Get(ctx, client.ObjectKey{Name: mp.Name}, mp)
|
||||
err := m.Get(ctx, client.ObjectKey{Name: mp.Name}, mp)
|
||||
if apierrors.IsNotFound(err) {
|
||||
return reconcile.Result{}, nil
|
||||
}
|
||||
if err != nil {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
if err := m.client.Delete(ctx, mp); err != nil && !apierrors.IsNotFound(err) {
|
||||
if err := m.Delete(ctx, mp); err != nil && !apierrors.IsNotFound(err) {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
logger.Info("Deleted MarketplacePanel because dashboard is not set", "name", mp.Name)
|
||||
@@ -40,14 +40,14 @@ func (m *Manager) ensureMarketplacePanel(ctx context.Context, crd *cozyv1alpha1.
|
||||
|
||||
// Skip module and tenant resources (they don't need MarketplacePanel)
|
||||
if crd.Spec.Dashboard.Module || crd.Spec.Application.Kind == "Tenant" {
|
||||
err := m.client.Get(ctx, client.ObjectKey{Name: mp.Name}, mp)
|
||||
err := m.Get(ctx, client.ObjectKey{Name: mp.Name}, mp)
|
||||
if apierrors.IsNotFound(err) {
|
||||
return reconcile.Result{}, nil
|
||||
}
|
||||
if err != nil {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
if err := m.client.Delete(ctx, mp); err != nil && !apierrors.IsNotFound(err) {
|
||||
if err := m.Delete(ctx, mp); err != nil && !apierrors.IsNotFound(err) {
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
logger.Info("Deleted MarketplacePanel because resource is a module", "name", mp.Name)
|
||||
@@ -86,8 +86,8 @@ func (m *Manager) ensureMarketplacePanel(ctx context.Context, crd *cozyv1alpha1.
|
||||
return reconcile.Result{}, err
|
||||
}
|
||||
|
||||
_, err = controllerutil.CreateOrUpdate(ctx, m.client, mp, func() error {
|
||||
if err := controllerutil.SetOwnerReference(crd, mp, m.scheme); err != nil {
|
||||
_, err = controllerutil.CreateOrUpdate(ctx, m.Client, mp, func() error {
|
||||
if err := controllerutil.SetOwnerReference(crd, mp, m.Scheme); err != nil {
|
||||
return err
|
||||
}
|
||||
// Add dashboard labels to dynamic resources
|
||||
|
||||
@@ -33,19 +33,11 @@ func (m *Manager) ensureSidebar(ctx context.Context, crd *cozyv1alpha1.Cozystack
|
||||
|
||||
// 1) Fetch all CRDs
|
||||
var all []cozyv1alpha1.CozystackResourceDefinition
|
||||
if m.crdListFn != nil {
|
||||
s, err := m.crdListFn(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
all = s
|
||||
} else {
|
||||
var crdList cozyv1alpha1.CozystackResourceDefinitionList
|
||||
if err := m.client.List(ctx, &crdList, &client.ListOptions{}); err != nil {
|
||||
return err
|
||||
}
|
||||
all = crdList.Items
|
||||
var crdList cozyv1alpha1.CozystackResourceDefinitionList
|
||||
if err := m.List(ctx, &crdList, &client.ListOptions{}); err != nil {
|
||||
return err
|
||||
}
|
||||
all = crdList.Items
|
||||
|
||||
// 2) Build category -> []item map (only for CRDs with spec.dashboard != nil)
|
||||
type item struct {
|
||||
@@ -251,7 +243,7 @@ func (m *Manager) upsertMultipleSidebars(
|
||||
obj := &dashv1alpha1.Sidebar{}
|
||||
obj.SetName(id)
|
||||
|
||||
if _, err := controllerutil.CreateOrUpdate(ctx, m.client, obj, func() error {
|
||||
if _, err := controllerutil.CreateOrUpdate(ctx, m.Client, obj, func() error {
|
||||
// Only set owner reference for dynamic sidebars (stock-project-factory-{kind}-details)
|
||||
// Static sidebars (stock-instance-*, stock-project-*) should not have owner references
|
||||
if strings.HasPrefix(id, "stock-project-factory-") && strings.HasSuffix(id, "-details") {
|
||||
@@ -260,7 +252,7 @@ func (m *Manager) upsertMultipleSidebars(
|
||||
lowerKind := strings.ToLower(kind)
|
||||
expectedID := fmt.Sprintf("stock-project-factory-%s-details", lowerKind)
|
||||
if id == expectedID {
|
||||
if err := controllerutil.SetOwnerReference(crd, obj, m.scheme); err != nil {
|
||||
if err := controllerutil.SetOwnerReference(crd, obj, m.Scheme); err != nil {
|
||||
return err
|
||||
}
|
||||
// Add dashboard labels to dynamic resources
|
||||
|
||||
@@ -531,7 +531,6 @@ func createBreadcrumbItem(key, label string, link ...string) map[string]any {
|
||||
|
||||
// createCustomColumn creates a custom column with factory type and badge
|
||||
func createCustomColumn(name, kind, plural, href string) map[string]any {
|
||||
badge := createUnifiedBadgeFromKind("header-badge", kind, plural, BadgeSizeMedium)
|
||||
link := antdLink("name-link", "{reqsJsonPath[0]['.metadata.name']['-']}", href)
|
||||
|
||||
return map[string]any{
|
||||
@@ -541,8 +540,18 @@ func createCustomColumn(name, kind, plural, href string) map[string]any {
|
||||
"disableEventBubbling": true,
|
||||
"items": []any{
|
||||
map[string]any{
|
||||
"children": []any{badge, link},
|
||||
"type": "antdFlex",
|
||||
"children": []any{
|
||||
map[string]any{
|
||||
"type": "ResourceBadge",
|
||||
"data": map[string]any{
|
||||
"id": "header-badge",
|
||||
"value": kind,
|
||||
// abbreviation auto-generated by ResourceBadge from value
|
||||
},
|
||||
},
|
||||
link,
|
||||
},
|
||||
"type": "antdFlex",
|
||||
"data": map[string]any{
|
||||
"align": "center",
|
||||
"gap": float64(6),
|
||||
@@ -554,16 +563,16 @@ func createCustomColumn(name, kind, plural, href string) map[string]any {
|
||||
}
|
||||
|
||||
// createCustomColumnWithBadge creates a custom column with a specific badge
|
||||
func createCustomColumnWithBadge(name, badgeText, badgeColor, title, href string) map[string]any {
|
||||
config := BadgeConfig{
|
||||
Text: badgeText,
|
||||
Color: badgeColor,
|
||||
Title: title,
|
||||
Size: BadgeSizeMedium,
|
||||
}
|
||||
badge := createUnifiedBadge("header-badge", config)
|
||||
// badgeValue should be the kind in PascalCase (e.g., "Service", "Pod")
|
||||
// abbreviation is auto-generated by ResourceBadge from badgeValue
|
||||
func createCustomColumnWithBadge(name, badgeValue, href string) map[string]any {
|
||||
link := antdLink("name-link", "{reqsJsonPath[0]['.metadata.name']['-']}", href)
|
||||
|
||||
badgeData := map[string]any{
|
||||
"id": "header-badge",
|
||||
"value": badgeValue,
|
||||
}
|
||||
|
||||
return map[string]any{
|
||||
"name": name,
|
||||
"type": "factory",
|
||||
@@ -571,8 +580,14 @@ func createCustomColumnWithBadge(name, badgeText, badgeColor, title, href string
|
||||
"disableEventBubbling": true,
|
||||
"items": []any{
|
||||
map[string]any{
|
||||
"children": []any{badge, link},
|
||||
"type": "antdFlex",
|
||||
"children": []any{
|
||||
map[string]any{
|
||||
"type": "ResourceBadge",
|
||||
"data": badgeData,
|
||||
},
|
||||
link,
|
||||
},
|
||||
"type": "antdFlex",
|
||||
"data": map[string]any{
|
||||
"align": "center",
|
||||
"gap": float64(6),
|
||||
@@ -583,17 +598,22 @@ func createCustomColumnWithBadge(name, badgeText, badgeColor, title, href string
|
||||
}
|
||||
}
|
||||
|
||||
// createCustomColumnWithSpecificColor creates a custom column with a specific color
|
||||
func createCustomColumnWithSpecificColor(name, kind, title, color, href string) map[string]any {
|
||||
config := BadgeConfig{
|
||||
Text: initialsFromKind(kind),
|
||||
Color: color,
|
||||
Title: title,
|
||||
Size: BadgeSizeMedium,
|
||||
}
|
||||
badge := createUnifiedBadge("header-badge", config)
|
||||
// createCustomColumnWithSpecificColor creates a custom column with a specific kind and optional color
|
||||
// badgeValue should be the kind in PascalCase (e.g., "Service", "Pod")
|
||||
func createCustomColumnWithSpecificColor(name, kind, color, href string) map[string]any {
|
||||
link := antdLink("name-link", "{reqsJsonPath[0]['.metadata.name']['-']}", href)
|
||||
|
||||
badgeData := map[string]any{
|
||||
"id": "header-badge",
|
||||
"value": kind,
|
||||
}
|
||||
// Add custom color if specified
|
||||
if color != "" {
|
||||
badgeData["style"] = map[string]any{
|
||||
"backgroundColor": color,
|
||||
}
|
||||
}
|
||||
|
||||
return map[string]any{
|
||||
"name": name,
|
||||
"type": "factory",
|
||||
@@ -602,8 +622,14 @@ func createCustomColumnWithSpecificColor(name, kind, title, color, href string)
|
||||
"disableEventBubbling": true,
|
||||
"items": []any{
|
||||
map[string]any{
|
||||
"children": []any{badge, link},
|
||||
"type": "antdFlex",
|
||||
"children": []any{
|
||||
map[string]any{
|
||||
"type": "ResourceBadge",
|
||||
"data": badgeData,
|
||||
},
|
||||
link,
|
||||
},
|
||||
"type": "antdFlex",
|
||||
"data": map[string]any{
|
||||
"align": "center",
|
||||
"gap": float64(6),
|
||||
@@ -668,7 +694,7 @@ func createTimestampColumn(name, jsonPath string) map[string]any {
|
||||
// createFactoryHeader creates a header for factory resources
|
||||
func createFactoryHeader(kind, plural string) map[string]any {
|
||||
lowerKind := strings.ToLower(kind)
|
||||
badge := createUnifiedBadgeFromKind("badge-"+lowerKind, kind, plural, BadgeSizeLarge)
|
||||
badge := createUnifiedBadgeFromKind("badge-"+lowerKind, kind)
|
||||
nameText := parsedText(lowerKind+"-name", "{reqsJsonPath[0]['.metadata.name']['-']}", map[string]any{
|
||||
"fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif",
|
||||
"fontSize": float64(20),
|
||||
@@ -718,13 +744,26 @@ func createFactorySpec(key string, sidebarTags []any, urlsToFetch []any, header
|
||||
}
|
||||
|
||||
// createCustomColumnWithJsonPath creates a column with a custom badge and link using jsonPath
|
||||
func createCustomColumnWithJsonPath(name, jsonPath, badgeText, badgeTitle, badgeColor, linkHref string) map[string]any {
|
||||
// badgeValue should be the kind in PascalCase (e.g., "Service", "VirtualMachine")
|
||||
// abbreviation is auto-generated by ResourceBadge from badgeValue
|
||||
func createCustomColumnWithJsonPath(name, jsonPath, badgeValue, badgeColor, linkHref string) map[string]any {
|
||||
// Determine link ID based on jsonPath
|
||||
linkId := "name-link"
|
||||
if jsonPath == ".metadata.namespace" {
|
||||
linkId = "namespace-link"
|
||||
}
|
||||
|
||||
badgeData := map[string]any{
|
||||
"id": "header-badge",
|
||||
"value": badgeValue,
|
||||
}
|
||||
// Add custom color if specified
|
||||
if badgeColor != "" {
|
||||
badgeData["style"] = map[string]any{
|
||||
"backgroundColor": badgeColor,
|
||||
}
|
||||
}
|
||||
|
||||
return map[string]any{
|
||||
"name": name,
|
||||
"type": "factory",
|
||||
@@ -741,26 +780,8 @@ func createCustomColumnWithJsonPath(name, jsonPath, badgeText, badgeTitle, badge
|
||||
},
|
||||
"children": []any{
|
||||
map[string]any{
|
||||
"type": "antdText",
|
||||
"data": map[string]any{
|
||||
"id": "header-badge",
|
||||
"text": badgeText,
|
||||
"title": badgeTitle,
|
||||
"style": map[string]any{
|
||||
"backgroundColor": badgeColor,
|
||||
"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",
|
||||
},
|
||||
},
|
||||
"type": "ResourceBadge",
|
||||
"data": badgeData,
|
||||
},
|
||||
map[string]any{
|
||||
"type": "antdLink",
|
||||
@@ -778,7 +799,20 @@ func createCustomColumnWithJsonPath(name, jsonPath, badgeText, badgeTitle, badge
|
||||
}
|
||||
|
||||
// createCustomColumnWithoutJsonPath creates a column with a custom badge and link without jsonPath
|
||||
func createCustomColumnWithoutJsonPath(name, badgeText, badgeTitle, badgeColor, linkHref string) map[string]any {
|
||||
// badgeValue should be the kind in PascalCase (e.g., "Node", "Pod")
|
||||
// abbreviation is auto-generated by ResourceBadge from badgeValue
|
||||
func createCustomColumnWithoutJsonPath(name, badgeValue, badgeColor, linkHref string) map[string]any {
|
||||
badgeData := map[string]any{
|
||||
"id": "header-badge",
|
||||
"value": badgeValue,
|
||||
}
|
||||
// Add custom color if specified
|
||||
if badgeColor != "" {
|
||||
badgeData["style"] = map[string]any{
|
||||
"backgroundColor": badgeColor,
|
||||
}
|
||||
}
|
||||
|
||||
return map[string]any{
|
||||
"name": name,
|
||||
"type": "factory",
|
||||
@@ -794,26 +828,8 @@ func createCustomColumnWithoutJsonPath(name, badgeText, badgeTitle, badgeColor,
|
||||
},
|
||||
"children": []any{
|
||||
map[string]any{
|
||||
"type": "antdText",
|
||||
"data": map[string]any{
|
||||
"id": "header-badge",
|
||||
"text": badgeText,
|
||||
"title": badgeTitle,
|
||||
"style": map[string]any{
|
||||
"backgroundColor": badgeColor,
|
||||
"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",
|
||||
},
|
||||
},
|
||||
"type": "ResourceBadge",
|
||||
"data": badgeData,
|
||||
},
|
||||
map[string]any{
|
||||
"type": "antdLink",
|
||||
|
||||
@@ -32,7 +32,7 @@ func (m *Manager) ensureStaticResource(ctx context.Context, obj client.Object) e
|
||||
// Add dashboard labels to static resources
|
||||
m.addDashboardLabels(resource, nil, ResourceTypeStatic)
|
||||
|
||||
_, err := controllerutil.CreateOrUpdate(ctx, m.client, resource, func() error {
|
||||
_, err := controllerutil.CreateOrUpdate(ctx, m.Client, resource, func() error {
|
||||
// For static resources, we don't need to set owner references
|
||||
// as they are meant to be persistent across CRD changes
|
||||
// Copy Spec from the original object to the live object
|
||||
|
||||
@@ -132,7 +132,7 @@ func CreateAllCustomColumnsOverrides() []*dashboardv1alpha1.CustomColumnsOverrid
|
||||
return []*dashboardv1alpha1.CustomColumnsOverride{
|
||||
// Factory details v1 services
|
||||
createCustomColumnsOverride("factory-details-v1.services", []any{
|
||||
createCustomColumnWithSpecificColor("Name", "Service", "service", getColorForType("service"), "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/kube-service-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
||||
createCustomColumnWithSpecificColor("Name", "Service", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/kube-service-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
||||
createStringColumn("ClusterIP", ".spec.clusterIP"),
|
||||
createStringColumn("LoadbalancerIP", ".spec.loadBalancerIP"),
|
||||
createTimestampColumn("Created", ".metadata.creationTimestamp"),
|
||||
@@ -140,7 +140,7 @@ func CreateAllCustomColumnsOverrides() []*dashboardv1alpha1.CustomColumnsOverrid
|
||||
|
||||
// Stock namespace v1 services
|
||||
createCustomColumnsOverride("stock-namespace-/v1/services", []any{
|
||||
createCustomColumnWithJsonPath("Name", ".metadata.name", "S", "service", getColorForType("service"), "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/kube-service-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
||||
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"),
|
||||
@@ -148,7 +148,7 @@ func CreateAllCustomColumnsOverrides() []*dashboardv1alpha1.CustomColumnsOverrid
|
||||
|
||||
// Stock namespace core cozystack io v1alpha1 tenantmodules
|
||||
createCustomColumnsOverride("stock-namespace-/core.cozystack.io/v1alpha1/tenantmodules", []any{
|
||||
createCustomColumnWithJsonPath("Name", ".metadata.name", "M", "module", getColorForType("module"), "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/{reqsJsonPath[0]['.metadata.name']['-']}-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
||||
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"),
|
||||
@@ -164,7 +164,7 @@ func CreateAllCustomColumnsOverrides() []*dashboardv1alpha1.CustomColumnsOverrid
|
||||
|
||||
// Factory details v1alpha1 cozystack io workloadmonitors
|
||||
createCustomColumnsOverride("factory-details-v1alpha1.cozystack.io.workloadmonitors", []any{
|
||||
createCustomColumnWithJsonPath("Name", ".metadata.name", "W", "workloadmonitor", getColorForType("workloadmonitor"), "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/workloadmonitor-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
||||
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"),
|
||||
@@ -175,7 +175,7 @@ func CreateAllCustomColumnsOverrides() []*dashboardv1alpha1.CustomColumnsOverrid
|
||||
|
||||
// Factory details v1alpha1 core cozystack io tenantsecretstables
|
||||
createCustomColumnsOverride("factory-details-v1alpha1.core.cozystack.io.tenantsecretstables", []any{
|
||||
createCustomColumnWithJsonPath("Name", ".metadata.name", "S", "secret", getColorForType("secret"), "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/kube-secret-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
||||
createCustomColumnWithJsonPath("Name", ".metadata.name", "Secret", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/kube-secret-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
||||
createStringColumn("Key", ".data.key"),
|
||||
createSecretBase64Column("Value", ".data.value"),
|
||||
createTimestampColumn("Created", ".metadata.creationTimestamp"),
|
||||
@@ -184,7 +184,7 @@ func CreateAllCustomColumnsOverrides() []*dashboardv1alpha1.CustomColumnsOverrid
|
||||
// Factory ingress details rules
|
||||
createCustomColumnsOverride("factory-kube-ingress-details-rules", []any{
|
||||
createStringColumn("Host", ".host"),
|
||||
createCustomColumnWithJsonPath("Service", ".http.paths[0].backend.service.name", "S", "service", getColorForType("service"), "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/kube-service-details/{reqsJsonPath[0]['.http.paths[0].backend.service.name']['-']}"),
|
||||
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"),
|
||||
}),
|
||||
@@ -250,7 +250,7 @@ func CreateAllCustomColumnsOverrides() []*dashboardv1alpha1.CustomColumnsOverrid
|
||||
|
||||
// Factory details networking k8s io v1 ingresses
|
||||
createCustomColumnsOverride("factory-details-networking.k8s.io.v1.ingresses", []any{
|
||||
createCustomColumnWithJsonPath("Name", ".metadata.name", "I", "ingress", getColorForType("ingress"), "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/kube-ingress-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
||||
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"),
|
||||
@@ -259,7 +259,7 @@ func CreateAllCustomColumnsOverrides() []*dashboardv1alpha1.CustomColumnsOverrid
|
||||
|
||||
// Stock namespace networking k8s io v1 ingresses
|
||||
createCustomColumnsOverride("stock-namespace-/networking.k8s.io/v1/ingresses", []any{
|
||||
createCustomColumnWithJsonPath("Name", ".metadata.name", "I", "ingress", getColorForType("ingress"), "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/kube-ingress-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
||||
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"),
|
||||
@@ -268,34 +268,34 @@ func CreateAllCustomColumnsOverrides() []*dashboardv1alpha1.CustomColumnsOverrid
|
||||
|
||||
// Stock cluster v1 configmaps
|
||||
createCustomColumnsOverride("stock-cluster-/v1/configmaps", []any{
|
||||
createCustomColumnWithJsonPath("Name", ".metadata.name", "CM", "configmap", getColorForType("configmap"), "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/configmap-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
||||
createCustomColumnWithJsonPath("Namespace", ".metadata.namespace", "NS", "namespace", getColorForType("namespace"), "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/marketplace"),
|
||||
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", "CM", "configmap", getColorForType("configmap"), "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/configmap-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
||||
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", "CM", "configmap", getColorForType("configmap"), "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/configmap-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
||||
createCustomColumnWithJsonPath("Namespace", ".metadata.namespace", "NS", "namespace", getColorForType("namespace"), "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/marketplace"),
|
||||
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", "N", "node", getColorForType("node"), "/openapi-ui/{2}/factory/node-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
||||
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", "P", "pod", getColorForType("pod"), "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/pod-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
||||
createCustomColumnWithJsonPath("Namespace", ".metadata.namespace", "NS", "namespace", getColorForType("namespace"), "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/marketplace"),
|
||||
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"),
|
||||
@@ -304,8 +304,8 @@ func CreateAllCustomColumnsOverrides() []*dashboardv1alpha1.CustomColumnsOverrid
|
||||
|
||||
// Factory v1 pods
|
||||
createCustomColumnsOverride("factory-v1.pods", []any{
|
||||
createCustomColumnWithJsonPath("Name", ".metadata.name", "P", "pod", getColorForType("pod"), "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/pod-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
||||
createCustomColumnWithoutJsonPath("Node", "N", "node", getColorForType("node"), "/openapi-ui/{2}/factory/node-details/{reqsJsonPath[0]['.spec.nodeName']['-']}"),
|
||||
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"),
|
||||
@@ -314,9 +314,9 @@ func CreateAllCustomColumnsOverrides() []*dashboardv1alpha1.CustomColumnsOverrid
|
||||
|
||||
// Stock cluster v1 pods
|
||||
createCustomColumnsOverride("stock-cluster-/v1/pods", []any{
|
||||
createCustomColumnWithJsonPath("Name", ".metadata.name", "P", "pod", "#009596", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/pod-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
||||
createCustomColumnWithJsonPath("Namespace", ".metadata.namespace", "NS", "namespace", "#a25792ff", "/openapi-ui/{2}/factory/tenantnamespace/{reqsJsonPath[0]['.metadata.namespace']['-']}"),
|
||||
createCustomColumnWithJsonPath("Node", ".spec.nodeName", "N", "node", "#8476d1", "/openapi-ui/{2}/factory/node-details/{reqsJsonPath[0]['.spec.nodeName']['-']}"),
|
||||
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"),
|
||||
@@ -325,8 +325,8 @@ func CreateAllCustomColumnsOverrides() []*dashboardv1alpha1.CustomColumnsOverrid
|
||||
|
||||
// Stock namespace v1 pods
|
||||
createCustomColumnsOverride("stock-namespace-/v1/pods", []any{
|
||||
createCustomColumnWithJsonPath("Name", ".metadata.name", "P", "pod", "#009596", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/pod-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
||||
createCustomColumnWithoutJsonPath("Node", "N", "node", "#8476d1", "/openapi-ui/{2}/factory/node-details/{reqsJsonPath[0]['.spec.nodeName']['-']}"),
|
||||
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"),
|
||||
@@ -335,15 +335,15 @@ func CreateAllCustomColumnsOverrides() []*dashboardv1alpha1.CustomColumnsOverrid
|
||||
|
||||
// Stock cluster v1 secrets
|
||||
createCustomColumnsOverride("stock-cluster-/v1/secrets", []any{
|
||||
createCustomColumnWithJsonPath("Name", ".metadata.name", "S", "secret", "#c46100", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/kube-secret-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
||||
createCustomColumnWithJsonPath("Namespace", ".metadata.namespace", "NS", "namespace", "#a25792ff", "/openapi-ui/{2}/factory/tenantnamespace/{reqsJsonPath[0]['.metadata.namespace']['-']}"),
|
||||
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", "S", "secret", "#c46100", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/kube-secret-details/{reqsJsonPath[0]['.metadata.name']['-']}"),
|
||||
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"),
|
||||
}),
|
||||
@@ -360,7 +360,7 @@ func CreateAllCustomColumnsOverrides() []*dashboardv1alpha1.CustomColumnsOverrid
|
||||
|
||||
// Stock cluster core cozystack io v1alpha1 tenantnamespaces
|
||||
createCustomColumnsOverride("stock-cluster-/core.cozystack.io/v1alpha1/tenantnamespaces", []any{
|
||||
createCustomColumnWithJsonPath("Name", ".metadata.name", "TN", "tenantnamespace", getColorForType("tenantnamespace"), "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.name']['-']}/factory/marketplace"),
|
||||
createCustomColumnWithJsonPath("Name", ".metadata.name", "TenantNamespace", "", "/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.name']['-']}/factory/marketplace"),
|
||||
createTimestampColumn("Created", ".metadata.creationTimestamp"),
|
||||
}),
|
||||
}
|
||||
@@ -496,7 +496,6 @@ func CreateAllFactories() []*dashboardv1alpha1.Factory {
|
||||
Kind: "Namespace",
|
||||
Plural: "namespaces",
|
||||
Title: "namespace",
|
||||
Size: BadgeSizeLarge,
|
||||
}
|
||||
namespaceSpec := createUnifiedFactory(namespaceConfig, nil, []any{"/api/clusters/{2}/k8s/api/v1/namespaces/{5}"})
|
||||
|
||||
@@ -796,6 +795,7 @@ func CreateAllFactories() []*dashboardv1alpha1.Factory {
|
||||
"substractHeight": float64(400),
|
||||
"type": "builtin",
|
||||
"typeName": "secrets",
|
||||
"readOnly": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1201,7 +1201,7 @@ func CreateAllFactories() []*dashboardv1alpha1.Factory {
|
||||
"gap": 6,
|
||||
},
|
||||
"children": []any{
|
||||
createUnifiedBadgeFromKind("ns-badge", "Namespace", "namespace", BadgeSizeMedium),
|
||||
createUnifiedBadgeFromKind("ns-badge", "Namespace"),
|
||||
antdLink("namespace-link",
|
||||
"{reqsJsonPath[0]['.metadata.namespace']['-']}",
|
||||
"/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/marketplace",
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package dashboard
|
||||
|
||||
import "strings"
|
||||
|
||||
// ---------------- UI helpers (use float64 for numeric fields) ----------------
|
||||
|
||||
func contentCard(id string, style map[string]any, children []any) map[string]any {
|
||||
@@ -200,10 +198,10 @@ func createBadge(id, text, color, title string) map[string]any {
|
||||
|
||||
// createBadgeFromKind creates a badge using the existing badge generation functions
|
||||
func createBadgeFromKind(id, kind, title string) map[string]any {
|
||||
return createUnifiedBadgeFromKind(id, kind, title, BadgeSizeMedium)
|
||||
return createUnifiedBadgeFromKind(id, kind)
|
||||
}
|
||||
|
||||
// createHeaderBadge creates a badge specifically for headers with consistent styling
|
||||
func createHeaderBadge(id, kind, plural string) map[string]any {
|
||||
return createUnifiedBadgeFromKind(id, kind, strings.ToLower(plural), BadgeSizeLarge)
|
||||
return createUnifiedBadgeFromKind(id, kind)
|
||||
}
|
||||
|
||||
@@ -81,86 +81,47 @@ func isAlphanumeric(c byte) bool {
|
||||
|
||||
// BadgeConfig holds configuration for badge generation
|
||||
type BadgeConfig struct {
|
||||
Text string
|
||||
Color string
|
||||
Title string
|
||||
Size BadgeSize
|
||||
Kind string // Resource kind in PascalCase (e.g., "VirtualMachine") - used for value and auto-generation
|
||||
Text string // Optional abbreviation override (if empty, ResourceBadge auto-generates from Kind)
|
||||
Color string // Optional custom backgroundColor override
|
||||
}
|
||||
|
||||
// BadgeSize represents the size of the badge
|
||||
type BadgeSize int
|
||||
|
||||
const (
|
||||
BadgeSizeSmall BadgeSize = iota
|
||||
BadgeSizeMedium
|
||||
BadgeSizeLarge
|
||||
)
|
||||
|
||||
// generateBadgeConfig creates a BadgeConfig from kind and optional custom values
|
||||
func generateBadgeConfig(kind string, customText, customColor, customTitle string) BadgeConfig {
|
||||
config := BadgeConfig{
|
||||
Text: initialsFromKind(kind),
|
||||
Color: hexColorForKind(kind),
|
||||
Title: strings.ToLower(kind),
|
||||
Size: BadgeSizeMedium,
|
||||
}
|
||||
|
||||
// Override with custom values if provided
|
||||
if customText != "" {
|
||||
config.Text = customText
|
||||
}
|
||||
if customColor != "" {
|
||||
config.Color = customColor
|
||||
}
|
||||
if customTitle != "" {
|
||||
config.Title = customTitle
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
// createUnifiedBadge creates a badge using the unified BadgeConfig
|
||||
// createUnifiedBadge creates a badge using the unified BadgeConfig with ResourceBadge component
|
||||
func createUnifiedBadge(id string, config BadgeConfig) map[string]any {
|
||||
fontSize := "15px"
|
||||
if config.Size == BadgeSizeLarge {
|
||||
fontSize = "20px"
|
||||
} else if config.Size == BadgeSizeSmall {
|
||||
fontSize = "12px"
|
||||
data := map[string]any{
|
||||
"id": id,
|
||||
"value": config.Kind,
|
||||
}
|
||||
|
||||
// Add abbreviation override if specified (otherwise ResourceBadge auto-generates from Kind)
|
||||
if config.Text != "" {
|
||||
data["abbreviation"] = config.Text
|
||||
}
|
||||
|
||||
// Add custom color if specified
|
||||
if config.Color != "" {
|
||||
data["style"] = map[string]any{
|
||||
"backgroundColor": config.Color,
|
||||
}
|
||||
}
|
||||
|
||||
return map[string]any{
|
||||
"type": "antdText",
|
||||
"data": map[string]any{
|
||||
"id": id,
|
||||
"text": config.Text,
|
||||
"title": config.Title,
|
||||
"style": map[string]any{
|
||||
"backgroundColor": config.Color,
|
||||
"borderRadius": "20px",
|
||||
"color": "#fff",
|
||||
"display": "inline-block",
|
||||
"fontFamily": "RedHatDisplay, Overpass, overpass, helvetica, arial, sans-serif",
|
||||
"fontSize": fontSize,
|
||||
"fontWeight": float64(400),
|
||||
"lineHeight": "24px",
|
||||
"minWidth": float64(24),
|
||||
"padding": "0 9px",
|
||||
"textAlign": "center",
|
||||
"whiteSpace": "nowrap",
|
||||
},
|
||||
},
|
||||
"type": "ResourceBadge",
|
||||
"data": data,
|
||||
}
|
||||
}
|
||||
|
||||
// createUnifiedBadgeFromKind creates a badge from kind with automatic color generation
|
||||
func createUnifiedBadgeFromKind(id, kind, title string, size BadgeSize) map[string]any {
|
||||
config := BadgeConfig{
|
||||
Text: initialsFromKind(kind),
|
||||
Color: hexColorForKind(kind),
|
||||
Title: title,
|
||||
Size: size,
|
||||
// createUnifiedBadgeFromKind creates a badge from kind with ResourceBadge component
|
||||
// Abbreviation is auto-generated by ResourceBadge from kind, but can be customized if needed
|
||||
func createUnifiedBadgeFromKind(id, kind string) map[string]any {
|
||||
return map[string]any{
|
||||
"type": "ResourceBadge",
|
||||
"data": map[string]any{
|
||||
"id": id,
|
||||
"value": kind,
|
||||
// abbreviation is optional - ResourceBadge auto-generates from value
|
||||
},
|
||||
}
|
||||
return createUnifiedBadge(id, config)
|
||||
}
|
||||
|
||||
// ---------------- Resource creation helpers with unified approach ----------------
|
||||
@@ -183,7 +144,9 @@ func createResourceConfig(components []string, kind, title string) ResourceConfi
|
||||
metadataName := generateMetadataName(specID)
|
||||
|
||||
// Generate badge config
|
||||
badgeConfig := generateBadgeConfig(kind, "", "", title)
|
||||
badgeConfig := BadgeConfig{
|
||||
Kind: kind,
|
||||
}
|
||||
|
||||
return ResourceConfig{
|
||||
SpecID: specID,
|
||||
@@ -196,35 +159,6 @@ func createResourceConfig(components []string, kind, title string) ResourceConfi
|
||||
|
||||
// ---------------- Enhanced color generation ----------------
|
||||
|
||||
// getColorForKind returns a color for a specific kind with improved distribution
|
||||
func getColorForKind(kind string) string {
|
||||
// Use existing hexColorForKind function
|
||||
return hexColorForKind(kind)
|
||||
}
|
||||
|
||||
// getColorForType returns a color for a specific type (like "namespace", "service", etc.)
|
||||
func getColorForType(typeName string) string {
|
||||
// Map common types to specific colors for consistency
|
||||
colorMap := map[string]string{
|
||||
"namespace": "#a25792ff",
|
||||
"service": "#6ca100",
|
||||
"pod": "#009596",
|
||||
"node": "#8476d1",
|
||||
"secret": "#c46100",
|
||||
"configmap": "#b48c78ff",
|
||||
"ingress": "#2e7dff",
|
||||
"workloadmonitor": "#c46100",
|
||||
"module": "#8b5cf6",
|
||||
}
|
||||
|
||||
if color, exists := colorMap[strings.ToLower(typeName)]; exists {
|
||||
return color
|
||||
}
|
||||
|
||||
// Fall back to hash-based color generation
|
||||
return hexColorForKind(typeName)
|
||||
}
|
||||
|
||||
// ---------------- Automatic ID generation for UI elements ----------------
|
||||
|
||||
// generateElementID creates an ID for UI elements based on context and type
|
||||
@@ -282,7 +216,6 @@ type UnifiedResourceConfig struct {
|
||||
Title string
|
||||
Color string
|
||||
BadgeText string
|
||||
Size BadgeSize
|
||||
}
|
||||
|
||||
// createUnifiedFactory creates a factory using unified approach
|
||||
@@ -292,16 +225,9 @@ func createUnifiedFactory(config UnifiedResourceConfig, tabs []any, urlsToFetch
|
||||
|
||||
// Create header with unified badge
|
||||
badgeConfig := BadgeConfig{
|
||||
Kind: config.Kind,
|
||||
Text: config.BadgeText,
|
||||
Color: config.Color,
|
||||
Title: config.Title,
|
||||
Size: config.Size,
|
||||
}
|
||||
if badgeConfig.Text == "" {
|
||||
badgeConfig.Text = initialsFromKind(config.Kind)
|
||||
}
|
||||
if badgeConfig.Color == "" {
|
||||
badgeConfig.Color = getColorForKind(config.Kind)
|
||||
}
|
||||
|
||||
badge := createUnifiedBadge(generateBadgeID("header", config.Kind), badgeConfig)
|
||||
@@ -348,7 +274,9 @@ func createUnifiedFactory(config UnifiedResourceConfig, tabs []any, urlsToFetch
|
||||
|
||||
// createUnifiedCustomColumn creates a custom column using unified approach
|
||||
func createUnifiedCustomColumn(name, jsonPath, kind, title, href string) map[string]any {
|
||||
badgeConfig := generateBadgeConfig(kind, "", "", title)
|
||||
badgeConfig := BadgeConfig{
|
||||
Kind: kind,
|
||||
}
|
||||
badge := createUnifiedBadge(generateBadgeID("column", kind), badgeConfig)
|
||||
|
||||
linkID := generateLinkID("column", "name")
|
||||
|
||||
439
internal/controller/workloadmonitor_reconciler.go
Normal file
439
internal/controller/workloadmonitor_reconciler.go
Normal file
@@ -0,0 +1,439 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
cozyv1alpha1 "github.com/cozystack/cozystack/api/v1alpha1"
|
||||
helmv2 "github.com/fluxcd/helm-controller/api/v2"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/utils/pointer"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
)
|
||||
|
||||
// WorkloadMonitorFromCRDReconciler reconciles HelmReleases and creates WorkloadMonitors
|
||||
// based on CozystackResourceDefinition templates
|
||||
type WorkloadMonitorFromCRDReconciler struct {
|
||||
client.Client
|
||||
Scheme *runtime.Scheme
|
||||
}
|
||||
|
||||
// +kubebuilder:rbac:groups=helm.toolkit.fluxcd.io,resources=helmreleases,verbs=get;list;watch
|
||||
// +kubebuilder:rbac:groups=cozystack.io,resources=cozystackresourcedefinitions,verbs=get;list;watch
|
||||
// +kubebuilder:rbac:groups=cozystack.io,resources=workloadmonitors,verbs=get;list;watch;create;update;patch;delete
|
||||
// +kubebuilder:rbac:groups=core,resources=configmaps,verbs=get;list;watch
|
||||
|
||||
const (
|
||||
WorkloadMonitorOwnerLabel = "workloadmonitor.cozystack.io/owned-by-crd"
|
||||
WorkloadMonitorSourceLabel = "workloadmonitor.cozystack.io/helm-release"
|
||||
)
|
||||
|
||||
// Reconcile processes HelmRelease resources and creates corresponding WorkloadMonitors
|
||||
func (r *WorkloadMonitorFromCRDReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
logger := log.FromContext(ctx)
|
||||
|
||||
// Get the HelmRelease
|
||||
hr := &helmv2.HelmRelease{}
|
||||
if err := r.Get(ctx, req.NamespacedName, hr); err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
// HelmRelease deleted - cleanup will be handled by owner references
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
logger.Error(err, "unable to fetch HelmRelease")
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
// Skip system HelmReleases
|
||||
if strings.HasPrefix(hr.Name, "tenant-") {
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
// Find the matching CozystackResourceDefinition
|
||||
crd, err := r.findCRDForHelmRelease(ctx, hr)
|
||||
if err != nil {
|
||||
if errors.IsNotFound(err) {
|
||||
// No CRD found for this HelmRelease - skip
|
||||
logger.V(1).Info("No CozystackResourceDefinition found for HelmRelease", "name", hr.Name)
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
logger.Error(err, "unable to find CozystackResourceDefinition")
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
// If CRD doesn't have WorkloadMonitors, cleanup any existing ones we created
|
||||
if len(crd.Spec.WorkloadMonitors) == 0 {
|
||||
if err := r.cleanupWorkloadMonitors(ctx, hr); err != nil {
|
||||
logger.Error(err, "failed to cleanup WorkloadMonitors")
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
// Get the HelmRelease values for template rendering
|
||||
values, err := r.getHelmReleaseValues(ctx, hr)
|
||||
if err != nil {
|
||||
logger.Error(err, "unable to get HelmRelease values")
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
// Create/update WorkloadMonitors based on templates
|
||||
if err := r.reconcileWorkloadMonitors(ctx, hr, crd, values); err != nil {
|
||||
logger.Error(err, "failed to reconcile WorkloadMonitors")
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
// findCRDForHelmRelease finds the CozystackResourceDefinition for a given HelmRelease
|
||||
func (r *WorkloadMonitorFromCRDReconciler) findCRDForHelmRelease(ctx context.Context, hr *helmv2.HelmRelease) (*cozyv1alpha1.CozystackResourceDefinition, error) {
|
||||
// List all CozystackResourceDefinitions
|
||||
var crdList cozyv1alpha1.CozystackResourceDefinitionList
|
||||
if err := r.List(ctx, &crdList); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Match by chart name and prefix
|
||||
for i := range crdList.Items {
|
||||
crd := &crdList.Items[i]
|
||||
if crd.Spec.Release.Chart.Name == hr.Spec.Chart.Spec.Chart {
|
||||
// Check if HelmRelease name matches the prefix
|
||||
if strings.HasPrefix(hr.Name, crd.Spec.Release.Prefix) {
|
||||
return crd, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.NewNotFound(schema.GroupResource{Group: "cozystack.io", Resource: "cozystackresourcedefinitions"}, "")
|
||||
}
|
||||
|
||||
// getHelmReleaseValues extracts the values from HelmRelease spec
|
||||
func (r *WorkloadMonitorFromCRDReconciler) getHelmReleaseValues(ctx context.Context, hr *helmv2.HelmRelease) (map[string]interface{}, error) {
|
||||
if hr.Spec.Values == nil {
|
||||
return make(map[string]interface{}), nil
|
||||
}
|
||||
|
||||
// Convert apiextensionsv1.JSON to map
|
||||
values := make(map[string]interface{})
|
||||
if err := json.Unmarshal(hr.Spec.Values.Raw, &values); err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal values: %w", err)
|
||||
}
|
||||
|
||||
return values, nil
|
||||
}
|
||||
|
||||
// reconcileWorkloadMonitors creates or updates WorkloadMonitors based on CRD templates
|
||||
func (r *WorkloadMonitorFromCRDReconciler) reconcileWorkloadMonitors(
|
||||
ctx context.Context,
|
||||
hr *helmv2.HelmRelease,
|
||||
crd *cozyv1alpha1.CozystackResourceDefinition,
|
||||
values map[string]interface{},
|
||||
) error {
|
||||
logger := log.FromContext(ctx)
|
||||
|
||||
// Get chart version from HelmRelease
|
||||
chartVersion := ""
|
||||
if hr.Status.History != nil && len(hr.Status.History) > 0 {
|
||||
chartVersion = hr.Status.History[0].ChartVersion
|
||||
}
|
||||
|
||||
// Template context
|
||||
templateData := map[string]interface{}{
|
||||
"Release": map[string]interface{}{
|
||||
"Name": hr.Name,
|
||||
"Namespace": hr.Namespace,
|
||||
},
|
||||
"Chart": map[string]interface{}{
|
||||
"Version": chartVersion,
|
||||
},
|
||||
"Values": values,
|
||||
}
|
||||
|
||||
// Track which monitors we should have
|
||||
expectedMonitors := make(map[string]bool)
|
||||
|
||||
// Process each WorkloadMonitor template
|
||||
for _, tmpl := range crd.Spec.WorkloadMonitors {
|
||||
// Check condition
|
||||
if tmpl.Condition != "" {
|
||||
shouldCreate, err := evaluateCondition(tmpl.Condition, templateData)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to evaluate condition", "template", tmpl.Name, "condition", tmpl.Condition)
|
||||
continue
|
||||
}
|
||||
if !shouldCreate {
|
||||
logger.V(1).Info("Skipping WorkloadMonitor due to condition", "template", tmpl.Name)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// Render monitor name
|
||||
monitorName, err := renderTemplate(tmpl.Name, templateData)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to render monitor name", "template", tmpl.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
expectedMonitors[monitorName] = true
|
||||
|
||||
// Render selector values
|
||||
selector := make(map[string]string)
|
||||
for key, valueTmpl := range tmpl.Selector {
|
||||
renderedValue, err := renderTemplate(valueTmpl, templateData)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to render selector value", "key", key, "template", valueTmpl)
|
||||
continue
|
||||
}
|
||||
selector[key] = renderedValue
|
||||
}
|
||||
|
||||
// Render replicas
|
||||
var replicas *int32
|
||||
if tmpl.Replicas != "" {
|
||||
replicasStr, err := renderTemplate(tmpl.Replicas, templateData)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to render replicas", "template", tmpl.Replicas)
|
||||
} else {
|
||||
if replicasInt, err := strconv.ParseInt(replicasStr, 10, 32); err == nil {
|
||||
replicas = pointer.Int32(int32(replicasInt))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Render minReplicas
|
||||
var minReplicas *int32
|
||||
if tmpl.MinReplicas != "" {
|
||||
minReplicasStr, err := renderTemplate(tmpl.MinReplicas, templateData)
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to render minReplicas", "template", tmpl.MinReplicas)
|
||||
} else {
|
||||
if minReplicasInt, err := strconv.ParseInt(minReplicasStr, 10, 32); err == nil {
|
||||
minReplicas = pointer.Int32(int32(minReplicasInt))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create or update WorkloadMonitor
|
||||
monitor := &cozyv1alpha1.WorkloadMonitor{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: monitorName,
|
||||
Namespace: hr.Namespace,
|
||||
},
|
||||
}
|
||||
|
||||
_, err = controllerutil.CreateOrUpdate(ctx, r.Client, monitor, func() error {
|
||||
// Set labels
|
||||
if monitor.Labels == nil {
|
||||
monitor.Labels = make(map[string]string)
|
||||
}
|
||||
monitor.Labels[WorkloadMonitorOwnerLabel] = "true"
|
||||
monitor.Labels[WorkloadMonitorSourceLabel] = hr.Name
|
||||
|
||||
// Set owner reference to HelmRelease for automatic cleanup
|
||||
if err := controllerutil.SetControllerReference(hr, monitor, r.Scheme); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Update spec
|
||||
monitor.Spec.Selector = selector
|
||||
monitor.Spec.Kind = tmpl.Kind
|
||||
monitor.Spec.Type = tmpl.Type
|
||||
monitor.Spec.Version = chartVersion
|
||||
monitor.Spec.Replicas = replicas
|
||||
monitor.Spec.MinReplicas = minReplicas
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
logger.Error(err, "failed to create/update WorkloadMonitor", "name", monitorName)
|
||||
continue
|
||||
}
|
||||
|
||||
logger.V(1).Info("WorkloadMonitor reconciled", "name", monitorName)
|
||||
}
|
||||
|
||||
// Cleanup WorkloadMonitors that are no longer in templates
|
||||
if err := r.cleanupUnexpectedMonitors(ctx, hr, expectedMonitors); err != nil {
|
||||
logger.Error(err, "failed to cleanup unexpected WorkloadMonitors")
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// cleanupWorkloadMonitors removes all WorkloadMonitors created for a HelmRelease
|
||||
func (r *WorkloadMonitorFromCRDReconciler) cleanupWorkloadMonitors(ctx context.Context, hr *helmv2.HelmRelease) error {
|
||||
return r.cleanupUnexpectedMonitors(ctx, hr, make(map[string]bool))
|
||||
}
|
||||
|
||||
// cleanupUnexpectedMonitors removes WorkloadMonitors that are no longer expected
|
||||
func (r *WorkloadMonitorFromCRDReconciler) cleanupUnexpectedMonitors(
|
||||
ctx context.Context,
|
||||
hr *helmv2.HelmRelease,
|
||||
expectedMonitors map[string]bool,
|
||||
) error {
|
||||
logger := log.FromContext(ctx)
|
||||
|
||||
// List all WorkloadMonitors in the namespace that we created
|
||||
var monitorList cozyv1alpha1.WorkloadMonitorList
|
||||
labelSelector := labels.SelectorFromSet(labels.Set{
|
||||
WorkloadMonitorOwnerLabel: "true",
|
||||
WorkloadMonitorSourceLabel: hr.Name,
|
||||
})
|
||||
if err := r.List(ctx, &monitorList,
|
||||
client.InNamespace(hr.Namespace),
|
||||
client.MatchingLabelsSelector{Selector: labelSelector},
|
||||
); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete monitors that are not expected
|
||||
for i := range monitorList.Items {
|
||||
monitor := &monitorList.Items[i]
|
||||
if !expectedMonitors[monitor.Name] {
|
||||
logger.Info("Deleting unexpected WorkloadMonitor", "name", monitor.Name)
|
||||
if err := r.Delete(ctx, monitor); err != nil && !errors.IsNotFound(err) {
|
||||
logger.Error(err, "failed to delete WorkloadMonitor", "name", monitor.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// renderTemplate renders a Go template string with the given data
|
||||
func renderTemplate(tmplStr string, data interface{}) (string, error) {
|
||||
// Check if it's already a simple value (no template markers)
|
||||
if !strings.Contains(tmplStr, "{{") {
|
||||
return tmplStr, nil
|
||||
}
|
||||
|
||||
// Add Sprig functions for compatibility with Helm templates
|
||||
tmpl, err := template.New("").Funcs(getTemplateFuncs()).Parse(tmplStr)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to parse template: %w", err)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
if err := tmpl.Execute(&buf, data); err != nil {
|
||||
return "", fmt.Errorf("failed to execute template: %w", err)
|
||||
}
|
||||
|
||||
return strings.TrimSpace(buf.String()), nil
|
||||
}
|
||||
|
||||
// evaluateCondition evaluates a template condition (should return "true" or non-empty for true)
|
||||
func evaluateCondition(condition string, data interface{}) (bool, error) {
|
||||
result, err := renderTemplate(condition, data)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Check for truthy values
|
||||
result = strings.TrimSpace(strings.ToLower(result))
|
||||
return result == "true" || result == "1" || result == "yes", nil
|
||||
}
|
||||
|
||||
// getTemplateFuncs returns template functions compatible with Helm
|
||||
func getTemplateFuncs() template.FuncMap {
|
||||
return template.FuncMap{
|
||||
// Math functions
|
||||
"add": func(a, b int) int { return a + b },
|
||||
"sub": func(a, b int) int { return a - b },
|
||||
"mul": func(a, b int) int { return a * b },
|
||||
"div": func(a, b int) int {
|
||||
if b == 0 {
|
||||
return 0
|
||||
}
|
||||
return a / b
|
||||
},
|
||||
"add1": func(a int) int { return a + 1 },
|
||||
"sub1": func(a int) int { return a - 1 },
|
||||
|
||||
// String functions
|
||||
"upper": strings.ToUpper,
|
||||
"lower": strings.ToLower,
|
||||
"trim": strings.TrimSpace,
|
||||
"trimAll": func(cutset, s string) string { return strings.Trim(s, cutset) },
|
||||
"replace": func(old, new string, n int, s string) string { return strings.Replace(s, old, new, n) },
|
||||
|
||||
// Logic functions
|
||||
"default": func(defaultVal, val interface{}) interface{} {
|
||||
if val == nil || val == "" {
|
||||
return defaultVal
|
||||
}
|
||||
return val
|
||||
},
|
||||
"empty": func(val interface{}) bool {
|
||||
return val == nil || val == ""
|
||||
},
|
||||
"not": func(val bool) bool {
|
||||
return !val
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// SetupWithManager sets up the controller with the Manager
|
||||
func (r *WorkloadMonitorFromCRDReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
Named("workloadmonitor-from-crd-controller").
|
||||
For(&helmv2.HelmRelease{}).
|
||||
Owns(&cozyv1alpha1.WorkloadMonitor{}).
|
||||
Watches(
|
||||
&cozyv1alpha1.CozystackResourceDefinition{},
|
||||
handler.EnqueueRequestsFromMapFunc(r.mapCRDToHelmReleases),
|
||||
).
|
||||
Complete(r)
|
||||
}
|
||||
|
||||
// mapCRDToHelmReleases maps CRD changes to HelmRelease reconcile requests
|
||||
func (r *WorkloadMonitorFromCRDReconciler) mapCRDToHelmReleases(ctx context.Context, obj client.Object) []reconcile.Request {
|
||||
crd, ok := obj.(*cozyv1alpha1.CozystackResourceDefinition)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
// List all HelmReleases
|
||||
var hrList helmv2.HelmReleaseList
|
||||
if err := r.List(ctx, &hrList); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var requests []reconcile.Request
|
||||
for i := range hrList.Items {
|
||||
hr := &hrList.Items[i]
|
||||
// Skip tenant HelmReleases
|
||||
if strings.HasPrefix(hr.Name, "tenant-") {
|
||||
continue
|
||||
}
|
||||
// Match by chart name and prefix
|
||||
if crd.Spec.Release.Chart.Name == hr.Spec.Chart.Spec.Chart {
|
||||
if strings.HasPrefix(hr.Name, crd.Spec.Release.Prefix) {
|
||||
requests = append(requests, reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{
|
||||
Name: hr.Name,
|
||||
Namespace: hr.Namespace,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return requests
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
---
|
||||
apiVersion: cozystack.io/v1alpha1
|
||||
kind: WorkloadMonitor
|
||||
metadata:
|
||||
name: {{ $.Release.Name }}
|
||||
spec:
|
||||
replicas: {{ .Values.replicas }}
|
||||
minReplicas: 1
|
||||
kind: clickhouse
|
||||
type: clickhouse
|
||||
selector:
|
||||
app.kubernetes.io/instance: {{ $.Release.Name }}
|
||||
version: {{ $.Chart.Version }}
|
||||
{{- if .Values.clickhouseKeeper.enabled }}
|
||||
---
|
||||
apiVersion: cozystack.io/v1alpha1
|
||||
kind: WorkloadMonitor
|
||||
metadata:
|
||||
name: {{ $.Release.Name }}-keeper
|
||||
spec:
|
||||
replicas: {{ .Values.clickhouseKeeper.replicas }}
|
||||
minReplicas: 1
|
||||
kind: clickhouse
|
||||
type: clickhouse
|
||||
selector:
|
||||
app: {{ $.Release.Name }}-keeper
|
||||
version: {{ $.Chart.Version }}
|
||||
{{- end }}
|
||||
@@ -1,13 +0,0 @@
|
||||
---
|
||||
apiVersion: cozystack.io/v1alpha1
|
||||
kind: WorkloadMonitor
|
||||
metadata:
|
||||
name: {{ $.Release.Name }}
|
||||
spec:
|
||||
replicas: {{ .Values.replicas }}
|
||||
minReplicas: 1
|
||||
kind: ferretdb
|
||||
type: ferretdb
|
||||
selector:
|
||||
app.kubernetes.io/instance: {{ $.Release.Name }}
|
||||
version: {{ $.Chart.Version }}
|
||||
@@ -1,20 +0,0 @@
|
||||
{{- if .Values.monitoring.enabled }}
|
||||
---
|
||||
apiVersion: cozystack.io/v1alpha1
|
||||
kind: WorkloadMonitor
|
||||
metadata:
|
||||
name: {{ .Release.Name }}
|
||||
labels:
|
||||
app.kubernetes.io/name: foundationdb
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||
spec:
|
||||
replicas: {{ .Values.cluster.processCounts.storage }}
|
||||
minReplicas: {{ include "foundationdb.minReplicas" . }}
|
||||
kind: foundationdb
|
||||
type: foundationdb
|
||||
selector:
|
||||
foundationdb.org/fdb-cluster-name: {{ .Release.Name }}
|
||||
foundationdb.org/fdb-process-class: storage
|
||||
version: {{ .Chart.Version }}
|
||||
{{- end }}
|
||||
@@ -1,39 +0,0 @@
|
||||
---
|
||||
apiVersion: cozystack.io/v1alpha1
|
||||
kind: WorkloadMonitor
|
||||
metadata:
|
||||
name: {{ $.Release.Name }}-haproxy
|
||||
spec:
|
||||
replicas: {{ .Values.haproxy.replicas }}
|
||||
minReplicas: 1
|
||||
kind: http-cache
|
||||
type: http-cache
|
||||
selector:
|
||||
app: {{ $.Release.Name }}-haproxy
|
||||
version: {{ $.Chart.Version }}
|
||||
---
|
||||
apiVersion: cozystack.io/v1alpha1
|
||||
kind: WorkloadMonitor
|
||||
metadata:
|
||||
name: {{ $.Release.Name }}-nginx
|
||||
spec:
|
||||
replicas: {{ .Values.nginx.replicas }}
|
||||
minReplicas: 1
|
||||
kind: http-cache
|
||||
type: http-cache
|
||||
selector:
|
||||
app: {{ $.Release.Name }}-nginx-cache
|
||||
version: {{ $.Chart.Version }}
|
||||
---
|
||||
apiVersion: cozystack.io/v1alpha1
|
||||
kind: WorkloadMonitor
|
||||
metadata:
|
||||
name: {{ $.Release.Name }}
|
||||
spec:
|
||||
replicas: {{ .Values.replicas }}
|
||||
minReplicas: 1
|
||||
kind: http-cache
|
||||
type: http-cache
|
||||
selector:
|
||||
app.kubernetes.io/instance: {{ $.Release.Name }}
|
||||
version: {{ $.Chart.Version }}
|
||||
@@ -1,30 +0,0 @@
|
||||
---
|
||||
apiVersion: cozystack.io/v1alpha1
|
||||
kind: WorkloadMonitor
|
||||
metadata:
|
||||
name: {{ $.Release.Name }}
|
||||
spec:
|
||||
replicas: {{ .Values.replicas }}
|
||||
minReplicas: 1
|
||||
kind: kafka
|
||||
type: kafka
|
||||
selector:
|
||||
app.kubernetes.io/instance: {{ $.Release.Name }}
|
||||
app.kubernetes.io/name: kafka
|
||||
version: {{ $.Chart.Version }}
|
||||
|
||||
---
|
||||
|
||||
apiVersion: cozystack.io/v1alpha1
|
||||
kind: WorkloadMonitor
|
||||
metadata:
|
||||
name: {{ $.Release.Name }}-zookeeper
|
||||
spec:
|
||||
replicas: {{ .Values.replicas }}
|
||||
minReplicas: 1
|
||||
kind: kafka
|
||||
type: zookeeper
|
||||
selector:
|
||||
app.kubernetes.io/instance: {{ $.Release.Name }}
|
||||
app.kubernetes.io/name: zookeeper
|
||||
version: {{ $.Chart.Version }}
|
||||
74
packages/apps/mysql/templates/hooks/cleanup-pvc.yaml
Normal file
74
packages/apps/mysql/templates/hooks/cleanup-pvc.yaml
Normal file
@@ -0,0 +1,74 @@
|
||||
---
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-cleanup
|
||||
labels:
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
annotations:
|
||||
"helm.sh/hook": post-delete
|
||||
"helm.sh/hook-weight": "10"
|
||||
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
policy.cozystack.io/allow-to-apiserver: "true"
|
||||
spec:
|
||||
serviceAccountName: {{ .Release.Name }}-cleanup
|
||||
restartPolicy: Never
|
||||
containers:
|
||||
- name: cleanup
|
||||
image: docker.io/clastix/kubectl:v1.32
|
||||
command:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- |
|
||||
echo "Deleting orphaned PVCs for {{ .Release.Name }}..."
|
||||
kubectl delete pvc -n {{ .Release.Namespace }} -l app.kubernetes.io/instance={{ .Release.Name }} || true
|
||||
echo "PVC cleanup complete."
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-cleanup
|
||||
labels:
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
annotations:
|
||||
"helm.sh/hook": post-delete
|
||||
helm.sh/hook-weight: "0"
|
||||
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-cleanup
|
||||
labels:
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
annotations:
|
||||
"helm.sh/hook": post-delete
|
||||
"helm.sh/hook-weight": "5"
|
||||
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["persistentvolumeclaims"]
|
||||
verbs: ["get", "list", "delete"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: {{ .Release.Name }}-cleanup
|
||||
labels:
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
annotations:
|
||||
"helm.sh/hook": post-delete
|
||||
helm.sh/hook-weight: "5"
|
||||
"helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: {{ .Release.Name }}-cleanup
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: {{ .Release.Name }}-cleanup
|
||||
@@ -1,13 +0,0 @@
|
||||
---
|
||||
apiVersion: cozystack.io/v1alpha1
|
||||
kind: WorkloadMonitor
|
||||
metadata:
|
||||
name: {{ $.Release.Name }}
|
||||
spec:
|
||||
replicas: {{ .Values.replicas }}
|
||||
minReplicas: 1
|
||||
kind: mysql
|
||||
type: mysql
|
||||
selector:
|
||||
app.kubernetes.io/instance: {{ $.Release.Name }}
|
||||
version: {{ $.Chart.Version }}
|
||||
@@ -47,18 +47,14 @@ spec:
|
||||
retries: -1
|
||||
values:
|
||||
nats:
|
||||
podTemplate:
|
||||
container:
|
||||
merge:
|
||||
spec:
|
||||
containers:
|
||||
- name: nats
|
||||
image: nats:2.10.17-alpine
|
||||
resources: {{- include "cozy-lib.resources.defaultingSanitize" (list .Values.resourcesPreset .Values.resources $) | nindent 22 }}
|
||||
resources: {{- include "cozy-lib.resources.defaultingSanitize" (list .Values.resourcesPreset .Values.resources $) | nindent 12 }}
|
||||
fullnameOverride: {{ .Release.Name }}
|
||||
config:
|
||||
{{- if or (gt (len $passwords) 0) (gt (len .Values.config.merge) 0) }}
|
||||
{{- if or $passwords .Values.config.merge }}
|
||||
merge:
|
||||
{{- if gt (len $passwords) 0 }}
|
||||
{{- if $passwords }}
|
||||
accounts:
|
||||
A:
|
||||
users:
|
||||
@@ -67,13 +63,13 @@ spec:
|
||||
password: "{{ $password }}"
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if and .Values.config (hasKey .Values.config "merge") }}
|
||||
{{ toYaml .Values.config.merge | nindent 12 }}
|
||||
{{- with .Values.config.merge }}
|
||||
{{- toYaml . | nindent 10 }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- if and .Values.config (hasKey .Values.config "resolver") }}
|
||||
{{- with .Values.config.resolver }}
|
||||
resolver:
|
||||
{{ toYaml .Values.config.resolver | nindent 12 }}
|
||||
{{- toYaml . | nindent 10 }}
|
||||
{{- end }}
|
||||
cluster:
|
||||
enabled: true
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
---
|
||||
apiVersion: cozystack.io/v1alpha1
|
||||
kind: WorkloadMonitor
|
||||
metadata:
|
||||
name: {{ $.Release.Name }}
|
||||
spec:
|
||||
replicas: {{ .Values.replicas }}
|
||||
minReplicas: 1
|
||||
kind: nats
|
||||
type: nats
|
||||
selector:
|
||||
app.kubernetes.io/instance: {{ $.Release.Name }}-system
|
||||
version: {{ $.Chart.Version }}
|
||||
@@ -79,17 +79,3 @@ spec:
|
||||
policy.cozystack.io/allow-to-apiserver: "true"
|
||||
app.kubernetes.io/name: postgres.apps.cozystack.io
|
||||
app.kubernetes.io/instance: {{ $.Release.Name }}
|
||||
---
|
||||
apiVersion: cozystack.io/v1alpha1
|
||||
kind: WorkloadMonitor
|
||||
metadata:
|
||||
name: {{ $.Release.Name }}
|
||||
spec:
|
||||
replicas: {{ .Values.replicas }}
|
||||
minReplicas: 1
|
||||
kind: postgres
|
||||
type: postgres
|
||||
selector:
|
||||
app.kubernetes.io/name: postgres.apps.cozystack.io
|
||||
app.kubernetes.io/instance: {{ $.Release.Name }}
|
||||
version: {{ $.Chart.Version }}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
---
|
||||
apiVersion: cozystack.io/v1alpha1
|
||||
kind: WorkloadMonitor
|
||||
metadata:
|
||||
name: {{ $.Release.Name }}
|
||||
spec:
|
||||
replicas: {{ .Values.replicas }}
|
||||
minReplicas: 1
|
||||
kind: rabbitmq
|
||||
type: rabbitmq
|
||||
selector:
|
||||
app.kubernetes.io/name: {{ $.Release.Name }}
|
||||
version: {{ $.Chart.Version }}
|
||||
@@ -27,6 +27,7 @@ spec:
|
||||
replicas: 3
|
||||
resources: {{- include "cozy-lib.resources.defaultingSanitize" (list .Values.resourcesPreset .Values.resources $) | nindent 6 }}
|
||||
redis:
|
||||
image: "redis:8.2.0"
|
||||
resources: {{- include "cozy-lib.resources.defaultingSanitize" (list .Values.resourcesPreset .Values.resources $) | nindent 6 }}
|
||||
replicas: {{ .Values.replicas }}
|
||||
{{- with .Values.size }}
|
||||
@@ -67,34 +68,3 @@ spec:
|
||||
auth:
|
||||
secretPath: {{ .Release.Name }}-auth
|
||||
{{- end }}
|
||||
|
||||
---
|
||||
apiVersion: cozystack.io/v1alpha1
|
||||
kind: WorkloadMonitor
|
||||
metadata:
|
||||
name: {{ $.Release.Name }}-redis
|
||||
namespace: {{ $.Release.Namespace }}
|
||||
spec:
|
||||
minReplicas: 1
|
||||
replicas: {{ .Values.replicas }}
|
||||
kind: redis
|
||||
type: redis
|
||||
selector:
|
||||
app.kubernetes.io/component: redis
|
||||
app.kubernetes.io/instance: {{ $.Release.Name }}
|
||||
version: {{ $.Chart.Version }}
|
||||
---
|
||||
apiVersion: cozystack.io/v1alpha1
|
||||
kind: WorkloadMonitor
|
||||
metadata:
|
||||
name: {{ $.Release.Name }}-sentinel
|
||||
namespace: {{ $.Release.Namespace }}
|
||||
spec:
|
||||
minReplicas: 2
|
||||
replicas: 3
|
||||
kind: redis
|
||||
type: sentinel
|
||||
selector:
|
||||
app.kubernetes.io/component: sentinel
|
||||
app.kubernetes.io/instance: {{ $.Release.Name }}
|
||||
version: {{ $.Chart.Version }}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
---
|
||||
apiVersion: cozystack.io/v1alpha1
|
||||
kind: WorkloadMonitor
|
||||
metadata:
|
||||
name: {{ $.Release.Name }}
|
||||
spec:
|
||||
replicas: {{ .Values.replicas }}
|
||||
minReplicas: 1
|
||||
kind: tcp-balancer
|
||||
type: haproxy
|
||||
selector:
|
||||
app.kubernetes.io/instance: {{ $.Release.Name }}
|
||||
version: {{ $.Chart.Version }}
|
||||
@@ -28,6 +28,7 @@ rules:
|
||||
- cozystack.io
|
||||
resources:
|
||||
- workloadmonitors
|
||||
- workloads
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups:
|
||||
- core.cozystack.io
|
||||
@@ -113,6 +114,7 @@ rules:
|
||||
- cozystack.io
|
||||
resources:
|
||||
- workloadmonitors
|
||||
- workloads
|
||||
verbs: ["get", "list", "watch"]
|
||||
---
|
||||
kind: RoleBinding
|
||||
@@ -184,6 +186,7 @@ rules:
|
||||
- cozystack.io
|
||||
resources:
|
||||
- workloadmonitors
|
||||
- workloads
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups:
|
||||
- core.cozystack.io
|
||||
@@ -270,6 +273,7 @@ rules:
|
||||
- vmdisks
|
||||
- vminstances
|
||||
- infos
|
||||
- virtualprivateclouds
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
@@ -282,6 +286,7 @@ rules:
|
||||
- cozystack.io
|
||||
resources:
|
||||
- workloadmonitors
|
||||
- workloads
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups:
|
||||
- core.cozystack.io
|
||||
@@ -356,6 +361,7 @@ rules:
|
||||
- cozystack.io
|
||||
resources:
|
||||
- workloadmonitors
|
||||
- workloads
|
||||
verbs: ["get", "list", "watch"]
|
||||
- apiGroups:
|
||||
- core.cozystack.io
|
||||
|
||||
@@ -48,6 +48,8 @@ virtctl ssh <user>@<vm>
|
||||
| `systemDisk.image` | The base image for the virtual machine. | `string` | `ubuntu` |
|
||||
| `systemDisk.storage` | The size of the disk allocated for the virtual machine. | `string` | `5Gi` |
|
||||
| `systemDisk.storageClass` | StorageClass used to store the data. | `string` | `replicated` |
|
||||
| `subnets` | Additional subnets | `[]object` | `[]` |
|
||||
| `subnets[i].name` | Subnet name | `string` | `""` |
|
||||
| `gpus` | List of GPUs to attach. | `[]object` | `[]` |
|
||||
| `gpus[i].name` | The name of the GPU resource to attach. | `string` | `""` |
|
||||
| `resources` | Resource configuration for the virtual machine. | `object` | `{}` |
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
# if subnets are specified and image is either ubunu or alpine
|
||||
{{- if and .Values.subnets (or (eq .Values.systemDisk.image "ubuntu") (eq .Values.systemDisk.image "alpine")) }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: {{ include "virtual-machine.fullname" $ }}-network-config
|
||||
stringData:
|
||||
networkData: |
|
||||
network:
|
||||
version: 1
|
||||
config:
|
||||
{{- if eq .Values.systemDisk.image "ubuntu" }}
|
||||
# main iface
|
||||
- type: physical
|
||||
name: enp1s0
|
||||
subnets:
|
||||
- type: dhcp
|
||||
# additional ifaces attached to subnets
|
||||
{{- range $i, $subnet := .Values.subnets }}
|
||||
- type: physical
|
||||
name: enp{{ add 2 $i }}s0
|
||||
subnets:
|
||||
- type: dhcp
|
||||
{{- end }}
|
||||
{{- else if eq .Values.systemDisk.image "alpine" }}
|
||||
#main iface
|
||||
- type: physical
|
||||
name: eth0
|
||||
subnets:
|
||||
- type: dhcp
|
||||
# additional ifaces attached to subnets
|
||||
{{- range $i, $subnet := .Values.subnets }}
|
||||
- type: physical
|
||||
name: eth{{ add1 $i }}
|
||||
subnets:
|
||||
- type: dhcp
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
@@ -51,9 +51,9 @@ spec:
|
||||
{{- else if eq .Values.systemDisk.image "ubuntu" }}
|
||||
url: https://cloud-images.ubuntu.com/noble/current/noble-server-cloudimg-amd64.img
|
||||
{{- else if eq .Values.systemDisk.image "fedora" }}
|
||||
url: https://download.fedoraproject.org/pub/fedora/linux/releases/40/Cloud/x86_64/images/Fedora-Cloud-Base-Generic.x86_64-40-1.14.qcow2
|
||||
url: https://download.fedoraproject.org/pub/fedora/linux/releases/42/Cloud/x86_64/images/Fedora-Cloud-Base-Generic-42-1.1.x86_64.qcow2
|
||||
{{- else if eq .Values.systemDisk.image "alpine" }}
|
||||
url: https://dl-cdn.alpinelinux.org/alpine/v3.20/releases/cloud/nocloud_alpine-3.20.2-x86_64-bios-tiny-r0.qcow2
|
||||
url: https://dl-cdn.alpinelinux.org/alpine/v3.20/releases/cloud/nocloud_alpine-3.20.8-x86_64-bios-cloudinit-r0.qcow2
|
||||
{{- else if eq .Values.systemDisk.image "talos" }}
|
||||
url: https://github.com/siderolabs/talos/releases/download/v1.7.6/nocloud-amd64.raw.xz
|
||||
{{- end }}
|
||||
@@ -93,7 +93,7 @@ spec:
|
||||
- disk:
|
||||
bus: scsi
|
||||
name: systemdisk
|
||||
{{- if or .Values.cloudInit .Values.sshKeys }}
|
||||
{{- if or .Values.cloudInit .Values.sshKeys (and .Values.subnets (or (eq .Values.systemDisk.image "ubuntu") (eq .Values.systemDisk.image "alpine"))) }}
|
||||
- disk:
|
||||
bus: virtio
|
||||
name: cloudinitdisk
|
||||
@@ -102,6 +102,12 @@ spec:
|
||||
interfaces:
|
||||
- name: default
|
||||
bridge: {}
|
||||
{{- if .Values.subnets }}
|
||||
{{- range $i, $subnet := .Values.subnets }}
|
||||
- name: {{ $subnet.name }}
|
||||
bridge: {}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
machine:
|
||||
type: ""
|
||||
@@ -123,13 +129,26 @@ spec:
|
||||
- name: systemdisk
|
||||
dataVolume:
|
||||
name: {{ include "virtual-machine.fullname" . }}
|
||||
{{- if or .Values.cloudInit .Values.sshKeys }}
|
||||
{{- if or .Values.cloudInit .Values.sshKeys (and .Values.subnets (or (eq .Values.systemDisk.image "ubuntu") (eq .Values.systemDisk.image "alpine"))) }}
|
||||
- name: cloudinitdisk
|
||||
cloudInitNoCloud:
|
||||
{{- if or .Values.cloudInit .Values.sshKeys }}
|
||||
secretRef:
|
||||
name: {{ include "virtual-machine.fullname" . }}-cloud-init
|
||||
{{- end }}
|
||||
{{- if and .Values.subnets (or (eq .Values.systemDisk.image "ubuntu") (eq .Values.systemDisk.image "alpine")) }}
|
||||
networkDataSecretRef:
|
||||
name: {{ include "virtual-machine.fullname" . }}-network-config
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
networks:
|
||||
- name: default
|
||||
pod: {}
|
||||
{{- if .Values.subnets }}
|
||||
{{- range $i, $subnet := .Values.subnets }}
|
||||
- name: {{ $subnet.name }}
|
||||
multus:
|
||||
networkName: {{ $.Release.Namespace }}/{{ $subnet.name }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
@@ -167,6 +167,20 @@
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"subnets": {
|
||||
"description": "Additional subnets",
|
||||
"type": "array",
|
||||
"default": [],
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"description": "Subnet name",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"systemDisk": {
|
||||
"description": "System disk configuration.",
|
||||
"type": "object",
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
## @field {string} storage - The size of the disk allocated for the virtual machine.
|
||||
## @field {string} [storageClass] - StorageClass used to store the data.
|
||||
|
||||
## @typedef {struct} Subnet - Additional subnets
|
||||
## @field {string} [name] - Subnet name
|
||||
|
||||
## @typedef {struct} GPU - GPU device configuration.
|
||||
## @field {string} name - The name of the GPU resource to attach.
|
||||
|
||||
@@ -50,6 +53,14 @@ systemDisk:
|
||||
storage: 5Gi
|
||||
storageClass: replicated
|
||||
|
||||
## @param {[]Subnet} subnets - Additional subnets
|
||||
subnets: []
|
||||
## Example:
|
||||
## subnets:
|
||||
## - name: subnet-84dbec17
|
||||
## - name: subnet-aa8896b5
|
||||
## - name: subnet-e9b97196
|
||||
|
||||
## @param {[]GPU} gpus - List of GPUs to attach.
|
||||
gpus: []
|
||||
## Example:
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
apiVersion: cozystack.io/v1alpha1
|
||||
kind: WorkloadMonitor
|
||||
metadata:
|
||||
name: {{ $.Release.Name }}
|
||||
spec:
|
||||
replicas: 0
|
||||
minReplicas: 0
|
||||
kind: vm-disk
|
||||
type: vm-disk
|
||||
selector:
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
version: {{ $.Chart.Version }}
|
||||
@@ -47,6 +47,8 @@ virtctl ssh <user>@<vm>
|
||||
| `disks` | List of disks to attach. | `[]object` | `[]` |
|
||||
| `disks[i].name` | Disk name. | `string` | `""` |
|
||||
| `disks[i].bus` | Disk bus type (e.g. "sata"). | `string` | `""` |
|
||||
| `subnets` | Additional subnets | `[]object` | `[]` |
|
||||
| `subnets[i].name` | Subnet name | `string` | `""` |
|
||||
| `gpus` | List of GPUs to attach (NVIDIA driver requires at least 4 GiB RAM). | `[]object` | `[]` |
|
||||
| `gpus[i].name` | The name of the GPU resource to attach. | `string` | `""` |
|
||||
| `resources` | Resource configuration for the virtual machine. | `object` | `{}` |
|
||||
|
||||
@@ -77,6 +77,12 @@ spec:
|
||||
interfaces:
|
||||
- name: default
|
||||
bridge: {}
|
||||
{{- if .Values.subnets }}
|
||||
{{- range $i, $subnet := .Values.subnets }}
|
||||
- name: {{ $subnet.name }}
|
||||
bridge: {}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
machine:
|
||||
type: ""
|
||||
{{- with .Values.sshKeys }}
|
||||
@@ -105,3 +111,10 @@ spec:
|
||||
networks:
|
||||
- name: default
|
||||
pod: {}
|
||||
{{- if .Values.subnets }}
|
||||
{{- range $i, $subnet := .Values.subnets }}
|
||||
- name: {{ $subnet.name }}
|
||||
multus:
|
||||
networkName: {{ $.Release.Namespace }}/{{ $subnet.name }}
|
||||
{{- end }}
|
||||
{{- end }}
|
||||
|
||||
@@ -187,6 +187,20 @@
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"subnets": {
|
||||
"description": "Additional subnets",
|
||||
"type": "array",
|
||||
"default": [],
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"description": "Subnet name",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
## @field {string} name - Disk name.
|
||||
## @field {string} [bus] - Disk bus type (e.g. "sata").
|
||||
|
||||
## @typedef {struct} Subnet - Additional subnets
|
||||
## @field {string} [name] - Subnet name
|
||||
|
||||
## @typedef {struct} GPU - GPU device configuration.
|
||||
## @field {string} name - The name of the GPU resource to attach.
|
||||
|
||||
@@ -45,6 +48,14 @@ disks: []
|
||||
## - name: example-data
|
||||
## bus: sata
|
||||
|
||||
## @param {[]Subnet} subnets - Additional subnets
|
||||
subnets: []
|
||||
## Example:
|
||||
## subnets:
|
||||
## - name: subnet-84dbec17
|
||||
## - name: subnet-aa8896b5
|
||||
## - name: subnet-e9b97196
|
||||
|
||||
## @param {[]GPU} gpus - List of GPUs to attach (NVIDIA driver requires at least 4 GiB RAM).
|
||||
gpus: []
|
||||
## Example:
|
||||
|
||||
6
packages/apps/vpc/Chart.yaml
Normal file
6
packages/apps/vpc/Chart.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
apiVersion: v2
|
||||
name: virtualprivatecloud
|
||||
description: Isolated networks
|
||||
icon: logos/vpc.svg
|
||||
type: application
|
||||
version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process
|
||||
8
packages/apps/vpc/Makefile
Normal file
8
packages/apps/vpc/Makefile
Normal file
@@ -0,0 +1,8 @@
|
||||
include ../../../scripts/package.mk
|
||||
|
||||
generate:
|
||||
cozyvalues-gen -v values.yaml -s values.schema.json
|
||||
../../../hack/update-crd.sh
|
||||
|
||||
update:
|
||||
echo
|
||||
48
packages/apps/vpc/README.md
Normal file
48
packages/apps/vpc/README.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# VPC
|
||||
|
||||
VPC offers a subset of dedicated subnets with networking services related to it.
|
||||
As the service evolves, it will provide more ways to isolate your workloads.
|
||||
|
||||
## Service details
|
||||
|
||||
The service utilizes kube-ovn VPC and Subnet resources, which use ovn logical routers and logical switches under the hood.
|
||||
Currently every workload will have a connection to a default management network which will also have a default gateway, and the majority of traffic will be going through it.
|
||||
VPC subnets are for now an additional dedicated networking spaces.
|
||||
|
||||
A VM or a pod may be connected to multiple secondary Subnets at once.
|
||||
Each secondary connection will be represented as an additional network interface.
|
||||
|
||||
## Deployment notes
|
||||
|
||||
VPC name must be unique within a tenant.
|
||||
Subnet name and ip address range must be unique within a VPC.
|
||||
Subnet ip address space must not overlap with the default management network ip address range, subsets of 172.16.0.0/12 are recommended.
|
||||
Currently there are no fail-safe checks, however they are planned for the future.
|
||||
|
||||
Different VPCs may have subnets with ovelapping ip address ranges.
|
||||
|
||||
## Parameters
|
||||
|
||||
### Common parameters
|
||||
|
||||
| Name | Description | Type | Value |
|
||||
| -------------------- | -------------------------------- | ------------------- | ------- |
|
||||
| `subnets` | Subnets of a VPC | `map[string]object` | `{...}` |
|
||||
| `subnets[name].cidr` | Subnet CIDR, e.g. 192.168.0.0/24 | `cidr` | `{}` |
|
||||
|
||||
|
||||
## Examples
|
||||
```yaml
|
||||
apiVersion: apps.cozystack.io/v1alpha1
|
||||
kind: VirtualPrivateCloud
|
||||
metadata:
|
||||
name: vpc00
|
||||
spec:
|
||||
subnets:
|
||||
sub00:
|
||||
cidr: 172.16.0.0/24
|
||||
sub01:
|
||||
cidr: 172.16.1.0/24
|
||||
sub02:
|
||||
cidr: 172.16.2.0/24
|
||||
```
|
||||
10
packages/apps/vpc/logos/vpc.svg
Normal file
10
packages/apps/vpc/logos/vpc.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="144" height="144" viewBox="0 0 144 144" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect width="144" height="144" rx="24" fill="url(#paint0_linear_1025_3)"/>
|
||||
<path d="M109.6 86.1H114.3C116.885 86.1 119 88.215 119 90.847V104.853C119 107.485 116.885 109.6 114.3 109.6H95.5C92.915 109.6 90.8 107.485 90.8 104.853V90.847C90.8 88.215 92.915 86.1 95.5 86.1H100.2V76.7H76.7V86.1H81.4C83.985 86.1 86.1 88.215 86.1 90.847V104.853C86.1 107.485 83.985 109.6 81.4 109.6H62.6C60.015 109.6 57.9 107.485 57.9 104.853V90.847C57.9 88.215 60.015 86.1 62.6 86.1H67.3V76.7H43.8V86.1H48.5C51.085 86.1 53.2 88.215 53.2 90.847V104.853C53.2 107.485 51.085 109.6 48.5 109.6H29.7C27.115 109.6 25 107.485 25 104.853V90.847C25 88.215 27.115 86.1 29.7 86.1H34.4V76.7C34.4 71.53 38.63 67.3 43.8 67.3H67.3V57.9H62.6C60.015 57.9 57.9 55.785 57.9 53.153V39.147C57.9 36.515 60.015 34.4 62.6 34.4H81.4C83.985 34.4 86.1 36.515 86.1 39.147V53.153C86.1 55.785 83.985 57.9 81.4 57.9H76.7V67.3H100.2C105.37 67.3 109.6 71.53 109.6 76.7V86.1Z" fill="white"/>
|
||||
<defs>
|
||||
<linearGradient id="paint0_linear_1025_3" x1="142.5" y1="143" x2="3.99999" y2="9.49999" gradientUnits="userSpaceOnUse">
|
||||
<stop stop-color="#00082E"/>
|
||||
<stop offset="1" stop-color="#2E3067"/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
72
packages/apps/vpc/templates/vpc.yaml
Normal file
72
packages/apps/vpc/templates/vpc.yaml
Normal file
@@ -0,0 +1,72 @@
|
||||
## Release.Namespace == tenant name
|
||||
## Release.Name == vpc name
|
||||
|
||||
{{ $vpcId := print "vpc-" (print .Release.Namespace "/" .Release.Name | sha256sum | trunc 6) }}
|
||||
|
||||
---
|
||||
apiVersion: kubeovn.io/v1
|
||||
kind: Vpc
|
||||
metadata:
|
||||
name: {{ $vpcId }}
|
||||
labels:
|
||||
cozystack.io/vpcName: {{ .Release.Name }}
|
||||
cozystack.io/tenantName: {{ .Release.Namespace }}
|
||||
spec:
|
||||
enableExternal: false
|
||||
namespaces:
|
||||
- {{ .Release.Namespace }}
|
||||
|
||||
{{- range $subnetName, $subnetConfig := .Values.subnets }}
|
||||
{{- $subnetId := print "subnet-" (print $.Release.Namespace "/" $vpcId "/" $subnetName | sha256sum | trunc 8) }}
|
||||
---
|
||||
apiVersion: k8s.cni.cncf.io/v1
|
||||
kind: NetworkAttachmentDefinition
|
||||
metadata:
|
||||
name: {{ $subnetId }}
|
||||
namespace: {{ $.Release.Namespace }}
|
||||
labels:
|
||||
cozystack.io/subnetName: {{ $subnetName }}
|
||||
cozystack.io/vpcId: {{ $vpcId }}
|
||||
cozystack.io/vpcName: {{ $.Release.Name }}
|
||||
cozystack.io/tenantName: {{ $.Release.Namespace }}
|
||||
spec:
|
||||
config: '{
|
||||
"cniVersion": "0.3.0",
|
||||
"type": "kube-ovn",
|
||||
"server_socket": "/run/openvswitch/kube-ovn-daemon.sock",
|
||||
"provider": "{{ $subnetId }}.{{ $.Release.Namespace }}.ovn"
|
||||
}'
|
||||
---
|
||||
apiVersion: kubeovn.io/v1
|
||||
kind: Subnet
|
||||
metadata:
|
||||
name: {{ $subnetId }}
|
||||
labels:
|
||||
cozystack.io/subnetName: {{ $subnetName }}
|
||||
cozystack.io/vpcId: {{ $vpcId }}
|
||||
cozystack.io/vpcName: {{ $.Release.Name }}
|
||||
cozystack.io/tenantName: {{ $.Release.Namespace }}
|
||||
spec:
|
||||
vpc: {{ $vpcId }}
|
||||
cidrBlock: {{ $subnetConfig.cidr }}
|
||||
provider: "{{ $subnetId }}.{{ $.Release.Namespace }}.ovn"
|
||||
protocol: IPv4
|
||||
enableLb: false
|
||||
private: true
|
||||
{{- end }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: {{ $.Release.Name }}-subnets
|
||||
labels:
|
||||
cozystack.io/vpcId: {{ $vpcId }}
|
||||
cozystack.io/tenantName: {{ $.Release.Namespace }}
|
||||
data:
|
||||
subnets: |
|
||||
{{- range $subnetName, $subnetConfig := .Values.subnets }}
|
||||
- subnetName: {{ $subnetName }}
|
||||
subnetId: {{ print "subnet-" (print $.Release.Namespace "/" $vpcId "/" $subnetName | sha256sum | trunc 8) }}
|
||||
subnetCIDR: {{ $subnetConfig.cidr }}
|
||||
{{- end }}
|
||||
|
||||
20
packages/apps/vpc/values.schema.json
Normal file
20
packages/apps/vpc/values.schema.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"title": "Chart Values",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"subnets": {
|
||||
"description": "Subnets of a VPC",
|
||||
"type": "object",
|
||||
"default": {},
|
||||
"additionalProperties": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"cidr": {
|
||||
"description": "IP address range",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
15
packages/apps/vpc/values.yaml
Normal file
15
packages/apps/vpc/values.yaml
Normal file
@@ -0,0 +1,15 @@
|
||||
##
|
||||
## @section Common parameters
|
||||
##
|
||||
|
||||
## @typedef {struct} Subnet - Subnet of a VPC
|
||||
## @field {string} [cidr] - IP address range
|
||||
|
||||
## @param {map[string]Subnet} subnets - Subnets of a VPC
|
||||
subnets: {}
|
||||
## Example:
|
||||
## subnets:
|
||||
## mysubnet0:
|
||||
## cidr: "172.16.0.0/24"
|
||||
## mysubnet1:
|
||||
## cidr: "172.16.1.0/24"
|
||||
@@ -1,12 +0,0 @@
|
||||
apiVersion: cozystack.io/v1alpha1
|
||||
kind: WorkloadMonitor
|
||||
metadata:
|
||||
name: {{ $.Release.Name }}
|
||||
spec:
|
||||
replicas: {{ .Values.replicas }}
|
||||
minReplicas: 1
|
||||
kind: vpn
|
||||
type: vpn
|
||||
selector:
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
version: {{ $.Chart.Version }}
|
||||
@@ -5,7 +5,7 @@ set -u
|
||||
TMPDIR=$(mktemp -d)
|
||||
PROFILES="initramfs kernel iso installer nocloud metal"
|
||||
FIRMWARES="amd-ucode amdgpu bnx2-bnx2x i915 intel-ice-firmware intel-ucode qlogic-firmware"
|
||||
EXTENSIONS="drbd zfs lldpd"
|
||||
EXTENSIONS="drbd zfs"
|
||||
|
||||
mkdir -p images/talos/profiles
|
||||
|
||||
@@ -90,7 +90,6 @@ input:
|
||||
- imageRef: ${QLOGIC_FIRMWARE_IMAGE}
|
||||
- imageRef: ${DRBD_IMAGE}
|
||||
- imageRef: ${ZFS_IMAGE}
|
||||
- imageRef: ${LLDPD_IMAGE}
|
||||
output:
|
||||
kind: ${kind}
|
||||
imageOptions: ${image_options}
|
||||
|
||||
@@ -21,7 +21,6 @@ input:
|
||||
- imageRef: ghcr.io/siderolabs/qlogic-firmware:20250917@sha256:7094e5db6931a1b68240416b65ddc0f3b546bd9b8520e3cfb1ddebcbfc83e890
|
||||
- imageRef: ghcr.io/siderolabs/drbd:9.2.14-v1.11.3@sha256:4393756875751e2664a04e96c1ccff84c99958ca819dd93b46b82ad8f3b4be67
|
||||
- imageRef: ghcr.io/siderolabs/zfs:2.3.3-v1.11.3@sha256:3c0b34a760914980ac234e66f130d829e428018e46420b7bca33219b1cc2dd87
|
||||
- imageRef: ghcr.io/siderolabs/lldpd:1.0.20@sha256:4c6370518f5b2e1f03214a6ed54778eaea663fda8850e3f4da174ed69b636172
|
||||
output:
|
||||
kind: initramfs
|
||||
imageOptions: {}
|
||||
|
||||
@@ -21,7 +21,6 @@ input:
|
||||
- imageRef: ghcr.io/siderolabs/qlogic-firmware:20250917@sha256:7094e5db6931a1b68240416b65ddc0f3b546bd9b8520e3cfb1ddebcbfc83e890
|
||||
- imageRef: ghcr.io/siderolabs/drbd:9.2.14-v1.11.3@sha256:4393756875751e2664a04e96c1ccff84c99958ca819dd93b46b82ad8f3b4be67
|
||||
- imageRef: ghcr.io/siderolabs/zfs:2.3.3-v1.11.3@sha256:3c0b34a760914980ac234e66f130d829e428018e46420b7bca33219b1cc2dd87
|
||||
- imageRef: ghcr.io/siderolabs/lldpd:1.0.20@sha256:4c6370518f5b2e1f03214a6ed54778eaea663fda8850e3f4da174ed69b636172
|
||||
output:
|
||||
kind: installer
|
||||
imageOptions: {}
|
||||
|
||||
@@ -21,7 +21,6 @@ input:
|
||||
- imageRef: ghcr.io/siderolabs/qlogic-firmware:20250917@sha256:7094e5db6931a1b68240416b65ddc0f3b546bd9b8520e3cfb1ddebcbfc83e890
|
||||
- imageRef: ghcr.io/siderolabs/drbd:9.2.14-v1.11.3@sha256:4393756875751e2664a04e96c1ccff84c99958ca819dd93b46b82ad8f3b4be67
|
||||
- imageRef: ghcr.io/siderolabs/zfs:2.3.3-v1.11.3@sha256:3c0b34a760914980ac234e66f130d829e428018e46420b7bca33219b1cc2dd87
|
||||
- imageRef: ghcr.io/siderolabs/lldpd:1.0.20@sha256:4c6370518f5b2e1f03214a6ed54778eaea663fda8850e3f4da174ed69b636172
|
||||
output:
|
||||
kind: iso
|
||||
imageOptions: {}
|
||||
|
||||
@@ -21,7 +21,6 @@ input:
|
||||
- imageRef: ghcr.io/siderolabs/qlogic-firmware:20250917@sha256:7094e5db6931a1b68240416b65ddc0f3b546bd9b8520e3cfb1ddebcbfc83e890
|
||||
- imageRef: ghcr.io/siderolabs/drbd:9.2.14-v1.11.3@sha256:4393756875751e2664a04e96c1ccff84c99958ca819dd93b46b82ad8f3b4be67
|
||||
- imageRef: ghcr.io/siderolabs/zfs:2.3.3-v1.11.3@sha256:3c0b34a760914980ac234e66f130d829e428018e46420b7bca33219b1cc2dd87
|
||||
- imageRef: ghcr.io/siderolabs/lldpd:1.0.20@sha256:4c6370518f5b2e1f03214a6ed54778eaea663fda8850e3f4da174ed69b636172
|
||||
output:
|
||||
kind: kernel
|
||||
imageOptions: {}
|
||||
|
||||
@@ -21,7 +21,6 @@ input:
|
||||
- imageRef: ghcr.io/siderolabs/qlogic-firmware:20250917@sha256:7094e5db6931a1b68240416b65ddc0f3b546bd9b8520e3cfb1ddebcbfc83e890
|
||||
- imageRef: ghcr.io/siderolabs/drbd:9.2.14-v1.11.3@sha256:4393756875751e2664a04e96c1ccff84c99958ca819dd93b46b82ad8f3b4be67
|
||||
- imageRef: ghcr.io/siderolabs/zfs:2.3.3-v1.11.3@sha256:3c0b34a760914980ac234e66f130d829e428018e46420b7bca33219b1cc2dd87
|
||||
- imageRef: ghcr.io/siderolabs/lldpd:1.0.20@sha256:4c6370518f5b2e1f03214a6ed54778eaea663fda8850e3f4da174ed69b636172
|
||||
output:
|
||||
kind: image
|
||||
imageOptions: { diskSize: 1306525696, diskFormat: raw }
|
||||
|
||||
@@ -21,7 +21,6 @@ input:
|
||||
- imageRef: ghcr.io/siderolabs/qlogic-firmware:20250917@sha256:7094e5db6931a1b68240416b65ddc0f3b546bd9b8520e3cfb1ddebcbfc83e890
|
||||
- imageRef: ghcr.io/siderolabs/drbd:9.2.14-v1.11.3@sha256:4393756875751e2664a04e96c1ccff84c99958ca819dd93b46b82ad8f3b4be67
|
||||
- imageRef: ghcr.io/siderolabs/zfs:2.3.3-v1.11.3@sha256:3c0b34a760914980ac234e66f130d829e428018e46420b7bca33219b1cc2dd87
|
||||
- imageRef: ghcr.io/siderolabs/lldpd:1.0.20@sha256:4c6370518f5b2e1f03214a6ed54778eaea663fda8850e3f4da174ed69b636172
|
||||
output:
|
||||
kind: image
|
||||
imageOptions: { diskSize: 1306525696, diskFormat: raw }
|
||||
|
||||
@@ -76,6 +76,13 @@ releases:
|
||||
namespace: cozy-kubeovn
|
||||
dependsOn: [cilium,kubeovn]
|
||||
|
||||
- name: multus
|
||||
releaseName: multus
|
||||
chart: cozy-multus
|
||||
namespace: cozy-multus
|
||||
privileged: true
|
||||
dependsOn: [cilium,kubeovn]
|
||||
|
||||
- name: cozy-proxy
|
||||
releaseName: cozystack
|
||||
chart: cozy-cozy-proxy
|
||||
|
||||
@@ -40,6 +40,10 @@ releases:
|
||||
chart: cozy-cozystack-api
|
||||
namespace: cozy-system
|
||||
dependsOn: [cozystack-controller]
|
||||
values:
|
||||
cozystackAPI:
|
||||
localK8sAPIEndpoint:
|
||||
enabled: false
|
||||
|
||||
- name: cozystack-controller
|
||||
releaseName: cozystack-controller
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
---
|
||||
apiVersion: cozystack.io/v1alpha1
|
||||
kind: WorkloadMonitor
|
||||
metadata:
|
||||
name: {{ $.Release.Name }}
|
||||
namespace: {{ $.Release.Namespace }}
|
||||
spec:
|
||||
replicas: {{ .Values.replicas }}
|
||||
minReplicas: {{ div .Values.replicas 2 | add1 }}
|
||||
kind: ingress
|
||||
type: controller
|
||||
selector:
|
||||
app.kubernetes.io/component: controller
|
||||
app.kubernetes.io/instance: ingress-nginx-system
|
||||
app.kubernetes.io/name: ingress-nginx
|
||||
version: {{ $.Chart.Version }}
|
||||
@@ -10,6 +10,7 @@ metadata:
|
||||
annotations:
|
||||
nginx.ingress.kubernetes.io/backend-protocol: HTTPS
|
||||
nginx.ingress.kubernetes.io/ssl-passthrough: "true"
|
||||
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
|
||||
name: kubernetes
|
||||
namespace: default
|
||||
spec:
|
||||
|
||||
87
packages/system/cozystack-api/templates/hook.yaml
Normal file
87
packages/system/cozystack-api/templates/hook.yaml
Normal file
@@ -0,0 +1,87 @@
|
||||
{{- $shouldRunUpdateHook := false }}
|
||||
{{- $previousKind := "Deployment" }}
|
||||
{{- $previousKindPlural := "deployments" }}
|
||||
{{- if not .Values.cozystackAPI.localK8sAPIEndpoint.enabled }}
|
||||
{{- $previousKind = "DaemonSet" }}
|
||||
{{- $previousKindPlural = "daemonsets" }}
|
||||
{{- end }}
|
||||
{{- $previous := lookup "apps/v1" $previousKind .Release.Namespace "cozystack-api" }}
|
||||
{{- if $previous }}
|
||||
{{- $shouldRunUpdateHook = true }}
|
||||
{{- end }}
|
||||
|
||||
{{- if $shouldRunUpdateHook }}
|
||||
---
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: "cozystack-api-hook"
|
||||
annotations:
|
||||
helm.sh/hook: post-upgrade
|
||||
helm.sh/hook-weight: "1"
|
||||
helm.sh/hook-delete-policy: hook-succeeded,before-hook-creation
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
policy.cozystack.io/allow-to-apiserver: "true"
|
||||
spec:
|
||||
serviceAccountName: "cozystack-api-hook"
|
||||
containers:
|
||||
- name: kubectl
|
||||
image: docker.io/alpine/k8s:1.33.4
|
||||
command:
|
||||
- sh
|
||||
args:
|
||||
- -exc
|
||||
- |-
|
||||
kubectl --namespace={{ .Release.Namespace }} delete --ignore-not-found \
|
||||
{{ $previousKindPlural }}.apps cozystack-api
|
||||
restartPolicy: Never
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
annotations:
|
||||
helm.sh/hook: post-upgrade
|
||||
helm.sh/hook-weight: "1"
|
||||
helm.sh/hook-delete-policy: hook-succeeded,before-hook-creation
|
||||
name: "cozystack-api-hook"
|
||||
rules:
|
||||
- apiGroups:
|
||||
- "apps"
|
||||
resources:
|
||||
- "{{ $previousKindPlural }}"
|
||||
verbs:
|
||||
- get
|
||||
- delete
|
||||
resourceNames:
|
||||
- "cozystack-api"
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: "cozystack-api-hook"
|
||||
annotations:
|
||||
helm.sh/hook: post-upgrade
|
||||
helm.sh/hook-weight: "1"
|
||||
helm.sh/hook-delete-policy: hook-succeeded,before-hook-creation
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: "cozystack-api-hook"
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: "cozystack-api-hook"
|
||||
namespace: "{{ .Release.Namespace }}"
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: "cozystack-api-hook"
|
||||
annotations:
|
||||
helm.sh/hook: post-upgrade
|
||||
helm.sh/hook-weight: "1"
|
||||
helm.sh/hook-delete-policy: hook-succeeded,before-hook-creation
|
||||
{{- end }}
|
||||
|
||||
@@ -671,6 +671,62 @@ spec:
|
||||
x-kubernetes-map-type: atomic
|
||||
type: array
|
||||
type: object
|
||||
workloadMonitors:
|
||||
description: |-
|
||||
WorkloadMonitors configuration for this resource
|
||||
List of WorkloadMonitor templates to be created for each application instance
|
||||
items:
|
||||
description: |-
|
||||
WorkloadMonitorTemplate defines a template for creating WorkloadMonitor resources
|
||||
for application instances. Fields support Go template syntax with the following variables:
|
||||
- {{ .Release.Name }}: The name of the Helm release
|
||||
- {{ .Release.Namespace }}: The namespace of the Helm release
|
||||
- {{ .Chart.Version }}: The version of the Helm chart
|
||||
- {{ .Values.<path> }}: Any value from the Helm values
|
||||
properties:
|
||||
condition:
|
||||
description: |-
|
||||
Condition is a Go template expression that must evaluate to "true" for the monitor to be created.
|
||||
Example: "{{ .Values.clickhouseKeeper.enabled }}"
|
||||
If empty, the monitor is always created.
|
||||
type: string
|
||||
kind:
|
||||
description: Kind specifies the kind of the workload (e.g.,
|
||||
"postgres", "kafka")
|
||||
type: string
|
||||
minReplicas:
|
||||
description: |-
|
||||
MinReplicas is a Go template expression that evaluates to the minimum number of replicas.
|
||||
Example: "1" or "{{ div .Values.replicas 2 | add1 }}"
|
||||
type: string
|
||||
name:
|
||||
description: |-
|
||||
Name is the name of the WorkloadMonitor.
|
||||
Supports Go template syntax (e.g., "{{ .Release.Name }}-keeper")
|
||||
type: string
|
||||
replicas:
|
||||
description: |-
|
||||
Replicas is a Go template expression that evaluates to the desired number of replicas.
|
||||
Example: "{{ .Values.replicas }}" or "{{ .Values.clickhouseKeeper.replicas }}"
|
||||
type: string
|
||||
selector:
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: |-
|
||||
Selector is a map of label key-value pairs for matching workloads.
|
||||
Supports Go template syntax in values (e.g., "app.kubernetes.io/instance: {{ .Release.Name }}")
|
||||
type: object
|
||||
type:
|
||||
description: Type specifies the type of the workload (e.g.,
|
||||
"postgres", "zookeeper")
|
||||
type: string
|
||||
required:
|
||||
- kind
|
||||
- name
|
||||
- selector
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
required:
|
||||
- application
|
||||
- release
|
||||
|
||||
@@ -37,3 +37,19 @@ spec:
|
||||
include:
|
||||
- resourceNames:
|
||||
- chendpoint-clickhouse-{{ .name }}
|
||||
workloadMonitors:
|
||||
- name: "{{ .Release.Name }}"
|
||||
kind: clickhouse
|
||||
type: clickhouse
|
||||
selector:
|
||||
app.kubernetes.io/instance: "{{ .Release.Name }}"
|
||||
replicas: "{{ .Values.replicas }}"
|
||||
minReplicas: "1"
|
||||
- name: "{{ .Release.Name }}-keeper"
|
||||
kind: clickhouse
|
||||
type: clickhouse
|
||||
selector:
|
||||
app: "{{ .Release.Name }}-keeper"
|
||||
replicas: "{{ .Values.clickhouseKeeper.replicas }}"
|
||||
minReplicas: "1"
|
||||
condition: "{{ .Values.clickhouseKeeper.enabled }}"
|
||||
|
||||
@@ -38,3 +38,11 @@ spec:
|
||||
include:
|
||||
- resourceNames:
|
||||
- ferretdb-{{ .name }}
|
||||
workloadMonitors:
|
||||
- name: "{{ .Release.Name }}"
|
||||
kind: ferretdb
|
||||
type: ferretdb
|
||||
selector:
|
||||
app.kubernetes.io/instance: "{{ .Release.Name }}"
|
||||
replicas: "{{ .Values.replicas }}"
|
||||
minReplicas: "1"
|
||||
|
||||
@@ -28,3 +28,13 @@ spec:
|
||||
- database
|
||||
icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxNDQiIGhlaWdodD0iMTQ0IiByeD0iMjQiIGZpbGw9InVybCgjcGFpbnQwX3JhZGlhbF84NThfMzA3MikiLz4KPHBhdGggZD0iTTEzNS43ODQgNzUuNjQ0NkwxMzUuOTM5IDg3Ljc2MzhMODkuNjg0NiA4MS41MzYyTDYyLjA4NjggODQuNTA3OUwzNS4zNDE3IDgxLjQzMjlMOC43NTE2NyA4NC41ODU0TDguNzI1ODMgODEuNTEwNEwzNS4zNjc2IDc3LjU4MjZWNjQuMTcxM0w2Mi4yOTM1IDcwLjczNDhMNjIuMzQ1MiA4MS4yNzc4TDg5LjQ3NzkgNzcuNjg2TDg5LjQwMDQgNjQuMTk3MkwxMzUuNzg0IDc1LjY0NDZaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNODkuNDc3OCA4Ni4wMzI1TDEzNS44ODggOTAuODM4OFYxMDIuNzI2SDguNjQ4MjVMOC41MTkwNCA5OS41NzNIMzUuMjY0MUMzNS4yNjQxIDk5LjU3MyAzNS4yNjQxIDkwLjczNTUgMzUuMjY0MSA4Ni4wNTgzQzQ0LjI1NjcgODYuOTM2OSA2Mi4wODY3IDg4LjY5NDEgNjIuMDg2NyA4OC42OTQxVjk5LjI2MjlIODkuNDc3OFY4Ni4wMzI1WiIgZmlsbD0id2hpdGUiLz4KPHBhdGggZD0iTTYyLjI5MzQgNjYuODg0Nkw2Mi4yMTU4IDYzLjYyODZDNjIuMjE1OCA2My42Mjg2IDc5LjgxMzMgNTguMzU3MSA4OC45MDkyIDU1LjY2OTdDODguOTA5MiA1MS4zMDI2IDg4LjkwOTIgNDcuMDkwNiA4OC45MDkyIDQyQzEwNC44NzkgNDguNDA4NSAxMjAuMjI4IDU0LjYxMDIgMTM1LjczMyA2MC44Mzc4QzEzNS43MzMgNjQuNzEzOSAxMzUuNzMzIDY4LjQzNSAxMzUuNzMzIDcyLjU2OTVDMTE5Ljg0MSA2OC4yMDI0IDEwNC4yODQgNjMuOTEyOSA4OS4xNjc2IDU5Ljc1MjVDNzkuOTY4NCA2Mi4yMDc0IDYyLjI5MzQgNjYuODg0NiA2Mi4yOTM0IDY2Ljg4NDZaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMzUuMzk2MiA4MS43MDczTDguODA2MTIgODQuODU5OEw4Ljc4MDI3IDgxLjc4NDhMMzUuNDIyIDc3Ljg1N1Y2NC40NDU3TDYyLjM0OCA3MS4wMDkzTDYyLjM5OTYgODEuNTUyMkw4OS41MzIzIDc3Ljk2MDRMODkuNDU0OCA2NC40NzE2TDEzNS44MzkgNzUuOTE5TDEzNS45OTQgODguMDM4Mkw4OS43MzkxIDgxLjgxMDZMNjIuMTQxMiA4NC43ODIzTDM1LjM5NjIgODEuNzA3M1oiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik04OS41MzIzIDg2LjMwNjlMMTM1Ljk0MiA5MS4xMTMzVjEwM0g4LjcwMjdMOC41NzM0OSA5OS44NDc0SDM1LjMxODZDMzUuMzE4NiA5OS44NDc0IDM1LjMxODYgOTEuMDA5OSAzNS4zMTg2IDg2LjMzMjhDNDQuMzExMSA4Ny4yMTE0IDYyLjE0MTIgODguOTY4NSA2Mi4xNDEyIDg4Ljk2ODVWOTkuNTM3M0g4OS41MzIzVjg2LjMwNjlaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNNjIuMzQ4MyA2Ny4xNTlMNjIuMjcwOCA2My45MDMxQzYyLjI3MDggNjMuOTAzMSA3OS44NjgyIDU4LjYzMTYgODguOTY0MiA1NS45NDQyQzg4Ljk2NDIgNTEuNTc3MSA4OC45NjQyIDQ3LjM2NTEgODguOTY0MiA0Mi4yNzQ0QzEwNC45MzQgNDguNjgyOSAxMjAuMjgzIDU0Ljg4NDcgMTM1Ljc4NyA2MS4xMTIzQzEzNS43ODcgNjQuOTg4NCAxMzUuNzg3IDY4LjcwOTQgMTM1Ljc4NyA3Mi44NDM5QzExOS44OTUgNjguNDc2OSAxMDQuMzM5IDY0LjE4NzMgODkuMjIyNiA2MC4wMjdDODAuMDIzMyA2Mi40ODE4IDYyLjM0ODMgNjcuMTU5IDYyLjM0ODMgNjcuMTU5WiIgZmlsbD0id2hpdGUiLz4KPGRlZnM+CjxyYWRpYWxHcmFkaWVudCBpZD0icGFpbnQwX3JhZGlhbF84NThfMzA3MiIgY3g9IjAiIGN5PSIwIiByPSIxIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgZ3JhZGllbnRUcmFuc2Zvcm09InRyYW5zbGF0ZSgtMjkuNSAtMTgpIHJvdGF0ZSgzOS42OTYzKSBzY2FsZSgzMDIuMTY4IDI3NS4yNzEpIj4KPHN0b3Agc3RvcC1jb2xvcj0iI0JFRERGRiIvPgo8c3RvcCBvZmZzZXQ9IjAuMjU5NjE1IiBzdG9wLWNvbG9yPSIjOUVDQ0ZEIi8+CjxzdG9wIG9mZnNldD0iMC41OTEzNDYiIHN0b3AtY29sb3I9IiMzRjlBRkIiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjMEI3MEUwIi8+CjwvcmFkaWFsR3JhZGllbnQ+CjwvZGVmcz4KPC9zdmc+Cg==
|
||||
# keysOrder: []
|
||||
workloadMonitors:
|
||||
- name: "{{ .Release.Name }}"
|
||||
kind: foundationdb
|
||||
type: foundationdb
|
||||
selector:
|
||||
foundationdb.org/fdb-cluster-name: "{{ .Release.Name }}"
|
||||
foundationdb.org/fdb-process-class: storage
|
||||
replicas: "{{ .Values.cluster.processCounts.storage }}"
|
||||
minReplicas: "{{ include \"foundationdb.minReplicas\" . }}"
|
||||
condition: "{{ .Values.monitoring.enabled }}"
|
||||
|
||||
@@ -32,3 +32,25 @@ spec:
|
||||
secrets:
|
||||
exclude: []
|
||||
include: []
|
||||
workloadMonitors:
|
||||
- name: "{{ .Release.Name }}-haproxy"
|
||||
kind: http-cache
|
||||
type: http-cache
|
||||
selector:
|
||||
app: "{{ .Release.Name }}-haproxy"
|
||||
replicas: "{{ .Values.haproxy.replicas }}"
|
||||
minReplicas: "1"
|
||||
- name: "{{ .Release.Name }}-nginx"
|
||||
kind: http-cache
|
||||
type: http-cache
|
||||
selector:
|
||||
app: "{{ .Release.Name }}-nginx-cache"
|
||||
replicas: "{{ .Values.nginx.replicas }}"
|
||||
minReplicas: "1"
|
||||
- name: "{{ .Release.Name }}"
|
||||
kind: http-cache
|
||||
type: http-cache
|
||||
selector:
|
||||
app.kubernetes.io/instance: "{{ .Release.Name }}"
|
||||
replicas: "{{ .Values.replicas }}"
|
||||
minReplicas: "1"
|
||||
|
||||
@@ -37,3 +37,13 @@ spec:
|
||||
include:
|
||||
- resourceNames:
|
||||
- "{{ slice .namespace 7 }}-ingress-controller"
|
||||
workloadMonitors:
|
||||
- name: "{{ .Release.Name }}"
|
||||
kind: ingress
|
||||
type: controller
|
||||
selector:
|
||||
app.kubernetes.io/component: controller
|
||||
app.kubernetes.io/instance: ingress-nginx-system
|
||||
app.kubernetes.io/name: ingress-nginx
|
||||
replicas: "{{ .Values.replicas }}"
|
||||
minReplicas: "{{ div .Values.replicas 2 | add1 }}"
|
||||
|
||||
@@ -38,3 +38,20 @@ spec:
|
||||
include:
|
||||
- resourceNames:
|
||||
- kafka-{{ .name }}-kafka-bootstrap
|
||||
workloadMonitors:
|
||||
- name: "{{ .Release.Name }}"
|
||||
kind: kafka
|
||||
type: kafka
|
||||
selector:
|
||||
app.kubernetes.io/instance: "{{ .Release.Name }}"
|
||||
app.kubernetes.io/name: kafka
|
||||
replicas: "{{ .Values.kafka.replicas }}"
|
||||
minReplicas: "1"
|
||||
- name: "{{ .Release.Name }}-zookeeper"
|
||||
kind: kafka
|
||||
type: zookeeper
|
||||
selector:
|
||||
app.kubernetes.io/instance: "{{ .Release.Name }}"
|
||||
app.kubernetes.io/name: zookeeper
|
||||
replicas: "{{ .Values.zookeeper.replicas }}"
|
||||
minReplicas: "1"
|
||||
|
||||
@@ -39,3 +39,11 @@ spec:
|
||||
- resourceNames:
|
||||
- mysql-{{ .name }}-primary
|
||||
- mysql-{{ .name }}-secondary
|
||||
workloadMonitors:
|
||||
- name: "{{ .Release.Name }}"
|
||||
kind: mysql
|
||||
type: mysql
|
||||
selector:
|
||||
app.kubernetes.io/instance: "{{ .Release.Name }}"
|
||||
replicas: "{{ .Values.replicas }}"
|
||||
minReplicas: "1"
|
||||
|
||||
@@ -38,3 +38,11 @@ spec:
|
||||
include:
|
||||
- resourceNames:
|
||||
- nats-{{ .name }}
|
||||
workloadMonitors:
|
||||
- name: "{{ .Release.Name }}"
|
||||
kind: nats
|
||||
type: nats
|
||||
selector:
|
||||
app.kubernetes.io/instance: "{{ .Release.Name }}-system"
|
||||
replicas: "{{ .Values.replicas }}"
|
||||
minReplicas: "1"
|
||||
|
||||
@@ -49,3 +49,12 @@ spec:
|
||||
- postgres-{{ .name }}-ro
|
||||
- postgres-{{ .name }}-rw
|
||||
- postgres-{{ .name }}-external-write
|
||||
workloadMonitors:
|
||||
- name: "{{ .Release.Name }}"
|
||||
kind: postgres
|
||||
type: postgres
|
||||
selector:
|
||||
app.kubernetes.io/name: postgres.apps.cozystack.io
|
||||
app.kubernetes.io/instance: "{{ .Release.Name }}"
|
||||
replicas: "{{ .Values.replicas }}"
|
||||
minReplicas: "1"
|
||||
|
||||
@@ -40,3 +40,11 @@ spec:
|
||||
include:
|
||||
- resourceNames:
|
||||
- rabbitmq-{{ .name }}
|
||||
workloadMonitors:
|
||||
- name: "{{ .Release.Name }}"
|
||||
kind: rabbitmq
|
||||
type: rabbitmq
|
||||
selector:
|
||||
app.kubernetes.io/name: "{{ .Release.Name }}"
|
||||
replicas: "{{ .Values.replicas }}"
|
||||
minReplicas: "1"
|
||||
|
||||
@@ -41,3 +41,20 @@ spec:
|
||||
- rfrm-redis-{{ .name }}
|
||||
- rfrs-redis-{{ .name }}
|
||||
- redis-{{ .name }}-external-lb
|
||||
workloadMonitors:
|
||||
- name: "{{ .Release.Name }}-redis"
|
||||
kind: redis
|
||||
type: redis
|
||||
selector:
|
||||
app.kubernetes.io/component: redis
|
||||
app.kubernetes.io/instance: "{{ .Release.Name }}"
|
||||
replicas: "{{ .Values.replicas }}"
|
||||
minReplicas: "1"
|
||||
- name: "{{ .Release.Name }}-sentinel"
|
||||
kind: redis
|
||||
type: sentinel
|
||||
selector:
|
||||
app.kubernetes.io/component: sentinel
|
||||
app.kubernetes.io/instance: "{{ .Release.Name }}"
|
||||
replicas: "3"
|
||||
minReplicas: "2"
|
||||
|
||||
@@ -31,3 +31,11 @@ spec:
|
||||
secrets:
|
||||
exclude: []
|
||||
include: []
|
||||
workloadMonitors:
|
||||
- name: "{{ .Release.Name }}"
|
||||
kind: tcp-balancer
|
||||
type: haproxy
|
||||
selector:
|
||||
app.kubernetes.io/instance: "{{ .Release.Name }}"
|
||||
replicas: "{{ .Values.replicas }}"
|
||||
minReplicas: "1"
|
||||
|
||||
@@ -8,7 +8,7 @@ spec:
|
||||
plural: virtualmachines
|
||||
singular: virtualmachine
|
||||
openAPISchema: |-
|
||||
{"title":"Chart Values","type":"object","properties":{"cloudInit":{"description":"Cloud-init user data.","type":"string","default":""},"cloudInitSeed":{"description":"Seed string to generate SMBIOS UUID for the VM.","type":"string","default":""},"external":{"description":"Enable external access from outside the cluster.","type":"boolean","default":false},"externalMethod":{"description":"Method to pass through traffic to the VM.","type":"string","default":"PortList","enum":["PortList","WholeIP"]},"externalPorts":{"description":"Ports to forward from outside the cluster.","type":"array","default":[22],"items":{"type":"integer"}},"gpus":{"description":"List of GPUs to attach.","type":"array","default":[],"items":{"type":"object","required":["name"],"properties":{"name":{"description":"The name of the GPU resource to attach.","type":"string"}}}},"instanceProfile":{"description":"Virtual Machine preferences profile.","type":"string","default":"ubuntu","enum":["alpine","centos.7","centos.7.desktop","centos.stream10","centos.stream10.desktop","centos.stream8","centos.stream8.desktop","centos.stream8.dpdk","centos.stream9","centos.stream9.desktop","centos.stream9.dpdk","cirros","fedora","fedora.arm64","opensuse.leap","opensuse.tumbleweed","rhel.10","rhel.10.arm64","rhel.7","rhel.7.desktop","rhel.8","rhel.8.desktop","rhel.8.dpdk","rhel.9","rhel.9.arm64","rhel.9.desktop","rhel.9.dpdk","rhel.9.realtime","sles","ubuntu","windows.10","windows.10.virtio","windows.11","windows.11.virtio","windows.2k16","windows.2k16.virtio","windows.2k19","windows.2k19.virtio","windows.2k22","windows.2k22.virtio","windows.2k25","windows.2k25.virtio",""]},"instanceType":{"description":"Virtual Machine instance type.","type":"string","default":"u1.medium"},"resources":{"description":"Resource configuration for the virtual machine.","type":"object","default":{},"properties":{"cpu":{"description":"Number of CPU cores allocated.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Amount of memory allocated.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"sockets":{"description":"Number of CPU sockets (vCPU topology).","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"running":{"description":"Whether the virtual machine should be running.","type":"boolean","default":true},"sshKeys":{"description":"List of SSH public keys for authentication.","type":"array","default":[],"items":{"type":"string"}},"systemDisk":{"description":"System disk configuration.","type":"object","default":{},"required":["image","storage"],"properties":{"image":{"description":"The base image for the virtual machine.","type":"string","default":"ubuntu","enum":["ubuntu","cirros","alpine","fedora","talos"]},"storage":{"description":"The size of the disk allocated for the virtual machine.","type":"string","default":"5Gi"},"storageClass":{"description":"StorageClass used to store the data.","type":"string","default":"replicated"}}}}}
|
||||
{"title":"Chart Values","type":"object","properties":{"cloudInit":{"description":"Cloud-init user data.","type":"string","default":""},"cloudInitSeed":{"description":"Seed string to generate SMBIOS UUID for the VM.","type":"string","default":""},"external":{"description":"Enable external access from outside the cluster.","type":"boolean","default":false},"externalMethod":{"description":"Method to pass through traffic to the VM.","type":"string","default":"PortList","enum":["PortList","WholeIP"]},"externalPorts":{"description":"Ports to forward from outside the cluster.","type":"array","default":[22],"items":{"type":"integer"}},"gpus":{"description":"List of GPUs to attach.","type":"array","default":[],"items":{"type":"object","required":["name"],"properties":{"name":{"description":"The name of the GPU resource to attach.","type":"string"}}}},"instanceProfile":{"description":"Virtual Machine preferences profile.","type":"string","default":"ubuntu","enum":["alpine","centos.7","centos.7.desktop","centos.stream10","centos.stream10.desktop","centos.stream8","centos.stream8.desktop","centos.stream8.dpdk","centos.stream9","centos.stream9.desktop","centos.stream9.dpdk","cirros","fedora","fedora.arm64","opensuse.leap","opensuse.tumbleweed","rhel.10","rhel.10.arm64","rhel.7","rhel.7.desktop","rhel.8","rhel.8.desktop","rhel.8.dpdk","rhel.9","rhel.9.arm64","rhel.9.desktop","rhel.9.dpdk","rhel.9.realtime","sles","ubuntu","windows.10","windows.10.virtio","windows.11","windows.11.virtio","windows.2k16","windows.2k16.virtio","windows.2k19","windows.2k19.virtio","windows.2k22","windows.2k22.virtio","windows.2k25","windows.2k25.virtio",""]},"instanceType":{"description":"Virtual Machine instance type.","type":"string","default":"u1.medium"},"resources":{"description":"Resource configuration for the virtual machine.","type":"object","default":{},"properties":{"cpu":{"description":"Number of CPU cores allocated.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Amount of memory allocated.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"sockets":{"description":"Number of CPU sockets (vCPU topology).","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"running":{"description":"Whether the virtual machine should be running.","type":"boolean","default":true},"sshKeys":{"description":"List of SSH public keys for authentication.","type":"array","default":[],"items":{"type":"string"}},"subnets":{"description":"Additional subnets","type":"array","default":[],"items":{"type":"object","properties":{"name":{"description":"Subnet name","type":"string"}}}},"systemDisk":{"description":"System disk configuration.","type":"object","default":{},"required":["image","storage"],"properties":{"image":{"description":"The base image for the virtual machine.","type":"string","default":"ubuntu","enum":["ubuntu","cirros","alpine","fedora","talos"]},"storage":{"description":"The size of the disk allocated for the virtual machine.","type":"string","default":"5Gi"},"storageClass":{"description":"StorageClass used to store the data.","type":"string","default":"replicated"}}}}}
|
||||
release:
|
||||
prefix: virtual-machine-
|
||||
labels:
|
||||
@@ -28,7 +28,7 @@ spec:
|
||||
tags:
|
||||
- compute
|
||||
icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxNDQiIGhlaWdodD0iMTQ0IiByeD0iMjQiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl82ODdfMzQ1NCkiLz4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzY4N18zNDU0KSI+CjxwYXRoIGQ9Ik04OS41MDM5IDExMS43MDdINTQuNDk3QzU0LjE3MjcgMTExLjcwNyA1NC4wMTA4IDExMS4yMjEgNTQuMzM0OSAxMTEuMDU5TDU3LjI1MjIgMTA4Ljk1MkM2MC4zMzE0IDEwNi42ODMgNjEuOTUyMiAxMDIuNjMxIDYwLjk3OTcgOTguNzQxMkg4My4wMjFDODIuMDQ4NSAxMDIuNjMxIDgzLjY2OTMgMTA2LjY4MyA4Ni43NDg1IDEwOC45NTJMODkuNjY1OCAxMTEuMDU5Qzg5Ljk5IDExMS4yMjEgODkuODI3OSAxMTEuNzA3IDg5LjUwMzkgMTExLjcwN1oiIGZpbGw9IiNCMEI2QkIiLz4KPHBhdGggZD0iTTExMy4zMjggOTguNzQxSDMwLjY3MjVDMjcuNTkzMSA5OC43NDEgMjUgOTYuMTQ4IDI1IDkzLjA2ODdWMzMuMTAzMkMyNSAzMC4wMjM5IDI3LjU5MzEgMjcuNDMwNyAzMC42NzI1IDI3LjQzMDdIMTEzLjMyOEMxMTYuNDA3IDI3LjQzMDcgMTE5IDMwLjAyMzcgMTE5IDMzLjEwMzJWOTMuMDY4N0MxMTkgOTYuMTQ4IDExNi40MDcgOTguNzQxIDExMy4zMjggOTguNzQxWiIgZmlsbD0iI0U4RURFRSIvPgo8cGF0aCBkPSJNMTE5IDg0LjE1NDlIMjVWMzMuMTAzMkMyNSAzMC4wMjM5IDI3LjU5MzEgMjcuNDMwNyAzMC42NzI1IDI3LjQzMDdIMTEzLjMyOEMxMTYuNDA3IDI3LjQzMDcgMTE5IDMwLjAyMzcgMTE5IDMzLjEwMzJMMTE5IDg0LjE1NDlaIiBmaWxsPSIjMDBCM0ZGIi8+CjxwYXRoIGQ9Ik05MC42Mzc0IDExNi41NjlINTMuMzYxNkM1Mi4wNjUxIDExNi41NjkgNTAuOTMwNyAxMTUuNDM1IDUwLjkzMDcgMTE0LjEzOEM1MC45MzA3IDExMi44NDEgNTIuMDY1MSAxMTEuNzA3IDUzLjM2MTYgMTExLjcwN0g5MC42Mzc0QzkxLjkzMzkgMTExLjcwNyA5My4wNjg0IDExMi44NDEgOTMuMDY4NCAxMTQuMTM4QzkzLjA2ODQgMTE1LjQzNSA5MS45MzM5IDExNi41NjkgOTAuNjM3NCAxMTYuNTY5WiIgZmlsbD0iI0U4RURFRSIvPgo8L2c+CjxwYXRoIGQ9Ik03Mi41Mjc1IDUzLjgzNjdDNzIuNDQzMSA1My44MzUxIDcyLjM2MDUgNTMuODEyMiA3Mi4yODczIDUzLjc3MDFMNTYuNDY5OSA0NC43OTM0QzU2LjM5ODMgNDQuNzUxOSA1Ni4zMzg4IDQ0LjY5MjMgNTYuMjk3MyA0NC42MjA3QzU2LjI1NTkgNDQuNTQ5IDU2LjIzMzggNDQuNDY3OCA1Ni4yMzM0IDQ0LjM4NUM1Ni4yMzM0IDQ0LjIxNjkgNTYuMzI1OCA0NC4wNjE3IDU2LjQ2OTkgNDMuOTc4NUw3Mi4xOTEyIDM1LjA2MDlDNzIuMjYzNyAzNS4wMjEgNzIuMzQ1IDM1IDcyLjQyNzcgMzVDNzIuNTEwNSAzNSA3Mi41OTE4IDM1LjAyMSA3Mi42NjQzIDM1LjA2MDlMODguNDg3MiA0NC4wMzk1Qzg4LjU1OTEgNDQuMDgwMSA4OC42MTg4IDQ0LjEzOTIgODguNjYgNDQuMjEwN0M4OC43MDEzIDQ0LjI4MjIgODguNzIyNyA0NC4zNjM1IDg4LjcyMTkgNDQuNDQ2Qzg4LjcyMjUgNDQuNTI4NSA4OC43MDEgNDQuNjA5NyA4OC42NTk4IDQ0LjY4MTJDODguNjE4NSA0NC43NTI2IDg4LjU1ODkgNDQuODExOCA4OC40ODcyIDQ0Ljg1MjVMNzIuNzcxNCA1My43NjgzQzcyLjY5NzIgNTMuODExNCA3Mi42MTMzIDUzLjgzNDkgNzIuNTI3NSA1My44MzY3IiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBvcGFjaXR5PSIwLjciIGQ9Ik03MC4yNTUzIDc1LjY1MTdDNzAuMTcxIDc1LjY1MzUgNzAuMDg3OCA3NS42MzE3IDcwLjAxNTEgNzUuNTg4OEw1NC4yNDU4IDY2LjY0MTdDNTQuMTcxNSA2Ni42MDI0IDU0LjEwOTUgNjYuNTQzNiA1NC4wNjYxIDY2LjQ3MTZDNTQuMDIyOCA2Ni4zOTk3IDU0IDY2LjMxNzMgNTQgNjYuMjMzM1Y0OC4yNzhDNTQgNDguMTA4IDU0LjA5MjQgNDcuOTU0NiA1NC4yNDM5IDQ3Ljg2OTZDNTQuMzE3MiA0Ny44MjcxIDU0LjQwMDQgNDcuODA0NyA1NC40ODUxIDQ3LjgwNDdDNTQuNTY5NyA0Ny44MDQ3IDU0LjY1MjkgNDcuODI3MSA1NC43MjYyIDQ3Ljg2OTZMNzAuNDkzNyA1Ni44MTMxQzcwLjU2NDIgNTYuODU2NSA3MC42MjI1IDU2LjkxNyA3MC42NjMyIDU2Ljk4OTFDNzAuNzAzOSA1Ny4wNjEyIDcwLjcyNTcgNTcuMTQyNCA3MC43MjY1IDU3LjIyNTFWNzUuMTgwNUM3MC43MjU5IDc1LjI2MjggNzAuNzA0MiA3NS4zNDM2IDcwLjY2MzUgNzUuNDE1MUM3MC42MjI3IDc1LjQ4NjYgNzAuNTY0MiA3NS41NDY0IDcwLjQ5MzcgNzUuNTg4OEM3MC40MjA2IDc1LjYyOTEgNzAuMzM4NyA3NS42NTA3IDcwLjI1NTMgNzUuNjUxNyIgZmlsbD0id2hpdGUiLz4KPHBhdGggb3BhY2l0eT0iMC40IiBkPSJNNzQuNzE5OCA3NS42NTExQzc0LjYzMzMgNzUuNjUxMiA3NC41NDgyIDc1LjYyOTYgNzQuNDcyMiA3NS41ODgzQzc0LjQwMTYgNzUuNTQ2MSA3NC4zNDMyIDc1LjQ4NjIgNzQuMzAyNyA3NS40MTQ3Qzc0LjI2MjMgNzUuMzQzMSA3NC4yNDExIDc1LjI2MjIgNzQuMjQxMiA3NS4xOFY1Ny4zMzczQzc0LjI0MTIgNTcuMTcxIDc0LjMzMzYgNTcuMDE1OCA3NC40NzIyIDU2LjkyOUw5MC4yMzk3IDQ3Ljk4NTVDOTAuMzExOSA0Ny45NDM4IDkwLjM5MzggNDcuOTIxOSA5MC40NzcxIDQ3LjkyMTlDOTAuNTYwNSA0Ny45MjE5IDkwLjY0MjQgNDcuOTQzOCA5MC43MTQ2IDQ3Ljk4NTVDOTAuNzg3NiA0OC4wMjU1IDkwLjg0ODUgNDguMDg0MiA5MC44OTExIDQ4LjE1NTdDOTAuOTMzNyA0OC4yMjcyIDkwLjk1NjMgNDguMzA4OCA5MC45NTY2IDQ4LjM5MlY2Ni4yMzI4QzkwLjk1NyA2Ni4zMTY0IDkwLjkzNDcgNjYuMzk4NSA5MC44OTIxIDY2LjQ3MDRDOTAuODQ5NSA2Ni41NDI0IDkwLjc4ODEgNjYuNjAxNCA5MC43MTQ2IDY2LjY0MTFMNzQuOTUyNiA3NS41ODgzQzc0Ljg4MjUgNzUuNjMwNyA3NC44MDE4IDc1LjY1MjUgNzQuNzE5OCA3NS42NTExIiBmaWxsPSJ3aGl0ZSIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyXzY4N18zNDU0IiB4MT0iMTYxIiB5MT0iMTgwIiB4Mj0iMy41OTI4NGUtMDciIHkyPSI0Ljk5OTk4IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjNTk1NjU2Ii8+CjwvbGluZWFyR3JhZGllbnQ+CjxjbGlwUGF0aCBpZD0iY2xpcDBfNjg3XzM0NTQiPgo8cmVjdCB3aWR0aD0iOTQiIGhlaWdodD0iOTQiIGZpbGw9IndoaXRlIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNSAyNSkiLz4KPC9jbGlwUGF0aD4KPC9kZWZzPgo8L3N2Zz4K
|
||||
keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "external"], ["spec", "externalMethod"], ["spec", "externalPorts"], ["spec", "running"], ["spec", "instanceType"], ["spec", "instanceProfile"], ["spec", "systemDisk"], ["spec", "systemDisk", "image"], ["spec", "systemDisk", "storage"], ["spec", "systemDisk", "storageClass"], ["spec", "gpus"], ["spec", "resources"], ["spec", "sshKeys"], ["spec", "cloudInit"], ["spec", "cloudInitSeed"]]
|
||||
keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "external"], ["spec", "externalMethod"], ["spec", "externalPorts"], ["spec", "running"], ["spec", "instanceType"], ["spec", "instanceProfile"], ["spec", "systemDisk"], ["spec", "systemDisk", "image"], ["spec", "systemDisk", "storage"], ["spec", "systemDisk", "storageClass"], ["spec", "subnets"], ["spec", "gpus"], ["spec", "resources"], ["spec", "sshKeys"], ["spec", "cloudInit"], ["spec", "cloudInitSeed"]]
|
||||
secrets:
|
||||
exclude: []
|
||||
include: []
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
apiVersion: cozystack.io/v1alpha1
|
||||
kind: CozystackResourceDefinition
|
||||
metadata:
|
||||
name: virtualprivatecloud
|
||||
spec:
|
||||
application:
|
||||
kind: VirtualPrivateCloud
|
||||
plural: virtualprivateclouds
|
||||
singular: virtualprivatecloud
|
||||
openAPISchema: |-
|
||||
{"title":"Chart Values","type":"object","properties":{"subnets":{"description":"Subnets of a VPC","type":"object","default":{},"additionalProperties":{"type":"object","properties":{"cidr":{"description":"IP address range","type":"string"}}}}}}
|
||||
release:
|
||||
prefix: "virtualprivatecloud-"
|
||||
labels:
|
||||
cozystack.io/ui: "true"
|
||||
chart:
|
||||
name: virtualprivatecloud
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: cozystack-apps
|
||||
namespace: cozy-public
|
||||
dashboard:
|
||||
category: IaaS
|
||||
singular: VPC
|
||||
plural: VPCs
|
||||
description: "Isolated networks"
|
||||
icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxNDQiIGhlaWdodD0iMTQ0IiByeD0iMjQiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl8xMDI1XzMpIi8+CjxwYXRoIGQ9Ik0xMDkuNiA4Ni4xSDExNC4zQzExNi44ODUgODYuMSAxMTkgODguMjE1IDExOSA5MC44NDdWMTA0Ljg1M0MxMTkgMTA3LjQ4NSAxMTYuODg1IDEwOS42IDExNC4zIDEwOS42SDk1LjVDOTIuOTE1IDEwOS42IDkwLjggMTA3LjQ4NSA5MC44IDEwNC44NTNWOTAuODQ3QzkwLjggODguMjE1IDkyLjkxNSA4Ni4xIDk1LjUgODYuMUgxMDAuMlY3Ni43SDc2LjdWODYuMUg4MS40QzgzLjk4NSA4Ni4xIDg2LjEgODguMjE1IDg2LjEgOTAuODQ3VjEwNC44NTNDODYuMSAxMDcuNDg1IDgzLjk4NSAxMDkuNiA4MS40IDEwOS42SDYyLjZDNjAuMDE1IDEwOS42IDU3LjkgMTA3LjQ4NSA1Ny45IDEwNC44NTNWOTAuODQ3QzU3LjkgODguMjE1IDYwLjAxNSA4Ni4xIDYyLjYgODYuMUg2Ny4zVjc2LjdINDMuOFY4Ni4xSDQ4LjVDNTEuMDg1IDg2LjEgNTMuMiA4OC4yMTUgNTMuMiA5MC44NDdWMTA0Ljg1M0M1My4yIDEwNy40ODUgNTEuMDg1IDEwOS42IDQ4LjUgMTA5LjZIMjkuN0MyNy4xMTUgMTA5LjYgMjUgMTA3LjQ4NSAyNSAxMDQuODUzVjkwLjg0N0MyNSA4OC4yMTUgMjcuMTE1IDg2LjEgMjkuNyA4Ni4xSDM0LjRWNzYuN0MzNC40IDcxLjUzIDM4LjYzIDY3LjMgNDMuOCA2Ny4zSDY3LjNWNTcuOUg2Mi42QzYwLjAxNSA1Ny45IDU3LjkgNTUuNzg1IDU3LjkgNTMuMTUzVjM5LjE0N0M1Ny45IDM2LjUxNSA2MC4wMTUgMzQuNCA2Mi42IDM0LjRIODEuNEM4My45ODUgMzQuNCA4Ni4xIDM2LjUxNSA4Ni4xIDM5LjE0N1Y1My4xNTNDODYuMSA1NS43ODUgODMuOTg1IDU3LjkgODEuNCA1Ny45SDc2LjdWNjcuM0gxMDAuMkMxMDUuMzcgNjcuMyAxMDkuNiA3MS41MyAxMDkuNiA3Ni43Vjg2LjFaIiBmaWxsPSJ3aGl0ZSIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyXzEwMjVfMyIgeDE9IjE0Mi41IiB5MT0iMTQzIiB4Mj0iMy45OTk5OSIgeTI9IjkuNDk5OTkiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agc3RvcC1jb2xvcj0iIzAwMDgyRSIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiMyRTMwNjciLz4KPC9saW5lYXJHcmFkaWVudD4KPC9kZWZzPgo8L3N2Zz4K
|
||||
keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "subnets"]]
|
||||
secrets:
|
||||
exclude: []
|
||||
include: []
|
||||
services:
|
||||
exclude: []
|
||||
include: []
|
||||
@@ -32,3 +32,11 @@ spec:
|
||||
secrets:
|
||||
exclude: []
|
||||
include: []
|
||||
workloadMonitors:
|
||||
- name: "{{ .Release.Name }}"
|
||||
kind: vm-disk
|
||||
type: vm-disk
|
||||
selector:
|
||||
app.kubernetes.io/instance: "{{ .Release.Name }}"
|
||||
replicas: "0"
|
||||
minReplicas: "0"
|
||||
|
||||
@@ -8,7 +8,7 @@ spec:
|
||||
singular: vminstance
|
||||
plural: vminstances
|
||||
openAPISchema: |-
|
||||
{"title":"Chart Values","type":"object","properties":{"cloudInit":{"description":"Cloud-init user data.","type":"string","default":""},"cloudInitSeed":{"description":"Seed string to generate SMBIOS UUID for the VM.","type":"string","default":""},"disks":{"description":"List of disks to attach.","type":"array","default":[],"items":{"type":"object","required":["name"],"properties":{"bus":{"description":"Disk bus type (e.g. \"sata\").","type":"string"},"name":{"description":"Disk name.","type":"string"}}}},"external":{"description":"Enable external access from outside the cluster.","type":"boolean","default":false},"externalMethod":{"description":"Method to pass through traffic to the VM.","type":"string","default":"PortList","enum":["PortList","WholeIP"]},"externalPorts":{"description":"Ports to forward from outside the cluster.","type":"array","default":[22],"items":{"type":"integer"}},"gpus":{"description":"List of GPUs to attach (NVIDIA driver requires at least 4 GiB RAM).","type":"array","default":[],"items":{"type":"object","required":["name"],"properties":{"name":{"description":"The name of the GPU resource to attach.","type":"string"}}}},"instanceProfile":{"description":"Virtual Machine preferences profile.","type":"string","default":"ubuntu","enum":["alpine","centos.7","centos.7.desktop","centos.stream10","centos.stream10.desktop","centos.stream8","centos.stream8.desktop","centos.stream8.dpdk","centos.stream9","centos.stream9.desktop","centos.stream9.dpdk","cirros","fedora","fedora.arm64","opensuse.leap","opensuse.tumbleweed","rhel.10","rhel.10.arm64","rhel.7","rhel.7.desktop","rhel.8","rhel.8.desktop","rhel.8.dpdk","rhel.9","rhel.9.arm64","rhel.9.desktop","rhel.9.dpdk","rhel.9.realtime","sles","ubuntu","windows.10","windows.10.virtio","windows.11","windows.11.virtio","windows.2k16","windows.2k16.virtio","windows.2k19","windows.2k19.virtio","windows.2k22","windows.2k22.virtio","windows.2k25","windows.2k25.virtio",""]},"instanceType":{"description":"Virtual Machine instance type.","type":"string","default":"u1.medium"},"resources":{"description":"Resource configuration for the virtual machine.","type":"object","default":{},"properties":{"cpu":{"description":"Number of CPU cores allocated.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Amount of memory allocated.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"sockets":{"description":"Number of CPU sockets (vCPU topology).","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"running":{"description":"Determines if the virtual machine should be running.","type":"boolean","default":true},"sshKeys":{"description":"List of SSH public keys for authentication.","type":"array","default":[],"items":{"type":"string"}}}}
|
||||
{"title":"Chart Values","type":"object","properties":{"cloudInit":{"description":"Cloud-init user data.","type":"string","default":""},"cloudInitSeed":{"description":"Seed string to generate SMBIOS UUID for the VM.","type":"string","default":""},"disks":{"description":"List of disks to attach.","type":"array","default":[],"items":{"type":"object","required":["name"],"properties":{"bus":{"description":"Disk bus type (e.g. \"sata\").","type":"string"},"name":{"description":"Disk name.","type":"string"}}}},"external":{"description":"Enable external access from outside the cluster.","type":"boolean","default":false},"externalMethod":{"description":"Method to pass through traffic to the VM.","type":"string","default":"PortList","enum":["PortList","WholeIP"]},"externalPorts":{"description":"Ports to forward from outside the cluster.","type":"array","default":[22],"items":{"type":"integer"}},"gpus":{"description":"List of GPUs to attach (NVIDIA driver requires at least 4 GiB RAM).","type":"array","default":[],"items":{"type":"object","required":["name"],"properties":{"name":{"description":"The name of the GPU resource to attach.","type":"string"}}}},"instanceProfile":{"description":"Virtual Machine preferences profile.","type":"string","default":"ubuntu","enum":["alpine","centos.7","centos.7.desktop","centos.stream10","centos.stream10.desktop","centos.stream8","centos.stream8.desktop","centos.stream8.dpdk","centos.stream9","centos.stream9.desktop","centos.stream9.dpdk","cirros","fedora","fedora.arm64","opensuse.leap","opensuse.tumbleweed","rhel.10","rhel.10.arm64","rhel.7","rhel.7.desktop","rhel.8","rhel.8.desktop","rhel.8.dpdk","rhel.9","rhel.9.arm64","rhel.9.desktop","rhel.9.dpdk","rhel.9.realtime","sles","ubuntu","windows.10","windows.10.virtio","windows.11","windows.11.virtio","windows.2k16","windows.2k16.virtio","windows.2k19","windows.2k19.virtio","windows.2k22","windows.2k22.virtio","windows.2k25","windows.2k25.virtio",""]},"instanceType":{"description":"Virtual Machine instance type.","type":"string","default":"u1.medium"},"resources":{"description":"Resource configuration for the virtual machine.","type":"object","default":{},"properties":{"cpu":{"description":"Number of CPU cores allocated.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"memory":{"description":"Amount of memory allocated.","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true},"sockets":{"description":"Number of CPU sockets (vCPU topology).","pattern":"^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$","anyOf":[{"type":"integer"},{"type":"string"}],"x-kubernetes-int-or-string":true}}},"running":{"description":"Determines if the virtual machine should be running.","type":"boolean","default":true},"sshKeys":{"description":"List of SSH public keys for authentication.","type":"array","default":[],"items":{"type":"string"}},"subnets":{"description":"Additional subnets","type":"array","default":[],"items":{"type":"object","properties":{"name":{"description":"Subnet name","type":"string"}}}}}}
|
||||
release:
|
||||
prefix: vm-instance-
|
||||
labels:
|
||||
@@ -28,7 +28,7 @@ spec:
|
||||
tags:
|
||||
- compute
|
||||
icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxNDQiIGhlaWdodD0iMTQ0IiByeD0iMjQiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl82ODdfMzQ1NCkiLz4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzY4N18zNDU0KSI+CjxwYXRoIGQ9Ik04OS41MDM5IDExMS43MDdINTQuNDk3QzU0LjE3MjcgMTExLjcwNyA1NC4wMTA4IDExMS4yMjEgNTQuMzM0OSAxMTEuMDU5TDU3LjI1MjIgMTA4Ljk1MkM2MC4zMzE0IDEwNi42ODMgNjEuOTUyMiAxMDIuNjMxIDYwLjk3OTcgOTguNzQxMkg4My4wMjFDODIuMDQ4NSAxMDIuNjMxIDgzLjY2OTMgMTA2LjY4MyA4Ni43NDg1IDEwOC45NTJMODkuNjY1OCAxMTEuMDU5Qzg5Ljk5IDExMS4yMjEgODkuODI3OSAxMTEuNzA3IDg5LjUwMzkgMTExLjcwN1oiIGZpbGw9IiNCMEI2QkIiLz4KPHBhdGggZD0iTTExMy4zMjggOTguNzQxSDMwLjY3MjVDMjcuNTkzMSA5OC43NDEgMjUgOTYuMTQ4IDI1IDkzLjA2ODdWMzMuMTAzMkMyNSAzMC4wMjM5IDI3LjU5MzEgMjcuNDMwNyAzMC42NzI1IDI3LjQzMDdIMTEzLjMyOEMxMTYuNDA3IDI3LjQzMDcgMTE5IDMwLjAyMzcgMTE5IDMzLjEwMzJWOTMuMDY4N0MxMTkgOTYuMTQ4IDExNi40MDcgOTguNzQxIDExMy4zMjggOTguNzQxWiIgZmlsbD0iI0U4RURFRSIvPgo8cGF0aCBkPSJNMTE5IDg0LjE1NDlIMjVWMzMuMTAzMkMyNSAzMC4wMjM5IDI3LjU5MzEgMjcuNDMwNyAzMC42NzI1IDI3LjQzMDdIMTEzLjMyOEMxMTYuNDA3IDI3LjQzMDcgMTE5IDMwLjAyMzcgMTE5IDMzLjEwMzJMMTE5IDg0LjE1NDlaIiBmaWxsPSIjMDBCM0ZGIi8+CjxwYXRoIGQ9Ik05MC42Mzc0IDExNi41NjlINTMuMzYxNkM1Mi4wNjUxIDExNi41NjkgNTAuOTMwNyAxMTUuNDM1IDUwLjkzMDcgMTE0LjEzOEM1MC45MzA3IDExMi44NDEgNTIuMDY1MSAxMTEuNzA3IDUzLjM2MTYgMTExLjcwN0g5MC42Mzc0QzkxLjkzMzkgMTExLjcwNyA5My4wNjg0IDExMi44NDEgOTMuMDY4NCAxMTQuMTM4QzkzLjA2ODQgMTE1LjQzNSA5MS45MzM5IDExNi41NjkgOTAuNjM3NCAxMTYuNTY5WiIgZmlsbD0iI0U4RURFRSIvPgo8L2c+CjxwYXRoIGQ9Ik03Mi41Mjc1IDUzLjgzNjdDNzIuNDQzMSA1My44MzUxIDcyLjM2MDUgNTMuODEyMiA3Mi4yODczIDUzLjc3MDFMNTYuNDY5OSA0NC43OTM0QzU2LjM5ODMgNDQuNzUxOSA1Ni4zMzg4IDQ0LjY5MjMgNTYuMjk3MyA0NC42MjA3QzU2LjI1NTkgNDQuNTQ5IDU2LjIzMzggNDQuNDY3OCA1Ni4yMzM0IDQ0LjM4NUM1Ni4yMzM0IDQ0LjIxNjkgNTYuMzI1OCA0NC4wNjE3IDU2LjQ2OTkgNDMuOTc4NUw3Mi4xOTEyIDM1LjA2MDlDNzIuMjYzNyAzNS4wMjEgNzIuMzQ1IDM1IDcyLjQyNzcgMzVDNzIuNTEwNSAzNSA3Mi41OTE4IDM1LjAyMSA3Mi42NjQzIDM1LjA2MDlMODguNDg3MiA0NC4wMzk1Qzg4LjU1OTEgNDQuMDgwMSA4OC42MTg4IDQ0LjEzOTIgODguNjYgNDQuMjEwN0M4OC43MDEzIDQ0LjI4MjIgODguNzIyNyA0NC4zNjM1IDg4LjcyMTkgNDQuNDQ2Qzg4LjcyMjUgNDQuNTI4NSA4OC43MDEgNDQuNjA5NyA4OC42NTk4IDQ0LjY4MTJDODguNjE4NSA0NC43NTI2IDg4LjU1ODkgNDQuODExOCA4OC40ODcyIDQ0Ljg1MjVMNzIuNzcxNCA1My43NjgzQzcyLjY5NzIgNTMuODExNCA3Mi42MTMzIDUzLjgzNDkgNzIuNTI3NSA1My44MzY3IiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBvcGFjaXR5PSIwLjciIGQ9Ik03MC4yNTUzIDc1LjY1MTdDNzAuMTcxIDc1LjY1MzUgNzAuMDg3OCA3NS42MzE3IDcwLjAxNTEgNzUuNTg4OEw1NC4yNDU4IDY2LjY0MTdDNTQuMTcxNSA2Ni42MDI0IDU0LjEwOTUgNjYuNTQzNiA1NC4wNjYxIDY2LjQ3MTZDNTQuMDIyOCA2Ni4zOTk3IDU0IDY2LjMxNzMgNTQgNjYuMjMzM1Y0OC4yNzhDNTQgNDguMTA4IDU0LjA5MjQgNDcuOTU0NiA1NC4yNDM5IDQ3Ljg2OTZDNTQuMzE3MiA0Ny44MjcxIDU0LjQwMDQgNDcuODA0NyA1NC40ODUxIDQ3LjgwNDdDNTQuNTY5NyA0Ny44MDQ3IDU0LjY1MjkgNDcuODI3MSA1NC43MjYyIDQ3Ljg2OTZMNzAuNDkzNyA1Ni44MTMxQzcwLjU2NDIgNTYuODU2NSA3MC42MjI1IDU2LjkxNyA3MC42NjMyIDU2Ljk4OTFDNzAuNzAzOSA1Ny4wNjEyIDcwLjcyNTcgNTcuMTQyNCA3MC43MjY1IDU3LjIyNTFWNzUuMTgwNUM3MC43MjU5IDc1LjI2MjggNzAuNzA0MiA3NS4zNDM2IDcwLjY2MzUgNzUuNDE1MUM3MC42MjI3IDc1LjQ4NjYgNzAuNTY0MiA3NS41NDY0IDcwLjQ5MzcgNzUuNTg4OEM3MC40MjA2IDc1LjYyOTEgNzAuMzM4NyA3NS42NTA3IDcwLjI1NTMgNzUuNjUxNyIgZmlsbD0id2hpdGUiLz4KPHBhdGggb3BhY2l0eT0iMC40IiBkPSJNNzQuNzE5OCA3NS42NTExQzc0LjYzMzMgNzUuNjUxMiA3NC41NDgyIDc1LjYyOTYgNzQuNDcyMiA3NS41ODgzQzc0LjQwMTYgNzUuNTQ2MSA3NC4zNDMyIDc1LjQ4NjIgNzQuMzAyNyA3NS40MTQ3Qzc0LjI2MjMgNzUuMzQzMSA3NC4yNDExIDc1LjI2MjIgNzQuMjQxMiA3NS4xOFY1Ny4zMzczQzc0LjI0MTIgNTcuMTcxIDc0LjMzMzYgNTcuMDE1OCA3NC40NzIyIDU2LjkyOUw5MC4yMzk3IDQ3Ljk4NTVDOTAuMzExOSA0Ny45NDM4IDkwLjM5MzggNDcuOTIxOSA5MC40NzcxIDQ3LjkyMTlDOTAuNTYwNSA0Ny45MjE5IDkwLjY0MjQgNDcuOTQzOCA5MC43MTQ2IDQ3Ljk4NTVDOTAuNzg3NiA0OC4wMjU1IDkwLjg0ODUgNDguMDg0MiA5MC44OTExIDQ4LjE1NTdDOTAuOTMzNyA0OC4yMjcyIDkwLjk1NjMgNDguMzA4OCA5MC45NTY2IDQ4LjM5MlY2Ni4yMzI4QzkwLjk1NyA2Ni4zMTY0IDkwLjkzNDcgNjYuMzk4NSA5MC44OTIxIDY2LjQ3MDRDOTAuODQ5NSA2Ni41NDI0IDkwLjc4ODEgNjYuNjAxNCA5MC43MTQ2IDY2LjY0MTFMNzQuOTUyNiA3NS41ODgzQzc0Ljg4MjUgNzUuNjMwNyA3NC44MDE4IDc1LjY1MjUgNzQuNzE5OCA3NS42NTExIiBmaWxsPSJ3aGl0ZSIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyXzY4N18zNDU0IiB4MT0iMTYxIiB5MT0iMTgwIiB4Mj0iMy41OTI4NGUtMDciIHkyPSI0Ljk5OTk4IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjNTk1NjU2Ii8+CjwvbGluZWFyR3JhZGllbnQ+CjxjbGlwUGF0aCBpZD0iY2xpcDBfNjg3XzM0NTQiPgo8cmVjdCB3aWR0aD0iOTQiIGhlaWdodD0iOTQiIGZpbGw9IndoaXRlIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgyNSAyNSkiLz4KPC9jbGlwUGF0aD4KPC9kZWZzPgo8L3N2Zz4K
|
||||
keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "external"], ["spec", "externalMethod"], ["spec", "externalPorts"], ["spec", "running"], ["spec", "instanceType"], ["spec", "instanceProfile"], ["spec", "disks"], ["spec", "gpus"], ["spec", "resources"], ["spec", "sshKeys"], ["spec", "cloudInit"], ["spec", "cloudInitSeed"]]
|
||||
keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "external"], ["spec", "externalMethod"], ["spec", "externalPorts"], ["spec", "running"], ["spec", "instanceType"], ["spec", "instanceProfile"], ["spec", "disks"], ["spec", "subnets"], ["spec", "gpus"], ["spec", "resources"], ["spec", "sshKeys"], ["spec", "cloudInit"], ["spec", "cloudInitSeed"]]
|
||||
secrets:
|
||||
exclude: []
|
||||
include: []
|
||||
|
||||
@@ -38,3 +38,11 @@ spec:
|
||||
include:
|
||||
- resourceNames:
|
||||
- vpn-{{ .name }}-vpn
|
||||
workloadMonitors:
|
||||
- name: "{{ .Release.Name }}"
|
||||
kind: vpn
|
||||
type: vpn
|
||||
selector:
|
||||
app.kubernetes.io/instance: "{{ .Release.Name }}"
|
||||
replicas: "{{ .Values.replicas }}"
|
||||
minReplicas: "1"
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
# imported from https://github.com/cozystack/openapi-ui-k8s-bff
|
||||
ARG NODE_VERSION=20.18.1
|
||||
FROM node:${NODE_VERSION}-alpine AS builder
|
||||
RUN apk add git
|
||||
WORKDIR /src
|
||||
|
||||
ARG COMMIT_REF=22f9143f5109fb90332651c857d70b51bffccd9b
|
||||
ARG COMMIT_REF=92906a7f21050cfb8e352f98d36b209c57844f63
|
||||
RUN wget -O- https://github.com/PRO-Robotech/openapi-ui-k8s-bff/archive/${COMMIT_REF}.tar.gz | tar xzf - --strip-components=1
|
||||
|
||||
COPY patches /patches
|
||||
RUN git apply /patches/*.diff
|
||||
|
||||
ENV PATH=/src/node_modules/.bin:$PATH
|
||||
RUN npm install
|
||||
RUN npm run build
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
diff --git a/src/endpoints/forms/formPrepare/formPrepare.ts b/src/endpoints/forms/formPrepare/formPrepare.ts
|
||||
index 7e437db..90c40f6 100644
|
||||
--- a/src/endpoints/forms/formPrepare/formPrepare.ts
|
||||
+++ b/src/endpoints/forms/formPrepare/formPrepare.ts
|
||||
@@ -15,6 +15,7 @@ export const prepareFormProps: RequestHandler = async (req: TPrepareFormReq, res
|
||||
|
||||
const filteredHeaders = { ...req.headers }
|
||||
delete filteredHeaders['host'] // Avoid passing internal host header
|
||||
+ delete filteredHeaders['content-length'] // This header causes "stream has been aborted"
|
||||
|
||||
const { data: formsOverridesData } = await userKubeApi.get<TFormsOverridesData>(
|
||||
`/apis/${BASE_API_GROUP}/${BASE_API_VERSION}/customformsoverrides`,
|
||||
@@ -40,7 +41,7 @@ export const prepareFormProps: RequestHandler = async (req: TPrepareFormReq, res
|
||||
},
|
||||
)
|
||||
|
||||
- const { data: namespacesData } = await userKubeApi.get<TBuiltinResources>(`/api/v1/namespaces`, {
|
||||
+ const { data: namespacesData } = await userKubeApi.get<TBuiltinResources>(`/apis/core.cozystack.io/v1alpha1/tenantnamespaces`, {
|
||||
headers: {
|
||||
// Authorization: `Bearer ${bearerToken}`,
|
||||
// Cookie: cookies,
|
||||
@@ -3,9 +3,14 @@ ARG NODE_VERSION=20.18.1
|
||||
# openapi-k8s-toolkit
|
||||
# imported from https://github.com/cozystack/openapi-k8s-toolkit
|
||||
FROM node:${NODE_VERSION}-alpine AS openapi-k8s-toolkit-builder
|
||||
RUN apk add git
|
||||
WORKDIR /src
|
||||
ARG COMMIT=4f57ab295b2a886eb294b0b987554194fbe67dcd
|
||||
ARG COMMIT=7086a2d8a07dcf6a94bb4276433db5d84acfcf3b
|
||||
RUN wget -O- https://github.com/cozystack/openapi-k8s-toolkit/archive/${COMMIT}.tar.gz | tar -xzvf- --strip-components=1
|
||||
|
||||
COPY openapi-k8s-toolkit/patches /patches
|
||||
RUN git apply /patches/*.diff
|
||||
|
||||
RUN npm install
|
||||
RUN npm install --build-from-source @swc/core
|
||||
RUN npm run build
|
||||
@@ -17,10 +22,10 @@ FROM node:${NODE_VERSION}-alpine AS builder
|
||||
RUN apk add git
|
||||
WORKDIR /src
|
||||
|
||||
ARG COMMIT_REF=65e7fa8b3dc530a36e94c8435622bb09961aef97
|
||||
ARG COMMIT_REF=fe237518348e94cead6d4f3283b2fce27f26aa12
|
||||
RUN wget -O- https://github.com/PRO-Robotech/openapi-ui/archive/${COMMIT_REF}.tar.gz | tar xzf - --strip-components=1
|
||||
|
||||
COPY patches /patches
|
||||
COPY openapi-ui/patches /patches
|
||||
RUN git apply /patches/*.diff
|
||||
|
||||
ENV PATH=/src/node_modules/.bin:$PATH
|
||||
|
||||
@@ -0,0 +1,230 @@
|
||||
diff --git a/src/components/molecules/BlackholeForm/molecules/FormObjectFromSwagger/FormObjectFromSwagger.tsx b/src/components/molecules/BlackholeForm/molecules/FormObjectFromSwagger/FormObjectFromSwagger.tsx
|
||||
index a7135d4..2fea0bb 100644
|
||||
--- a/src/components/molecules/BlackholeForm/molecules/FormObjectFromSwagger/FormObjectFromSwagger.tsx
|
||||
+++ b/src/components/molecules/BlackholeForm/molecules/FormObjectFromSwagger/FormObjectFromSwagger.tsx
|
||||
@@ -68,13 +68,60 @@ export const FormObjectFromSwagger: FC<TFormObjectFromSwaggerProps> = ({
|
||||
properties?: OpenAPIV2.SchemaObject['properties']
|
||||
required?: string
|
||||
}
|
||||
+
|
||||
+ // Check if the field name exists in additionalProperties.properties
|
||||
+ // If so, use the type from that property definition
|
||||
+ const nestedProp = addProps?.properties?.[additionalPropValue] as OpenAPIV2.SchemaObject | undefined
|
||||
+ let fieldType: string = addProps.type
|
||||
+ let fieldItems: { type: string } | undefined = addProps.items
|
||||
+ let fieldNestedProperties = addProps.properties || {}
|
||||
+ let fieldRequired: string | undefined = addProps.required
|
||||
+
|
||||
+ if (nestedProp) {
|
||||
+ // Use the nested property definition if it exists
|
||||
+ // Handle type - it can be string or string[] in OpenAPI v2
|
||||
+ if (nestedProp.type) {
|
||||
+ if (Array.isArray(nestedProp.type)) {
|
||||
+ fieldType = nestedProp.type[0] || addProps.type
|
||||
+ } else if (typeof nestedProp.type === 'string') {
|
||||
+ fieldType = nestedProp.type
|
||||
+ } else {
|
||||
+ fieldType = addProps.type
|
||||
+ }
|
||||
+ } else {
|
||||
+ fieldType = addProps.type
|
||||
+ }
|
||||
+
|
||||
+ // Handle items - it can be ItemsObject or ReferenceObject
|
||||
+ if (nestedProp.items) {
|
||||
+ // Check if it's a valid ItemsObject with type property
|
||||
+ if ('type' in nestedProp.items && typeof nestedProp.items.type === 'string') {
|
||||
+ fieldItems = { type: nestedProp.items.type }
|
||||
+ } else {
|
||||
+ fieldItems = addProps.items
|
||||
+ }
|
||||
+ } else {
|
||||
+ fieldItems = addProps.items
|
||||
+ }
|
||||
+
|
||||
+ fieldNestedProperties = nestedProp.properties || {}
|
||||
+ // Handle required field - it can be string[] in OpenAPI schema
|
||||
+ if (Array.isArray(nestedProp.required)) {
|
||||
+ fieldRequired = nestedProp.required.join(',')
|
||||
+ } else if (typeof nestedProp.required === 'string') {
|
||||
+ fieldRequired = nestedProp.required
|
||||
+ } else {
|
||||
+ fieldRequired = addProps.required
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
inputProps?.addField({
|
||||
path: Array.isArray(name) ? [...name, String(collapseTitle)] : [name, String(collapseTitle)],
|
||||
name: additionalPropValue,
|
||||
- type: addProps.type,
|
||||
- items: addProps.items,
|
||||
- nestedProperties: addProps.properties || {},
|
||||
- required: addProps.required,
|
||||
+ type: fieldType,
|
||||
+ items: fieldItems,
|
||||
+ nestedProperties: fieldNestedProperties,
|
||||
+ required: fieldRequired,
|
||||
})
|
||||
setAddditionalPropValue(undefined)
|
||||
}
|
||||
diff --git a/src/components/molecules/BlackholeForm/molecules/FormStringInput/FormStringInput.tsx b/src/components/molecules/BlackholeForm/molecules/FormStringInput/FormStringInput.tsx
|
||||
index 487d480..3ca46c1 100644
|
||||
--- a/src/components/molecules/BlackholeForm/molecules/FormStringInput/FormStringInput.tsx
|
||||
+++ b/src/components/molecules/BlackholeForm/molecules/FormStringInput/FormStringInput.tsx
|
||||
@@ -42,7 +42,11 @@ export const FormStringInput: FC<TFormStringInputProps> = ({
|
||||
const formValue = Form.useWatch(formFieldName)
|
||||
|
||||
// Derive multiline based on current local value
|
||||
- const isMultiline = useMemo(() => isMultilineString(formValue), [formValue])
|
||||
+ const isMultiline = useMemo(() => {
|
||||
+ // Normalize value for multiline check
|
||||
+ const value = typeof formValue === 'string' ? formValue : (formValue === null || formValue === undefined ? '' : String(formValue))
|
||||
+ return isMultilineString(value)
|
||||
+ }, [formValue])
|
||||
|
||||
const title = (
|
||||
<>
|
||||
@@ -77,6 +81,23 @@ export const FormStringInput: FC<TFormStringInputProps> = ({
|
||||
rules={[{ required: forceNonRequired === false && required?.includes(getStringByName(name)) }]}
|
||||
validateTrigger="onBlur"
|
||||
hasFeedback={designNewLayout ? { icons: feedbackIcons } : true}
|
||||
+ normalize={(value) => {
|
||||
+ // Normalize value to string - prevent "[object Object]" display
|
||||
+ if (value === undefined || value === null) {
|
||||
+ return ''
|
||||
+ }
|
||||
+ if (typeof value === 'string') {
|
||||
+ return value
|
||||
+ }
|
||||
+ if (typeof value === 'number' || typeof value === 'boolean') {
|
||||
+ return String(value)
|
||||
+ }
|
||||
+ // If it's an object or array, it shouldn't be in a string field - return empty string
|
||||
+ if (typeof value === 'object') {
|
||||
+ return ''
|
||||
+ }
|
||||
+ return String(value)
|
||||
+ }}
|
||||
>
|
||||
<Input.TextArea
|
||||
placeholder={getStringByName(name)}
|
||||
diff --git a/src/components/molecules/BlackholeForm/organisms/BlackholeForm/helpers/casts.ts b/src/components/molecules/BlackholeForm/organisms/BlackholeForm/helpers/casts.ts
|
||||
index 6f9eb39..835224c 100644
|
||||
--- a/src/components/molecules/BlackholeForm/organisms/BlackholeForm/helpers/casts.ts
|
||||
+++ b/src/components/molecules/BlackholeForm/organisms/BlackholeForm/helpers/casts.ts
|
||||
@@ -124,8 +124,26 @@ export const materializeAdditionalFromValues = (
|
||||
*
|
||||
* This is used when a new field appears in the data but doesn't yet exist in the schema.
|
||||
*/
|
||||
- const makeChildFromAP = (ap: any): OpenAPIV2.SchemaObject => {
|
||||
- const t = ap?.type ?? 'object'
|
||||
+ const makeChildFromAP = (ap: any, value?: unknown): OpenAPIV2.SchemaObject => {
|
||||
+ // Determine type based on actual value if not explicitly defined in additionalProperties
|
||||
+ let t = ap?.type
|
||||
+ if (!t && value !== undefined && value !== null) {
|
||||
+ if (Array.isArray(value)) {
|
||||
+ t = 'array'
|
||||
+ } else if (typeof value === 'object') {
|
||||
+ t = 'object'
|
||||
+ } else if (typeof value === 'string') {
|
||||
+ t = 'string'
|
||||
+ } else if (typeof value === 'number') {
|
||||
+ t = 'number'
|
||||
+ } else if (typeof value === 'boolean') {
|
||||
+ t = 'boolean'
|
||||
+ } else {
|
||||
+ t = 'object'
|
||||
+ }
|
||||
+ }
|
||||
+ t = t ?? 'object'
|
||||
+
|
||||
const child: OpenAPIV2.SchemaObject = { type: t } as any
|
||||
|
||||
// Copy common schema details (if present)
|
||||
@@ -134,6 +152,20 @@ export const materializeAdditionalFromValues = (
|
||||
if (ap?.required)
|
||||
(child as any).required = _.cloneDeep(ap.required)
|
||||
|
||||
+ // If value is an array and items type is not defined, infer it from the first item
|
||||
+ if (t === 'array' && Array.isArray(value) && value.length > 0 && !ap?.items) {
|
||||
+ const firstItem = value[0]
|
||||
+ if (typeof firstItem === 'string') {
|
||||
+ ;(child as any).items = { type: 'string' }
|
||||
+ } else if (typeof firstItem === 'number') {
|
||||
+ ;(child as any).items = { type: 'number' }
|
||||
+ } else if (typeof firstItem === 'boolean') {
|
||||
+ ;(child as any).items = { type: 'boolean' }
|
||||
+ } else if (typeof firstItem === 'object') {
|
||||
+ ;(child as any).items = { type: 'object' }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
// Mark as originating from `additionalProperties`
|
||||
;(child as any).isAdditionalProperties = true
|
||||
return child
|
||||
@@ -177,7 +209,16 @@ export const materializeAdditionalFromValues = (
|
||||
|
||||
// If the key doesn't exist in schema, create it from `additionalProperties`
|
||||
if (!schemaNode.properties![k]) {
|
||||
- schemaNode.properties![k] = makeChildFromAP(ap)
|
||||
+ // Check if there's a nested property definition in additionalProperties
|
||||
+ const nestedProp = ap?.properties?.[k]
|
||||
+ if (nestedProp) {
|
||||
+ // Use the nested property definition from additionalProperties
|
||||
+ schemaNode.properties![k] = _.cloneDeep(nestedProp) as any
|
||||
+ ;(schemaNode.properties![k] as any).isAdditionalProperties = true
|
||||
+ } else {
|
||||
+ // Create from additionalProperties with value-based type inference
|
||||
+ schemaNode.properties![k] = makeChildFromAP(ap, vo[k])
|
||||
+ }
|
||||
// If it's an existing additional property, merge any nested structure
|
||||
} else if ((schemaNode.properties![k] as any).isAdditionalProperties && ap?.properties) {
|
||||
;(schemaNode.properties![k] as any).properties ??= _.cloneDeep(ap.properties)
|
||||
diff --git a/src/components/molecules/BlackholeForm/organisms/BlackholeForm/utils.tsx b/src/components/molecules/BlackholeForm/organisms/BlackholeForm/utils.tsx
|
||||
index 2d887c7..d69d711 100644
|
||||
--- a/src/components/molecules/BlackholeForm/organisms/BlackholeForm/utils.tsx
|
||||
+++ b/src/components/molecules/BlackholeForm/organisms/BlackholeForm/utils.tsx
|
||||
@@ -394,9 +394,11 @@ export const getArrayFormItemFromSwagger = ({
|
||||
{(fields, { add, remove }, { errors }) => (
|
||||
<>
|
||||
{fields.map(field => {
|
||||
- const fieldType = (
|
||||
+ const rawFieldType = (
|
||||
schema.items as (OpenAPIV2.ItemsObject & { properties?: OpenAPIV2.SchemaObject }) | undefined
|
||||
)?.type
|
||||
+ // Handle type as string or string[] (OpenAPI v2 allows both)
|
||||
+ const fieldType = Array.isArray(rawFieldType) ? rawFieldType[0] : rawFieldType
|
||||
const description = (schema.items as (OpenAPIV2.ItemsObject & { description?: string }) | undefined)
|
||||
?.description
|
||||
const entry = schema.items as
|
||||
@@ -577,7 +579,29 @@ export const getArrayFormItemFromSwagger = ({
|
||||
type="text"
|
||||
size="small"
|
||||
onClick={() => {
|
||||
- add()
|
||||
+ // Determine initial value based on item type
|
||||
+ const fieldType = (
|
||||
+ schema.items as (OpenAPIV2.ItemsObject & { properties?: OpenAPIV2.SchemaObject }) | undefined
|
||||
+ )?.type
|
||||
+
|
||||
+ let initialValue: unknown
|
||||
+ // Handle type as string or string[] (OpenAPI v2 allows both)
|
||||
+ const typeStr = Array.isArray(fieldType) ? fieldType[0] : fieldType
|
||||
+ if (typeStr === 'string') {
|
||||
+ initialValue = ''
|
||||
+ } else if (typeStr === 'number' || typeStr === 'integer') {
|
||||
+ initialValue = 0
|
||||
+ } else if (typeStr === 'boolean') {
|
||||
+ initialValue = false
|
||||
+ } else if (typeStr === 'array') {
|
||||
+ initialValue = []
|
||||
+ } else if (typeStr === 'object') {
|
||||
+ initialValue = {}
|
||||
+ } else {
|
||||
+ initialValue = ''
|
||||
+ }
|
||||
+
|
||||
+ add(initialValue)
|
||||
}}
|
||||
>
|
||||
<PlusIcon />
|
||||
@@ -0,0 +1,50 @@
|
||||
diff --git a/src/components/molecules/EnrichedTable/organisms/EnrichedTable/utils.tsx b/src/components/molecules/EnrichedTable/organisms/EnrichedTable/utils.tsx
|
||||
index 8bcef4d..2551e92 100644
|
||||
--- a/src/components/molecules/EnrichedTable/organisms/EnrichedTable/utils.tsx
|
||||
+++ b/src/components/molecules/EnrichedTable/organisms/EnrichedTable/utils.tsx
|
||||
@@ -22,6 +22,15 @@ import { TableFactory } from '../../molecules'
|
||||
import { ShortenedTextWithTooltip, FilterDropdown, TrimmedTags, TextAlignContainer, TinyButton } from './atoms'
|
||||
import { TInternalDataForControls } from './types'
|
||||
|
||||
+const getPluralForm = (singular: string): string => {
|
||||
+ // If already ends with 's', add 'es'
|
||||
+ if (singular.endsWith('s')) {
|
||||
+ return `${singular}es`
|
||||
+ }
|
||||
+ // Otherwise just add 's'
|
||||
+ return `${singular}s`
|
||||
+}
|
||||
+
|
||||
export const getCellRender = ({
|
||||
value,
|
||||
record,
|
||||
@@ -255,7 +264,7 @@ export const getEnrichedColumnsWithControls = ({
|
||||
key: 'controls',
|
||||
className: 'controls',
|
||||
width: 60,
|
||||
- render: (value: TInternalDataForControls) => {
|
||||
+ render: (value: TInternalDataForControls, record: unknown) => {
|
||||
return (
|
||||
// <TextAlignContainer $align="right" className="hideable">
|
||||
<TextAlignContainer $align="center">
|
||||
@@ -279,10 +288,19 @@ export const getEnrichedColumnsWithControls = ({
|
||||
domEvent.stopPropagation()
|
||||
domEvent.preventDefault()
|
||||
if (key === 'edit') {
|
||||
+ // Special case: redirect tenantmodules from core.cozystack.io to apps.cozystack.io with plural form
|
||||
+ let apiGroupAndVersion = value.apiGroupAndVersion
|
||||
+ let typeName = value.typeName
|
||||
+ if (apiGroupAndVersion?.startsWith('core.cozystack.io/') && typeName === 'tenantmodules') {
|
||||
+ const appsApiVersion = apiGroupAndVersion.replace('core.cozystack.io/', 'apps.cozystack.io/')
|
||||
+ const pluralTypeName = getPluralForm(value.entryName)
|
||||
+ apiGroupAndVersion = appsApiVersion
|
||||
+ typeName = pluralTypeName
|
||||
+ }
|
||||
navigate(
|
||||
`${baseprefix}/${value.cluster}${value.namespace ? `/${value.namespace}` : ''}${
|
||||
value.syntheticProject ? `/${value.syntheticProject}` : ''
|
||||
- }/${value.pathPrefix}/${value.apiGroupAndVersion}/${value.typeName}/${value.entryName}?backlink=${
|
||||
+ }/${value.pathPrefix}/${apiGroupAndVersion}/${typeName}/${value.entryName}?backlink=${
|
||||
value.backlink
|
||||
}`,
|
||||
)
|
||||
@@ -1,16 +1,18 @@
|
||||
diff --git a/src/components/organisms/ListInsideClusterAndNs/ListInsideClusterAndNs.tsx b/src/components/organisms/ListInsideClusterAndNs/ListInsideClusterAndNs.tsx
|
||||
index 577ba0f..018df9c 100644
|
||||
index ac56e5f..c6e2350 100644
|
||||
--- a/src/components/organisms/ListInsideClusterAndNs/ListInsideClusterAndNs.tsx
|
||||
+++ b/src/components/organisms/ListInsideClusterAndNs/ListInsideClusterAndNs.tsx
|
||||
@@ -1,11 +1,16 @@
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { FC, useState } from 'react'
|
||||
import { Button, Alert, Spin, Typography } from 'antd'
|
||||
-import { filterSelectOptions, Spacer, useBuiltinResources } from '@prorobotech/openapi-k8s-toolkit'
|
||||
-import { filterSelectOptions, Spacer, useBuiltinResources, useApiResources } from '@prorobotech/openapi-k8s-toolkit'
|
||||
+import { filterSelectOptions, Spacer, useApiResources } from '@prorobotech/openapi-k8s-toolkit'
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { useSelector, useDispatch } from 'react-redux'
|
||||
import { RootState } from 'store/store'
|
||||
import { setCluster } from 'store/cluster/cluster/cluster'
|
||||
@@ -11,6 +11,11 @@ import {
|
||||
CUSTOM_NAMESPACE_API_RESOURCE_RESOURCE_NAME,
|
||||
} from 'constants/customizationApiGroupAndVersion'
|
||||
import { Styled } from './styled'
|
||||
+import {
|
||||
+ BASE_PROJECTS_API_GROUP,
|
||||
@@ -20,22 +22,22 @@ index 577ba0f..018df9c 100644
|
||||
|
||||
export const ListInsideClusterAndNs: FC = () => {
|
||||
const clusterList = useSelector((state: RootState) => state.clusterList.clusterList)
|
||||
@@ -17,9 +22,11 @@ export const ListInsideClusterAndNs: FC = () => {
|
||||
const [selectedCluster, setSelectedCluster] = useState<string>()
|
||||
const [selectedNamespace, setSelectedNamespace] = useState<string>()
|
||||
@@ -33,9 +38,11 @@ export const ListInsideClusterAndNs: FC = () => {
|
||||
typeof CUSTOM_NAMESPACE_API_RESOURCE_RESOURCE_NAME === 'string' &&
|
||||
CUSTOM_NAMESPACE_API_RESOURCE_RESOURCE_NAME.length > 0
|
||||
|
||||
- const namespacesData = useBuiltinResources({
|
||||
+ const namespacesData = useApiResources({
|
||||
clusterName: cluster,
|
||||
clusterName: selectedCluster || '',
|
||||
- typeName: 'namespaces',
|
||||
+ apiGroup: BASE_PROJECTS_API_GROUP,
|
||||
+ apiVersion: BASE_PROJECTS_VERSION,
|
||||
+ typeName: BASE_PROJECTS_RESOURCE_NAME,
|
||||
limit: null,
|
||||
isEnabled: selectedCluster !== undefined && !isCustomNamespaceResource,
|
||||
})
|
||||
|
||||
diff --git a/src/hooks/useNavSelectorInside.ts b/src/hooks/useNavSelectorInside.ts
|
||||
index d69405e..5adbd5d 100644
|
||||
index 5736e2b..1ec0f71 100644
|
||||
--- a/src/hooks/useNavSelectorInside.ts
|
||||
+++ b/src/hooks/useNavSelectorInside.ts
|
||||
@@ -1,6 +1,11 @@
|
||||
@@ -63,8 +65,8 @@ index d69405e..5adbd5d 100644
|
||||
+ apiVersion: BASE_PROJECTS_VERSION,
|
||||
+ typeName: BASE_PROJECTS_RESOURCE_NAME,
|
||||
limit: null,
|
||||
isEnabled: Boolean(clusterName),
|
||||
})
|
||||
|
||||
diff --git a/src/utils/getBacklink.ts b/src/utils/getBacklink.ts
|
||||
index a862354..f24e2bc 100644
|
||||
--- a/src/utils/getBacklink.ts
|
||||
@@ -0,0 +1,15 @@
|
||||
diff --git a/src/components/organisms/Header/organisms/User/User.tsx b/src/components/organisms/Header/organisms/User/User.tsx
|
||||
index efe7ac3..80b715c 100644
|
||||
--- a/src/components/organisms/Header/organisms/User/User.tsx
|
||||
+++ b/src/components/organisms/Header/organisms/User/User.tsx
|
||||
@@ -23,10 +23,6 @@ export const User: FC = () => {
|
||||
// key: '1',
|
||||
// label: <ThemeSelector />,
|
||||
// },
|
||||
- {
|
||||
- key: '2',
|
||||
- label: <div onClick={() => navigate(`${baseprefix}/inside/clusters`)}>Inside</div>,
|
||||
- },
|
||||
{
|
||||
key: '3',
|
||||
label: (
|
||||
File diff suppressed because one or more lines are too long
@@ -42,6 +42,8 @@ spec:
|
||||
value: dashboard.cozystack.io
|
||||
- name: BASE_API_VERSION
|
||||
value: v1alpha1
|
||||
- name: BASE_NAMESPACE_FULL_PATH
|
||||
value: "/apis/core.cozystack.io/v1alpha1/tenantnamespaces"
|
||||
- name: LOGGER
|
||||
value: "TRUE"
|
||||
- name: LOGGER_WITH_HEADERS
|
||||
@@ -122,6 +124,12 @@ spec:
|
||||
value: tenantnamespaces
|
||||
- name: PROJECTS_VERSION
|
||||
value: v1alpha1
|
||||
- name: CUSTOM_NAMESPACE_API_RESOURCE_API_GROUP
|
||||
value: core.cozystack.io
|
||||
- name: CUSTOM_NAMESPACE_API_RESOURCE_API_VERSION
|
||||
value: v1alpha1
|
||||
- name: CUSTOM_NAMESPACE_API_RESOURCE_RESOURCE_NAME
|
||||
value: tenantnamespaces
|
||||
- name: USE_NAMESPACE_NAV
|
||||
value: "true"
|
||||
- name: LOGIN_URL
|
||||
@@ -140,21 +148,21 @@ spec:
|
||||
configMapKeyRef:
|
||||
name: incloud-web-dashboard-config
|
||||
key: TITLE_TEXT
|
||||
- name: TENANT_TEXT
|
||||
- name: CUSTOM_TENANT_TEXT
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: incloud-web-dashboard-config
|
||||
key: TENANT_TEXT
|
||||
key: CUSTOM_TENANT_TEXT
|
||||
- name: LOGO_TEXT
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: incloud-web-dashboard-config
|
||||
key: LOGO_TEXT
|
||||
- name: LOGO_SVG
|
||||
- name: CUSTOM_LOGO_SVG
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
name: incloud-web-dashboard-config
|
||||
key: LOGO_SVG
|
||||
key: CUSTOM_LOGO_SVG
|
||||
- name: ICON_SVG
|
||||
valueFrom:
|
||||
configMapKeyRef:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
openapiUI:
|
||||
image: ghcr.io/cozystack/cozystack/openapi-ui:v0.37.0@sha256:13f38cf56830e899eb5e3d9dc8184965dd8dba9f8cd3c5ca10df0970355842d6
|
||||
image: ghcr.io/cozystack/cozystack/openapi-ui:latest@sha256:b942d98ff0ea36e3c6e864b6459b404d37ed68bc2b0ebc5d3007a1be4faf60c5
|
||||
openapiUIK8sBff:
|
||||
image: ghcr.io/cozystack/cozystack/openapi-ui-k8s-bff:v0.37.0@sha256:2b626dbbf87241e8621ac5b0285f402edbc2c2069ba254ca2ace2dd5c9248ac8
|
||||
image: ghcr.io/cozystack/cozystack/openapi-ui-k8s-bff:latest@sha256:5ddc6546baf3acdb8e0572536665fe73053a7f985b05e51366454efa11c201d2
|
||||
tokenProxy:
|
||||
image: ghcr.io/cozystack/cozystack/token-proxy:v0.37.0@sha256:fad27112617bb17816702571e1f39d0ac3fe5283468d25eb12f79906cdab566b
|
||||
image: ghcr.io/cozystack/cozystack/token-proxy:latest@sha256:fad27112617bb17816702571e1f39d0ac3fe5283468d25eb12f79906cdab566b
|
||||
|
||||
@@ -10,3 +10,4 @@ update:
|
||||
rm -rf charts
|
||||
helm pull oci://ghcr.io/controlplaneio-fluxcd/charts/flux-operator --untar --untardir charts
|
||||
patch --no-backup-if-mismatch -p1 < patches/kubernetesEnvs.diff
|
||||
patch --no-backup-if-mismatch -p1 < patches/networkPolicy.diff
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
{{- if .Capabilities.APIVersions.Has "cilium.io/v2/CiliumClusterwideNetworkPolicy" }}
|
||||
---
|
||||
apiVersion: cilium.io/v2
|
||||
kind: CiliumClusterwideNetworkPolicy
|
||||
metadata:
|
||||
name: {{ include "flux-operator.fullname" . }}-restrict
|
||||
spec:
|
||||
nodeSelector: {}
|
||||
ingressDeny:
|
||||
- fromEntities:
|
||||
- world
|
||||
toPorts:
|
||||
- ports:
|
||||
- port: "8080"
|
||||
protocol: TCP
|
||||
- port: "8081"
|
||||
protocol: TCP
|
||||
ingress:
|
||||
- fromEntities:
|
||||
- cluster
|
||||
{{- end }}
|
||||
25
packages/system/fluxcd-operator/patches/networkPolicy.diff
Normal file
25
packages/system/fluxcd-operator/patches/networkPolicy.diff
Normal file
@@ -0,0 +1,25 @@
|
||||
diff --git a/packages/system/fluxcd-operator/charts/flux-operator/templates/network-policy.yaml b/packages/system/fluxcd-operator/charts/flux-operator/templates/network-policy.yaml
|
||||
new file mode 100644
|
||||
--- /dev/null (revision 52a23eacfc32430d8b008b765c64a81526521bae)
|
||||
+++ b/packages/system/fluxcd-operator/charts/flux-operator/templates/network-policy.yaml (revision 52a23eacfc32430d8b008b765c64a81526521bae)
|
||||
@@ -0,0 +1,18 @@
|
||||
+{{- if .Capabilities.APIVersions.Has "cilium.io/v2/CiliumClusterwideNetworkPolicy" }}
|
||||
+apiVersion: cilium.io/v2
|
||||
+kind: CiliumClusterwideNetworkPolicy
|
||||
+metadata:
|
||||
+ name: {{ include "flux-operator.fullname" . }}-restrict
|
||||
+spec:
|
||||
+ nodeSelector: {}
|
||||
+ ingressDeny:
|
||||
+ - fromEntities:
|
||||
+ - world
|
||||
+ toPorts:
|
||||
+ - ports:
|
||||
+ - port: "8080"
|
||||
+ protocol: TCP
|
||||
+ - port: "8081"
|
||||
+ protocol: TCP
|
||||
+ ingress:
|
||||
+ - fromEntities:
|
||||
+ - cluster
|
||||
+{{- end }}
|
||||
@@ -10,7 +10,7 @@ spec:
|
||||
expr: |
|
||||
max_over_time(
|
||||
kubevirt_vm_info{
|
||||
status!="Running",
|
||||
status!="running",
|
||||
exported_namespace=~".+",
|
||||
name=~".+"
|
||||
}[10m]
|
||||
@@ -27,7 +27,7 @@ spec:
|
||||
expr: |
|
||||
max_over_time(
|
||||
kubevirt_vmi_info{
|
||||
phase!="running",
|
||||
phase!="Running",
|
||||
exported_namespace=~".+",
|
||||
name=~".+"
|
||||
}[10m]
|
||||
|
||||
@@ -3,8 +3,8 @@ name: piraeus
|
||||
description: |
|
||||
The Piraeus Operator manages software defined storage clusters using LINSTOR in Kubernetes.
|
||||
type: application
|
||||
version: 2.9.0
|
||||
appVersion: "v2.9.0"
|
||||
version: 2.9.1
|
||||
appVersion: "v2.9.1"
|
||||
maintainers:
|
||||
- name: Piraeus Datastore
|
||||
url: https://piraeus.io
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# DO NOT EDIT; Automatically created by hack/copy-image-config-to-chart.sh
|
||||
# DO NOT EDIT; Automatically created by tools/copy-image-config-to-chart.sh
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
@@ -17,16 +17,16 @@ data:
|
||||
# quay.io/piraeusdatastore/piraeus-server:v1.24.2
|
||||
components:
|
||||
linstor-controller:
|
||||
tag: v1.31.3
|
||||
tag: v1.32.3
|
||||
image: piraeus-server
|
||||
linstor-satellite:
|
||||
tag: v1.31.3
|
||||
tag: v1.32.3
|
||||
image: piraeus-server
|
||||
linstor-csi:
|
||||
tag: v1.8.0
|
||||
tag: v1.9.0
|
||||
image: piraeus-csi
|
||||
drbd-reactor:
|
||||
tag: v1.8.0
|
||||
tag: v1.9.0
|
||||
image: drbd-reactor
|
||||
ha-controller:
|
||||
tag: v1.3.0
|
||||
@@ -35,45 +35,45 @@ data:
|
||||
tag: v1.0.0
|
||||
image: drbd-shutdown-guard
|
||||
ktls-utils:
|
||||
tag: v1.1.0
|
||||
tag: v1.2.1
|
||||
image: ktls-utils
|
||||
drbd-module-loader:
|
||||
tag: v9.2.14
|
||||
tag: v9.2.15
|
||||
# The special "match" attribute is used to select an image based on the node's reported OS.
|
||||
# The operator will first check the k8s node's ".status.nodeInfo.osImage" field, and compare it against the list
|
||||
# here. If one matches, that specific image name will be used instead of the fallback image.
|
||||
image: drbd9-noble # Fallback image: chose a recent kernel, which can hopefully compile whatever config is actually in use
|
||||
match:
|
||||
- osImage: Red Hat Enterprise Linux Server 7\.
|
||||
image: drbd9-centos7
|
||||
- osImage: Red Hat Enterprise Linux 8\.
|
||||
image: drbd9-almalinux8
|
||||
- osImage: Red Hat Enterprise Linux 9\.
|
||||
image: drbd9-almalinux9
|
||||
- osImage: Red Hat Enterprise Linux 10\.
|
||||
image: drbd9-almalinux10
|
||||
- osImage: "Red Hat Enterprise Linux CoreOS 41[3-9]"
|
||||
image: drbd9-almalinux9
|
||||
- osImage: Red Hat Enterprise Linux CoreOS
|
||||
image: drbd9-almalinux8
|
||||
- osImage: CentOS Linux 7
|
||||
image: drbd9-centos7
|
||||
- osImage: CentOS Linux 8
|
||||
image: drbd9-almalinux8
|
||||
- osImage: AlmaLinux 8
|
||||
image: drbd9-almalinux8
|
||||
- osImage: AlmaLinux 9
|
||||
image: drbd9-almalinux9
|
||||
- osImage: AlmaLinux 10
|
||||
image: drbd9-almalinux10
|
||||
- osImage: Oracle Linux Server 8\.
|
||||
image: drbd9-almalinux8
|
||||
- osImage: Oracle Linux Server 9\.
|
||||
image: drbd9-almalinux9
|
||||
- osImage: Oracle Linux Server 10\.
|
||||
image: drbd9-almalinux10
|
||||
- osImage: Rocky Linux 8
|
||||
image: drbd9-almalinux8
|
||||
- osImage: Rocky Linux 9
|
||||
image: drbd9-almalinux9
|
||||
- osImage: Ubuntu 18\.04
|
||||
image: drbd9-bionic
|
||||
- osImage: Ubuntu 20\.04
|
||||
image: drbd9-focal
|
||||
- osImage: Rocky Linux 10
|
||||
image: drbd9-almalinux10
|
||||
- osImage: Ubuntu 22\.04
|
||||
image: drbd9-jammy
|
||||
- osImage: Ubuntu 24\.04
|
||||
@@ -82,32 +82,30 @@ data:
|
||||
image: drbd9-bookworm
|
||||
- osImage: Debian GNU/Linux 11
|
||||
image: drbd9-bullseye
|
||||
- osImage: Debian GNU/Linux 10
|
||||
image: drbd9-buster
|
||||
0_sig_storage_images.yaml: |
|
||||
---
|
||||
base: registry.k8s.io/sig-storage
|
||||
components:
|
||||
csi-attacher:
|
||||
tag: v4.9.0
|
||||
tag: v4.10.0
|
||||
image: csi-attacher
|
||||
csi-livenessprobe:
|
||||
tag: v2.16.0
|
||||
tag: v2.17.0
|
||||
image: livenessprobe
|
||||
csi-provisioner:
|
||||
tag: v5.3.0
|
||||
image: csi-provisioner
|
||||
csi-snapshotter:
|
||||
tag: v8.2.1
|
||||
tag: v8.3.0
|
||||
image: csi-snapshotter
|
||||
csi-resizer:
|
||||
tag: v1.13.2
|
||||
tag: v1.14.0
|
||||
image: csi-resizer
|
||||
csi-external-health-monitor-controller:
|
||||
tag: v0.15.0
|
||||
tag: v0.16.0
|
||||
image: csi-external-health-monitor-controller
|
||||
csi-node-driver-registrar:
|
||||
tag: v2.14.0
|
||||
tag: v2.15.0
|
||||
image: csi-node-driver-registrar
|
||||
{{- range $idx, $value := .Values.imageConfigOverride }}
|
||||
{{ add $idx 1 }}_helm_override.yaml: |
|
||||
|
||||
@@ -1,11 +1,5 @@
|
||||
{{ if .Values.serviceAccount.create }}
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: {{ include "piraeus-operator.serviceAccountName" . }}
|
||||
labels:
|
||||
{{- include "piraeus-operator.labels" . | nindent 4 }}
|
||||
# DO NOT EDIT; Automatically created by tools/copy-rbac-config-to-chart.sh
|
||||
{{ if .Values.rbac.create }}
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
@@ -14,448 +8,288 @@ metadata:
|
||||
labels:
|
||||
{{- include "piraeus-operator.labels" . | nindent 4 }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- configmaps
|
||||
- events
|
||||
- persistentvolumes
|
||||
- secrets
|
||||
- serviceaccounts
|
||||
- services
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- configmaps
|
||||
- pods
|
||||
- secrets
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- nodes
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- nodes
|
||||
- persistentvolumeclaims
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- persistentvolumeclaims/status
|
||||
verbs:
|
||||
- patch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- pods
|
||||
verbs:
|
||||
- delete
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- pods/eviction
|
||||
verbs:
|
||||
- create
|
||||
- apiGroups:
|
||||
- apiextensions.k8s.io
|
||||
resources:
|
||||
- customresourcedefinitions
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- apps
|
||||
resources:
|
||||
- daemonsets
|
||||
- deployments
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- apps
|
||||
resources:
|
||||
- replicasets
|
||||
verbs:
|
||||
- get
|
||||
- apiGroups:
|
||||
- cert-manager.io
|
||||
resources:
|
||||
- certificates
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- events.k8s.io
|
||||
resources:
|
||||
- events
|
||||
verbs:
|
||||
- create
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- internal.linstor.linbit.com
|
||||
resources:
|
||||
- '*'
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- deletecollection
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- piraeus.io
|
||||
resources:
|
||||
- linstorclusters
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- piraeus.io
|
||||
resources:
|
||||
- linstorclusters/finalizers
|
||||
verbs:
|
||||
- update
|
||||
- apiGroups:
|
||||
- piraeus.io
|
||||
resources:
|
||||
- linstorclusters/status
|
||||
verbs:
|
||||
- get
|
||||
- patch
|
||||
- update
|
||||
- apiGroups:
|
||||
- piraeus.io
|
||||
resources:
|
||||
- linstornodeconnections
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- piraeus.io
|
||||
resources:
|
||||
- linstornodeconnections/finalizers
|
||||
verbs:
|
||||
- update
|
||||
- apiGroups:
|
||||
- piraeus.io
|
||||
resources:
|
||||
- linstornodeconnections/status
|
||||
verbs:
|
||||
- get
|
||||
- patch
|
||||
- update
|
||||
- apiGroups:
|
||||
- piraeus.io
|
||||
resources:
|
||||
- linstorsatelliteconfigurations
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- piraeus.io
|
||||
resources:
|
||||
- linstorsatelliteconfigurations/status
|
||||
verbs:
|
||||
- get
|
||||
- patch
|
||||
- update
|
||||
- apiGroups:
|
||||
- piraeus.io
|
||||
resources:
|
||||
- linstorsatellites
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- piraeus.io
|
||||
resources:
|
||||
- linstorsatellites/finalizers
|
||||
verbs:
|
||||
- update
|
||||
- apiGroups:
|
||||
- piraeus.io
|
||||
resources:
|
||||
- linstorsatellites/status
|
||||
verbs:
|
||||
- get
|
||||
- patch
|
||||
- update
|
||||
- apiGroups:
|
||||
- rbac.authorization.k8s.io
|
||||
resources:
|
||||
- clusterrolebindings
|
||||
- clusterroles
|
||||
- rolebindings
|
||||
- roles
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- security.openshift.io
|
||||
resourceNames:
|
||||
- privileged
|
||||
resources:
|
||||
- securitycontextconstraints
|
||||
verbs:
|
||||
- use
|
||||
- apiGroups:
|
||||
- snapshot.storage.k8s.io
|
||||
resources:
|
||||
- volumesnapshotclasses
|
||||
- volumesnapshots
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- snapshot.storage.k8s.io
|
||||
resources:
|
||||
- volumesnapshotcontents
|
||||
verbs:
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- snapshot.storage.k8s.io
|
||||
resources:
|
||||
- volumesnapshotcontents/status
|
||||
verbs:
|
||||
- patch
|
||||
- update
|
||||
- apiGroups:
|
||||
- storage.k8s.io
|
||||
resources:
|
||||
- csidrivers
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- storage.k8s.io
|
||||
resources:
|
||||
- csinodes
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- watch
|
||||
- apiGroups:
|
||||
- storage.k8s.io
|
||||
resources:
|
||||
- csistoragecapacities
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- storage.k8s.io
|
||||
resources:
|
||||
- storageclasses
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- storage.k8s.io
|
||||
resources:
|
||||
- volumeattachments
|
||||
verbs:
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- watch
|
||||
- apiGroups:
|
||||
- storage.k8s.io
|
||||
resources:
|
||||
- volumeattachments/status
|
||||
verbs:
|
||||
- patch
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: {{ include "piraeus-operator.fullname" . }}-manager-rolebinding
|
||||
labels:
|
||||
{{- include "piraeus-operator.labels" . | nindent 4 }}
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: '{{ include "piraeus-operator.fullname" . }}-controller-manager'
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: '{{ include "piraeus-operator.serviceAccountName" . }}'
|
||||
namespace: '{{ .Release.Namespace }}'
|
||||
{{ end }}
|
||||
{{ if.Values.rbac.create }}
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: {{ include "piraeus-operator.fullname" . }}-proxy-role
|
||||
labels:
|
||||
{{- include "piraeus-operator.labels" . | nindent 4 }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- authentication.k8s.io
|
||||
resources:
|
||||
- tokenreviews
|
||||
verbs:
|
||||
- create
|
||||
- apiGroups:
|
||||
- authorization.k8s.io
|
||||
resources:
|
||||
- subjectaccessreviews
|
||||
verbs:
|
||||
- create
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: {{ include "piraeus-operator.fullname" . }}-proxy-rolebinding
|
||||
labels:
|
||||
{{- include "piraeus-operator.labels" . | nindent 4 }}
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: '{{ include "piraeus-operator.fullname" . }}-proxy-role'
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: {{ include "piraeus-operator.serviceAccountName" . }}
|
||||
namespace: '{{ .Release.Namespace }}'
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- configmaps
|
||||
- events
|
||||
- persistentvolumes
|
||||
- pods
|
||||
- secrets
|
||||
- serviceaccounts
|
||||
- services
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- nodes
|
||||
- persistentvolumeclaims
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- persistentvolumeclaims/status
|
||||
verbs:
|
||||
- patch
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- pods/eviction
|
||||
verbs:
|
||||
- create
|
||||
- apiGroups:
|
||||
- apiextensions.k8s.io
|
||||
resources:
|
||||
- customresourcedefinitions
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- apps
|
||||
resources:
|
||||
- daemonsets
|
||||
- deployments
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- apps
|
||||
resources:
|
||||
- replicasets
|
||||
verbs:
|
||||
- get
|
||||
- apiGroups:
|
||||
- cert-manager.io
|
||||
resources:
|
||||
- certificates
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- events.k8s.io
|
||||
resources:
|
||||
- events
|
||||
verbs:
|
||||
- create
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- internal.linstor.linbit.com
|
||||
resources:
|
||||
- '*'
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- deletecollection
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- piraeus.io
|
||||
resources:
|
||||
- linstorclusters
|
||||
- linstornodeconnections
|
||||
- linstorsatellites
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- piraeus.io
|
||||
resources:
|
||||
- linstorclusters/finalizers
|
||||
- linstornodeconnections/finalizers
|
||||
- linstorsatellites/finalizers
|
||||
verbs:
|
||||
- update
|
||||
- apiGroups:
|
||||
- piraeus.io
|
||||
resources:
|
||||
- linstorclusters/status
|
||||
- linstornodeconnections/status
|
||||
- linstorsatelliteconfigurations/status
|
||||
- linstorsatellites/status
|
||||
verbs:
|
||||
- get
|
||||
- patch
|
||||
- update
|
||||
- apiGroups:
|
||||
- piraeus.io
|
||||
resources:
|
||||
- linstorsatelliteconfigurations
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- rbac.authorization.k8s.io
|
||||
resources:
|
||||
- clusterrolebindings
|
||||
- clusterroles
|
||||
- rolebindings
|
||||
- roles
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- security.openshift.io
|
||||
resourceNames:
|
||||
- privileged
|
||||
resources:
|
||||
- securitycontextconstraints
|
||||
verbs:
|
||||
- use
|
||||
- apiGroups:
|
||||
- snapshot.storage.k8s.io
|
||||
resources:
|
||||
- volumesnapshotclasses
|
||||
- volumesnapshots
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- snapshot.storage.k8s.io
|
||||
resources:
|
||||
- volumesnapshotcontents
|
||||
verbs:
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- snapshot.storage.k8s.io
|
||||
resources:
|
||||
- volumesnapshotcontents/status
|
||||
verbs:
|
||||
- patch
|
||||
- update
|
||||
- apiGroups:
|
||||
- storage.k8s.io
|
||||
resources:
|
||||
- csidrivers
|
||||
- csistoragecapacities
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- storage.k8s.io
|
||||
resources:
|
||||
- csinodes
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- watch
|
||||
- apiGroups:
|
||||
- storage.k8s.io
|
||||
resources:
|
||||
- storageclasses
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- apiGroups:
|
||||
- storage.k8s.io
|
||||
resources:
|
||||
- volumeattachments
|
||||
verbs:
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- watch
|
||||
- apiGroups:
|
||||
- storage.k8s.io
|
||||
resources:
|
||||
- volumeattachments/status
|
||||
verbs:
|
||||
- patch
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: {{ include "piraeus-operator.fullname" . }}-leader-election-role
|
||||
name: {{ include "piraeus-operator.fullname" . }}-leader-election
|
||||
labels:
|
||||
{{- include "piraeus-operator.labels" . | nindent 4 }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- configmaps
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- create
|
||||
- update
|
||||
- patch
|
||||
- delete
|
||||
- apiGroups:
|
||||
- coordination.k8s.io
|
||||
resources:
|
||||
- leases
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- create
|
||||
- update
|
||||
- patch
|
||||
- delete
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- events
|
||||
verbs:
|
||||
- create
|
||||
- patch
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: {{ include "piraeus-operator.fullname" . }}-leader-election-rolebinding
|
||||
labels:
|
||||
{{- include "piraeus-operator.labels" . | nindent 4 }}
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: '{{ include "piraeus-operator.fullname" . }}-leader-election-role'
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: {{ include "piraeus-operator.serviceAccountName" . }}
|
||||
namespace: '{{ .Release.Namespace }}'
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- configmaps
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- create
|
||||
- update
|
||||
- patch
|
||||
- delete
|
||||
- apiGroups:
|
||||
- coordination.k8s.io
|
||||
resources:
|
||||
- leases
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- create
|
||||
- update
|
||||
- patch
|
||||
- delete
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- events
|
||||
verbs:
|
||||
- create
|
||||
- patch
|
||||
{{ end }}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user