From d0bad07beef4b1b11e14451e14073dabd3e23335 Mon Sep 17 00:00:00 2001 From: majiayu000 <1835304752@qq.com> Date: Fri, 2 Jan 2026 04:17:56 +0800 Subject: [PATCH 001/125] docs: add Hubble network observability documentation and dashboards Add documentation explaining how to enable Hubble for network observability in Grafana. Include four pre-built Hubble dashboards (overview, dns-namespace, l7-http-metrics, network-overview) and register them in the monitoring hub's dashboard list. Closes #749 Signed-off-by: majiayu000 <1835304752@qq.com> --- dashboards/hubble/dns-namespace.json | 602 ++++ dashboards/hubble/l7-http-metrics.json | 1394 +++++++++ dashboards/hubble/network-overview.json | 1001 ++++++ dashboards/hubble/overview.json | 3357 +++++++++++++++++++++ docs/hubble-observability.md | 99 + packages/extra/monitoring/dashboards.list | 4 + 6 files changed, 6457 insertions(+) create mode 100644 dashboards/hubble/dns-namespace.json create mode 100644 dashboards/hubble/l7-http-metrics.json create mode 100644 dashboards/hubble/network-overview.json create mode 100644 dashboards/hubble/overview.json create mode 100644 docs/hubble-observability.md diff --git a/dashboards/hubble/dns-namespace.json b/dashboards/hubble/dns-namespace.json new file mode 100644 index 00000000..57f804cf --- /dev/null +++ b/dashboards/hubble/dns-namespace.json @@ -0,0 +1,602 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__elements": {}, + "__requires": [ + { + "type": "panel", + "id": "bargauge", + "name": "Bar gauge", + "version": "" + }, + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "9.4.7" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "timeseries", + "name": "Time series", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "description": "", + "editable": true, + "fiscalYearStartMonth": 0, + "gnetId": 16612, + "graphTooltip": 0, + "id": null, + "links": [ + { + "asDropdown": true, + "icon": "external link", + "includeVars": true, + "keepTime": true, + "tags": [ + "cilium-overview" + ], + "targetBlank": false, + "title": "Cilium Overviews", + "tooltip": "", + "type": "dashboards", + "url": "" + }, + { + "asDropdown": true, + "icon": "external link", + "includeVars": false, + "keepTime": true, + "tags": [ + "hubble" + ], + "targetBlank": false, + "title": "Hubble", + "tooltip": "", + "type": "dashboards", + "url": "" + } + ], + "liveNow": false, + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 2, + "panels": [], + "title": "DNS", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "description": "", + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 1 + }, + "id": 37, + "options": { + "legend": { + "calcs": [ + "mean", + "lastNotNull" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(rate(hubble_dns_queries_total{cluster=~\"$cluster\", source_namespace=~\"$source_namespace\", destination_namespace=~\"$destination_namespace\"}[$__rate_interval])) by (source) > 0", + "legendFormat": "{{source}}", + "range": true, + "refId": "A" + } + ], + "title": "DNS queries", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 1 + }, + "id": 41, + "options": { + "displayMode": "gradient", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showUnfilled": true + }, + "pluginVersion": "9.4.7", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "topk(10, sum(rate(hubble_dns_queries_total{cluster=~\"$cluster\", source_namespace=~\"$source_namespace\", destination_namespace=~\"$destination_namespace\"}[$__rate_interval])*60) by (query))", + "legendFormat": "{{query}}", + "range": true, + "refId": "A" + } + ], + "title": "Top 10 DNS queries", + "type": "bargauge" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 10 + }, + "id": 39, + "options": { + "legend": { + "calcs": [ + "mean", + "lastNotNull" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "round(sum(rate(hubble_dns_queries_total{cluster=~\"$cluster\", source_namespace=~\"$source_namespace\", destination_namespace=~\"$destination_namespace\"}[$__rate_interval])) by (source) - sum(label_replace(sum(rate(hubble_dns_responses_total{cluster=~\"$cluster\", source_namespace=~\"$destination_namespace\", destination_namespace=~\"$source_namespace\"}[$__rate_interval])) by (destination), \"source\", \"$1\", \"destination\", \"(.*)\")) without (destination), 0.001) > 0", + "legendFormat": "{{source}}", + "range": true, + "refId": "A" + } + ], + "title": "Missing DNS responses", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 10 + }, + "id": 43, + "options": { + "legend": { + "calcs": [ + "mean", + "lastNotNull" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(rate(hubble_dns_responses_total{cluster=~\"$cluster\", source_namespace=~\"$destination_namespace\", destination_namespace=~\"$source_namespace\", rcode!=\"No Error\"}[$__rate_interval])) by (destination, rcode) > 0", + "legendFormat": "{{destination}}: {{rcode}}", + "range": true, + "refId": "A" + } + ], + "title": "DNS errors", + "type": "timeseries" + } + ], + "refresh": "", + "revision": 1, + "schemaVersion": 38, + "style": "dark", + "tags": [ + "kubecon-demo" + ], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "default", + "value": "default" + }, + "hide": 0, + "includeAll": false, + "label": "Data Source", + "multi": false, + "name": "DS_PROMETHEUS", + "options": [], + "query": "prometheus", + "queryValue": "", + "refresh": 1, + "regex": "(?!grafanacloud-usage|grafanacloud-ml-metrics).+", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(cilium_version, cluster)", + "hide": 0, + "includeAll": true, + "multi": true, + "name": "cluster", + "options": [], + "query": { + "query": "label_values(cilium_version, cluster)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "allValue": ".*", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(source_namespace)", + "hide": 0, + "includeAll": true, + "label": "Source Namespace", + "multi": true, + "name": "source_namespace", + "options": [], + "query": { + "query": "label_values(source_namespace)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "allValue": ".*", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(destination_namespace)", + "hide": 0, + "includeAll": true, + "label": "Destination Namespace", + "multi": true, + "name": "destination_namespace", + "options": [], + "query": { + "query": "label_values(destination_namespace)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + } + ] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "timezone": "", + "title": "Hubble / DNS Overview (Namespace)", + "uid": "_f0DUpY4k", + "version": 26, + "weekStart": "" + } + \ No newline at end of file diff --git a/dashboards/hubble/l7-http-metrics.json b/dashboards/hubble/l7-http-metrics.json new file mode 100644 index 00000000..b21004a6 --- /dev/null +++ b/dashboards/hubble/l7-http-metrics.json @@ -0,0 +1,1394 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__elements": {}, + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "9.4.7" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "timeseries", + "name": "Time series", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 1, + "id": null, + "links": [], + "liveNow": false, + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 14, + "panels": [], + "title": "General", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 0, + "y": 1 + }, + "id": 16, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.4.7", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "expr": "round(sum(rate(hubble_http_requests_total{reporter=~\"${reporter}\", destination_namespace=~\"${destination_namespace}\", destination_workload=~\"${destination_workload}\"}[$__rate_interval])), 0.001)", + "refId": "A" + } + ], + "title": "Incoming Request Volume", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 8, + "y": 1 + }, + "id": 17, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "textMode": "auto" + }, + "pluginVersion": "9.4.7", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "sum(rate(hubble_http_requests_total{cluster=~\"${cluster}\", destination_namespace=~\"${destination_namespace}\", destination_workload=~\"${destination_workload}\", reporter=\"${reporter}\", status!~\"5.*\"}[$__rate_interval]))\n/\nsum(rate(hubble_http_requests_total{cluster=~\"${cluster}\", destination_namespace=~\"${destination_namespace}\", destination_workload=~\"${destination_workload}\", reporter=\"${reporter}\"}[$__rate_interval]))", + "interval": "", + "legendFormat": "{{ cluster }} {{ method }} {{ source_namespace }}/{{ source_workload }}", + "range": true, + "refId": "A" + } + ], + "title": "Incoming Request Success Rate (non-5xx responses)", + "type": "stat" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 5, + "w": 8, + "x": 16, + "y": 1 + }, + "id": 18, + "options": { + "legend": { + "calcs": [ + "lastNotNull" + ], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "9.0.5", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "histogram_quantile(0.50, sum(rate(hubble_http_request_duration_seconds_bucket{cluster=~\"${cluster}\", destination_namespace=~\"${destination_namespace}\", destination_workload=~\"${destination_workload}\", reporter=\"${reporter}\"}[$__rate_interval])) by (le))", + "interval": "", + "legendFormat": "P50", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "histogram_quantile(0.95, sum(rate(hubble_http_request_duration_seconds_bucket{cluster=~\"${cluster}\", destination_namespace=~\"${destination_namespace}\", destination_workload=~\"${destination_workload}\", reporter=\"${reporter}\"}[$__rate_interval])) by (le))", + "hide": false, + "interval": "", + "legendFormat": "P95", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "histogram_quantile(0.99, sum(rate(hubble_http_request_duration_seconds_bucket{cluster=~\"${cluster}\", destination_namespace=~\"${destination_namespace}\", destination_workload=~\"${destination_workload}\", reporter=\"${reporter}\"}[$__rate_interval])) by (le))", + "hide": false, + "interval": "", + "legendFormat": "P99", + "range": true, + "refId": "C" + } + ], + "title": "Request Duration", + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 6 + }, + "id": 6, + "panels": [], + "title": "Requests by Source", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 0, + "y": 7 + }, + "id": 3, + "options": { + "legend": { + "calcs": [ + "max", + "mean", + "sum", + "lastNotNull" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "round(sum(rate(hubble_http_requests_total{cluster=~\"${cluster}\", destination_namespace=~\"${destination_namespace}\", destination_workload=~\"${destination_workload}\", reporter=\"${reporter}\", source_namespace=~\"${source_namespace}\", source_workload=~\"${source_workload}\"}[$__rate_interval])) by (cluster, source_namespace, source_workload, status), 0.001)", + "interval": "", + "legendFormat": "{{ cluster }} {{ method }} {{ source_namespace }}/{{ source_workload }}: {{ status }}", + "range": true, + "refId": "A" + } + ], + "title": "Incoming Requests by Source and Response Code", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 12, + "y": 7 + }, + "id": 7, + "options": { + "legend": { + "calcs": [ + "mean", + "min", + "max", + "lastNotNull" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "sum(rate(hubble_http_requests_total{cluster=~\"${cluster}\", destination_namespace=~\"${destination_namespace}\", destination_workload=~\"${destination_workload}\", reporter=\"${reporter}\", source_namespace=~\"${source_namespace}\", source_workload=~\"${source_workload}\",status!~\"5.*\"}[$__rate_interval])) by (cluster, source_namespace, source_workload)\n/\nsum(rate(hubble_http_requests_total{cluster=~\"${cluster}\", destination_namespace=~\"${destination_namespace}\", destination_workload=~\"${destination_workload}\", reporter=\"${reporter}\", source_namespace=~\"${source_namespace}\", source_workload=~\"${source_workload}\"}[$__rate_interval])) by (cluster, source_namespace, source_workload)", + "interval": "", + "legendFormat": "{{ cluster }} {{ method }} {{ source_namespace }}/{{ source_workload }}", + "range": true, + "refId": "A" + } + ], + "title": "Incoming Request Success Rate (non-5xx responses) By Source", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 0, + "y": 17 + }, + "id": 4, + "options": { + "legend": { + "calcs": [ + "min", + "max", + "mean", + "lastNotNull" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": true, + "expr": "histogram_quantile(0.50, sum(rate(hubble_http_request_duration_seconds_bucket{cluster=~\"${cluster}\", destination_namespace=~\"${destination_namespace}\", destination_workload=~\"${destination_workload}\", reporter=\"${reporter}\", source_namespace=~\"${source_namespace}\", source_workload=~\"${source_workload}\"}[$__rate_interval])) by (cluster, source_namespace, source_workload, le))", + "interval": "", + "legendFormat": "{{ cluster }} {{ source_namespace }}/{{ source_workload }} P50", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": true, + "expr": "histogram_quantile(0.95, sum(rate(hubble_http_request_duration_seconds_bucket{cluster=~\"${cluster}\", destination_namespace=~\"${destination_namespace}\", destination_workload=~\"${destination_workload}\", reporter=\"${reporter}\", source_namespace=~\"${source_namespace}\", source_workload=~\"${source_workload}\"}[$__rate_interval])) by (cluster, source_namespace, source_workload, le))", + "hide": false, + "interval": "", + "legendFormat": "{{ cluster }} {{ source_namespace }}/{{ source_workload }} P95", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": true, + "expr": "histogram_quantile(0.99, sum(rate(hubble_http_request_duration_seconds_bucket{cluster=~\"${cluster}\", destination_namespace=~\"${destination_namespace}\", destination_workload=~\"${destination_workload}\", reporter=\"${reporter}\", source_namespace=~\"${source_namespace}\", source_workload=~\"${source_workload}\"}[$__rate_interval])) by (cluster, source_namespace, source_workload, le))", + "hide": false, + "interval": "", + "legendFormat": "{{ cluster }} {{ source_namespace }}/{{ source_workload }} P99", + "range": true, + "refId": "C" + } + ], + "title": "HTTP Request Duration by Source", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 12, + "y": 17 + }, + "id": 20, + "options": { + "legend": { + "calcs": [ + "min", + "max", + "mean", + "lastNotNull" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "sum(\n node_namespace_pod_container:container_cpu_usage_seconds_total:sum_irate{cluster=~\"${cluster}\", namespace=~\"${destination_namespace}\"}\n * on(namespace,pod)\n group_left(workload, workload_type) namespace_workload_pod:kube_pod_owner:relabel{cluster=~\"${cluster}\", namespace=~\"${destination_namespace}\", workload=~\"${source_workload}\"}\n) by (namespace, workload)", + "interval": "", + "legendFormat": "{{ cluster }} {{ namespace }}/{{ workload }}", + "range": true, + "refId": "A" + } + ], + "title": "CPU Usage by Source", + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 27 + }, + "id": 9, + "panels": [], + "title": "Requests by Destination", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "reqps" + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 0, + "y": 28 + }, + "id": 10, + "options": { + "legend": { + "calcs": [ + "max", + "mean", + "sum", + "lastNotNull" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "round(sum(rate(hubble_http_requests_total{cluster=~\"${cluster}\", destination_namespace=~\"${destination_namespace}\", destination_workload=~\"${destination_workload}\", reporter=\"${reporter}\", source_namespace=~\"${source_namespace}\", source_workload=~\"${source_workload}\"}[$__rate_interval])) by (cluster, destination_namespace, destination_workload, status), 0.001)", + "interval": "", + "legendFormat": "{{ cluster }} {{ method }} {{ destination_namespace }}/{{ destination_workload }}: {{ status }}", + "range": true, + "refId": "A" + } + ], + "title": "Incoming Requests by Destination and Response Code", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "percentunit" + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 12, + "y": 28 + }, + "id": 11, + "options": { + "legend": { + "calcs": [ + "mean", + "min", + "max", + "lastNotNull" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "sum(rate(hubble_http_requests_total{cluster=~\"${cluster}\", destination_namespace=~\"${destination_namespace}\", destination_workload=~\"${destination_workload}\", reporter=\"${reporter}\", source_namespace=~\"${source_namespace}\", source_workload=~\"${source_workload}\",status!~\"5.*\"}[$__rate_interval])) by (cluster, destination_namespace, destination_workload)\n/\nsum(rate(hubble_http_requests_total{cluster=~\"${cluster}\", destination_namespace=~\"${destination_namespace}\", destination_workload=~\"${destination_workload}\", reporter=\"${reporter}\", source_namespace=~\"${source_namespace}\", source_workload=~\"${source_workload}\"}[$__rate_interval])) by (cluster, destination_namespace, destination_workload)", + "interval": "", + "legendFormat": "{{ cluster }} {{ method }} {{ destination_namespace }}/{{ destination_workload }}", + "range": true, + "refId": "A" + } + ], + "title": "Incoming Request Success Rate (non-5xx responses) By Destination", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "s" + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 0, + "y": 38 + }, + "id": 12, + "options": { + "legend": { + "calcs": [ + "min", + "max", + "mean", + "lastNotNull" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": true, + "expr": "histogram_quantile(0.50, sum(rate(hubble_http_request_duration_seconds_bucket{cluster=~\"${cluster}\", destination_namespace=~\"${destination_namespace}\", destination_workload=~\"${destination_workload}\", reporter=\"${reporter}\", source_namespace=~\"${source_namespace}\", source_workload=~\"${source_workload}\"}[$__rate_interval])) by (cluster, destination_namespace, destination_workload, le))", + "interval": "", + "legendFormat": "{{ cluster }} {{ destination_namespace }}/{{ destination_workload }} P50", + "range": true, + "refId": "A" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": true, + "expr": "histogram_quantile(0.95, sum(rate(hubble_http_request_duration_seconds_bucket{cluster=~\"${cluster}\", destination_namespace=~\"${destination_namespace}\", destination_workload=~\"${destination_workload}\", reporter=\"${reporter}\", source_namespace=~\"${source_namespace}\", source_workload=~\"${source_workload}\"}[$__rate_interval])) by (cluster, destination_namespace, destination_workload, le))", + "hide": false, + "interval": "", + "legendFormat": "{{ cluster }} {{ destination_namespace }}/{{ destination_workload }} P95", + "range": true, + "refId": "B" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": true, + "expr": "histogram_quantile(0.99, sum(rate(hubble_http_request_duration_seconds_bucket{cluster=~\"${cluster}\", destination_namespace=~\"${destination_namespace}\", destination_workload=~\"${destination_workload}\", reporter=\"${reporter}\", source_namespace=~\"${source_namespace}\", source_workload=~\"${source_workload}\"}[$__rate_interval])) by (cluster, destination_namespace, destination_workload, le))", + "hide": false, + "interval": "", + "legendFormat": "{{ cluster }} {{ destination_namespace }}/{{ destination_workload }} P99", + "range": true, + "refId": "C" + } + ], + "title": "HTTP Request Duration by Destination", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green" + } + ] + }, + "unit": "none" + }, + "overrides": [] + }, + "gridPos": { + "h": 10, + "w": 12, + "x": 12, + "y": 38 + }, + "id": 19, + "options": { + "legend": { + "calcs": [ + "min", + "max", + "mean", + "lastNotNull" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "exemplar": false, + "expr": "sum(\n node_namespace_pod_container:container_cpu_usage_seconds_total:sum_irate{cluster=~\"${cluster}\", namespace=~\"${destination_namespace}\"}\n * on(namespace,pod)\n group_left(workload, workload_type) namespace_workload_pod:kube_pod_owner:relabel{cluster=~\"${cluster}\", namespace=~\"${destination_namespace}\", workload=\"${destination_workload}\"}\n) by (namespace, workload)", + "interval": "", + "legendFormat": "{{ cluster }} {{ namespace }}/{{ workload }}", + "range": true, + "refId": "A" + } + ], + "title": "CPU Usage by Destination", + "type": "timeseries" + } + ], + "refresh": "30s", + "revision": 1, + "schemaVersion": 38, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": {}, + "hide": 0, + "includeAll": false, + "label": "Prometheus", + "multi": false, + "name": "DS_PROMETHEUS", + "options": [], + "query": "prometheus", + "queryValue": "", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(hubble_http_requests_total, cluster)", + "hide": 0, + "includeAll": false, + "label": "Cluster", + "multi": false, + "name": "cluster", + "options": [], + "query": { + "query": "label_values(hubble_http_requests_total, cluster)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 2, + "type": "query" + }, + { + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(hubble_http_requests_total{cluster=~\"${cluster}\"}, destination_namespace)", + "description": "", + "hide": 0, + "includeAll": false, + "label": "Destination Namespace", + "multi": false, + "name": "destination_namespace", + "options": [], + "query": { + "query": "label_values(hubble_http_requests_total{cluster=~\"${cluster}\"}, destination_namespace)", + "refId": "StandardVariableQuery" + }, + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 2, + "type": "query" + }, + { + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(hubble_http_requests_total{cluster=~\"${cluster}\", destination_namespace=~\"${destination_namespace}\"}, destination_workload)", + "hide": 0, + "includeAll": false, + "label": "Destination Workload", + "multi": false, + "name": "destination_workload", + "options": [], + "query": { + "query": "label_values(hubble_http_requests_total{cluster=~\"${cluster}\", destination_namespace=~\"${destination_namespace}\"}, destination_workload)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 2, + "type": "query" + }, + { + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(hubble_http_requests_total, reporter)", + "hide": 0, + "includeAll": false, + "label": "Reporter", + "multi": false, + "name": "reporter", + "options": [], + "query": { + "query": "label_values(hubble_http_requests_total, reporter)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(hubble_http_requests_total{cluster=~\"${cluster}\", destination_namespace=~\"${destination_namespace}\", destination_workload=~\"${destination_workload}\"}, source_namespace)", + "hide": 0, + "includeAll": true, + "label": "Source Namespace", + "multi": true, + "name": "source_namespace", + "options": [], + "query": { + "query": "label_values(hubble_http_requests_total{cluster=~\"${cluster}\", destination_namespace=~\"${destination_namespace}\", destination_workload=~\"${destination_workload}\"}, source_namespace)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(hubble_http_requests_total{cluster=~\"${cluster}\", destination_namespace=~\"${destination_namespace}\", destination_workload=~\"${destination_workload}\", source_namespace=~\"${source_namespace}\"}, source_workload)", + "hide": 0, + "includeAll": true, + "label": "Source Workload", + "multi": true, + "name": "source_workload", + "options": [], + "query": { + "query": "label_values(hubble_http_requests_total{cluster=~\"${cluster}\", destination_namespace=~\"${destination_namespace}\", destination_workload=~\"${destination_workload}\", source_namespace=~\"${source_namespace}\"}, source_workload)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + } + ] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Hubble L7 HTTP Metrics by Workload", + "uid": "3g264CZVz", + "version": 3, + "weekStart": "" +} diff --git a/dashboards/hubble/network-overview.json b/dashboards/hubble/network-overview.json new file mode 100644 index 00000000..cddb473d --- /dev/null +++ b/dashboards/hubble/network-overview.json @@ -0,0 +1,1001 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "Prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "__elements": {}, + "__requires": [ + { + "type": "panel", + "id": "bargauge", + "name": "Bar gauge", + "version": "" + }, + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "9.4.7" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + }, + { + "type": "panel", + "id": "timeseries", + "name": "Time series", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "datasource", + "uid": "grafana" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "target": { + "limit": 100, + "matchAny": false, + "tags": [], + "type": "dashboard" + }, + "type": "dashboard" + } + ] + }, + "description": "", + "editable": true, + "fiscalYearStartMonth": 0, + "gnetId": 16612, + "graphTooltip": 0, + "id": null, + "links": [ + { + "asDropdown": true, + "icon": "external link", + "includeVars": true, + "keepTime": true, + "tags": [ + "cilium-overview" + ], + "targetBlank": false, + "title": "Cilium Overviews", + "tooltip": "", + "type": "dashboards", + "url": "" + }, + { + "asDropdown": true, + "icon": "external link", + "includeVars": false, + "keepTime": true, + "tags": [ + "hubble" + ], + "targetBlank": false, + "title": "Hubble", + "tooltip": "", + "type": "dashboards", + "url": "" + } + ], + "liveNow": false, + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 8, + "panels": [], + "title": "Flows processed", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ops" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 1 + }, + "id": 12, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(rate(hubble_flows_processed_total{cluster=~\"$cluster\", source_namespace=~\"$source_namespace\", destination_namespace=~\"$destination_namespace\"}[$__rate_interval])) by (type, subtype)", + "legendFormat": "{{type}}/{{subtype}}", + "range": true, + "refId": "A" + } + ], + "title": "Flows processed by type", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "ops" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 1 + }, + "id": 35, + "options": { + "legend": { + "calcs": [], + "displayMode": "list", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(rate(hubble_flows_processed_total{cluster=~\"$cluster\", source_namespace=~\"$source_namespace\", destination_namespace=~\"$destination_namespace\"}[$__rate_interval])) by (verdict)", + "legendFormat": "{{verdict}}", + "range": true, + "refId": "A" + } + ], + "title": "Flows processed by verdict", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "pps" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 10 + }, + "id": 36, + "options": { + "displayMode": "gradient", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showUnfilled": true + }, + "pluginVersion": "9.4.7", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "topk(10, sum(rate(hubble_flows_processed_total{cluster=~\"$cluster\", source_namespace=~\"$source_namespace\", destination_namespace=~\"$destination_namespace\"}[$__rate_interval])) by (source))", + "legendFormat": "{{source}}", + "range": true, + "refId": "A" + } + ], + "title": "Top 10 sources", + "type": "bargauge" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + } + ] + }, + "unit": "pps" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 10 + }, + "id": 37, + "options": { + "displayMode": "gradient", + "minVizHeight": 10, + "minVizWidth": 0, + "orientation": "horizontal", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showUnfilled": true + }, + "pluginVersion": "9.4.7", + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "topk(10, sum(rate(hubble_flows_processed_total{cluster=~\"$cluster\", source_namespace=~\"$source_namespace\", destination_namespace=~\"$destination_namespace\"}[$__rate_interval])) by (destination))", + "legendFormat": "{{source}}", + "range": true, + "refId": "A" + } + ], + "title": "Top 10 destinations", + "type": "bargauge" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 19 + }, + "id": 10, + "panels": [], + "title": "Connection drops", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "pps" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 20 + }, + "id": 23, + "options": { + "legend": { + "calcs": [ + "mean", + "lastNotNull" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(rate(hubble_tcp_flags_total{cluster=~\"$cluster\", source_namespace=~\"$source_namespace\", destination_namespace=~\"$destination_namespace\", flag=\"SYN\"}[$__rate_interval])) by (source) - sum(label_replace(sum(rate(hubble_tcp_flags_total{cluster=~\"$cluster\", source_namespace=~\"$destination_namespace\", destination_namespace=~\"$source_namespace\", flag=\"SYN-ACK\"}[$__rate_interval])) by (destination), \"source\", \"$1\", \"destination\", \"(.*)\")) without (destination) > 0", + "hide": false, + "legendFormat": "{{source}}", + "range": true, + "refId": "A" + } + ], + "title": "Missing TCP SYN-ACKs", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "pps" + }, + "overrides": [] + }, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 20 + }, + "id": 34, + "options": { + "legend": { + "calcs": [ + "mean", + "lastNotNull" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(rate(hubble_icmp_total{cluster=~\"$cluster\", source_namespace=~\"$source_namespace\", destination_namespace=~\"$destination_namespace\", type=\"EchoRequest\"}[$__rate_interval])) by (source) - sum(label_replace(sum(rate(hubble_icmp_total{cluster=~\"$cluster\", source_namespace=~\"$destination_namespace\", destination_namespace=~\"$source_namespace\", type=\"EchoReply\"}[$__rate_interval])) by (destination), \"source\", \"$1\", \"destination\", \"(.*)\")) without (destination) > 0", + "legendFormat": "{{source}}", + "range": true, + "refId": "A" + } + ], + "title": "Missing ICMP Echo Replys", + "type": "timeseries" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 29 + }, + "id": 6, + "panels": [], + "title": "Network Policy drops", + "type": "row" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "pps" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 30 + }, + "id": 29, + "options": { + "legend": { + "calcs": [ + "mean", + "lastNotNull" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(rate(hubble_drop_total{cluster=~\"$cluster\", source_namespace=~\"$source_namespace\", destination_namespace=~\"$destination_namespace\"}[$__rate_interval])) by (source, reason) > 0", + "legendFormat": "{{source}}: {{reason}}", + "range": true, + "refId": "A" + } + ], + "title": "Network Policy drops by source", + "type": "timeseries" + }, + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic" + }, + "custom": { + "axisCenteredZero": false, + "axisColorMode": "text", + "axisLabel": "", + "axisPlacement": "auto", + "barAlignment": 0, + "drawStyle": "line", + "fillOpacity": 10, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "normal" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "min": 0, + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": null + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "pps" + }, + "overrides": [ + { + "__systemRef": "hideSeriesFrom", + "matcher": { + "id": "byNames", + "options": { + "mode": "exclude", + "names": [ + "kube-dns-7d44cdb5d5-g85vg: UNSUPPORTED_PROTOCOL_FOR_NAT_MASQUERADE" + ], + "prefix": "All except:", + "readOnly": true + } + }, + "properties": [ + { + "id": "custom.hideFrom", + "value": { + "legend": false, + "tooltip": false, + "viz": true + } + } + ] + } + ] + }, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 30 + }, + "id": 30, + "options": { + "legend": { + "calcs": [ + "mean", + "lastNotNull" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "mode": "single", + "sort": "none" + } + }, + "targets": [ + { + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "editorMode": "code", + "expr": "sum(rate(hubble_drop_total{cluster=~\"$cluster\", source_namespace=~\"$source_namespace\", destination_namespace=~\"$destination_namespace\"}[$__rate_interval])) by (destination, reason) > 0", + "legendFormat": "{{destination}}: {{reason}}", + "range": true, + "refId": "A" + } + ], + "title": "Network Policy drops by destination", + "type": "timeseries" + } + ], + "refresh": "", + "revision": 1, + "schemaVersion": 38, + "style": "dark", + "tags": [ + "kubecon-demo" + ], + "templating": { + "list": [ + { + "current": { + "selected": false, + "text": "default", + "value": "default" + }, + "hide": 0, + "includeAll": false, + "label": "Data Source", + "multi": false, + "name": "DS_PROMETHEUS", + "options": [], + "query": "prometheus", + "queryValue": "", + "refresh": 1, + "regex": "(?!grafanacloud-usage|grafanacloud-ml-metrics).+", + "skipUrlSync": false, + "type": "datasource" + }, + { + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(cilium_version, cluster)", + "hide": 0, + "includeAll": true, + "multi": true, + "name": "cluster", + "options": [], + "query": { + "query": "label_values(cilium_version, cluster)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "allValue": ".*", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(source_namespace)", + "hide": 0, + "includeAll": true, + "label": "Source Namespace", + "multi": true, + "name": "source_namespace", + "options": [], + "query": { + "query": "label_values(source_namespace)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + }, + { + "allValue": ".*", + "current": {}, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "definition": "label_values(destination_namespace)", + "hide": 0, + "includeAll": true, + "label": "Destination Namespace", + "multi": true, + "name": "destination_namespace", + "options": [], + "query": { + "query": "label_values(destination_namespace)", + "refId": "StandardVariableQuery" + }, + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "type": "query" + } + ] + }, + "time": { + "from": "now-1h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "timezone": "", + "title": "Hubble / Network Overview (Namespace)", + "uid": "nlsO8tYVz", + "version": 18, + "weekStart": "" + } + \ No newline at end of file diff --git a/dashboards/hubble/overview.json b/dashboards/hubble/overview.json new file mode 100644 index 00000000..783aa131 --- /dev/null +++ b/dashboards/hubble/overview.json @@ -0,0 +1,3357 @@ +{ + "__inputs": [ + { + "name": "DS_PROMETHEUS", + "label": "prometheus", + "description": "", + "type": "datasource", + "pluginId": "prometheus", + "pluginName": "Prometheus" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": null, + "graphTooltip": 0, + "id": 3, + "links": [], + "panels": [ + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 14, + "panels": [], + "title": "General Processing", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fill": 1, + "gridPos": { + "h": 5, + "w": 12, + "x": 0, + "y": 1 + }, + "id": 12, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "max", + "fillBelowTo": "avg", + "lines": false + }, + { + "alias": "avg", + "fill": 0, + "fillBelowTo": "min" + }, + { + "alias": "min", + "lines": false + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "avg(sum(rate(hubble_flows_processed_total[1m])) by (pod))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "avg", + "refId": "A" + }, + { + "expr": "min(sum(rate(hubble_flows_processed_total[1m])) by (pod))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "min", + "refId": "B" + }, + { + "expr": "max(sum(rate(hubble_flows_processed_total[1m])) by (pod))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "max", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Flows processed Per Node", + "tooltip": { + "shared": true, + "sort": 1, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fill": 1, + "gridPos": { + "h": 5, + "w": 12, + "x": 12, + "y": 1 + }, + "id": 32, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(hubble_flows_processed_total[1m])) by (pod, type)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Flows Types", + "tooltip": { + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fill": 1, + "gridPos": { + "h": 5, + "w": 12, + "x": 0, + "y": 6 + }, + "id": 59, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(hubble_flows_processed_total{type=\"L7\"}[1m])) by (pod, subtype)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{subtype}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "L7 Flow Distribution", + "tooltip": { + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fill": 1, + "gridPos": { + "h": 5, + "w": 12, + "x": 12, + "y": 6 + }, + "id": 60, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(hubble_flows_processed_total{type=\"Trace\"}[1m])) by (pod, subtype)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{subtype}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Trace Flow Distribution", + "tooltip": { + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 11 + }, + "id": 16, + "panels": [], + "title": "Network", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fill": 1, + "gridPos": { + "h": 5, + "w": 12, + "x": 0, + "y": 12 + }, + "id": 33, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(hubble_flows_processed_total[1m])) by (pod, verdict)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{verdict}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Forwarded vs Dropped", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fill": 1, + "gridPos": { + "h": 5, + "w": 12, + "x": 12, + "y": 12 + }, + "id": 8, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(hubble_drop_total[1m])) by (pod, reason)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{reason}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Drop Reason", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "pps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fill": 1, + "gridPos": { + "h": 5, + "w": 12, + "x": 0, + "y": 17 + }, + "id": 34, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": true, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum (rate(hubble_port_distribution_total[1m])) by (pod, protocol)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{protocol}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Protocol Usage", + "tooltip": { + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "pps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fill": 1, + "gridPos": { + "h": 5, + "w": 12, + "x": 12, + "y": 17 + }, + "id": 18, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "hideEmpty": false, + "hideZero": false, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "topk(10, sum (rate(hubble_port_distribution_total{port!=\"0\"}[1m])) by (pod, port, protocol))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{port}}/{{protocol}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Top 10 Port Distribution", + "tooltip": { + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "pps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fill": 1, + "gridPos": { + "h": 5, + "w": 12, + "x": 0, + "y": 22 + }, + "id": 10, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "fin", + "yaxis": 1 + }, + { + "alias": "FIN", + "yaxis": 2 + }, + { + "alias": "RST", + "yaxis": 2 + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(hubble_tcp_flags_total{family=\"IPv4\"}[1m])) by (pod, flag)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{flag}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "TCPv4", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "pps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "conditions": [ + { + "evaluator": { + "params": [ + 0.2 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "avg" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "1m", + "handler": 1, + "name": "Missing TCP SYN-ACK", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fill": 1, + "gridPos": { + "h": 5, + "w": 12, + "x": 12, + "y": 22 + }, + "id": 62, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "fin", + "yaxis": 1 + }, + { + "alias": "FIN", + "yaxis": 2 + }, + { + "alias": "RST", + "yaxis": 2 + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(hubble_tcp_flags_total{family=\"IPv4\", flag=\"SYN\"}[1m])) by (pod) - sum(rate(hubble_tcp_flags_total{family=\"IPv4\", flag=\"SYN-ACK\"}[1m])) by (pod)", + "format": "time_series", + "hide": false, + "intervalFactor": 1, + "legendFormat": "Missing SYN-ACK", + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 0.2 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Missing TCPv4 SYN-ACKs", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "pps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fill": 1, + "gridPos": { + "h": 5, + "w": 12, + "x": 0, + "y": 27 + }, + "id": 35, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "fin", + "yaxis": 1 + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(hubble_tcp_flags_total{family=\"IPv6\"}[1m])) by (pod, flag)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{flag}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "TCPv6", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "pps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "conditions": [ + { + "evaluator": { + "params": [ + 0.2 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "avg" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "1m", + "handler": 1, + "name": "Missing TCPv6 SYN-ACKs alert", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fill": 1, + "gridPos": { + "h": 5, + "w": 12, + "x": 12, + "y": 27 + }, + "id": 63, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "fin", + "yaxis": 1 + }, + { + "alias": "FIN", + "yaxis": 2 + }, + { + "alias": "RST", + "yaxis": 2 + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(hubble_tcp_flags_total{family=\"IPv6\", flag=\"SYN\"}[1m])) by (pod) - sum(rate(hubble_tcp_flags_total{family=\"IPv6\", flag=\"SYN-ACK\"}[1m])) by (pod)", + "format": "time_series", + "hide": false, + "intervalFactor": 1, + "legendFormat": "Missing SYN-ACK", + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 0.2 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Missing TCPv6 SYN-ACKs", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "pps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fill": 1, + "gridPos": { + "h": 5, + "w": 12, + "x": 0, + "y": 32 + }, + "id": 31, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(hubble_icmp_total{family=\"IPv4\"}[1m])) by (pod, type)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "ICMPv4", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "conditions": [ + { + "evaluator": { + "params": [ + 0.1 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "B", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "avg" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "1m", + "handler": 1, + "name": "Missing ICMPv4 Echo-Reply alert", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fill": 1, + "gridPos": { + "h": 5, + "w": 12, + "x": 12, + "y": 32 + }, + "id": 64, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(hubble_icmp_total{family=\"IPv4\", type=\"EchoRequest\"}[1m])) by (pod) - sum(rate(hubble_icmp_total{family=\"IPv4\", type=\"EchoReply\"}[1m])) by (pod)", + "format": "time_series", + "hide": false, + "intervalFactor": 1, + "legendFormat": "Missing ICMP Echo-Reply", + "refId": "B" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 0.1 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Missing ICMPv4 Echo-Reply", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "pps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fill": 1, + "gridPos": { + "h": 5, + "w": 12, + "x": 0, + "y": 37 + }, + "id": 36, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(hubble_icmp_total{family=\"IPv6\"}[1m])) by (pod, type)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "ICMPv6", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fill": 1, + "gridPos": { + "h": 5, + "w": 12, + "x": 12, + "y": 37 + }, + "id": 65, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(hubble_icmp_total{family=\"IPv6\", type=\"EchoRequest\"}[1m])) by (pod) - sum(rate(hubble_icmp_total{family=\"IPv6\", type=\"EchoReply\"}[1m])) by (pod)", + "format": "time_series", + "hide": false, + "intervalFactor": 1, + "legendFormat": "Missing ICMP Echo-Reply", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Missing ICMPv6 Echo-Reply", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "pps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 42 + }, + "id": 42, + "panels": [], + "title": "Network Policy", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fill": 1, + "gridPos": { + "h": 4, + "w": 12, + "x": 0, + "y": 43 + }, + "id": 43, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(hubble_drop_total{reason=\"POLICY_DENIED\"}[1m])) by (pod, reason)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{reason}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Denies by Reason", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "pps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fill": 1, + "gridPos": { + "h": 4, + "w": 12, + "x": 12, + "y": 43 + }, + "id": 61, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(hubble_drop_total{reason=\"POLICY_DENIED\"}[1m])) by (pod, protocol)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{protocol}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Denied Packets by Protocol", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "pps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fill": 1, + "gridPos": { + "h": 5, + "w": 12, + "x": 0, + "y": 47 + }, + "id": 55, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "topk(10, sum(rate(hubble_drop_total{reason=\"POLICY_DENIED\"}[1m])) by (pod, source))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{source}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Top 10 Source Pods with Denied Packets", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "pps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fill": 1, + "gridPos": { + "h": 5, + "w": 12, + "x": 12, + "y": 47 + }, + "id": 54, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "topk(10, sum(rate(hubble_drop_total{reason=\"POLICY_DENIED\"}[1m])) by (pod, destination))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{destination}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Top 10 Destination Pods with Denied Packets", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "pps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 52 + }, + "id": 47, + "panels": [], + "title": "HTTP", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fill": 1, + "gridPos": { + "h": 6, + "w": 12, + "x": 0, + "y": 53 + }, + "id": 45, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(hubble_http_requests_total[1m])) by (pod, method)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{method}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "HTTP Requests", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": 1, + "format": "reqps", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fill": 1, + "gridPos": { + "h": 6, + "w": 12, + "x": 12, + "y": 53 + }, + "id": 49, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(hubble_http_responses_total[1m])) by (pod, status)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{status}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "HTTP responses", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": 1, + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fill": 1, + "gridPos": { + "h": 5, + "w": 12, + "x": 0, + "y": 59 + }, + "id": 51, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.5, rate(hubble_http_request_duration_seconds_bucket[1m]))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{method}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "HTTP Request/Response Latency (p50)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fill": 1, + "gridPos": { + "h": 5, + "w": 12, + "x": 12, + "y": 59 + }, + "id": 58, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, rate(hubble_http_request_duration_seconds_bucket[1m]))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{method}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "HTTP Request/Response Latency (p99)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fill": 1, + "gridPos": { + "h": 5, + "w": 12, + "x": 0, + "y": 64 + }, + "id": 53, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": true, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": true, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(hubble_http_requests_total[5m])) by (pod, protocol)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{protocol}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "HTTP Protocol Usage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": 0, + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 69 + }, + "id": 6, + "panels": [], + "title": "DNS", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fill": 1, + "gridPos": { + "h": 5, + "w": 8, + "x": 0, + "y": 70 + }, + "id": 2, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(hubble_dns_queries_total[1m])) by (pod, qtypes)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{qtypes}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "DNS Requests", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fill": 1, + "gridPos": { + "h": 5, + "w": 8, + "x": 8, + "y": 70 + }, + "id": 4, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(hubble_dns_responses_total{rcode=\"No Error\"}[1m])) by (pod, qtypes)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{qtypes}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "DNS responses", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": 1, + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "alert": { + "conditions": [ + { + "evaluator": { + "params": [ + 0.5 + ], + "type": "gt" + }, + "operator": { + "type": "and" + }, + "query": { + "params": [ + "A", + "5m", + "now" + ] + }, + "reducer": { + "params": [], + "type": "avg" + }, + "type": "query" + } + ], + "executionErrorState": "alerting", + "for": "5m", + "frequency": "1m", + "handler": 1, + "name": "DNS Request/Response Symmetry alert", + "noDataState": "no_data", + "notifications": [] + }, + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fill": 1, + "gridPos": { + "h": 5, + "w": 8, + "x": 16, + "y": 70 + }, + "id": 66, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(hubble_dns_queries_total[1m])) by (pod, qtypes) - sum(rate(hubble_dns_responses_total[1m])) by (pod, qtypes)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{qtypes}}", + "refId": "A" + } + ], + "thresholds": [ + { + "colorMode": "critical", + "fill": true, + "line": true, + "op": "gt", + "value": 0.5 + } + ], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Missing DNS Responses", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fill": 1, + "gridPos": { + "h": 5, + "w": 12, + "x": 0, + "y": 75 + }, + "id": 40, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(hubble_dns_response_types_total[1m])) by (pod, type)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{type}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "DNS Response Record Type", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": 1, + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fill": 1, + "gridPos": { + "h": 5, + "w": 12, + "x": 12, + "y": 75 + }, + "id": 57, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(hubble_dns_responses_total{rcode=\"No Error\"}[1m])) by (pod,ips_returned)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{ips_returned}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "DNS Response IPs Returned", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": 1, + "format": "reqps", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fill": 1, + "gridPos": { + "h": 5, + "w": 12, + "x": 0, + "y": 80 + }, + "id": 28, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(hubble_dns_responses_total{rcode!=\"No Error\"}[1m])) by (pod, qtypes, rcode)", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{rcode}} ({{qtypes}})", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "DNS Errors", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": 4, + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fill": 1, + "gridPos": { + "h": 5, + "w": 12, + "x": 12, + "y": 80 + }, + "id": 56, + "legend": { + "alignAsTable": false, + "avg": false, + "current": false, + "max": false, + "min": false, + "rightSide": false, + "show": true, + "sideWidth": null, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "topk(10,sum(rate(hubble_dns_responses_total{rcode!=\"No Error\"}[1m])) by (pod, destination))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{destination}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Pods with DNS errors", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": 4, + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": { + "type": "prometheus", + "uid": "${DS_PROMETHEUS}" + }, + "fill": 1, + "gridPos": { + "h": 6, + "w": 24, + "x": 0, + "y": 85 + }, + "id": 20, + "legend": { + "alignAsTable": true, + "avg": false, + "current": true, + "max": false, + "min": false, + "rightSide": true, + "show": true, + "sort": "current", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "options": {}, + "percentage": false, + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "topk(10, sum(rate(hubble_dns_queries_total[10m])*60) by (query, qtypes))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "{{query}} ({{qtypes}})", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Top 10 DNS Queries per minute", + "tooltip": { + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": "30s", + "schemaVersion": 18, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "current": {}, + "hide": 0, + "includeAll": false, + "label": "Prometheus", + "multi": false, + "name": "DS_PROMETHEUS", + "options": [], + "query": "prometheus", + "queryValue": "", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + } + ] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h", + "1d" + ], + "time_options": [ + "5m", + "15m", + "1h", + "6h", + "12h", + "24h", + "2d", + "7d", + "30d" + ] + }, + "timezone": "", + "title": "Hubble Metrics and Monitoring", + "uid": "5HftnJAWz", + "version": 24 +} diff --git a/docs/hubble-observability.md b/docs/hubble-observability.md new file mode 100644 index 00000000..33597eec --- /dev/null +++ b/docs/hubble-observability.md @@ -0,0 +1,99 @@ +# Enabling Hubble for Network Observability + +Hubble is a network and security observability platform built on top of Cilium. It provides deep visibility into the communication and behavior of services in your Kubernetes cluster. + +## Prerequisites + +- Cozystack platform running with Cilium as the CNI +- Monitoring hub enabled for Grafana access + +## Configuration + +Hubble is disabled by default in Cozystack. To enable it, update the Cilium configuration. + +### Enable Hubble + +Edit the Cilium values in your platform configuration to enable Hubble: + +```yaml +cilium: + hubble: + enabled: true + relay: + enabled: true + ui: + enabled: true + metrics: + enabled: + - dns + - drop + - tcp + - flow + - port-distribution + - icmp + - httpV2:exemplars=true;labelsContext=source_ip,source_namespace,source_workload,destination_ip,destination_namespace,destination_workload,traffic_direction +``` + +### Components + +When Hubble is enabled, the following components become available: + +- **Hubble Relay**: Aggregates flow data from all Cilium agents +- **Hubble UI**: Web-based interface for exploring network flows +- **Hubble Metrics**: Prometheus metrics for network observability + +## Grafana Dashboards + +Once Hubble is enabled and the monitoring hub is deployed, the following dashboards become available in Grafana under the `hubble` folder: + +| Dashboard | Description | +|-----------|-------------| +| **Overview** | General Hubble metrics including processing statistics | +| **DNS Namespace** | DNS query and response metrics by namespace | +| **L7 HTTP Metrics** | HTTP layer 7 metrics by workload | +| **Network Overview** | Network flow overview by namespace | + +### Accessing Dashboards + +1. Navigate to Grafana via the monitoring hub +2. Browse to the `hubble` folder in the dashboard browser +3. Select a dashboard to view network observability data + +## Metrics Available + +Hubble exposes various metrics that can be queried in Grafana: + +- `hubble_flows_processed_total`: Total number of flows processed +- `hubble_dns_queries_total`: DNS queries by type +- `hubble_dns_responses_total`: DNS responses by status +- `hubble_drop_total`: Dropped packets by reason +- `hubble_tcp_flags_total`: TCP connections by flag +- `hubble_http_requests_total`: HTTP requests by method and status + +## Troubleshooting + +### Verify Hubble Status + +Check if Hubble is running: + +```bash +kubectl get pods -n cozy-cilium -l k8s-app=hubble-relay +kubectl get pods -n cozy-cilium -l k8s-app=hubble-ui +``` + +### Check Metrics Endpoint + +Verify Hubble metrics are being scraped: + +```bash +kubectl port-forward -n cozy-cilium svc/hubble-metrics 9965:9965 +curl http://localhost:9965/metrics +``` + +### Verify ServiceMonitor + +Ensure the ServiceMonitor is created for Prometheus scraping: + +```bash +kubectl get servicemonitor -n cozy-cilium +``` diff --git a/packages/extra/monitoring/dashboards.list b/packages/extra/monitoring/dashboards.list index 21ecb974..1f4b2eea 100644 --- a/packages/extra/monitoring/dashboards.list +++ b/packages/extra/monitoring/dashboards.list @@ -39,3 +39,7 @@ goldpinger/goldpinger clickhouse/altinity-clickhouse-operator-dashboard storage/linstor seaweedfs/seaweedfs +hubble/overview +hubble/dns-namespace +hubble/l7-http-metrics +hubble/network-overview From d73773eaa128149f616a633bc1e3454dfe3a63ed Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Tue, 6 Jan 2026 18:06:01 +0100 Subject: [PATCH 002/125] feat(kubeovn): update to v1.14.25 Update kube-ovn from v1.14.11 to v1.14.25. Sync chart templates with upstream changes. Co-Authored-By: Claude Signed-off-by: Andrei Kvapil --- packages/system/kubeovn/Chart.yaml | 2 +- packages/system/kubeovn/Makefile | 2 +- .../system/kubeovn/charts/kube-ovn/Chart.yaml | 4 +- .../system/kubeovn/charts/kube-ovn/README.md | 12 +++++ .../charts/kube-ovn/templates/_helpers.tpl | 5 +- .../kube-ovn/templates/central-deploy.yaml | 4 ++ .../kube-ovn/templates/controller-deploy.yaml | 4 ++ .../templates/ic-controller-deploy.yaml | 4 ++ .../kube-ovn/templates/kube-ovn-crd.yaml | 48 +++++++++++++++++++ .../kube-ovn/templates/monitor-deploy.yaml | 4 ++ .../kube-ovn/templates/ovn-dpdk-ds.yaml | 4 ++ .../charts/kube-ovn/templates/ovn-sa.yaml | 4 ++ .../charts/kube-ovn/templates/ovncni-ds.yaml | 17 +++++-- .../charts/kube-ovn/templates/ovsovn-ds.yaml | 5 ++ .../charts/kube-ovn/templates/pinger-ds.yaml | 6 ++- .../kube-ovn/templates/post-delete-hook.yaml | 6 ++- .../kube-ovn/templates/upgrade-ovs-ovn.yaml | 6 ++- .../kubeovn/charts/kube-ovn/values.yaml | 3 +- packages/system/kubeovn/values.yaml | 2 +- 19 files changed, 129 insertions(+), 13 deletions(-) diff --git a/packages/system/kubeovn/Chart.yaml b/packages/system/kubeovn/Chart.yaml index d1532794..b1a4e05b 100644 --- a/packages/system/kubeovn/Chart.yaml +++ b/packages/system/kubeovn/Chart.yaml @@ -1,3 +1,3 @@ apiVersion: v2 name: cozy-kubeovn -version: 0.39.0 +version: 0.38.0 diff --git a/packages/system/kubeovn/Makefile b/packages/system/kubeovn/Makefile index a4a0d1ed..45c5ab25 100644 --- a/packages/system/kubeovn/Makefile +++ b/packages/system/kubeovn/Makefile @@ -1,4 +1,4 @@ -KUBEOVN_TAG=v0.39.0 +KUBEOVN_TAG=v0.40.0 export NAME=kubeovn export NAMESPACE=cozy-$(NAME) diff --git a/packages/system/kubeovn/charts/kube-ovn/Chart.yaml b/packages/system/kubeovn/charts/kube-ovn/Chart.yaml index f7be2d3b..0621c7c7 100644 --- a/packages/system/kubeovn/charts/kube-ovn/Chart.yaml +++ b/packages/system/kubeovn/charts/kube-ovn/Chart.yaml @@ -15,12 +15,12 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: v1.14.11 +version: v1.14.25 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "1.14.11" +appVersion: "1.14.25" kubeVersion: ">= 1.29.0-0" diff --git a/packages/system/kubeovn/charts/kube-ovn/README.md b/packages/system/kubeovn/charts/kube-ovn/README.md index 3af408e6..74e5c3f1 100644 --- a/packages/system/kubeovn/charts/kube-ovn/README.md +++ b/packages/system/kubeovn/charts/kube-ovn/README.md @@ -2,6 +2,18 @@ Currently supported version: 1.9 +## Installing the Chart + +### From OCI Registry + +The Helm chart is available from GitHub Container Registry: + +```bash +helm install kube-ovn oci://ghcr.io/kubeovn/charts/kube-ovn --version v1.15.0 +``` + +### From Source + Installation : ```bash diff --git a/packages/system/kubeovn/charts/kube-ovn/templates/_helpers.tpl b/packages/system/kubeovn/charts/kube-ovn/templates/_helpers.tpl index e6697c6e..fd6db240 100644 --- a/packages/system/kubeovn/charts/kube-ovn/templates/_helpers.tpl +++ b/packages/system/kubeovn/charts/kube-ovn/templates/_helpers.tpl @@ -1,8 +1,10 @@ {{/* -Get IP-addresses of master nodes +Get IP-addresses of master nodes. If no nodes are returned, we assume this is +a dry-run/template call and return nothing. */}} {{- define "kubeovn.nodeIPs" -}} {{- $nodes := lookup "v1" "Node" "" "" -}} +{{- if $nodes -}} {{- $ips := list -}} {{- range $node := $nodes.items -}} {{- $label := splitList "=" $.Values.MASTER_NODES_LABEL }} @@ -25,6 +27,7 @@ Get IP-addresses of master nodes {{- end -}} {{ join "," $ips }} {{- end -}} +{{- end -}} {{/* Number of master nodes diff --git a/packages/system/kubeovn/charts/kube-ovn/templates/central-deploy.yaml b/packages/system/kubeovn/charts/kube-ovn/templates/central-deploy.yaml index bbc1e09d..505e0925 100644 --- a/packages/system/kubeovn/charts/kube-ovn/templates/central-deploy.yaml +++ b/packages/system/kubeovn/charts/kube-ovn/templates/central-deploy.yaml @@ -39,7 +39,11 @@ spec: topologyKey: kubernetes.io/hostname priorityClassName: system-cluster-critical serviceAccountName: ovn-ovs + automountServiceAccountToken: true hostNetwork: true + securityContext: + seccompProfile: + type: RuntimeDefault initContainers: - name: hostpath-init image: {{ .Values.global.registry.address }}/{{ .Values.global.images.kubeovn.repository }}:{{ .Values.global.images.kubeovn.tag }} diff --git a/packages/system/kubeovn/charts/kube-ovn/templates/controller-deploy.yaml b/packages/system/kubeovn/charts/kube-ovn/templates/controller-deploy.yaml index 5c4587f9..cd0728b3 100644 --- a/packages/system/kubeovn/charts/kube-ovn/templates/controller-deploy.yaml +++ b/packages/system/kubeovn/charts/kube-ovn/templates/controller-deploy.yaml @@ -46,7 +46,11 @@ spec: topologyKey: kubernetes.io/hostname priorityClassName: system-cluster-critical serviceAccountName: ovn + automountServiceAccountToken: true hostNetwork: true + securityContext: + seccompProfile: + type: RuntimeDefault initContainers: - name: hostpath-init image: {{ .Values.global.registry.address }}/{{ .Values.global.images.kubeovn.repository }}:{{ .Values.global.images.kubeovn.tag }} diff --git a/packages/system/kubeovn/charts/kube-ovn/templates/ic-controller-deploy.yaml b/packages/system/kubeovn/charts/kube-ovn/templates/ic-controller-deploy.yaml index ee3e1461..53ecfa24 100644 --- a/packages/system/kubeovn/charts/kube-ovn/templates/ic-controller-deploy.yaml +++ b/packages/system/kubeovn/charts/kube-ovn/templates/ic-controller-deploy.yaml @@ -40,7 +40,11 @@ spec: topologyKey: kubernetes.io/hostname priorityClassName: system-cluster-critical serviceAccountName: ovn + automountServiceAccountToken: true hostNetwork: true + securityContext: + seccompProfile: + type: RuntimeDefault initContainers: - name: hostpath-init image: {{ .Values.global.registry.address }}/{{ .Values.global.images.kubeovn.repository }}:{{ .Values.global.images.kubeovn.tag }} diff --git a/packages/system/kubeovn/charts/kube-ovn/templates/kube-ovn-crd.yaml b/packages/system/kubeovn/charts/kube-ovn/templates/kube-ovn-crd.yaml index 3bddfbe1..78ac7d38 100644 --- a/packages/system/kubeovn/charts/kube-ovn/templates/kube-ovn-crd.yaml +++ b/packages/system/kubeovn/charts/kube-ovn/templates/kube-ovn-crd.yaml @@ -1200,6 +1200,52 @@ spec: required: - key - operator + tolerations: + description: optional tolerations applied to the workload pods + items: + description: |- + The pod this Toleration is attached to tolerates any taint that matches + the triple using the matching operator . + properties: + effect: + description: |- + Effect indicates the taint effect to match. Empty means match all taint effects. + When specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + enum: + - NoSchedule + - PreferNoSchedule + - NoExecute + key: + description: |- + Key is the taint key that the toleration applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; this combination means to match all values and all keys. + type: string + operator: + description: |- + Operator represents a key's relationship to the value. + Valid operators are Exists and Equal. Defaults to Equal. + Exists is equivalent to wildcard for value, so that a pod can + tolerate all taints of a particular category. + type: string + enum: + - Exists + - Equal + tolerationSeconds: + description: |- + TolerationSeconds represents the period of time the toleration (which must be + of effect NoExecute, otherwise this field is ignored) tolerates the taint. By default, + it is not set, which means tolerate the taint forever (do not evict). Zero and + negative values will be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: |- + Value is the taint value the toleration matches to. + If the operator is Exists, the value should be empty, otherwise just a regular string. + type: string + type: object + type: array --- apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition @@ -2871,6 +2917,8 @@ spec: type: array items: type: string + autoCreateVlanSubinterfaces: + type: boolean required: - defaultInterface status: diff --git a/packages/system/kubeovn/charts/kube-ovn/templates/monitor-deploy.yaml b/packages/system/kubeovn/charts/kube-ovn/templates/monitor-deploy.yaml index e4c3322c..dc4eac22 100644 --- a/packages/system/kubeovn/charts/kube-ovn/templates/monitor-deploy.yaml +++ b/packages/system/kubeovn/charts/kube-ovn/templates/monitor-deploy.yaml @@ -37,7 +37,11 @@ spec: topologyKey: kubernetes.io/hostname priorityClassName: system-cluster-critical serviceAccountName: kube-ovn-app + automountServiceAccountToken: true hostNetwork: true + securityContext: + seccompProfile: + type: RuntimeDefault initContainers: - name: hostpath-init image: {{ .Values.global.registry.address }}/{{ .Values.global.images.kubeovn.repository }}:{{ .Values.global.images.kubeovn.tag }} diff --git a/packages/system/kubeovn/charts/kube-ovn/templates/ovn-dpdk-ds.yaml b/packages/system/kubeovn/charts/kube-ovn/templates/ovn-dpdk-ds.yaml index 9a1d591f..330c9b6f 100644 --- a/packages/system/kubeovn/charts/kube-ovn/templates/ovn-dpdk-ds.yaml +++ b/packages/system/kubeovn/charts/kube-ovn/templates/ovn-dpdk-ds.yaml @@ -27,8 +27,12 @@ spec: - operator: Exists priorityClassName: system-node-critical serviceAccountName: ovn-ovs + automountServiceAccountToken: true hostNetwork: true hostPID: true + securityContext: + seccompProfile: + type: RuntimeDefault containers: - name: openvswitch image: {{ .Values.global.registry.address }}/{{ .Values.global.images.kubeovn.repository }}:{{ .Values.DPDK_IMAGE_TAG }} diff --git a/packages/system/kubeovn/charts/kube-ovn/templates/ovn-sa.yaml b/packages/system/kubeovn/charts/kube-ovn/templates/ovn-sa.yaml index 1e5e9b5c..744b3b90 100644 --- a/packages/system/kubeovn/charts/kube-ovn/templates/ovn-sa.yaml +++ b/packages/system/kubeovn/charts/kube-ovn/templates/ovn-sa.yaml @@ -3,6 +3,7 @@ kind: ServiceAccount metadata: name: ovn namespace: {{ .Values.namespace }} +automountServiceAccountToken: false {{- if .Values.global.registry.imagePullSecrets }} imagePullSecrets: {{- range $index, $secret := .Values.global.registry.imagePullSecrets }} @@ -18,6 +19,7 @@ kind: ServiceAccount metadata: name: ovn-ovs namespace: {{ .Values.namespace }} +automountServiceAccountToken: false {{- if .Values.global.registry.imagePullSecrets }} imagePullSecrets: {{- range $index, $secret := .Values.global.registry.imagePullSecrets }} @@ -33,6 +35,7 @@ kind: ServiceAccount metadata: name: kube-ovn-cni namespace: {{ .Values.namespace }} +automountServiceAccountToken: false {{- if .Values.global.registry.imagePullSecrets }} imagePullSecrets: {{- range $index, $secret := .Values.global.registry.imagePullSecrets }} @@ -48,6 +51,7 @@ kind: ServiceAccount metadata: name: kube-ovn-app namespace: {{ .Values.namespace }} +automountServiceAccountToken: false {{- if .Values.global.registry.imagePullSecrets }} imagePullSecrets: {{- range $index, $secret := .Values.global.registry.imagePullSecrets }} diff --git a/packages/system/kubeovn/charts/kube-ovn/templates/ovncni-ds.yaml b/packages/system/kubeovn/charts/kube-ovn/templates/ovncni-ds.yaml index 947ec454..c68e041d 100644 --- a/packages/system/kubeovn/charts/kube-ovn/templates/ovncni-ds.yaml +++ b/packages/system/kubeovn/charts/kube-ovn/templates/ovncni-ds.yaml @@ -26,8 +26,12 @@ spec: operator: Exists priorityClassName: system-node-critical serviceAccountName: kube-ovn-cni + automountServiceAccountToken: true hostNetwork: true hostPID: true + securityContext: + seccompProfile: + type: RuntimeDefault initContainers: - name: hostpath-init image: {{ .Values.global.registry.address }}/{{ .Values.global.images.kubeovn.repository }}:{{ .Values.global.images.kubeovn.tag }} @@ -35,7 +39,9 @@ spec: command: - sh - -xec - - iptables -V + - | + chmod +t /usr/local/sbin + iptables -V securityContext: allowPrivilegeEscalation: true capabilities: @@ -60,16 +66,21 @@ spec: imagePullPolicy: {{ .Values.image.pullPolicy }} command: - /kube-ovn/install-cni.sh - - --cni-conf-dir={{ .Values.cni_conf.CNI_CONF_DIR }} + - --cni-conf-dir={{ .Values.cni_conf.MOUNT_CNI_CONF_DIR }} - --cni-conf-file={{ .Values.cni_conf.CNI_CONF_FILE }} - --cni-conf-name={{- .Values.cni_conf.CNI_CONFIG_PRIORITY -}}-kube-ovn.conflist + env: + - name: POD_IPS + valueFrom: + fieldRef: + fieldPath: status.podIPs securityContext: runAsUser: 0 privileged: true volumeMounts: - mountPath: /opt/cni/bin name: cni-bin - - mountPath: /etc/cni/net.d + - mountPath: {{ .Values.cni_conf.MOUNT_CNI_CONF_DIR }} name: cni-conf {{- if .Values.cni_conf.MOUNT_LOCAL_BIN_DIR }} - mountPath: /usr/local/bin diff --git a/packages/system/kubeovn/charts/kube-ovn/templates/ovsovn-ds.yaml b/packages/system/kubeovn/charts/kube-ovn/templates/ovsovn-ds.yaml index 17743d5f..7146ec71 100644 --- a/packages/system/kubeovn/charts/kube-ovn/templates/ovsovn-ds.yaml +++ b/packages/system/kubeovn/charts/kube-ovn/templates/ovsovn-ds.yaml @@ -34,8 +34,12 @@ spec: operator: Exists priorityClassName: system-node-critical serviceAccountName: ovn-ovs + automountServiceAccountToken: true hostNetwork: true hostPID: true + securityContext: + seccompProfile: + type: RuntimeDefault initContainers: - name: hostpath-init image: {{ .Values.global.registry.address }}/{{ .Values.global.images.kubeovn.repository }}:{{ .Values.global.images.kubeovn.tag }} @@ -44,6 +48,7 @@ spec: - sh - -xec - | + chmod +t /usr/local/sbin chown -R nobody: /var/run/ovn /var/log/ovn /etc/openvswitch /var/run/openvswitch /var/log/openvswitch iptables -V {{- if not .Values.DISABLE_MODULES_MANAGEMENT }} diff --git a/packages/system/kubeovn/charts/kube-ovn/templates/pinger-ds.yaml b/packages/system/kubeovn/charts/kube-ovn/templates/pinger-ds.yaml index 66a34853..fbc82171 100644 --- a/packages/system/kubeovn/charts/kube-ovn/templates/pinger-ds.yaml +++ b/packages/system/kubeovn/charts/kube-ovn/templates/pinger-ds.yaml @@ -28,7 +28,11 @@ spec: - key: CriticalAddonsOnly operator: Exists serviceAccountName: kube-ovn-app - hostPID: true + automountServiceAccountToken: true + hostPID: false + securityContext: + seccompProfile: + type: RuntimeDefault initContainers: - name: hostpath-init image: {{ .Values.global.registry.address }}/{{ .Values.global.images.kubeovn.repository }}:{{ .Values.global.images.kubeovn.tag }} diff --git a/packages/system/kubeovn/charts/kube-ovn/templates/post-delete-hook.yaml b/packages/system/kubeovn/charts/kube-ovn/templates/post-delete-hook.yaml index a4c0d618..682b5a96 100644 --- a/packages/system/kubeovn/charts/kube-ovn/templates/post-delete-hook.yaml +++ b/packages/system/kubeovn/charts/kube-ovn/templates/post-delete-hook.yaml @@ -9,6 +9,7 @@ metadata: "helm.sh/hook": post-delete "helm.sh/hook-weight": "1" "helm.sh/hook-delete-policy": hook-succeeded +automountServiceAccountToken: false --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -102,8 +103,11 @@ spec: hostNetwork: true nodeSelector: kubernetes.io/os: "linux" - serviceAccount: kube-ovn-post-delete-hook serviceAccountName: kube-ovn-post-delete-hook + automountServiceAccountToken: true + securityContext: + seccompProfile: + type: RuntimeDefault containers: - name: remove-subnet-finalizer image: "{{ .Values.global.registry.address}}/{{ .Values.global.images.kubeovn.repository }}:{{ .Values.global.images.kubeovn.tag }}" diff --git a/packages/system/kubeovn/charts/kube-ovn/templates/upgrade-ovs-ovn.yaml b/packages/system/kubeovn/charts/kube-ovn/templates/upgrade-ovs-ovn.yaml index fc5ac4ba..ab646e03 100644 --- a/packages/system/kubeovn/charts/kube-ovn/templates/upgrade-ovs-ovn.yaml +++ b/packages/system/kubeovn/charts/kube-ovn/templates/upgrade-ovs-ovn.yaml @@ -11,6 +11,7 @@ metadata: "helm.sh/hook": post-upgrade "helm.sh/hook-weight": "1" "helm.sh/hook-delete-policy": hook-succeeded +automountServiceAccountToken: false --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -133,8 +134,11 @@ spec: hostNetwork: true nodeSelector: kubernetes.io/os: "linux" - serviceAccount: ovs-ovn-upgrade serviceAccountName: ovs-ovn-upgrade + automountServiceAccountToken: true + securityContext: + seccompProfile: + type: RuntimeDefault containers: - name: ovs-ovn-upgrade image: "{{ .Values.global.registry.address}}/{{ .Values.global.images.kubeovn.repository }}:{{ .Values.global.images.kubeovn.tag }}" diff --git a/packages/system/kubeovn/charts/kube-ovn/values.yaml b/packages/system/kubeovn/charts/kube-ovn/values.yaml index 3652386d..430ea428 100644 --- a/packages/system/kubeovn/charts/kube-ovn/values.yaml +++ b/packages/system/kubeovn/charts/kube-ovn/values.yaml @@ -9,7 +9,7 @@ global: kubeovn: repository: kube-ovn vpcRepository: vpc-nat-gateway - tag: v1.14.11 + tag: v1.14.25 support_arm: true thirdparty: true @@ -111,6 +111,7 @@ debug: cni_conf: CNI_CONFIG_PRIORITY: "01" CNI_CONF_DIR: "/etc/cni/net.d" + MOUNT_CNI_CONF_DIR: "/etc/cni/net.d" CNI_BIN_DIR: "/opt/cni/bin" CNI_CONF_FILE: "/kube-ovn/01-kube-ovn.conflist" LOCAL_BIN_DIR: "/usr/local/bin" diff --git a/packages/system/kubeovn/values.yaml b/packages/system/kubeovn/values.yaml index 89f20e7c..700960ee 100644 --- a/packages/system/kubeovn/values.yaml +++ b/packages/system/kubeovn/values.yaml @@ -65,4 +65,4 @@ global: images: kubeovn: repository: kubeovn - tag: v1.14.11@sha256:0e3e9db960a9600d58c33c0787cabd0e9bf263930fd8c9fe65417e258c383d01 + tag: v1.14.25@sha256:d0b29daaf36e81cac0f9fb15d0ea6b1b49f1abba81a14c73b88a2e60ffcc5978 From a2a07471427207d2204dd1b9323dd196a3664e2d Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Thu, 8 Jan 2026 12:13:24 +0100 Subject: [PATCH 003/125] fix(linstor): update skip-adjust-when-device-inaccessible patch Add physical device path existence check to prevent race condition where lsblk is called before kernel creates DRBD device node after drbdadm adjust. This fixes issue where resources ended up in Unknown state after satellite restart because: - drbdadm adjust completes successfully (brings devices up) - updateDiscGran() immediately tries lsblk - /dev/drbd* doesn't exist yet (kernel hasn't created node) - lsblk fails with "not a block device" exit code 32 - StorageException interrupts DeviceManager cycle - Device remains in incomplete state The patch now checks Files.exists(devicePath) before lsblk, allowing the check to be retried in next cycle when device node is available. Upstream: https://github.com/LINBIT/linstor-server/pull/471 Co-Authored-By: Claude Signed-off-by: Andrei Kvapil --- .../skip-adjust-when-device-inaccessible.diff | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/packages/system/linstor/images/piraeus-server/patches/skip-adjust-when-device-inaccessible.diff b/packages/system/linstor/images/piraeus-server/patches/skip-adjust-when-device-inaccessible.diff index 09e7ccf9..18399c41 100644 --- a/packages/system/linstor/images/piraeus-server/patches/skip-adjust-when-device-inaccessible.diff +++ b/packages/system/linstor/images/piraeus-server/patches/skip-adjust-when-device-inaccessible.diff @@ -1,3 +1,28 @@ +diff --git a/satellite/src/main/java/com/linbit/linstor/core/devmgr/DeviceHandlerImpl.java b/satellite/src/main/java/com/linbit/linstor/core/devmgr/DeviceHandlerImpl.java +index abc123def..def456abc 100644 +--- a/satellite/src/main/java/com/linbit/linstor/core/devmgr/DeviceHandlerImpl.java ++++ b/satellite/src/main/java/com/linbit/linstor/core/devmgr/DeviceHandlerImpl.java +@@ -83,6 +83,8 @@ import java.util.TreeMap; + import java.util.TreeSet; + import java.util.concurrent.atomic.AtomicBoolean; + import java.util.function.Function; ++import java.nio.file.Files; ++import java.nio.file.Paths; + + @Singleton + public class DeviceHandlerImpl implements DeviceHandler +@@ -1646,7 +1648,10 @@ public class DeviceHandlerImpl implements DeviceHandler + private void updateDiscGran(VlmProviderObject vlmData) throws DatabaseException, StorageException + { + String devicePath = vlmData.getDevicePath(); +- if (devicePath != null && vlmData.exists()) ++ // Check if device path physically exists before calling lsblk ++ // This is important for DRBD devices which might be temporarily unavailable during adjust ++ // (drbdadm adjust brings devices down/up, and kernel might not have created the device node yet) ++ if (devicePath != null && vlmData.exists() && Files.exists(Paths.get(devicePath))) + { + if (vlmData.getDiscGran() == VlmProviderObject.UNINITIALIZED_SIZE) + { diff --git a/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java b/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java index 01967a3..871d830 100644 --- a/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java From 407e2f2930a75e7ab32ca299d99d5fd8a230ce61 Mon Sep 17 00:00:00 2001 From: nbykov0 <166552198+nbykov0@users.noreply.github.com> Date: Thu, 8 Jan 2026 20:48:28 +0300 Subject: [PATCH 004/125] [multus] Remove memory limit Signed-off-by: nbykov0 <166552198+nbykov0@users.noreply.github.com> --- packages/system/multus/patches/customize-deployment.patch | 3 +-- packages/system/multus/templates/multus-daemonset-thick.yml | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/system/multus/patches/customize-deployment.patch b/packages/system/multus/patches/customize-deployment.patch index 7ea3aeeb..b62d57e7 100644 --- a/packages/system/multus/patches/customize-deployment.patch +++ b/packages/system/multus/patches/customize-deployment.patch @@ -34,7 +34,7 @@ labels: tier: node app: multus -@@ -159,10 +159,10 @@ spec: +@@ -159,10 +159,9 @@ spec: resources: requests: cpu: "100m" @@ -43,7 +43,6 @@ limits: cpu: "100m" - memory: "50Mi" -+ memory: "900Mi" securityContext: privileged: true terminationMessagePolicy: FallbackToLogsOnError diff --git a/packages/system/multus/templates/multus-daemonset-thick.yml b/packages/system/multus/templates/multus-daemonset-thick.yml index ba7eedfb..2f00de85 100644 --- a/packages/system/multus/templates/multus-daemonset-thick.yml +++ b/packages/system/multus/templates/multus-daemonset-thick.yml @@ -162,7 +162,6 @@ spec: memory: "100Mi" limits: cpu: "100m" - memory: "900Mi" securityContext: privileged: true terminationMessagePolicy: FallbackToLogsOnError From 1c7c3b221fbddcfbbcc427555d134dfb18f9e229 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Wed, 7 Jan 2026 21:05:51 +0100 Subject: [PATCH 005/125] feat(linstor): add linstor-csi image build with RWX validation Add custom linstor-csi image build to packages/system/linstor: - Add Dockerfile based on upstream linstor-csi - Import patch from upstream PR #403 for RWX block volume validation (prevents misuse of allow-two-primaries in KubeVirt live migration) - Update Makefile to build both piraeus-server and linstor-csi images - Configure LinstorCluster CR to use custom linstor-csi image in CSI controller and node pods The RWX validation patch ensures that RWX block volumes with allow-two-primaries are only used by pods belonging to the same KubeVirt VM during live migration. Upstream PR: https://github.com/piraeusdatastore/linstor-csi/pull/403 Co-Authored-By: Claude Signed-off-by: Andrei Kvapil --- packages/system/linstor/Makefile | 20 +- .../linstor/images/linstor-csi/Dockerfile | 36 + .../patches/001-rwx-validation.diff | 652 ++++++++++++++++++ .../system/linstor/templates/cluster.yaml | 18 + packages/system/linstor/values.yaml | 5 + 5 files changed, 730 insertions(+), 1 deletion(-) create mode 100644 packages/system/linstor/images/linstor-csi/Dockerfile create mode 100644 packages/system/linstor/images/linstor-csi/patches/001-rwx-validation.diff diff --git a/packages/system/linstor/Makefile b/packages/system/linstor/Makefile index da895085..b5d7c227 100644 --- a/packages/system/linstor/Makefile +++ b/packages/system/linstor/Makefile @@ -5,8 +5,11 @@ include ../../../scripts/common-envs.mk include ../../../scripts/package.mk LINSTOR_VERSION ?= 1.32.3 +LINSTOR_CSI_VERSION ?= v1.10.5 -image: +image: image-piraeus-server image-linstor-csi + +image-piraeus-server: docker buildx build images/piraeus-server \ --build-arg LINSTOR_VERSION=$(LINSTOR_VERSION) \ --build-arg K8S_AWAIT_ELECTION_VERSION=v0.4.2 \ @@ -21,3 +24,18 @@ image: TAG="$(call settag,$(LINSTOR_VERSION))@$$(yq e '."containerimage.digest"' images/piraeus-server.json -o json -r)" \ yq -i '.piraeusServer.image.tag = strenv(TAG)' values.yaml rm -f images/piraeus-server.json + +image-linstor-csi: + docker buildx build images/linstor-csi \ + --build-arg VERSION=$(LINSTOR_CSI_VERSION) \ + --tag $(REGISTRY)/linstor-csi:$(call settag,$(LINSTOR_CSI_VERSION)) \ + --tag $(REGISTRY)/linstor-csi:$(call settag,$(LINSTOR_CSI_VERSION)-$(TAG)) \ + --cache-from type=registry,ref=$(REGISTRY)/linstor-csi:latest \ + --cache-to type=inline \ + --metadata-file images/linstor-csi.json \ + $(BUILDX_ARGS) + REPOSITORY="$(REGISTRY)/linstor-csi" \ + yq -i '.linstorCSI.image.repository = strenv(REPOSITORY)' values.yaml + TAG="$(call settag,$(LINSTOR_CSI_VERSION))@$$(yq e '."containerimage.digest"' images/linstor-csi.json -o json -r)" \ + yq -i '.linstorCSI.image.tag = strenv(TAG)' values.yaml + rm -f images/linstor-csi.json diff --git a/packages/system/linstor/images/linstor-csi/Dockerfile b/packages/system/linstor/images/linstor-csi/Dockerfile new file mode 100644 index 00000000..6cfd44aa --- /dev/null +++ b/packages/system/linstor/images/linstor-csi/Dockerfile @@ -0,0 +1,36 @@ +FROM golang:1.25 AS builder + +ARG VERSION=v1.10.5 +ARG LINSTOR_WAIT_UNTIL_VERSION=v0.3.1 +ARG TARGETARCH +ARG TARGETOS + +WORKDIR /src + +RUN curl -sSL https://github.com/piraeusdatastore/linstor-csi/archive/refs/tags/${VERSION}.tar.gz | tar -xzvf- --strip=1 + +COPY patches /patches +RUN git apply /patches/*.diff + +RUN go mod download + +RUN GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 \ + go build \ + -a \ + -ldflags "-X github.com/piraeusdatastore/linstor-csi/pkg/driver.Version=$VERSION -extldflags -static" \ + -o /linstor-csi \ + ./cmd/linstor-csi/linstor-csi.go + +RUN curl -fsSL https://github.com/LINBIT/linstor-wait-until/releases/download/$LINSTOR_WAIT_UNTIL_VERSION/linstor-wait-until-$LINSTOR_WAIT_UNTIL_VERSION-$TARGETOS-$TARGETARCH.tar.gz | tar xvzC / + +FROM debian:trixie-slim + +RUN apt-get update && apt-get install -y --no-install-recommends \ + xfsprogs e2fsprogs nfs-common \ + && apt-get clean && rm -rf /var/lib/apt/lists/* \ + && ln -sf /proc/mounts /etc/mtab + +COPY --from=builder /linstor-csi / +COPY --from=builder /linstor-wait-until /linstor-wait-until + +ENTRYPOINT ["/linstor-csi"] diff --git a/packages/system/linstor/images/linstor-csi/patches/001-rwx-validation.diff b/packages/system/linstor/images/linstor-csi/patches/001-rwx-validation.diff new file mode 100644 index 00000000..f6f0d83d --- /dev/null +++ b/packages/system/linstor/images/linstor-csi/patches/001-rwx-validation.diff @@ -0,0 +1,652 @@ +diff --git a/pkg/driver/driver.go b/pkg/driver/driver.go +index bea69a8..848bd5b 100644 +--- a/pkg/driver/driver.go ++++ b/pkg/driver/driver.go +@@ -401,6 +401,16 @@ func (d Driver) NodePublishVolume(ctx context.Context, req *csi.NodePublishVolum + } + } + ++ // Validate RWX block volumes on node side using local filesystem check ++ // This provides protection for the edge case where two pods from different VMs land on the same node ++ if req.GetVolumeCapability().GetBlock() != nil && ++ req.GetVolumeCapability().GetAccessMode().GetMode() == csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER { ++ if err := d.validateRWXBlockOnNode(ctx, req.GetVolumeId(), req.GetTargetPath()); err != nil { ++ return nil, status.Errorf(codes.FailedPrecondition, ++ "NodePublishVolume failed for %s: %v", req.GetVolumeId(), err) ++ } ++ } ++ + if block := req.GetVolumeCapability().GetBlock(); block != nil { + volCtx.MountOptions = []string{"bind"} + } +@@ -707,6 +717,255 @@ func (d Driver) DeleteVolume(ctx context.Context, req *csi.DeleteVolumeRequest) + return &csi.DeleteVolumeResponse{}, nil + } + ++// KubeVirtVMLabel is the label that KubeVirt adds to pods to identify the VM they belong to. ++const KubeVirtVMLabel = "vm.kubevirt.io/name" ++ ++// KubeVirtHotplugDiskLabel is the label that KubeVirt adds to hotplug disk pods. ++const KubeVirtHotplugDiskLabel = "kubevirt.io" ++ ++// podGVR is the GroupVersionResource for pods. ++var podGVR = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"} ++ ++// pvGVR is the GroupVersionResource for persistent volumes. ++var pvGVR = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "persistentvolumes"} ++ ++// getVMNameFromPod extracts the VM name from a pod, handling both regular virt-launcher pods ++// and hotplug disk pods (which reference the virt-launcher pod via ownerReferences). ++func (d Driver) getVMNameFromPod(ctx context.Context, pod *unstructured.Unstructured) (string, error) { ++ labels := pod.GetLabels() ++ if labels == nil { ++ return "", nil ++ } ++ ++ // Direct case: pod has vm.kubevirt.io/name label (virt-launcher pod) ++ if vmName, ok := labels[KubeVirtVMLabel]; ok && vmName != "" { ++ return vmName, nil ++ } ++ ++ // Hotplug disk case: pod has kubevirt.io: hotplug-disk label ++ // Follow ownerReferences to find the virt-launcher pod ++ if hotplugValue, ok := labels[KubeVirtHotplugDiskLabel]; ok && hotplugValue == "hotplug-disk" { ++ ownerRefs := pod.GetOwnerReferences() ++ for _, owner := range ownerRefs { ++ if owner.Kind != "Pod" || owner.Controller == nil || !*owner.Controller { ++ continue ++ } ++ ++ // Get the owner pod (virt-launcher) ++ ownerPod, err := d.kubeClient.Resource(podGVR).Namespace(pod.GetNamespace()).Get(ctx, owner.Name, metav1.GetOptions{}) ++ if err != nil { ++ return "", fmt.Errorf("failed to get owner pod %s: %w", owner.Name, err) ++ } ++ ++ // Extract VM name from owner pod ++ ownerLabels := ownerPod.GetLabels() ++ if ownerLabels != nil { ++ if vmName, ok := ownerLabels[KubeVirtVMLabel]; ok && vmName != "" { ++ d.log.WithFields(logrus.Fields{ ++ "hotplugPod": pod.GetName(), ++ "virtLauncher": owner.Name, ++ "vmName": vmName, ++ }).Debug("resolved VM name from hotplug disk pod via owner reference") ++ ++ return vmName, nil ++ } ++ } ++ ++ return "", fmt.Errorf("owner pod %s does not have %s label", owner.Name, KubeVirtVMLabel) ++ } ++ ++ return "", fmt.Errorf("hotplug disk pod %s has no controller owner reference", pod.GetName()) ++ } ++ ++ return "", nil ++} ++ ++// validateRWXBlockAttachment checks that RWX block volumes are only used by pods belonging to the same VM. ++// This prevents misuse of allow-two-primaries while still permitting live migration. ++// Returns the VM name if validation passes, or an error if: ++// - Multiple pods from different VMs are trying to use the same volume ++// - A pod without the KubeVirt VM label is trying to use a volume already attached elsewhere (strict mode) ++// Returns empty string for VM name when no pods are using the volume or validation is skipped. ++func (d Driver) validateRWXBlockAttachment(ctx context.Context, volumeID string) (string, error) { ++ d.log.WithField("volumeID", volumeID).Info("validateRWXBlockAttachment called") ++ ++ if d.kubeClient == nil { ++ // Not running in Kubernetes, skip validation ++ d.log.Warn("validateRWXBlockAttachment: kubeClient is nil, skipping validation") ++ return "", nil ++ } ++ ++ // Get PV to find PVC reference (volumeID == PV name in CSI) ++ pv, err := d.kubeClient.Resource(pvGVR).Get(ctx, volumeID, metav1.GetOptions{}) ++ if err != nil { ++ d.log.WithError(err).Warn("cannot validate RWX attachment: failed to get PV") ++ return "", nil ++ } ++ ++ // Extract claimRef from PV ++ claimRef, found, _ := unstructured.NestedMap(pv.Object, "spec", "claimRef") ++ if !found { ++ d.log.Warn("cannot validate RWX attachment: PV has no claimRef") ++ return "", nil ++ } ++ ++ pvcName, _, _ := unstructured.NestedString(claimRef, "name") ++ pvcNamespace, _, _ := unstructured.NestedString(claimRef, "namespace") ++ ++ if pvcNamespace == "" || pvcName == "" { ++ d.log.Warn("cannot validate RWX attachment: PVC name or namespace is empty in claimRef") ++ return "", nil ++ } ++ ++ // List all pods in the namespace ++ podList, err := d.kubeClient.Resource(podGVR).Namespace(pvcNamespace).List(ctx, metav1.ListOptions{}) ++ if err != nil { ++ return "", fmt.Errorf("failed to list pods in namespace %s: %w", pvcNamespace, err) ++ } ++ ++ // Filter pods that use this PVC and are in a running/pending state ++ type podInfo struct { ++ name string ++ vmName string ++ } ++ ++ var podsUsingPVC []podInfo ++ ++ for _, item := range podList.Items { ++ // Get pod phase from status ++ phase, _, _ := unstructured.NestedString(item.Object, "status", "phase") ++ if phase == "Succeeded" || phase == "Failed" { ++ continue ++ } ++ ++ // Check if pod uses the PVC ++ volumes, found, _ := unstructured.NestedSlice(item.Object, "spec", "volumes") ++ if !found { ++ continue ++ } ++ ++ for _, vol := range volumes { ++ volMap, ok := vol.(map[string]interface{}) ++ if !ok { ++ continue ++ } ++ ++ pvc, found, _ := unstructured.NestedMap(volMap, "persistentVolumeClaim") ++ if !found { ++ continue ++ } ++ ++ claimName, _, _ := unstructured.NestedString(pvc, "claimName") ++ if claimName == pvcName { ++ // Extract VM name, handling both regular and hotplug disk pods ++ vmName, err := d.getVMNameFromPod(ctx, &item) ++ if err != nil { ++ d.log.WithError(err).WithField("pod", item.GetName()).Warn("failed to get VM name from pod") ++ // Continue with empty vmName - will be caught by strict mode check ++ vmName = "" ++ } ++ ++ podsUsingPVC = append(podsUsingPVC, podInfo{ ++ name: item.GetName(), ++ vmName: vmName, ++ }) ++ ++ break ++ } ++ } ++ } ++ ++ // If 0 or 1 pod uses the PVC, no conflict possible ++ if len(podsUsingPVC) <= 1 { ++ // Return VM name if there's exactly one pod ++ if len(podsUsingPVC) == 1 { ++ d.log.WithFields(logrus.Fields{ ++ "volumeID": volumeID, ++ "vmName": podsUsingPVC[0].vmName, ++ "podCount": 1, ++ "pvcNamespace": pvcNamespace, ++ "pvcName": pvcName, ++ }).Info("validateRWXBlockAttachment: single pod found, returning VM name") ++ ++ return podsUsingPVC[0].vmName, nil ++ } ++ ++ d.log.WithFields(logrus.Fields{ ++ "volumeID": volumeID, ++ "pvcNamespace": pvcNamespace, ++ "pvcName": pvcName, ++ }).Info("validateRWXBlockAttachment: no pods found using PVC") ++ ++ return "", nil ++ } ++ ++ // Check that all pods belong to the same VM ++ var vmName string ++ for _, pod := range podsUsingPVC { ++ if pod.vmName == "" { ++ // Strict mode: if any pod doesn't have the KubeVirt label and there are multiple pods, ++ // deny the attachment ++ return "", fmt.Errorf("RWX block volume %s/%s is used by multiple pods but pod %s does not have the %s label; "+ ++ "RWX block volumes with allow-two-primaries are only supported for KubeVirt live migration", ++ pvcNamespace, pvcName, pod.name, KubeVirtVMLabel) ++ } ++ ++ if vmName == "" { ++ vmName = pod.vmName ++ } else if vmName != pod.vmName { ++ // Different VMs are trying to use the same volume ++ return "", fmt.Errorf("RWX block volume %s/%s is being used by pods from different VMs (%s and %s); "+ ++ "this is not supported - RWX block volumes with allow-two-primaries are only for live migration of a single VM", ++ pvcNamespace, pvcName, vmName, pod.vmName) ++ } ++ } ++ ++ d.log.WithFields(logrus.Fields{ ++ "pvcNamespace": pvcNamespace, ++ "pvcName": pvcName, ++ "vmName": vmName, ++ "podCount": len(podsUsingPVC), ++ }).Debug("RWX block attachment validated: all pods belong to the same VM (likely live migration)") ++ ++ return vmName, nil ++} ++ ++// validateRWXBlockOnNode performs node-side validation of RWX block volumes using local filesystem check. ++// This provides protection against the edge case where two pods from different VMs land on the same node. ++// Since ControllerPublishVolume is called only once per (volumeID, nodeID) pair, not per pod, ++// we need to check if the volume is already mounted for another pod on this node. ++func (d Driver) validateRWXBlockOnNode(ctx context.Context, volumeID, targetPath string) error { ++ // Extract base directory for this volume (contains subdirectories per pod UID) ++ baseDir := filepath.Dir(targetPath) ++ ++ // List existing mounts for this volume ++ entries, err := os.ReadDir(baseDir) ++ if err != nil { ++ if os.IsNotExist(err) { ++ // Directory doesn't exist yet - first mount ++ return nil ++ } ++ ++ d.log.WithError(err).Warn("cannot check existing mounts for RWX validation") ++ ++ return nil ++ } ++ ++ // If there are already other mounts, block the second one ++ if len(entries) > 0 { ++ d.log.WithFields(logrus.Fields{ ++ "volumeID": volumeID, ++ "existingMounts": len(entries), ++ "baseDir": baseDir, ++ }).Warn("blocking RWX block volume mount: already mounted for another pod on this node") ++ ++ return fmt.Errorf("RWX block volume is already mounted for another pod on this node - " + ++ "multiple pods on the same node sharing a block device is not supported (only for live migration across nodes)") ++ } ++ ++ return nil ++} ++ + // ControllerPublishVolume https://github.com/container-storage-interface/spec/blob/v1.9.0/spec.md#controllerpublishvolume + func (d Driver) ControllerPublishVolume(ctx context.Context, req *csi.ControllerPublishVolumeRequest) (*csi.ControllerPublishVolumeResponse, error) { + if req.GetVolumeId() == "" { +@@ -751,6 +1010,15 @@ func (d Driver) ControllerPublishVolume(ctx context.Context, req *csi.Controller + // ReadWriteMany block volume + rwxBlock := req.VolumeCapability.AccessMode.GetMode() == csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER && req.VolumeCapability.GetBlock() != nil + ++ // Validate RWX block attachment to prevent misuse of allow-two-primaries ++ if rwxBlock { ++ _, err := d.validateRWXBlockAttachment(ctx, req.GetVolumeId()) ++ if err != nil { ++ return nil, status.Errorf(codes.FailedPrecondition, ++ "ControllerPublishVolume failed for %s: %v", req.GetVolumeId(), err) ++ } ++ } ++ + devPath, err := d.Assignments.Attach(ctx, req.GetVolumeId(), req.GetNodeId(), rwxBlock) + if err != nil { + return nil, status.Errorf(codes.Internal, +diff --git a/pkg/driver/rwx_validation_test.go b/pkg/driver/rwx_validation_test.go +new file mode 100644 +index 0000000..92c1046 +--- /dev/null ++++ b/pkg/driver/rwx_validation_test.go +@@ -0,0 +1,353 @@ ++/* ++CSI Driver for Linstor ++Copyright © 2018 LINBIT USA, LLC ++ ++This program is free software; you can redistribute it and/or modify ++it under the terms of the GNU General Public License as published by ++the Free Software Foundation; either version 2 of the License, or ++(at your option) any later version. ++ ++This program is distributed in the hope that it will be useful, ++but WITHOUT ANY WARRANTY; without even the implied warranty of ++MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++GNU General Public License for more details. ++ ++You should have received a copy of the GNU General Public License ++along with this program; if not, see . ++*/ ++ ++package driver ++ ++import ( ++ "context" ++ "testing" ++ ++ "github.com/sirupsen/logrus" ++ "github.com/stretchr/testify/assert" ++ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ++ "k8s.io/apimachinery/pkg/runtime" ++ "k8s.io/apimachinery/pkg/runtime/schema" ++ dynamicfake "k8s.io/client-go/dynamic/fake" ++) ++ ++func TestValidateRWXBlockAttachment(t *testing.T) { ++ testCases := []struct { ++ name string ++ pods []*unstructured.Unstructured ++ pvcName string ++ namespace string ++ expectError bool ++ errorMsg string ++ }{ ++ { ++ name: "no pods using PVC", ++ pods: []*unstructured.Unstructured{}, ++ pvcName: "test-pvc", ++ namespace: "default", ++ expectError: false, ++ }, ++ { ++ name: "single pod using PVC", ++ pods: []*unstructured.Unstructured{ ++ createUnstructuredPod("pod1", "default", "test-pvc", map[string]string{KubeVirtVMLabel: "vm1"}, "Running"), ++ }, ++ pvcName: "test-pvc", ++ namespace: "default", ++ expectError: false, ++ }, ++ { ++ name: "two pods same VM (live migration)", ++ pods: []*unstructured.Unstructured{ ++ createUnstructuredPod("virt-launcher-vm1-abc", "default", "test-pvc", map[string]string{KubeVirtVMLabel: "vm1"}, "Running"), ++ createUnstructuredPod("virt-launcher-vm1-xyz", "default", "test-pvc", map[string]string{KubeVirtVMLabel: "vm1"}, "Running"), ++ }, ++ pvcName: "test-pvc", ++ namespace: "default", ++ expectError: false, ++ }, ++ { ++ name: "two pods different VMs (should fail)", ++ pods: []*unstructured.Unstructured{ ++ createUnstructuredPod("virt-launcher-vm1-abc", "default", "test-pvc", map[string]string{KubeVirtVMLabel: "vm1"}, "Running"), ++ createUnstructuredPod("virt-launcher-vm2-xyz", "default", "test-pvc", map[string]string{KubeVirtVMLabel: "vm2"}, "Running"), ++ }, ++ pvcName: "test-pvc", ++ namespace: "default", ++ expectError: true, ++ errorMsg: "different VMs", ++ }, ++ { ++ name: "pod without KubeVirt label when multiple pods exist (strict mode)", ++ pods: []*unstructured.Unstructured{ ++ createUnstructuredPod("pod1", "default", "test-pvc", map[string]string{KubeVirtVMLabel: "vm1"}, "Running"), ++ createUnstructuredPod("pod2", "default", "test-pvc", map[string]string{}, "Running"), ++ }, ++ pvcName: "test-pvc", ++ namespace: "default", ++ expectError: true, ++ errorMsg: "does not have the vm.kubevirt.io/name label", ++ }, ++ { ++ name: "completed pods should be ignored", ++ pods: []*unstructured.Unstructured{ ++ createUnstructuredPod("pod1", "default", "test-pvc", map[string]string{KubeVirtVMLabel: "vm1"}, "Running"), ++ createUnstructuredPod("pod2", "default", "test-pvc", map[string]string{KubeVirtVMLabel: "vm2"}, "Succeeded"), ++ }, ++ pvcName: "test-pvc", ++ namespace: "default", ++ expectError: false, ++ }, ++ { ++ name: "failed pods should be ignored", ++ pods: []*unstructured.Unstructured{ ++ createUnstructuredPod("pod1", "default", "test-pvc", map[string]string{KubeVirtVMLabel: "vm1"}, "Running"), ++ createUnstructuredPod("pod2", "default", "test-pvc", map[string]string{KubeVirtVMLabel: "vm2"}, "Failed"), ++ }, ++ pvcName: "test-pvc", ++ namespace: "default", ++ expectError: false, ++ }, ++ { ++ name: "pods in different namespace should not conflict", ++ pods: []*unstructured.Unstructured{ ++ createUnstructuredPod("pod1", "default", "test-pvc", map[string]string{KubeVirtVMLabel: "vm1"}, "Running"), ++ createUnstructuredPod("pod2", "other", "test-pvc", map[string]string{KubeVirtVMLabel: "vm2"}, "Running"), ++ }, ++ pvcName: "test-pvc", ++ namespace: "default", ++ expectError: false, ++ }, ++ { ++ name: "pods using different PVCs should not conflict", ++ pods: []*unstructured.Unstructured{ ++ createUnstructuredPod("pod1", "default", "test-pvc", map[string]string{KubeVirtVMLabel: "vm1"}, "Running"), ++ createUnstructuredPod("pod2", "default", "other-pvc", map[string]string{KubeVirtVMLabel: "vm2"}, "Running"), ++ }, ++ pvcName: "test-pvc", ++ namespace: "default", ++ expectError: false, ++ }, ++ { ++ name: "three pods from same VM (multi-node live migration scenario)", ++ pods: []*unstructured.Unstructured{ ++ createUnstructuredPod("virt-launcher-vm1-a", "default", "test-pvc", map[string]string{KubeVirtVMLabel: "vm1"}, "Running"), ++ createUnstructuredPod("virt-launcher-vm1-b", "default", "test-pvc", map[string]string{KubeVirtVMLabel: "vm1"}, "Running"), ++ createUnstructuredPod("virt-launcher-vm1-c", "default", "test-pvc", map[string]string{KubeVirtVMLabel: "vm1"}, "Pending"), ++ }, ++ pvcName: "test-pvc", ++ namespace: "default", ++ expectError: false, ++ }, ++ { ++ name: "hotplug disk pod with virt-launcher (should succeed)", ++ pods: []*unstructured.Unstructured{ ++ createUnstructuredPod("virt-launcher-vm1-abc", "default", "test-pvc", map[string]string{KubeVirtVMLabel: "vm1"}, "Running"), ++ createHotplugDiskPod("hp-volume-xyz", "default", "test-pvc", "virt-launcher-vm1-abc", "Running"), ++ }, ++ pvcName: "test-pvc", ++ namespace: "default", ++ expectError: false, ++ }, ++ { ++ name: "hotplug disks from different VMs (should fail)", ++ pods: []*unstructured.Unstructured{ ++ createUnstructuredPod("virt-launcher-vm1-abc", "default", "test-pvc", map[string]string{KubeVirtVMLabel: "vm1"}, "Running"), ++ createHotplugDiskPod("hp-volume-vm1", "default", "test-pvc", "virt-launcher-vm1-abc", "Running"), ++ createUnstructuredPod("virt-launcher-vm2-xyz", "default", "test-pvc", map[string]string{KubeVirtVMLabel: "vm2"}, "Running"), ++ createHotplugDiskPod("hp-volume-vm2", "default", "test-pvc", "virt-launcher-vm2-xyz", "Running"), ++ }, ++ pvcName: "test-pvc", ++ namespace: "default", ++ expectError: true, ++ errorMsg: "different VMs", ++ }, ++ } ++ ++ for _, tc := range testCases { ++ t.Run(tc.name, func(t *testing.T) { ++ // Create fake dynamic client with test pods and PV ++ scheme := runtime.NewScheme() ++ ++ // Create PV object that references the PVC ++ pv := createUnstructuredPV("test-volume-id", tc.namespace, tc.pvcName) ++ ++ objects := make([]runtime.Object, 0, len(tc.pods)+1) ++ objects = append(objects, pv) ++ ++ for _, pod := range tc.pods { ++ objects = append(objects, pod) ++ } ++ ++ gvrToListKind := map[schema.GroupVersionResource]string{ ++ podGVR: "PodList", ++ pvGVR: "PersistentVolumeList", ++ } ++ client := dynamicfake.NewSimpleDynamicClientWithCustomListKinds(scheme, gvrToListKind, objects...) ++ ++ // Create driver with fake client ++ logger := logrus.NewEntry(logrus.New()) ++ logger.Logger.SetLevel(logrus.DebugLevel) ++ ++ driver := &Driver{ ++ kubeClient: client, ++ log: logger, ++ } ++ ++ // Run validation ++ vmName, err := driver.validateRWXBlockAttachment(context.Background(), "test-volume-id") ++ ++ if tc.expectError { ++ assert.Error(t, err) ++ ++ if tc.errorMsg != "" { ++ assert.Contains(t, err.Error(), tc.errorMsg) ++ } ++ } else { ++ assert.NoError(t, err) ++ // VM name is returned when there are pods using the volume ++ if len(tc.pods) > 0 { ++ assert.NotEmpty(t, vmName) ++ } ++ } ++ }) ++ } ++} ++ ++func TestValidateRWXBlockAttachmentNoKubeClient(t *testing.T) { ++ // When not running in Kubernetes (no client), validation should be skipped ++ logger := logrus.NewEntry(logrus.New()) ++ driver := &Driver{ ++ kubeClient: nil, ++ log: logger, ++ } ++ ++ vmName, err := driver.validateRWXBlockAttachment(context.Background(), "test-volume-id") ++ assert.NoError(t, err) ++ assert.Empty(t, vmName) ++} ++ ++func TestValidateRWXBlockAttachmentPVNotFound(t *testing.T) { ++ // When PV is not found, validation should be skipped with warning ++ scheme := runtime.NewScheme() ++ ++ gvrToListKind := map[schema.GroupVersionResource]string{ ++ podGVR: "PodList", ++ pvGVR: "PersistentVolumeList", ++ } ++ client := dynamicfake.NewSimpleDynamicClientWithCustomListKinds(scheme, gvrToListKind) ++ ++ logger := logrus.NewEntry(logrus.New()) ++ logger.Logger.SetLevel(logrus.DebugLevel) ++ ++ driver := &Driver{ ++ kubeClient: client, ++ log: logger, ++ } ++ ++ vmName, err := driver.validateRWXBlockAttachment(context.Background(), "non-existent-pv") ++ assert.NoError(t, err) ++ assert.Empty(t, vmName) ++} ++ ++// createUnstructuredPod creates an unstructured pod object for testing. ++func createUnstructuredPod(name, namespace, pvcName string, labels map[string]string, phase string) *unstructured.Unstructured { ++ pod := &unstructured.Unstructured{ ++ Object: map[string]interface{}{ ++ "apiVersion": "v1", ++ "kind": "Pod", ++ "metadata": map[string]interface{}{ ++ "name": name, ++ "namespace": namespace, ++ "labels": toStringInterfaceMap(labels), ++ }, ++ "spec": map[string]interface{}{ ++ "volumes": []interface{}{ ++ map[string]interface{}{ ++ "name": "data", ++ "persistentVolumeClaim": map[string]interface{}{ ++ "claimName": pvcName, ++ }, ++ }, ++ }, ++ }, ++ "status": map[string]interface{}{ ++ "phase": phase, ++ }, ++ }, ++ } ++ ++ return pod ++} ++ ++// createUnstructuredPV creates an unstructured PersistentVolume object for testing. ++func createUnstructuredPV(name, pvcNamespace, pvcName string) *unstructured.Unstructured { ++ pv := &unstructured.Unstructured{ ++ Object: map[string]interface{}{ ++ "apiVersion": "v1", ++ "kind": "PersistentVolume", ++ "metadata": map[string]interface{}{ ++ "name": name, ++ }, ++ "spec": map[string]interface{}{ ++ "claimRef": map[string]interface{}{ ++ "name": pvcName, ++ "namespace": pvcNamespace, ++ }, ++ }, ++ }, ++ } ++ ++ return pv ++} ++ ++// toStringInterfaceMap converts map[string]string to map[string]interface{}. ++func toStringInterfaceMap(m map[string]string) map[string]interface{} { ++ result := make(map[string]interface{}) ++ ++ for k, v := range m { ++ result[k] = v ++ } ++ ++ return result ++} ++ ++// createHotplugDiskPod creates a hotplug disk pod that references a virt-launcher pod via ownerReferences. ++func createHotplugDiskPod(name, namespace, pvcName, ownerPodName, phase string) *unstructured.Unstructured { ++ pod := &unstructured.Unstructured{ ++ Object: map[string]interface{}{ ++ "apiVersion": "v1", ++ "kind": "Pod", ++ "metadata": map[string]interface{}{ ++ "name": name, ++ "namespace": namespace, ++ "labels": map[string]interface{}{ ++ "kubevirt.io": "hotplug-disk", ++ }, ++ "ownerReferences": []interface{}{ ++ map[string]interface{}{ ++ "apiVersion": "v1", ++ "kind": "Pod", ++ "name": ownerPodName, ++ "controller": true, ++ "blockOwnerDeletion": true, ++ }, ++ }, ++ }, ++ "spec": map[string]interface{}{ ++ "volumes": []interface{}{ ++ map[string]interface{}{ ++ "name": "data", ++ "persistentVolumeClaim": map[string]interface{}{ ++ "claimName": pvcName, ++ }, ++ }, ++ }, ++ }, ++ "status": map[string]interface{}{ ++ "phase": phase, ++ }, ++ }, ++ } ++ ++ return pod ++} diff --git a/packages/system/linstor/templates/cluster.yaml b/packages/system/linstor/templates/cluster.yaml index bde24726..ba611e17 100644 --- a/packages/system/linstor/templates/cluster.yaml +++ b/packages/system/linstor/templates/cluster.yaml @@ -60,6 +60,24 @@ spec: configMap: name: linstor-plunger defaultMode: 0755 + csiController: + podTemplate: + spec: + initContainers: + - name: linstor-wait-api-online + image: {{ .Values.linstorCSI.image.repository }}:{{ .Values.linstorCSI.image.tag }} + containers: + - name: linstor-csi + image: {{ .Values.linstorCSI.image.repository }}:{{ .Values.linstorCSI.image.tag }} + csiNode: + podTemplate: + spec: + initContainers: + - name: linstor-wait-node-online + image: {{ .Values.linstorCSI.image.repository }}:{{ .Values.linstorCSI.image.tag }} + containers: + - name: linstor-csi + image: {{ .Values.linstorCSI.image.repository }}:{{ .Values.linstorCSI.image.tag }} patches: - target: kind: Deployment diff --git a/packages/system/linstor/values.yaml b/packages/system/linstor/values.yaml index 1a94793b..470cf2e5 100644 --- a/packages/system/linstor/values.yaml +++ b/packages/system/linstor/values.yaml @@ -8,3 +8,8 @@ linstor: enabled: true minutes: 30 allowCleanup: true + +linstorCSI: + image: + repository: ghcr.io/cozystack/cozystack/linstor-csi + tag: latest@sha256:e8329b3e07c47ec73a7d9644535639fd80dbe5c27a7e03181b999ebe072a3a2e From 0b45fbbd63aea45538299419721ba000004009e5 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Fri, 9 Jan 2026 15:24:57 +0100 Subject: [PATCH 006/125] [platform] fix migration for removing fluxcd-operator Signed-off-by: Andrei Kvapil --- scripts/migrations/21 | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/scripts/migrations/21 b/scripts/migrations/21 index 7a367668..abefdc77 100755 --- a/scripts/migrations/21 +++ b/scripts/migrations/21 @@ -3,7 +3,16 @@ set -euo pipefail +for crd in $(kubectl get crd -o name | grep 'fluxcd.io$'); do + kubectl annotate $crd fluxcd.controlplane.io/prune=disabled +done + kubectl delete hr -n cozy-fluxcd fluxcd --ignore-not-found +kubectl delete hr -n cozy-fluxcd fluxcd-operator --ignore-not-found + +for crd in $(kubectl get crd -o name | grep 'fluxcd.io$'); do + kubectl label $crd fluxcd.controlplane.io/name- fluxcd.controlplane.io/namespace- +done # Stamp version kubectl create configmap -n cozy-system cozystack-version \ From 6faa4d6e4d5cd69802ec6338d3b38f327c604c38 Mon Sep 17 00:00:00 2001 From: nbykov0 <166552198+nbykov0@users.noreply.github.com> Date: Fri, 9 Jan 2026 19:48:23 +0300 Subject: [PATCH 007/125] [paas-full] Add multus dependency as other CNIs Signed-off-by: nbykov0 <166552198+nbykov0@users.noreply.github.com> --- packages/core/platform/bundles/paas-full.yaml | 89 ++++++++++--------- 1 file changed, 45 insertions(+), 44 deletions(-) diff --git a/packages/core/platform/bundles/paas-full.yaml b/packages/core/platform/bundles/paas-full.yaml index 6959fed7..d25d885c 100644 --- a/packages/core/platform/bundles/paas-full.yaml +++ b/packages/core/platform/bundles/paas-full.yaml @@ -69,25 +69,25 @@ releases: releaseName: cozystack chart: cozy-cozy-proxy namespace: cozy-system - dependsOn: [cilium,kubeovn] + dependsOn: [cilium,kubeovn,multus] - name: cert-manager-crds releaseName: cert-manager-crds chart: cozy-cert-manager-crds namespace: cozy-cert-manager - dependsOn: [cilium, kubeovn] + dependsOn: [] - name: cozystack-api releaseName: cozystack-api chart: cozy-cozystack-api namespace: cozy-system - dependsOn: [cilium,kubeovn,cozystack-controller] + dependsOn: [cilium,kubeovn,multus,cozystack-controller] - name: cozystack-controller releaseName: cozystack-controller chart: cozy-cozystack-controller namespace: cozy-system - dependsOn: [cilium,kubeovn] + dependsOn: [cilium,kubeovn,multus] {{- if eq (index $cozyConfig.data "telemetry-enabled") "false" }} values: cozystackController: @@ -98,19 +98,19 @@ releases: releaseName: backup-controller chart: cozy-backup-controller namespace: cozy-backup-controller - dependsOn: [cilium,kubeovn] + dependsOn: [cilium,kubeovn,multus] - name: lineage-controller-webhook releaseName: lineage-controller-webhook chart: cozy-lineage-controller-webhook namespace: cozy-system - dependsOn: [cozystack-controller,cilium,kubeovn,cert-manager] + dependsOn: [cozystack-controller,cilium,kubeovn,multus,cert-manager] - name: cozystack-resource-definition-crd releaseName: cozystack-resource-definition-crd chart: cozystack-resource-definition-crd namespace: cozy-system - dependsOn: [cilium,kubeovn,cozystack-api,cozystack-controller] + dependsOn: [cilium,kubeovn,multus,cozystack-api,cozystack-controller] - name: bootbox-rd releaseName: bootbox-rd @@ -266,13 +266,13 @@ releases: releaseName: cert-manager chart: cozy-cert-manager namespace: cozy-cert-manager - dependsOn: [cert-manager-crds] + dependsOn: [cilium,kubeovn,multus,cert-manager-crds] - name: cert-manager-issuers releaseName: cert-manager-issuers chart: cozy-cert-manager-issuers namespace: cozy-cert-manager - dependsOn: [cilium,kubeovn,cert-manager] + dependsOn: [cert-manager] - name: prometheus-operator-crds releaseName: prometheus-operator-crds @@ -284,20 +284,20 @@ releases: releaseName: metrics-server chart: cozy-metrics-server namespace: cozy-monitoring - dependsOn: [cilium,kubeovn,prometheus-operator-crds] + dependsOn: [cilium,kubeovn,multus,prometheus-operator-crds] - name: victoria-metrics-operator releaseName: victoria-metrics-operator chart: cozy-victoria-metrics-operator namespace: cozy-victoria-metrics-operator - dependsOn: [cilium,kubeovn,cert-manager,prometheus-operator-crds] + dependsOn: [cilium,kubeovn,multus,cert-manager,prometheus-operator-crds] - name: monitoring-agents releaseName: monitoring-agents chart: cozy-monitoring-agents namespace: cozy-monitoring privileged: true - dependsOn: [victoria-metrics-operator, vertical-pod-autoscaler-crds, metrics-server] + dependsOn: [victoria-metrics-operator,vertical-pod-autoscaler-crds,metrics-server] values: scrapeRules: etcd: @@ -307,14 +307,14 @@ releases: releaseName: kubevirt-operator chart: cozy-kubevirt-operator namespace: cozy-kubevirt - dependsOn: [cilium,kubeovn,victoria-metrics-operator] + dependsOn: [cilium,kubeovn,multus,victoria-metrics-operator] - name: kubevirt releaseName: kubevirt chart: cozy-kubevirt namespace: cozy-kubevirt privileged: true - dependsOn: [cilium,kubeovn,kubevirt-operator] + dependsOn: [cilium,kubeovn,multus,kubevirt-operator] {{- $cpuAllocationRatio := index $cozyConfig.data "cpu-allocation-ratio" }} {{- if $cpuAllocationRatio }} values: @@ -325,19 +325,19 @@ releases: releaseName: kubevirt-instancetypes chart: cozy-kubevirt-instancetypes namespace: cozy-kubevirt - dependsOn: [cilium,kubeovn,kubevirt-operator,kubevirt] + dependsOn: [cilium,kubeovn,multus,kubevirt-operator,kubevirt] - name: kubevirt-cdi-operator releaseName: kubevirt-cdi-operator chart: cozy-kubevirt-cdi-operator namespace: cozy-kubevirt-cdi - dependsOn: [cilium,kubeovn] + dependsOn: [cilium,kubeovn,multus] - name: kubevirt-cdi releaseName: kubevirt-cdi chart: cozy-kubevirt-cdi namespace: cozy-kubevirt-cdi - dependsOn: [cilium,kubeovn,kubevirt-cdi-operator] + dependsOn: [cilium,kubeovn,multus,kubevirt-cdi-operator] - name: gpu-operator releaseName: gpu-operator @@ -345,7 +345,7 @@ releases: namespace: cozy-gpu-operator privileged: true optional: true - dependsOn: [cilium,kubeovn] + dependsOn: [cilium,kubeovn,multus] valuesFiles: - values.yaml - values-talos.yaml @@ -355,25 +355,25 @@ releases: chart: cozy-metallb namespace: cozy-metallb privileged: true - dependsOn: [cilium,kubeovn] + dependsOn: [cilium,kubeovn,multus] - name: etcd-operator releaseName: etcd-operator chart: cozy-etcd-operator namespace: cozy-etcd-operator - dependsOn: [cilium,kubeovn,cert-manager] + dependsOn: [cilium,kubeovn,multus,cert-manager] - name: grafana-operator releaseName: grafana-operator chart: cozy-grafana-operator namespace: cozy-grafana-operator - dependsOn: [cilium,kubeovn] + dependsOn: [cilium,kubeovn,multus] - name: mariadb-operator releaseName: mariadb-operator chart: cozy-mariadb-operator namespace: cozy-mariadb-operator - dependsOn: [cilium,kubeovn,cert-manager,victoria-metrics-operator] + dependsOn: [cilium,kubeovn,multus,cert-manager,victoria-metrics-operator] values: mariadb-operator: clusterName: {{ $clusterDomain }} @@ -382,13 +382,13 @@ releases: releaseName: postgres-operator chart: cozy-postgres-operator namespace: cozy-postgres-operator - dependsOn: [cilium,kubeovn,cert-manager] + dependsOn: [cilium,kubeovn,multus,cert-manager] - name: kafka-operator releaseName: kafka-operator chart: cozy-kafka-operator namespace: cozy-kafka-operator - dependsOn: [cilium,kubeovn,victoria-metrics-operator] + dependsOn: [cilium,kubeovn,multus,victoria-metrics-operator] values: strimzi-kafka-operator: kubernetesServiceDnsDomain: {{ $clusterDomain }} @@ -397,38 +397,38 @@ releases: releaseName: clickhouse-operator chart: cozy-clickhouse-operator namespace: cozy-clickhouse-operator - dependsOn: [cilium,kubeovn,victoria-metrics-operator] + dependsOn: [cilium,kubeovn,multus,victoria-metrics-operator] - name: foundationdb-operator releaseName: foundationdb-operator chart: cozy-foundationdb-operator namespace: cozy-foundationdb-operator - dependsOn: [cilium,kubeovn,cert-manager] + dependsOn: [cilium,kubeovn,multus,cert-manager] - name: rabbitmq-operator releaseName: rabbitmq-operator chart: cozy-rabbitmq-operator namespace: cozy-rabbitmq-operator - dependsOn: [cilium,kubeovn] + dependsOn: [cilium,kubeovn,multus] - name: redis-operator releaseName: redis-operator chart: cozy-redis-operator namespace: cozy-redis-operator - dependsOn: [cilium,kubeovn] + dependsOn: [cilium,kubeovn,multus] - name: piraeus-operator releaseName: piraeus-operator chart: cozy-piraeus-operator namespace: cozy-linstor - dependsOn: [cilium,kubeovn,cert-manager,victoria-metrics-operator] + dependsOn: [cilium,kubeovn,multus,cert-manager,victoria-metrics-operator] - name: linstor releaseName: linstor chart: cozy-linstor namespace: cozy-linstor privileged: true - dependsOn: [piraeus-operator,cilium,kubeovn,cert-manager,snapshot-controller] + dependsOn: [piraeus-operator,cilium,kubeovn,multus,cert-manager,snapshot-controller] - name: linstor-scheduler releaseName: linstor-scheduler @@ -441,27 +441,27 @@ releases: chart: cozy-nfs-driver namespace: cozy-nfs-driver privileged: true - dependsOn: [cilium,kubeovn] + dependsOn: [cilium,kubeovn,multus] optional: true - name: snapshot-controller releaseName: snapshot-controller chart: cozy-snapshot-controller namespace: cozy-snapshot-controller - dependsOn: [cilium,kubeovn,cert-manager-issuers] + dependsOn: [cilium,kubeovn,multus,cert-manager-issuers] - name: objectstorage-controller releaseName: objectstorage-controller chart: cozy-objectstorage-controller namespace: cozy-objectstorage-controller - dependsOn: [cilium,kubeovn] + dependsOn: [cilium,kubeovn,multus] - name: telepresence releaseName: traffic-manager chart: cozy-telepresence namespace: cozy-telepresence optional: true - dependsOn: [cilium,kubeovn] + dependsOn: [cilium,kubeovn,multus] - name: dashboard releaseName: dashboard @@ -474,6 +474,7 @@ releases: dependsOn: - cilium - kubeovn + - multus {{- if eq $oidcEnabled "true" }} - keycloak-configure {{- end }} @@ -482,56 +483,56 @@ releases: releaseName: kamaji chart: cozy-kamaji namespace: cozy-kamaji - dependsOn: [cilium,kubeovn,cert-manager] + dependsOn: [cilium,kubeovn,multus,cert-manager] - name: capi-operator releaseName: capi-operator chart: cozy-capi-operator namespace: cozy-cluster-api privileged: true - dependsOn: [cilium,kubeovn,cert-manager] + dependsOn: [cilium,kubeovn,multus,cert-manager] - name: capi-providers-bootstrap releaseName: capi-providers-bootstrap chart: cozy-capi-providers-bootstrap namespace: cozy-cluster-api privileged: true - dependsOn: [cilium,kubeovn,capi-operator] + dependsOn: [cilium,kubeovn,multus,capi-operator] - name: capi-providers-core releaseName: capi-providers-core chart: cozy-capi-providers-core namespace: cozy-cluster-api privileged: true - dependsOn: [cilium,kubeovn,capi-operator] + dependsOn: [cilium,kubeovn,multus,capi-operator] - name: capi-providers-cpprovider releaseName: capi-providers-cpprovider chart: cozy-capi-providers-cpprovider namespace: cozy-cluster-api privileged: true - dependsOn: [cilium,kubeovn,capi-operator] + dependsOn: [cilium,kubeovn,multus,capi-operator] - name: capi-providers-infraprovider releaseName: capi-providers-infraprovider chart: cozy-capi-providers-infraprovider namespace: cozy-cluster-api privileged: true - dependsOn: [cilium,kubeovn,capi-operator] + dependsOn: [cilium,kubeovn,multus,capi-operator] - name: external-dns releaseName: external-dns chart: cozy-external-dns namespace: cozy-external-dns optional: true - dependsOn: [cilium,kubeovn] + dependsOn: [cilium,kubeovn,multus] - name: external-secrets-operator releaseName: external-secrets-operator chart: cozy-external-secrets-operator namespace: cozy-external-secrets-operator optional: true - dependsOn: [cilium,kubeovn] + dependsOn: [cilium,kubeovn,multus] - name: bootbox releaseName: bootbox @@ -539,7 +540,7 @@ releases: namespace: cozy-bootbox privileged: true optional: true - dependsOn: [cilium,kubeovn] + dependsOn: [cilium,kubeovn,multus] {{- if $oidcEnabled }} - name: keycloak @@ -588,7 +589,7 @@ releases: chart: cozy-vertical-pod-autoscaler-crds namespace: cozy-vertical-pod-autoscaler privileged: true - dependsOn: [cilium, kubeovn] + dependsOn: [] - name: reloader releaseName: reloader From 88f469b3cd95aa91fd9a5fada9bf2a76cb15c608 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Sat, 10 Jan 2026 00:34:44 +0100 Subject: [PATCH 008/125] fix(registry): implement field selector filtering for label-based resources Controller-runtime cache doesn't support field selectors, causing incorrect filtering when using kubectl with field selectors like --field-selector=metadata.namespace=tenant-kvaps or metadata.name=test. Changes: - Created pkg/registry/fields package with ParseFieldSelector utility - Refactored field selector parsing logic in application, tenantmodule, and tenantsecret registries to use common implementation - Implemented manual filtering for metadata.name and metadata.namespace in List() and Watch() methods - Removed Raw field usage and field selectors from client.ListOptions - Label selectors passed directly via LabelSelector field Field selectors now properly filter resources by name and namespace through manual post-processing after label-based filtering. See: https://github.com/kubernetes-sigs/controller-runtime/issues/612 Co-Authored-By: Claude Signed-off-by: Andrei Kvapil --- pkg/registry/apps/application/rest.go | 132 +++++++++++++------------ pkg/registry/core/tenantmodule/rest.go | 117 +++++++++++----------- pkg/registry/core/tenantsecret/rest.go | 38 ++++--- pkg/registry/fields/filter.go | 70 +++++++++++++ 4 files changed, 227 insertions(+), 130 deletions(-) create mode 100644 pkg/registry/fields/filter.go diff --git a/pkg/registry/apps/application/rest.go b/pkg/registry/apps/application/rest.go index 9e8fb891..4f3204d5 100644 --- a/pkg/registry/apps/application/rest.go +++ b/pkg/registry/apps/application/rest.go @@ -28,7 +28,7 @@ import ( helmv2 "github.com/fluxcd/helm-controller/api/v2" metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - fields "k8s.io/apimachinery/pkg/fields" + "k8s.io/apimachinery/pkg/fields" labels "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" @@ -42,6 +42,7 @@ import ( appsv1alpha1 "github.com/cozystack/cozystack/pkg/apis/apps/v1alpha1" "github.com/cozystack/cozystack/pkg/config" + fieldfilter "github.com/cozystack/cozystack/pkg/registry/fields" "github.com/cozystack/cozystack/pkg/registry/sorting" internalapiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions" apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" @@ -257,26 +258,32 @@ func (r *REST) List(ctx context.Context, options *metainternalversion.ListOption } // Initialize variables for selector mapping - var helmFieldSelector string - var helmLabelSelector string + var helmLabelSelector labels.Selector - // Process field.selector - if options.FieldSelector != nil { - fs, err := fields.ParseSelector(options.FieldSelector.String()) - if err != nil { - klog.Errorf("Invalid field selector: %v", err) - return nil, fmt.Errorf("invalid field selector: %v", err) - } - // Check if selector is for metadata.name - if name, exists := fs.RequiresExactMatch("metadata.name"); exists { - // Convert Application name to HelmRelease name - mappedName := r.releaseConfig.Prefix + name - // Create new field.selector for HelmRelease - helmFieldSelector = fields.OneTermEqualSelector("metadata.name", mappedName).String() - } else { - // If field.selector contains other fields, map them directly - helmFieldSelector = fs.String() - } + // Parse field selector for manual filtering + // controller-runtime cache doesn't support field selectors + // See: https://github.com/kubernetes-sigs/controller-runtime/issues/612 + fieldFilter, err := fieldfilter.ParseFieldSelector(options.FieldSelector) + if err != nil { + klog.Errorf("Error parsing field selector: %v", err) + return nil, err + } + + // If field selector specifies namespace different from context, return empty list + if fieldFilter.Namespace != "" && namespace != "" && namespace != fieldFilter.Namespace { + klog.V(6).Infof("Field selector namespace %s doesn't match context namespace %s, returning empty list", fieldFilter.Namespace, namespace) + return &appsv1alpha1.ApplicationList{ + TypeMeta: metav1.TypeMeta{ + APIVersion: appsv1alpha1.SchemeGroupVersion.String(), + Kind: r.kindName + "List", + }, + }, nil + } + + // Convert Application name to HelmRelease name for manual filtering + var filterByName string + if fieldFilter.Name != "" { + filterByName = r.releaseConfig.Prefix + fieldFilter.Name } // Process label.selector @@ -315,21 +322,16 @@ func (r *REST) List(ctx context.Context, options *metainternalversion.ListOption labelRequirements = append(labelRequirements, prefixedReqs...) } } - helmLabelSelector = labels.NewSelector().Add(labelRequirements...).String() + helmLabelSelector = labels.NewSelector().Add(labelRequirements...) klog.V(6).Infof("Using label selector: %s for kind: %s, group: %s", helmLabelSelector, r.kindName, r.gvk.Group) - // Set ListOptions for HelmRelease with selector mapping - metaOptions := metav1.ListOptions{ - FieldSelector: helmFieldSelector, - LabelSelector: helmLabelSelector, - } - - // List HelmReleases with mapped selectors + // List HelmReleases with label selector only + // Field selectors are not supported by controller-runtime cache, so we filter manually below hrList := &helmv2.HelmReleaseList{} err = r.c.List(ctx, hrList, &client.ListOptions{ - Namespace: namespace, - Raw: &metaOptions, + Namespace: namespace, + LabelSelector: helmLabelSelector, }) if err != nil { klog.Errorf("Error listing HelmReleases: %v", err) @@ -346,6 +348,16 @@ func (r *REST) List(ctx context.Context, options *metainternalversion.ListOption for i := range hrList.Items { hr := &hrList.Items[i] + // Apply manual field selector filtering (metadata.name and metadata.namespace) + // controller-runtime cache doesn't support field selectors + // See: https://github.com/kubernetes-sigs/controller-runtime/issues/612 + if filterByName != "" && hr.Name != filterByName { + continue + } + if !fieldFilter.MatchesNamespace(hr.Namespace) { + continue + } + app, err := r.ConvertHelmReleaseToApplication(hr) if err != nil { klog.Errorf("Error converting HelmRelease %s to Application: %v", hr.GetName(), err) @@ -569,27 +581,21 @@ func (r *REST) Watch(ctx context.Context, options *metainternalversion.ListOptio } // Initialize variables for selector mapping - var helmFieldSelector string - var helmLabelSelector string + var helmLabelSelector labels.Selector - // Process field.selector - if options.FieldSelector != nil { - fs, err := fields.ParseSelector(options.FieldSelector.String()) - if err != nil { - klog.Errorf("Invalid field selector: %v", err) - return nil, fmt.Errorf("invalid field selector: %v", err) - } + // Parse field selector for manual filtering + // controller-runtime cache doesn't support field selectors + // See: https://github.com/kubernetes-sigs/controller-runtime/issues/612 + fieldFilter, err := fieldfilter.ParseFieldSelector(options.FieldSelector) + if err != nil { + klog.Errorf("Error parsing field selector: %v", err) + return nil, err + } - // Check if selector is for metadata.name - if name, exists := fs.RequiresExactMatch("metadata.name"); exists { - // Convert Application name to HelmRelease name - mappedName := r.releaseConfig.Prefix + name - // Create new field.selector for HelmRelease - helmFieldSelector = fields.OneTermEqualSelector("metadata.name", mappedName).String() - } else { - // If field.selector contains other fields, map them directly - helmFieldSelector = fs.String() - } + // Convert Application name to HelmRelease name for manual filtering + var filterByName string + if fieldFilter.Name != "" { + filterByName = r.releaseConfig.Prefix + fieldFilter.Name } // Process label.selector @@ -628,21 +634,15 @@ func (r *REST) Watch(ctx context.Context, options *metainternalversion.ListOptio labelRequirements = append(labelRequirements, prefixedReqs...) } } - helmLabelSelector = labels.NewSelector().Add(labelRequirements...).String() + helmLabelSelector = labels.NewSelector().Add(labelRequirements...) - // Set ListOptions for HelmRelease with selector mapping - metaOptions := metav1.ListOptions{ - Watch: true, - ResourceVersion: options.ResourceVersion, - FieldSelector: helmFieldSelector, - LabelSelector: helmLabelSelector, - } - - // Start watch on HelmRelease with mapped selectors + // Start watch on HelmRelease with label selector only + // Field selectors are not supported by controller-runtime cache + // See: https://github.com/kubernetes-sigs/controller-runtime/issues/612 hrList := &helmv2.HelmReleaseList{} helmWatcher, err := r.w.Watch(ctx, hrList, &client.ListOptions{ - Namespace: namespace, - Raw: &metaOptions, + Namespace: namespace, + LabelSelector: helmLabelSelector, }) if err != nil { klog.Errorf("Error setting up watch for HelmReleases: %v", err) @@ -682,6 +682,16 @@ func (r *REST) Watch(ctx context.Context, options *metainternalversion.ListOptio continue } + // Apply manual field selector filtering (metadata.name and metadata.namespace) + // controller-runtime cache doesn't support field selectors + // See: https://github.com/kubernetes-sigs/controller-runtime/issues/612 + if filterByName != "" && hr.Name != filterByName { + continue + } + if !fieldFilter.MatchesNamespace(hr.Namespace) { + continue + } + // Note: All HelmReleases already match the required labels due to server-side label selector filtering // Convert HelmRelease to Application app, err := r.ConvertHelmReleaseToApplication(hr) diff --git a/pkg/registry/core/tenantmodule/rest.go b/pkg/registry/core/tenantmodule/rest.go index 12da0e0d..b2cda2db 100644 --- a/pkg/registry/core/tenantmodule/rest.go +++ b/pkg/registry/core/tenantmodule/rest.go @@ -40,6 +40,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" corev1alpha1 "github.com/cozystack/cozystack/pkg/apis/core/v1alpha1" + fieldfilter "github.com/cozystack/cozystack/pkg/registry/fields" "github.com/cozystack/cozystack/pkg/registry/sorting" apierrors "k8s.io/apimachinery/pkg/api/errors" ) @@ -161,24 +162,26 @@ func (r *REST) List(ctx context.Context, options *metainternalversion.ListOption } // Initialize variables for selector mapping - var helmFieldSelector string - var helmLabelSelector string + var helmLabelSelector labels.Selector - // Process field.selector - if options.FieldSelector != nil { - fs, err := fields.ParseSelector(options.FieldSelector.String()) - if err != nil { - klog.Errorf("Invalid field selector: %v", err) - return nil, fmt.Errorf("invalid field selector: %v", err) - } - // Check if selector is for metadata.name - if name, exists := fs.RequiresExactMatch("metadata.name"); exists { - // Create new field.selector for HelmRelease - helmFieldSelector = fields.OneTermEqualSelector("metadata.name", name).String() - } else { - // If field.selector contains other fields, map them directly - helmFieldSelector = fs.String() - } + // Parse field selector for manual filtering + // controller-runtime cache doesn't support field selectors + // See: https://github.com/kubernetes-sigs/controller-runtime/issues/612 + fieldFilter, err := fieldfilter.ParseFieldSelector(options.FieldSelector) + if err != nil { + klog.Errorf("Error parsing field selector: %v", err) + return nil, err + } + + // If field selector specifies namespace different from context, return empty list + if fieldFilter.Namespace != "" && namespace != "" && namespace != fieldFilter.Namespace { + klog.V(6).Infof("Field selector namespace %s doesn't match context namespace %s, returning empty list", fieldFilter.Namespace, namespace) + return &corev1alpha1.TenantModuleList{ + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1alpha1.SchemeGroupVersion.String(), + Kind: "TenantModuleList", + }, + }, nil } // Process label.selector - add the tenant module label requirement @@ -202,19 +205,15 @@ func (r *REST) List(ctx context.Context, options *metainternalversion.ListOption } } - helmLabelSelector = labels.NewSelector().Add(labelRequirements...).String() + helmLabelSelector = labels.NewSelector().Add(labelRequirements...) - // Set ListOptions for HelmRelease with selector mapping - metaOptions := metav1.ListOptions{ - FieldSelector: helmFieldSelector, - LabelSelector: helmLabelSelector, - } - - // List HelmReleases with mapped selectors + // List HelmReleases with label selector only + // Field selectors are not supported by controller-runtime cache + // See: https://github.com/kubernetes-sigs/controller-runtime/issues/612 hrList := &helmv2.HelmReleaseList{} err = r.c.List(ctx, hrList, &client.ListOptions{ - Namespace: namespace, - Raw: &metaOptions, + Namespace: namespace, + LabelSelector: helmLabelSelector, }) if err != nil { klog.Errorf("Error listing HelmReleases: %v", err) @@ -226,6 +225,16 @@ func (r *REST) List(ctx context.Context, options *metainternalversion.ListOption // Iterate over HelmReleases and convert to TenantModules for i := range hrList.Items { + // Apply manual field selector filtering (metadata.name and metadata.namespace) + // controller-runtime cache doesn't support field selectors + // See: https://github.com/kubernetes-sigs/controller-runtime/issues/612 + if !fieldFilter.MatchesName(hrList.Items[i].Name) { + continue + } + if !fieldFilter.MatchesNamespace(hrList.Items[i].Namespace) { + continue + } + // Double-check the label requirement if !r.hasTenantModuleLabel(&hrList.Items[i]) { continue @@ -305,25 +314,15 @@ func (r *REST) Watch(ctx context.Context, options *metainternalversion.ListOptio } // Initialize variables for selector mapping - var helmFieldSelector string - var helmLabelSelector string + var helmLabelSelector labels.Selector - // Process field.selector - if options.FieldSelector != nil { - fs, err := fields.ParseSelector(options.FieldSelector.String()) - if err != nil { - klog.Errorf("Invalid field selector: %v", err) - return nil, fmt.Errorf("invalid field selector: %v", err) - } - - // Check if selector is for metadata.name - if name, exists := fs.RequiresExactMatch("metadata.name"); exists { - // Create new field.selector for HelmRelease - helmFieldSelector = fields.OneTermEqualSelector("metadata.name", name).String() - } else { - // If field.selector contains other fields, map them directly - helmFieldSelector = fs.String() - } + // Parse field selector for manual filtering + // controller-runtime cache doesn't support field selectors + // See: https://github.com/kubernetes-sigs/controller-runtime/issues/612 + fieldFilter, err := fieldfilter.ParseFieldSelector(options.FieldSelector) + if err != nil { + klog.Errorf("Error parsing field selector: %v", err) + return nil, err } // Process label.selector - add the tenant module label requirement @@ -347,21 +346,15 @@ func (r *REST) Watch(ctx context.Context, options *metainternalversion.ListOptio } } - helmLabelSelector = labels.NewSelector().Add(labelRequirements...).String() + helmLabelSelector = labels.NewSelector().Add(labelRequirements...) - // Set ListOptions for HelmRelease with selector mapping - metaOptions := metav1.ListOptions{ - Watch: true, - ResourceVersion: options.ResourceVersion, - FieldSelector: helmFieldSelector, - LabelSelector: helmLabelSelector, - } - - // Start watch on HelmRelease with mapped selectors + // Start watch on HelmRelease with label selector only + // Field selectors are not supported by controller-runtime cache + // See: https://github.com/kubernetes-sigs/controller-runtime/issues/612 hrList := &helmv2.HelmReleaseList{} helmWatcher, err := r.w.Watch(ctx, hrList, &client.ListOptions{ - Namespace: namespace, - Raw: &metaOptions, + Namespace: namespace, + LabelSelector: helmLabelSelector, }) if err != nil { klog.Errorf("Error setting up watch for HelmReleases: %v", err) @@ -401,6 +394,16 @@ func (r *REST) Watch(ctx context.Context, options *metainternalversion.ListOptio continue } + // Apply manual field selector filtering (metadata.name and metadata.namespace) + // controller-runtime cache doesn't support field selectors + // See: https://github.com/kubernetes-sigs/controller-runtime/issues/612 + if !fieldFilter.MatchesName(hr.Name) { + continue + } + if !fieldFilter.MatchesNamespace(hr.Namespace) { + continue + } + if !r.hasTenantModuleLabel(hr) { continue } diff --git a/pkg/registry/core/tenantsecret/rest.go b/pkg/registry/core/tenantsecret/rest.go index e5dbf9dd..d069c6ae 100644 --- a/pkg/registry/core/tenantsecret/rest.go +++ b/pkg/registry/core/tenantsecret/rest.go @@ -27,6 +27,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" corev1alpha1 "github.com/cozystack/cozystack/pkg/apis/core/v1alpha1" + fieldfilter "github.com/cozystack/cozystack/pkg/registry/fields" "github.com/cozystack/cozystack/pkg/registry/sorting" ) @@ -248,9 +249,22 @@ func (r *REST) List(ctx context.Context, opts *metainternal.ListOptions) (runtim } } - fieldSel := "" - if opts.FieldSelector != nil { - fieldSel = opts.FieldSelector.String() + // Parse field selector for manual filtering + // controller-runtime cache doesn't support field selectors + // See: https://github.com/kubernetes-sigs/controller-runtime/issues/612 + fieldFilter, err := fieldfilter.ParseFieldSelector(opts.FieldSelector) + if err != nil { + return nil, err + } + + // If field selector specifies namespace different from context, return empty list + if fieldFilter.Namespace != "" && ns != "" && ns != fieldFilter.Namespace { + return &corev1alpha1.TenantSecretList{ + TypeMeta: metav1.TypeMeta{ + APIVersion: corev1alpha1.SchemeGroupVersion.String(), + Kind: kindTenantSecretList, + }, + }, nil } list := &corev1.SecretList{} @@ -258,10 +272,6 @@ func (r *REST) List(ctx context.Context, opts *metainternal.ListOptions) (runtim &client.ListOptions{ Namespace: ns, LabelSelector: ls, - Raw: &metav1.ListOptions{ - LabelSelector: ls.String(), - FieldSelector: fieldSel, - }, }) if err != nil { return nil, err @@ -276,6 +286,15 @@ func (r *REST) List(ctx context.Context, opts *metainternal.ListOptions) (runtim } for i := range list.Items { + // Apply manual field selector filtering (metadata.name and metadata.namespace) + // controller-runtime cache doesn't support field selectors + // See: https://github.com/kubernetes-sigs/controller-runtime/issues/612 + if !fieldFilter.MatchesName(list.Items[i].Name) { + continue + } + if !fieldFilter.MatchesNamespace(list.Items[i].Namespace) { + continue + } out.Items = append(out.Items, *secretToTenant(&list.Items[i])) } sorting.ByNamespacedName[corev1alpha1.TenantSecret, *corev1alpha1.TenantSecret](out.Items) @@ -413,11 +432,6 @@ func (r *REST) Watch(ctx context.Context, opts *metainternal.ListOptions) (watch base, err := r.w.Watch(ctx, secList, &client.ListOptions{ Namespace: ns, LabelSelector: ls, - Raw: &metav1.ListOptions{ - Watch: true, - LabelSelector: ls.String(), - ResourceVersion: opts.ResourceVersion, - }, }) if err != nil { return nil, err diff --git a/pkg/registry/fields/filter.go b/pkg/registry/fields/filter.go new file mode 100644 index 00000000..9b1fc246 --- /dev/null +++ b/pkg/registry/fields/filter.go @@ -0,0 +1,70 @@ +// Copyright 2024 The Cozystack Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package fields + +import ( + "fmt" + + "k8s.io/apimachinery/pkg/fields" +) + +// Filter holds field selector filters extracted from a field selector string. +type Filter struct { + // Name is the value from metadata.name field selector, empty if not specified + Name string + // Namespace is the value from metadata.namespace field selector, empty if not specified + Namespace string +} + +// ParseFieldSelector parses a field selector and extracts metadata.name and metadata.namespace values. +// Other field selectors are silently ignored as controller-runtime cache doesn't support them. +// See: https://github.com/kubernetes-sigs/controller-runtime/issues/612 +func ParseFieldSelector(fieldSelector fields.Selector) (*Filter, error) { + if fieldSelector == nil { + return &Filter{}, nil + } + + fs, err := fields.ParseSelector(fieldSelector.String()) + if err != nil { + return nil, fmt.Errorf("invalid field selector: %v", err) + } + + filter := &Filter{} + + // Check if selector is for metadata.name + if name, exists := fs.RequiresExactMatch("metadata.name"); exists { + filter.Name = name + } + + // Check if selector is for metadata.namespace + if namespace, exists := fs.RequiresExactMatch("metadata.namespace"); exists { + filter.Namespace = namespace + } + + // Note: Other field selectors are silently ignored as controller-runtime cache + // doesn't support them. See: https://github.com/kubernetes-sigs/controller-runtime/issues/612 + + return filter, nil +} + +// MatchesName returns true if the filter has no name constraint or if the name matches. +func (f *Filter) MatchesName(name string) bool { + return f.Name == "" || f.Name == name +} + +// MatchesNamespace returns true if the filter has no namespace constraint or if the namespace matches. +func (f *Filter) MatchesNamespace(namespace string) bool { + return f.Namespace == "" || f.Namespace == namespace +} From 248eed338fdf4d8dfacbd0c6b4319e47f0028904 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Fri, 9 Jan 2026 21:36:39 +0100 Subject: [PATCH 009/125] [platform] fix migrations for v0.40 release Signed-off-by: Andrei Kvapil --- scripts/migrations/20 | 4 ++-- scripts/migrations/21 | 17 ++++++++++++++--- scripts/migrations/22 | 21 +++++++++++++++++++++ 3 files changed, 37 insertions(+), 5 deletions(-) diff --git a/scripts/migrations/20 b/scripts/migrations/20 index a27464ac..0c885338 100755 --- a/scripts/migrations/20 +++ b/scripts/migrations/20 @@ -33,11 +33,11 @@ else kubectl rollout status deploy/cozystack-api -n cozy-system --timeout=5m || exit 1 fi -helm upgrade --install -n cozy-system cozystack-controller ./packages/system/cozystack-controller/ --take-ownership +cozyhr -n cozy-system -C ./packages/system/cozystack-controller apply cozystack-controller --take-ownership echo "Waiting for cozystack-controller" kubectl rollout status deploy/cozystack-controller -n cozy-system --timeout=5m || exit 1 -helm upgrade --install -n cozy-system lineage-controller-webhook ./packages/system/lineage-controller-webhook/ --take-ownership +cozyhr -n cozy-system -C ./packages/system/lineage-controller-webhook/ apply lineage-controller-webhook --take-ownership echo "Waiting for lineage-webhook" kubectl rollout status ds/lineage-controller-webhook -n cozy-system --timeout=5m || exit 1 diff --git a/scripts/migrations/21 b/scripts/migrations/21 index abefdc77..bd5fda5f 100755 --- a/scripts/migrations/21 +++ b/scripts/migrations/21 @@ -3,14 +3,25 @@ set -euo pipefail +# Disable pruning on Flux CRDs for crd in $(kubectl get crd -o name | grep 'fluxcd.io$'); do kubectl annotate $crd fluxcd.controlplane.io/prune=disabled done -kubectl delete hr -n cozy-fluxcd fluxcd --ignore-not-found -kubectl delete hr -n cozy-fluxcd fluxcd-operator --ignore-not-found +# Remove flux instance +if kubectl get fluxinstance flux -n cozy-fluxcd >/dev/null 2>&1; then + kubectl annotate fluxinstance flux -n cozy-fluxcd fluxcd.controlplane.io/reconcile=disabled + kubectl delete fluxinstance flux -n cozy-fluxcd +fi +kubectl delete deploy -n cozy-fluxcd -l app.kubernetes.io/part-of=flux --ignore-not-found +kubectl delete hr -n cozy-fluxcd fluxcd --ignore-not-found --wait=false -for crd in $(kubectl get crd -o name | grep 'fluxcd.io$'); do +# Remove fluxcd-operator +kubectl delete hr -n cozy-fluxcd fluxcd-operator --ignore-not-found --wait=false +kubectl delete deploy -n cozy-fluxcd flux-operator --ignore-not-found + +# Remove labels from CRDs +for crd in $(kubectl get crd -o name | grep 'fluxcd\.io$'); do kubectl label $crd fluxcd.controlplane.io/name- fluxcd.controlplane.io/namespace- done diff --git a/scripts/migrations/22 b/scripts/migrations/22 index 192e431c..2dc8f281 100755 --- a/scripts/migrations/22 +++ b/scripts/migrations/22 @@ -3,6 +3,17 @@ set -euo pipefail +# Migrate Victoria Metrics Operator CRDs to prometheus-operator-crds Helm release +for crd in $(kubectl get crd -o name | grep 'coreos\.com$'); do + kubectl annotate $crd meta.helm.sh/release-namespace=cozy-victoria-metrics-operator meta.helm.sh/release-name=prometheus-operator-crds --overwrite + kubectl label $crd app.kubernetes.io/managed-by=Helm helm.toolkit.fluxcd.io/namespace=cozy-victoria-metrics-operator helm.toolkit.fluxcd.io/name=prometheus-operator-crds --overwrite +done +kubectl delete secret -n cozy-victoria-metrics-operator -l name=victoria-metrics-operator,owner=helm --ignore-not-found + +# Remove CozyStack Resource Definitions HR +kubectl delete hr -n cozy-system cozystack-resource-definitions --ignore-not-found --wait=false +kubectl delete cozystackresourcedefinitions.cozystack.io --all --ignore-not-found --wait=false + echo "Migrating HelmReleases: adding application labels for tenant-* namespaces" # Function to determine application type from HelmRelease name @@ -155,6 +166,16 @@ kubectl get helmreleases --all-namespaces -l cozystack.io/ui=true -o json | \ echo "Added application labels to $namespace/$name: $labels" done +echo "Migrating PostgreSQL HelmReleases: adding default version v17" + +# Patch all PostgreSQL HelmReleases to add spec.values.version: v17 +kubectl get helmreleases --all-namespaces -l apps.cozystack.io/application.kind=PostgreSQL -o json | \ + jq -r '.items[] | select(.spec.values.version == null) | "\(.metadata.namespace)|\(.metadata.name)"' | \ + while IFS='|' read -r namespace name; do + echo "Patching PostgreSQL HelmRelease $namespace/$name to add version v17" + kubectl patch helmrelease -n "$namespace" "$name" --type=merge -p '{"spec":{"values":{"version":"v17"}}}' + done + echo "Migration completed" # Stamp version From fd3c9cc737ab24bde0eafcba642f3a96b8b5af8d Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Sat, 10 Jan 2026 03:34:50 +0100 Subject: [PATCH 010/125] [docs] Generate missing changelogs for v0.37.10, v0.38.5-v0.38.8, v0.39.2-v0.39.4, and v0.40.0 Generated changelogs for missing releases following changelog.md instructions: - v0.37.10: Backports for dashboard schema fix and VM resize improvements - v0.38.5-v0.38.8: Backports for SeaweedFS, Cilium, VM scheduling, and Multus fixes - v0.39.2-v0.39.4: Backports for VM services, tenant policies, LINSTOR fixes, and Multus dependencies - v0.40.0: Minor release with LINSTOR scheduler, SeaweedFS traffic locality, valuesFrom mechanism, auto-diskful, and version management systems All changelogs include proper PR author attribution using GitHub CLI and user impact descriptions. Signed-off-by: Andrei Kvapil --- docs/changelogs/v0.37.10.md | 16 +++ docs/changelogs/v0.38.5.md | 18 ++++ docs/changelogs/v0.38.6.md | 12 +++ docs/changelogs/v0.38.7.md | 13 +++ docs/changelogs/v0.38.8.md | 12 +++ docs/changelogs/v0.39.2.md | 19 ++++ docs/changelogs/v0.39.3.md | 36 +++++++ docs/changelogs/v0.39.4.md | 12 +++ docs/changelogs/v0.40.0.md | 206 ++++++++++++++++++++++++++++++++++++ 9 files changed, 344 insertions(+) create mode 100644 docs/changelogs/v0.37.10.md create mode 100644 docs/changelogs/v0.38.5.md create mode 100644 docs/changelogs/v0.38.6.md create mode 100644 docs/changelogs/v0.38.7.md create mode 100644 docs/changelogs/v0.38.8.md create mode 100644 docs/changelogs/v0.39.2.md create mode 100644 docs/changelogs/v0.39.3.md create mode 100644 docs/changelogs/v0.39.4.md create mode 100644 docs/changelogs/v0.40.0.md diff --git a/docs/changelogs/v0.37.10.md b/docs/changelogs/v0.37.10.md new file mode 100644 index 00000000..18f0dd8b --- /dev/null +++ b/docs/changelogs/v0.37.10.md @@ -0,0 +1,16 @@ + + +## Features and Improvements + +* **[virtual-machine] Improve check for resizing job**: Improved storage resize logic to only expand persistent volume claims when storage is being increased, preventing unintended storage reduction operations. Added validation to accurately compare current and desired storage sizes before triggering resize operations ([**@kvaps**](https://github.com/kvaps) in #1688, #1702). + +## Fixes + +* **[dashboard] Fix CustomFormsOverride schema to nest properties under spec.properties**: Fixed the logic for generating CustomFormsOverride schema to properly nest properties under `spec.properties` instead of directly under `properties`, ensuring correct form schema generation in the dashboard ([**@kvaps**](https://github.com/kvaps) in #1692, #1699). + +--- + +**Full Changelog**: [v0.37.9...v0.37.10](https://github.com/cozystack/cozystack/compare/v0.37.9...v0.37.10) + diff --git a/docs/changelogs/v0.38.5.md b/docs/changelogs/v0.38.5.md new file mode 100644 index 00000000..0cae8b2a --- /dev/null +++ b/docs/changelogs/v0.38.5.md @@ -0,0 +1,18 @@ + + +## Features and Improvements + +* **[virtual-machine,vm-instance] Add nodeAffinity for Windows VMs based on scheduling config**: Added nodeAffinity configuration to virtual-machine and vm-instance charts to support dedicated nodes for Windows VMs. When `dedicatedNodesForWindowsVMs` is enabled in the `cozystack-scheduling` ConfigMap, Windows VMs are scheduled on nodes with label `scheduling.cozystack.io/vm-windows=true`, while non-Windows VMs prefer nodes without this label ([**@kvaps**](https://github.com/kvaps) in #1693, #1744). +* **[cilium] Enable automatic pod rollout on configmap updates**: Cilium and Cilium operator pods now automatically restart when the cilium-config ConfigMap is updated, ensuring configuration changes are applied immediately without manual intervention ([**@kvaps**](https://github.com/kvaps) in #1728, #1745). +* **Update SeaweedFS v4.02**: Updated SeaweedFS to version 4.02 with improved S3 daemon performance and fixes. This update includes better S3 compatibility and performance improvements ([**@kvaps**](https://github.com/kvaps) in #1725, #1732). + +## Fixes + +* **[apps] Refactor apiserver to use typed objects and fix UnstructuredList GVK**: Refactored the apiserver REST handlers to use typed objects (`appsv1alpha1.Application`) instead of `unstructured.Unstructured`, eliminating the need for runtime conversions and simplifying the codebase. Additionally, fixed an issue where `UnstructuredList` objects were using the first registered kind from `typeToGVK` instead of the kind from the object's field when multiple kinds are registered with the same Go type. This fix includes the upstream fix from kubernetes/kubernetes#135537 ([**@kvaps**](https://github.com/kvaps) in #1679, #1709). + +--- + +**Full Changelog**: [v0.38.4...v0.38.5](https://github.com/cozystack/cozystack/compare/v0.38.4...v0.38.5) + diff --git a/docs/changelogs/v0.38.6.md b/docs/changelogs/v0.38.6.md new file mode 100644 index 00000000..9c39b66d --- /dev/null +++ b/docs/changelogs/v0.38.6.md @@ -0,0 +1,12 @@ + + +## Development, Testing, and CI/CD + +* **[kubernetes] Add lb tests for tenant k8s**: Added load balancer tests for tenant Kubernetes clusters, improving test coverage and ensuring proper load balancer functionality in tenant environments ([**@IvanHunters**](https://github.com/IvanHunters) in #1783, #1792). + +--- + +**Full Changelog**: [v0.38.5...v0.38.6](https://github.com/cozystack/cozystack/compare/v0.38.5...v0.38.6) + diff --git a/docs/changelogs/v0.38.7.md b/docs/changelogs/v0.38.7.md new file mode 100644 index 00000000..3edfd94c --- /dev/null +++ b/docs/changelogs/v0.38.7.md @@ -0,0 +1,13 @@ + + +## Fixes + +* **[kubevirt-operator] Fix typo in VMNotRunningFor10Minutes alert**: Fixed typo in VM alert name, ensuring proper alert triggering and monitoring for virtual machines that are not running for extended periods ([**@lexfrei**](https://github.com/lexfrei) in #1770). +* **[kubevirt-operator] Revert incorrect case change in VM alerts**: Reverted incorrect case change in VM alert names to maintain consistency with alert naming conventions ([**@lexfrei**](https://github.com/lexfrei) in #1804, #1805). + +--- + +**Full Changelog**: [v0.38.6...v0.38.7](https://github.com/cozystack/cozystack/compare/v0.38.6...v0.38.7) + diff --git a/docs/changelogs/v0.38.8.md b/docs/changelogs/v0.38.8.md new file mode 100644 index 00000000..0dbfe7b8 --- /dev/null +++ b/docs/changelogs/v0.38.8.md @@ -0,0 +1,12 @@ + + +## Improvements + +* **[multus] Remove memory limit**: Removed memory limit for Multus daemonset due to unpredictable memory consumption spikes during startup after node reboots (reported up to 3Gi). This temporary change prevents out-of-memory issues while the root cause is addressed in future releases ([**@nbykov0**](https://github.com/nbykov0) in #1834). + +--- + +**Full Changelog**: [v0.38.7...v0.38.8](https://github.com/cozystack/cozystack/compare/v0.38.7...v0.38.8) + diff --git a/docs/changelogs/v0.39.2.md b/docs/changelogs/v0.39.2.md new file mode 100644 index 00000000..dacb094f --- /dev/null +++ b/docs/changelogs/v0.39.2.md @@ -0,0 +1,19 @@ + + +## Features and Improvements + +* **[vm] Always expose VMs with a service**: Virtual machines are now always exposed with at least a ClusterIP service, ensuring they have in-cluster DNS names and can be accessed from other pods even without public IP addresses ([**@lllamnyp**](https://github.com/lllamnyp) in #1738, #1751). +* **[tenant] Allow egress to parent ingress pods**: Updated tenant network policies to allow egress traffic to parent cluster ingress pods, enabling proper communication patterns between tenant namespaces and parent cluster ingress controllers ([**@lexfrei**](https://github.com/lexfrei) in #1765, #1776). +* **[system] Add resource requests and limits to etcd-defrag**: Added resource requests and limits to etcd-defrag job to ensure proper resource allocation and prevent resource contention during etcd maintenance operations ([**@matthieu-robin**](https://github.com/matthieu-robin) in #1785, #1786). +* **[tenant] Run cleanup job from system namespace**: Moved tenant cleanup job to run from system namespace, improving security and resource isolation for tenant cleanup operations ([**@lllamnyp**](https://github.com/lllamnyp) in #1774, #1777). + +## Fixes + +* **[kubevirt-operator] Fix typo in VMNotRunningFor10Minutes alert**: Fixed typo in VM alert name, ensuring proper alert triggering and monitoring for virtual machines that are not running for extended periods ([**@lexfrei**](https://github.com/lexfrei) in #1770, #1775). + +--- + +**Full Changelog**: [v0.39.1...v0.39.2](https://github.com/cozystack/cozystack/compare/v0.39.1...v0.39.2) + diff --git a/docs/changelogs/v0.39.3.md b/docs/changelogs/v0.39.3.md new file mode 100644 index 00000000..f493795b --- /dev/null +++ b/docs/changelogs/v0.39.3.md @@ -0,0 +1,36 @@ + + +## Features and Improvements + +* **[seaweedfs] Traffic locality**: Upgraded SeaweedFS to v4.05 with traffic locality capabilities, new admin component with web-based UI, worker component for distributed operations, and enhanced S3 monitoring with Grafana dashboards. Improves S3 service performance by routing requests to nearest available volume servers ([**@nbykov0**](https://github.com/nbykov0) in #1748, #1830). +* **[kube-ovn] Update to v1.14.25**: Updated Kube-OVN to version 1.14.25 with improved stability and new features ([**@kvaps**](https://github.com/kvaps) in #1819, #1837). +* **[linstor] Build linstor-server with custom patches**: Added custom patches to linstor-server build process, enabling platform-specific optimizations and fixes ([**@kvaps**](https://github.com/kvaps) in #1726, #1818). +* **[api, lineage] Tolerate all taints**: Updated API and lineage webhook to tolerate all taints, ensuring controllers can run on any node regardless of taint configuration ([**@nbykov0**](https://github.com/nbykov0) in #1781, #1827). +* **[ingress] Add topology anti-affinities**: Added topology anti-affinity rules to ingress controller deployment for better pod distribution across nodes ([**@kvaps**](https://github.com/kvaps) in commit 25f31022). + +## Fixes + +* **[linstor] fix: prevent DRBD device race condition in updateDiscGran**: Fixed race condition in DRBD device management during granularity updates, preventing potential data corruption or device conflicts ([**@kvaps**](https://github.com/kvaps) in #1829, #1836). +* **fix(linstor): prevent orphaned DRBD devices during toggle-disk retry**: Fixed issue where retry logic during disk toggle operations could leave orphaned DRBD devices, now properly cleans up devices during retry attempts ([**@kvaps**](https://github.com/kvaps) in #1823, #1825). +* **[kubernetes] Fix endpoints for cilium-gateway**: Fixed endpoint configuration for cilium-gateway, ensuring proper service discovery and connectivity ([**@kvaps**](https://github.com/kvaps) in #1729, #1808). +* **[kubevirt-operator] Revert incorrect case change in VM alerts**: Reverted incorrect case change in VM alert names to maintain consistency with alert naming conventions ([**@lexfrei**](https://github.com/lexfrei) in #1804, #1806). + +## System Configuration + +* **[kubeovn] Package from external repo**: Extracted Kube-OVN packaging from main repository to external repository, improving modularity ([**@lllamnyp**](https://github.com/lllamnyp) in #1535). + +## Development, Testing, and CI/CD + +* **[testing] Add aliases and autocomplete**: Added shell aliases and autocomplete support for testing commands, improving developer experience ([**@lllamnyp**](https://github.com/lllamnyp) in #1803, #1809). + +## Dependencies + +* **[seaweedfs] Traffic locality**: Upgraded SeaweedFS to v4.05 with traffic locality capabilities ([**@nbykov0**](https://github.com/nbykov0) in #1748, #1830). +* **[kube-ovn] Update to v1.14.25**: Updated Kube-OVN to version 1.14.25 ([**@kvaps**](https://github.com/kvaps) in #1819, #1837). + +--- + +**Full Changelog**: [v0.39.2...v0.39.3](https://github.com/cozystack/cozystack/compare/v0.39.2...v0.39.3) + diff --git a/docs/changelogs/v0.39.4.md b/docs/changelogs/v0.39.4.md new file mode 100644 index 00000000..36d9f016 --- /dev/null +++ b/docs/changelogs/v0.39.4.md @@ -0,0 +1,12 @@ + + +## Features and Improvements + +* **[paas-full] Add multus dependencies similar to other CNIs**: Added Multus as a dependency in the paas-full package, consistent with how other CNIs are included. This ensures proper dependency management and simplifies the installation process for environments using Multus networking ([**@nbykov0**](https://github.com/nbykov0) in #1835). + +--- + +**Full Changelog**: [v0.39.3...v0.39.4](https://github.com/cozystack/cozystack/compare/v0.39.3...v0.39.4) + diff --git a/docs/changelogs/v0.40.0.md b/docs/changelogs/v0.40.0.md new file mode 100644 index 00000000..caaa08f6 --- /dev/null +++ b/docs/changelogs/v0.40.0.md @@ -0,0 +1,206 @@ +# Cozystack v0.40 — "Enhanced Storage & Platform Architecture" + +This release introduces LINSTOR scheduler for optimal pod placement, SeaweedFS traffic locality, a new valuesFrom-based configuration mechanism, auto-diskful for LINSTOR, automated version management systems, and numerous improvements across the platform. + +## Feature Highlights + +### LINSTOR Scheduler for Optimal Pod Placement + +Cozystack now includes a custom Kubernetes scheduler extender that works alongside the default kube-scheduler to optimize pod placement on nodes with LINSTOR storage. When a pod requests LINSTOR-backed storage, the scheduler communicates with the LINSTOR controller to find nodes that have local replicas of the requested volumes, prioritizing placement on nodes with existing data to minimize network traffic and improve I/O performance. + +The scheduler includes an admission webhook that automatically routes pods using LINSTOR CSI volumes to the custom scheduler, ensuring seamless integration without manual configuration. This feature significantly improves performance for workloads using LINSTOR storage by reducing network latency and improving data locality. + +Learn more about LINSTOR in the [documentation](https://cozystack.io/docs/operations/storage/linstor/). + +### SeaweedFS Traffic Locality + +SeaweedFS has been upgraded to version 4.05 with new traffic locality capabilities that optimize S3 service traffic distribution. The update includes a new admin component with a web-based UI and authentication support, as well as a worker component for distributed operations. These enhancements improve S3 service performance and provide better visibility through enhanced Grafana dashboard panels for buckets, API calls, costs, and performance metrics. + +The traffic locality feature ensures that S3 requests are routed to the nearest available volume servers, reducing latency and improving overall performance for distributed storage operations. TLS certificate support for admin and worker components adds an extra layer of security for management operations. + +### ValuesFrom Configuration Mechanism + +Cozystack now uses FluxCD's valuesFrom mechanism to replace Helm lookup functions for configuration propagation. This architectural improvement provides cleaner config propagation and eliminates the need for force reconcile controllers. Configuration from ConfigMaps (cozystack, cozystack-branding, cozystack-scheduling) and namespace service references (etcd, host, ingress, monitoring, seaweedfs) is now centrally managed through a `cozystack-values` Secret in each namespace. + +This change simplifies Helm chart templates by replacing complex lookup functions with direct value references, improves configuration consistency, and reduces the reconciliation overhead. All HelmReleases now automatically receive cluster and namespace configuration through the valuesFrom mechanism, making configuration management more transparent and maintainable. + +### Auto-diskful for LINSTOR + +The LINSTOR integration now includes automatic diskful functionality that converts diskless nodes to diskful when they hold DRBD resources in Primary state for an extended period (30 minutes). This feature addresses scenarios where workloads are scheduled on nodes without local storage replicas by automatically creating local disk replicas when needed, improving I/O performance for long-running workloads. + +When enabled with cleanup options, the system can automatically remove disk replicas that are no longer needed, preventing storage waste from temporary replicas. This intelligent storage management reduces network traffic for frequently accessed data while maintaining efficient storage utilization. + +### Automated Version Management Systems + +Cozystack now includes automated version management systems for PostgreSQL, Kubernetes, MariaDB, and Redis applications. These systems automatically track upstream versions and provide mechanisms for automated version updates, ensuring that platform users always have access to the latest stable versions while maintaining compatibility with existing deployments. + +The version management systems integrate with the Cozystack API and dashboard, providing administrators with visibility into available versions and update paths. This infrastructure sets the foundation for future automated upgrade workflows and version compatibility management. + +--- + +## Major Features and Improvements + +### Storage + +* **[linstor] Add linstor-scheduler package**: Added LINSTOR scheduler extender for optimal pod placement on nodes with LINSTOR storage. Includes admission webhook that automatically routes pods using LINSTOR CSI volumes to the custom scheduler, ensuring pods are placed on nodes with local replicas to minimize network traffic and improve I/O performance ([**@kvaps**](https://github.com/kvaps) in #1824). +* **[linstor] Enable auto-diskful for diskless nodes**: Enabled DRBD auto-diskful functionality to automatically convert diskless nodes to diskful when they hold volumes in Primary state for more than 30 minutes. Improves I/O performance for long-running workloads by creating local replicas and includes automatic cleanup options to prevent storage waste ([**@kvaps**](https://github.com/kvaps) in #1826). +* **[linstor] Build linstor-server with custom patches**: Added custom patches to linstor-server build process, enabling platform-specific optimizations and fixes ([**@kvaps**](https://github.com/kvaps) in #1726). +* **[seaweedfs] Traffic locality**: Upgraded SeaweedFS to v4.05 with traffic locality capabilities, new admin component with web-based UI, worker component for distributed operations, and enhanced S3 monitoring with Grafana dashboards. Improves S3 service performance by routing requests to nearest available volume servers ([**@nbykov0**](https://github.com/nbykov0) in #1748). +* **[linstor] fix: prevent DRBD device race condition in updateDiscGran**: Fixed race condition in DRBD device management during granularity updates, preventing potential data corruption or device conflicts ([**@kvaps**](https://github.com/kvaps) in #1829). +* **fix(linstor): prevent orphaned DRBD devices during toggle-disk retry**: Fixed issue where retry logic during disk toggle operations could leave orphaned DRBD devices, now properly cleans up devices during retry attempts ([**@kvaps**](https://github.com/kvaps) in #1823). + +### Platform Architecture + +* **[platform] Replace Helm lookup with valuesFrom mechanism**: Replaced Helm lookup functions with FluxCD valuesFrom mechanism for configuration propagation. Configuration from ConfigMaps and namespace references is now managed through `cozystack-values` Secret, simplifying templates and eliminating force reconcile controllers ([**@kvaps**](https://github.com/kvaps) in #1787). +* **[platform] refactor: split cozystack-resource-definitions into separate packages**: Refactored cozystack-resource-definitions into separate packages for better organization and maintainability, improving code structure and reducing coupling between components ([**@kvaps**](https://github.com/kvaps) in #1778). +* **[platform] Separate assets server into dedicated deployment**: Separated assets server from main platform deployment, improving scalability and allowing independent scaling of asset delivery infrastructure ([**@kvaps**](https://github.com/kvaps) in #1705). +* **[core] Extract Talos package from installer**: Extracted Talos package configuration from installer into a separate package, improving modularity and enabling independent updates ([**@kvaps**](https://github.com/kvaps) in #1724). +* **[registry] Add application labels and update filtering mechanism**: Added application labels to registry resources and improved filtering mechanism for better resource discovery and organization ([**@kvaps**](https://github.com/kvaps) in #1707). +* **fix(registry): implement field selector filtering for label-based resources**: Implemented field selector filtering for label-based resources in the registry, improving query performance and resource lookup efficiency ([**@kvaps**](https://github.com/kvaps) in #1845). +* **[platform] Add alphabetical sorting to registry resource lists**: Added alphabetical sorting to registry resource lists in the API and dashboard, improving user experience when browsing available applications ([**@lexfrei**](https://github.com/lexfrei) in #1764). + +### Version Management + +* **[postgres] Add version management system with automated version updates**: Introduced version management system for PostgreSQL with automated version tracking and update mechanisms ([**@kvaps**](https://github.com/kvaps) in #1671). +* **[kubernetes] Add version management system with automated version updates**: Added version management system for Kubernetes tenant clusters with automated version tracking and update capabilities ([**@kvaps**](https://github.com/kvaps) in #1672). +* **[mariadb] Add version management system with automated version updates**: Implemented version management system for MariaDB with automated version tracking and update mechanisms ([**@kvaps**](https://github.com/kvaps) in #1680). +* **[redis] Add version management system with automated version updates**: Added version management system for Redis with automated version tracking and update capabilities ([**@kvaps**](https://github.com/kvaps) in #1681). + +### Networking + +* **[kube-ovn] Update to v1.14.25**: Updated Kube-OVN to version 1.14.25 with improved stability and new features ([**@kvaps**](https://github.com/kvaps) in #1819). +* **[kubeovn] Package from external repo**: Extracted Kube-OVN packaging from main repository to external repository, improving modularity ([**@lllamnyp**](https://github.com/lllamnyp) in #1535). +* **[cilium] Update Cilium to v1.18.5**: Updated Cilium to version 1.18.5 with latest features and bug fixes ([**@lexfrei**](https://github.com/lexfrei) in #1769). +* **[system/cilium] Enable topology-aware routing for services**: Enabled topology-aware routing for Cilium services, improving traffic distribution and reducing latency by routing traffic to endpoints in the same zone when possible ([**@nbykov0**](https://github.com/nbykov0) in #1734). +* **[cilium] Enable automatic pod rollout on configmap updates**: Cilium and Cilium operator pods now automatically restart when the cilium-config ConfigMap is updated, ensuring configuration changes are applied immediately ([**@kvaps**](https://github.com/kvaps) in #1728). +* **[kubernetes] Fix endpoints for cilium-gateway**: Fixed endpoint configuration for cilium-gateway, ensuring proper service discovery and connectivity ([**@kvaps**](https://github.com/kvaps) in #1729). +* **[multus] Increase memory limit**: Increased memory limits for Multus components to handle larger network configurations and reduce out-of-memory issues ([**@nbykov0**](https://github.com/nbykov0) in #1773). +* **[main][paas-full] Add multus dependencies similar to other CNIs**: Added Multus as a dependency in the paas-full package, consistent with how other CNIs are included ([**@nbykov0**](https://github.com/nbykov0) in #1842). + +### Virtual Machines + +* **[vm] Always expose VMs with a service**: Virtual machines are now always exposed with at least a ClusterIP service, ensuring they have in-cluster DNS names and can be accessed from other pods even without public IP addresses ([**@lllamnyp**](https://github.com/lllamnyp) in #1738). +* **[virtual-machine] Improve check for resizing job**: Improved storage resize logic to only expand persistent volume claims when storage is being increased, preventing unintended storage reduction operations ([**@kvaps**](https://github.com/kvaps) in #1688). +* **[virtual-machine,vm-instance] Add nodeAffinity for Windows VMs based on scheduling config**: Added nodeAffinity configuration to virtual-machine and vm-instance charts to support dedicated nodes for Windows VMs ([**@kvaps**](https://github.com/kvaps) in #1693). + +### Monitoring + +* **[monitoring] Add SLACK_SEVERITY_FILTER field and VMAgent for tenant monitoring**: Introduced SLACK_SEVERITY_FILTER environment variable in Alerta deployment to enable filtering of alert severities for Slack notifications. Added VMAgent resource template for scraping metrics within tenant namespaces, improving monitoring granularity ([**@IvanHunters**](https://github.com/IvanHunters) in #1712). +* **[monitoring] Improve tenant metrics collection**: Improved tenant metrics collection mechanisms for better observability and monitoring coverage ([**@IvanHunters**](https://github.com/IvanHunters) in #1684). + +### System Configuration + +* **[api, lineage] Tolerate all taints**: Updated API and lineage webhook to tolerate all taints, ensuring controllers can run on any node regardless of taint configuration ([**@nbykov0**](https://github.com/nbykov0) in #1781). +* **[system] Add resource requests and limits to etcd-defrag**: Added resource requests and limits to etcd-defrag job to ensure proper resource allocation and prevent resource contention ([**@matthieu-robin**](https://github.com/matthieu-robin) in #1785). +* **[system:coredns] update coredns app labels to match Talos coredns labels**: Updated coredns app labels to match Talos coredns labels, ensuring consistency across the platform ([**@nbykov0**](https://github.com/nbykov0) in #1675). +* **[system:monitoring-agents] rename coredns metrics service**: Renamed coredns metrics service to avoid interference with coredns service used for name resolution in tenant k8s clusters ([**@nbykov0**](https://github.com/nbykov0) in #1676). + +### Tenants and Namespaces + +* **[tenant] Allow egress to parent ingress pods**: Updated tenant network policies to allow egress traffic to parent cluster ingress pods, enabling proper communication patterns ([**@lexfrei**](https://github.com/lexfrei) in #1765). +* **[tenant] Run cleanup job from system namespace**: Moved tenant cleanup job to run from system namespace, improving security and resource isolation ([**@lllamnyp**](https://github.com/lllamnyp) in #1774). + +### FluxCD + +* **[fluxcd] Add flux-aio module and migration**: Added FluxCD all-in-one module with migration support, simplifying FluxCD installation and management ([**@kvaps**](https://github.com/kvaps) in #1698). +* **[fluxcd] Enable source-watcher**: Enabled source-watcher in FluxCD configuration for improved GitOps synchronization and faster update detection ([**@kvaps**](https://github.com/kvaps) in #1706). + +### Applications + +* **[dashboard] Fix CustomFormsOverride schema to nest properties under spec.properties**: Fixed CustomFormsOverride schema generation to properly nest properties under `spec.properties` instead of directly under `properties`, ensuring correct form schema generation ([**@kvaps**](https://github.com/kvaps) in #1692). +* **[apps] Refactor apiserver to use typed objects and fix UnstructuredList GVK**: Refactored apiserver REST handlers to use typed objects instead of unstructured.Unstructured, eliminating runtime conversions. Fixed UnstructuredList GVK issue where objects were using the first registered kind instead of the correct kind ([**@kvaps**](https://github.com/kvaps) in #1679). +* **[keycloak] Make kubernetes client public**: Made Kubernetes client public in Keycloak configuration, enabling broader access patterns for Kubernetes integrations ([**@lllamnyp**](https://github.com/lllamnyp) in #1802). + +## Improvements + +* **[granular kubernetes application extensions dependencies]**: Improved dependency management for Kubernetes application extensions with more granular control over dependencies ([**@nbykov0**](https://github.com/nbykov0) in #1683). +* **[core:installer] Address buildx warnings**: Fixed Dockerfile syntax warnings from buildx, ensuring clean builds without warnings ([**@nbykov0**](https://github.com/nbykov0) in #1682). +* **[linstor] Update piraeus-operator v2.10.2**: Updated LINSTOR CSI to version 2.10.2 with improved stability and bug fixes ([**@kvaps**](https://github.com/kvaps) in #1689). +* **Update SeaweedFS v4.02**: Updated SeaweedFS to version 4.02 with improved S3 daemon performance and fixes ([**@kvaps**](https://github.com/kvaps) in #1725). +* **[installer,dx] Rename cozypkg to cozyhr**: Renamed cozypkg tool to cozyhr for better branding and consistency ([**@kvaps**](https://github.com/kvaps) in #1763). + +## Fixes + +* **fix(platform): fix migrations for v0.40 release**: Fixed platform migrations for v0.40 release, ensuring smooth upgrades from previous versions ([**@kvaps**](https://github.com/kvaps) in #1846). +* **[platform] fix migration for removing fluxcd-operator**: Fixed migration logic for removing fluxcd-operator, ensuring clean removal without leaving orphaned resources ([**@kvaps**](https://github.com/kvaps) in commit 4a83d2c7). +* **[kubevirt-operator] Fix typo in VMNotRunningFor10Minutes alert**: Fixed typo in VM alert name, ensuring proper alert triggering and monitoring ([**@kvaps**](https://github.com/kvaps) in #1770). +* **[kubevirt-operator] Revert incorrect case change in VM alerts**: Reverted incorrect case change in VM alert names to maintain consistency ([**@lexfrei**](https://github.com/lexfrei) in #1804). +* **[cozystack-controller] Fix: move crds to definitions**: Fixed CRD placement by moving them to definitions directory, ensuring proper resource organization ([**@kvaps**](https://github.com/kvaps) in #1759). + +## Dependencies + +* **Update SeaweedFS v4.02**: Updated SeaweedFS to version 4.02 ([**@kvaps**](https://github.com/kvaps) in #1725). +* **[seaweedfs] Traffic locality**: Upgraded SeaweedFS to v4.05 with traffic locality capabilities ([**@nbykov0**](https://github.com/nbykov0) in #1748). +* **[linstor] Update piraeus-operator v2.10.2**: Updated piraeus-operator to version 2.10.2 ([**@kvaps**](https://github.com/kvaps) in #1689). +* **[kube-ovn] Update to v1.14.25**: Updated Kube-OVN to version 1.14.25 ([**@kvaps**](https://github.com/kvaps) in #1819). +* **[cilium] Update Cilium to v1.18.5**: Updated Cilium to version 1.18.5 ([**@lexfrei**](https://github.com/lexfrei) in #1769). +* **Update go modules**: Updated Go modules to latest versions ([**@kvaps**](https://github.com/kvaps) in #1736). + +## Development, Testing, and CI/CD + +* **[ci] Fix auto-release workflow**: Fixed auto-release workflow to ensure correct release publishing and tagging ([**@kvaps**](https://github.com/kvaps) in commit 526af294). +* **fix(ci): ensure correct latest release after backport publishing**: Fixed CI workflow to correctly identify and tag the latest release after backport publishing ([**@kvaps**](https://github.com/kvaps) in #1800). +* **[workflows] Add auto patch release workflow**: Added automated patch release workflow for streamlined release management ([**@kvaps**](https://github.com/kvaps) in #1754). +* **[workflow] Add GitHub Action to update release notes from changelogs**: Added GitHub Action to automatically update release notes from changelog files ([**@kvaps**](https://github.com/kvaps) in #1752). +* **[ci] Improve backport workflow with merge_commits skip and conflict resolution**: Improved backport workflow with better merge commit handling and conflict resolution ([**@kvaps**](https://github.com/kvaps) in #1694). +* **[testing] Add aliases and autocomplete**: Added shell aliases and autocomplete support for testing commands, improving developer experience ([**@lllamnyp**](https://github.com/lllamnyp) in #1803). +* **[kubernetes] Add lb tests for tenant k8s**: Added load balancer tests for tenant Kubernetes clusters, improving test coverage ([**@IvanHunters**](https://github.com/IvanHunters) in #1783). +* **[agents] Add instructions for working with unresolved code review comments**: Added documentation and instructions for working with unresolved code review comments in agent workflows ([**@kvaps**](https://github.com/kvaps) in #1710). +* **feat(ci): add /retest command to rerun tests from Prepare environment**: Added `/retest` command to rerun tests from Prepare environment workflow ([**@kvaps**](https://github.com/kvaps) in commit 30c1041e). +* **fix(ci): remove GITHUB_TOKEN extraheader to trigger workflows**: Removed GITHUB_TOKEN extraheader to properly trigger workflows ([**@kvaps**](https://github.com/kvaps) in commit 68a639b3). +* **Fix: Add missing components to `distro-full` bundle**: Fixed missing components in distro-full bundle, ensuring all required components are included ([**@LoneExile**](https://github.com/LoneExile) in #1620). +* **Update Flux Operator (v0.33.0)**: Updated Flux Operator to version 0.33.0 ([**@kingdonb**](https://github.com/kingdonb) in #1649). +* **Add changelogs for v0.38.3 and v.0.38.4**: Added missing changelogs for v0.38.3 and v0.38.4 releases ([**@androndo**](https://github.com/androndo) in #1743). +* **Add changelogs to v.0.39.1**: Added changelog for v0.39.1 release ([**@androndo**](https://github.com/androndo) in #1750). +* **Add Cloupard to ADOPTERS.md**: Added Cloupard to the adopters list ([**@SerjioTT**](https://github.com/SerjioTT) in #1733). + +## Documentation + +* **[website] docs: expand monitoring and alerting documentation**: Expanded monitoring and alerting documentation with comprehensive guides, examples, and troubleshooting information ([**@IvanHunters**](https://github.com/IvanHunters) in [cozystack/website#388](https://github.com/cozystack/website/pull/388)). +* **[website] fix auto-generation of documentation**: Fixed automatic documentation generation process, ensuring all documentation is properly generated and formatted ([**@IvanHunters**](https://github.com/IvanHunters) in [cozystack/website#391](https://github.com/cozystack/website/pull/391)). +* **[website] secure boot**: Added documentation for Secure Boot support in Talos Linux ([**@kvaps**](https://github.com/kvaps) in [cozystack/website#387](https://github.com/cozystack/website/pull/387)). + +## Tools + +* **[talm] feat(helpers): add bond interface discovery helpers**: Added bond interface discovery helpers to talm for easier network configuration ([**@kvaps**](https://github.com/kvaps) in [cozystack/talm#94](https://github.com/cozystack/talm/pull/94)). +* **[talm] feat(talosconfig): add certificate regeneration from secrets.yaml**: Added certificate regeneration functionality to talm talosconfig command, allowing certificates to be regenerated from secrets.yaml ([**@kvaps**](https://github.com/kvaps) in cozystack/talm@1319dde). +* **[talm] fix(init): make name optional for -u flag**: Made name parameter optional for init command with -u flag, improving flexibility ([**@kvaps**](https://github.com/kvaps) in cozystack/talm@da29320). +* **[talm] fix(wrapper): copy NoOptDefVal when remapping -f to -F flag**: Fixed wrapper to properly copy NoOptDefVal when remapping flags, ensuring correct default value handling ([**@kvaps**](https://github.com/kvaps) in cozystack/talm@f6a6f1d). +* **[talm] fix(root): detect project root with secrets.encrypted.yaml**: Fixed root detection to properly identify project root when secrets.encrypted.yaml is present ([**@kvaps**](https://github.com/kvaps) in cozystack/talm@cf56780). +* **[talm] Fix interfaces helper for Talos v1.12**: Fixed interfaces helper to work correctly with Talos v1.12 ([**@kvaps**](https://github.com/kvaps) in cozystack/talm@34984ae). +* **[talm] Fix typo on README.md**: Fixed typo in README documentation ([**@diegolakatos**](https://github.com/diegolakatos) in [cozystack/talm#92](https://github.com/cozystack/talm/pull/92)). +* **[talm] fix(template): return error for invalid YAML in template output**: Fixed template command to return proper error for invalid YAML output ([**@kvaps**](https://github.com/kvaps) in [cozystack/talm#93](https://github.com/cozystack/talm/pull/93)). +* **[talm] feat(cozystack): enable allocateNodeCIDRs by default**: Enabled allocateNodeCIDRs by default in talm cozystack preset ([**@lexfrei**](https://github.com/lexfrei) in [cozystack/talm#91](https://github.com/cozystack/talm/pull/91)). +* **[boot-to-talos] feat(network): add VLAN interface support via netlink**: Added VLAN interface support via netlink in boot-to-talos for advanced network configuration ([**@kvaps**](https://github.com/kvaps) in cozystack/boot-to-talos@02874d7). +* **[boot-to-talos] feat(network): add bond interface support via netlink**: Added bond interface support via netlink in boot-to-talos for network bonding configurations ([**@kvaps**](https://github.com/kvaps) in cozystack/boot-to-talos@067822d). +* **[boot-to-talos] Draft EFI Support**: Added draft EFI support in boot-to-talos for UEFI boot scenarios ([**@kvaps**](https://github.com/kvaps) in cozystack/boot-to-talos@e194bc8). +* **[boot-to-talos] Change default install image size from 2GB to 3GB**: Changed default install image size from 2GB to 3GB to accommodate larger installations ([**@kvaps**](https://github.com/kvaps) in cozystack/boot-to-talos@3bfb035). +* **[cozyhr] feat(values): add valuesFrom support for HelmRelease**: Added valuesFrom support for HelmRelease in cozyhr tool, enabling better configuration management ([**@kvaps**](https://github.com/kvaps) in cozystack/cozyhr@7dff0c8). +* **[cozyhr] Rename cozypkg to cozyhr**: Renamed cozypkg tool to cozyhr for better branding ([**@kvaps**](https://github.com/kvaps) in cozystack/cozyhr@1029461). + +--- + +## Contributors + +We'd like to thank all contributors who made this release possible: + +* [**@IvanHunters**](https://github.com/IvanHunters) +* [**@kvaps**](https://github.com/kvaps) +* [**@lexfrei**](https://github.com/lexfrei) +* [**@lllamnyp**](https://github.com/lllamnyp) +* [**@nbykov0**](https://github.com/nbykov0) +* [**@LoneExile**](https://github.com/LoneExile) +* [**@kingdonb**](https://github.com/kingdonb) +* [**@androndo**](https://github.com/androndo) +* [**@SerjioTT**](https://github.com/SerjioTT) +* [**@matthieu-robin**](https://github.com/matthieu-robin) +* [**@diegolakatos**](https://github.com/diegolakatos) + +--- + +**Full Changelog**: [v0.39.0...v0.40.0](https://github.com/cozystack/cozystack/compare/v0.39.0...v0.40.0) + + + From dc2773ba267ee82ec5128f3c49d57912483ae0d0 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Mon, 12 Jan 2026 15:31:46 +0100 Subject: [PATCH 011/125] [linstor] Update piraeus-server patches with critical fixes Update piraeus-server patches to address critical production issues: - Add fix-duplicate-tcp-ports.diff to prevent duplicate TCP ports after toggle-disk operations (upstream PR #476) - Update skip-adjust-when-device-inaccessible.diff with comprehensive fixes for resources stuck in StandAlone after reboot, Unknown state race condition, and encrypted LUKS resource deletion (upstream PR #477) ```release-note [linstor] Fix DRBD resources stuck in StandAlone state after reboot and encrypted resource deletion issues ``` Co-Authored-By: Claude Signed-off-by: Andrei Kvapil --- .../images/piraeus-server/patches/README.md | 6 +- .../patches/fix-duplicate-tcp-ports.diff | 87 ++++++++ .../skip-adjust-when-device-inaccessible.diff | 192 +++++++++++++++--- 3 files changed, 258 insertions(+), 27 deletions(-) create mode 100644 packages/system/linstor/images/piraeus-server/patches/fix-duplicate-tcp-ports.diff diff --git a/packages/system/linstor/images/piraeus-server/patches/README.md b/packages/system/linstor/images/piraeus-server/patches/README.md index c219eaf3..5ee29318 100644 --- a/packages/system/linstor/images/piraeus-server/patches/README.md +++ b/packages/system/linstor/images/piraeus-server/patches/README.md @@ -8,5 +8,7 @@ Custom patches for piraeus-server (linstor-server) v1.32.3. - Upstream: [#475](https://github.com/LINBIT/linstor-server/pull/475) - **force-metadata-check-on-disk-add.diff** — Create metadata during toggle-disk from diskless to diskful - Upstream: [#474](https://github.com/LINBIT/linstor-server/pull/474) -- **skip-adjust-when-device-inaccessible.diff** — Skip DRBD adjust/res file regeneration when child layer device is inaccessible - - Upstream: [#471](https://github.com/LINBIT/linstor-server/pull/471) +- **fix-duplicate-tcp-ports.diff** — Prevent duplicate TCP ports after toggle-disk operations + - Upstream: [#476](https://github.com/LINBIT/linstor-server/pull/476) +- **skip-adjust-when-device-inaccessible.diff** — Fix resources stuck in StandAlone after reboot, Unknown state race condition, and encrypted resource deletion + - Upstream: [#477](https://github.com/LINBIT/linstor-server/pull/477) diff --git a/packages/system/linstor/images/piraeus-server/patches/fix-duplicate-tcp-ports.diff b/packages/system/linstor/images/piraeus-server/patches/fix-duplicate-tcp-ports.diff new file mode 100644 index 00000000..07cd9eac --- /dev/null +++ b/packages/system/linstor/images/piraeus-server/patches/fix-duplicate-tcp-ports.diff @@ -0,0 +1,87 @@ +From 1250abe99d64a0501795e37d3b6af62410002239 Mon Sep 17 00:00:00 2001 +From: Andrei Kvapil +Date: Mon, 12 Jan 2026 13:44:46 +0100 +Subject: [PATCH] fix(drbd): prevent duplicate TCP ports after toggle-disk + operations + +Remove redundant ensureStackDataExists() call with empty payload from +resetStoragePools() method that was causing TCP port conflicts after +toggle-disk operations. + +Root Cause: +----------- +The resetStoragePools() method, introduced in 2019 (commit 95cc17d0b8), +calls ensureStackDataExists() with an empty LayerPayload. This worked +correctly when TCP ports were stored at RscDfn level. + +After the TCP port migration to per-node level (commit f754943463, May +2025), this empty payload results in DrbdRscData being created without +TCP ports assigned. The controller then sends a Pojo with an empty port +Set to satellites. + +On satellites, when DrbdRscData is initialized with an empty port list, +initPorts() uses preferredNewPortsRef from peer resources. Since +SatelliteDynamicNumberPool.tryAllocate() always returns true (no-op), +any port from preferredNewPortsRef is accepted without conflict checking, +leading to duplicate TCP port assignments. + +Impact: +------- +This regression affects toggle-disk operations, particularly: +- Snapshot creation/restore operations +- Manual toggle-disk operations +- Any operation calling resetStoragePools() + +Symptoms include: +- DRBD resources failing to adjust with "port is also used" errors +- Resources stuck in StandAlone or Connecting states +- Multiple resources on the same node using identical TCP ports + +Solution: +--------- +Remove the ensureStackDataExists() call from resetStoragePools() as it +is redundant. The calling code (e.g., CtrlRscToggleDiskApiCallHandler +line 1071) already invokes ensureStackDataExists() with the correct +payload immediately after resetStoragePools(). + +This fix ensures: +1. resetStoragePools() only resets storage pool assignments +2. Layer data creation with proper TCP ports happens via the caller's + ensureStackDataExists() with correct payload +3. No DrbdRscData objects are created without TCP port assignments + +Related Issues: +--------------- +Fixes #454 - Duplicate TCP ports after backup/restore operations +Related to user reports of resources stuck in StandAlone after node +reboots when toggle-disk or backup operations were in progress. + +Testing: +-------- +Verified that: +- Toggle-disk operations no longer create resources without TCP ports +- Backup/restore operations complete without TCP port conflicts +- Resources maintain unique TCP ports across toggle-disk cycles + +Co-Authored-By: Claude +Signed-off-by: Andrei Kvapil +--- + .../linbit/linstor/layer/resource/CtrlRscLayerDataFactory.java | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/controller/src/main/java/com/linbit/linstor/layer/resource/CtrlRscLayerDataFactory.java b/controller/src/main/java/com/linbit/linstor/layer/resource/CtrlRscLayerDataFactory.java +index 3538b380c..4f589145e 100644 +--- a/controller/src/main/java/com/linbit/linstor/layer/resource/CtrlRscLayerDataFactory.java ++++ b/controller/src/main/java/com/linbit/linstor/layer/resource/CtrlRscLayerDataFactory.java +@@ -276,8 +276,6 @@ public class CtrlRscLayerDataFactory + + rscDataToProcess.addAll(rscData.getChildren()); + } +- +- ensureStackDataExists(rscRef, null, new LayerPayload()); + } + catch (AccessDeniedException exc) + { +-- +2.39.5 (Apple Git-154) + diff --git a/packages/system/linstor/images/piraeus-server/patches/skip-adjust-when-device-inaccessible.diff b/packages/system/linstor/images/piraeus-server/patches/skip-adjust-when-device-inaccessible.diff index 18399c41..65c8be7c 100644 --- a/packages/system/linstor/images/piraeus-server/patches/skip-adjust-when-device-inaccessible.diff +++ b/packages/system/linstor/images/piraeus-server/patches/skip-adjust-when-device-inaccessible.diff @@ -1,30 +1,27 @@ -diff --git a/satellite/src/main/java/com/linbit/linstor/core/devmgr/DeviceHandlerImpl.java b/satellite/src/main/java/com/linbit/linstor/core/devmgr/DeviceHandlerImpl.java -index abc123def..def456abc 100644 ---- a/satellite/src/main/java/com/linbit/linstor/core/devmgr/DeviceHandlerImpl.java -+++ b/satellite/src/main/java/com/linbit/linstor/core/devmgr/DeviceHandlerImpl.java -@@ -83,6 +83,8 @@ import java.util.TreeMap; - import java.util.TreeSet; - import java.util.concurrent.atomic.AtomicBoolean; - import java.util.function.Function; -+import java.nio.file.Files; -+import java.nio.file.Paths; +From 6a556821b9a0996d34389a27b941694ce810a44c Mon Sep 17 00:00:00 2001 +From: Andrei Kvapil +Date: Mon, 12 Jan 2026 14:26:57 +0100 +Subject: [PATCH 1/3] Fix: Skip DRBD adjust/res file regeneration when child + layer device is inaccessible + +When deleting encrypted (LUKS) resources, the LUKS layer may close its device +before the DRBD layer attempts to adjust the resource or regenerate the res +file. This causes 'Failed to adjust DRBD resource' errors. + +This fix adds checks before regenerateResFile() and drbdUtils.adjust() +to verify that child layer devices are accessible. If a child device doesn't +exist or is not accessible (e.g., LUKS device is closed during resource +deletion), these operations are skipped and the adjustRequired flag is cleared, +allowing resource deletion to proceed successfully. + +Co-Authored-By: Claude +Signed-off-by: Andrei Kvapil +--- + .../linbit/linstor/layer/drbd/DrbdLayer.java | 72 ++++++++++++++++--- + 1 file changed, 61 insertions(+), 11 deletions(-) - @Singleton - public class DeviceHandlerImpl implements DeviceHandler -@@ -1646,7 +1648,10 @@ public class DeviceHandlerImpl implements DeviceHandler - private void updateDiscGran(VlmProviderObject vlmData) throws DatabaseException, StorageException - { - String devicePath = vlmData.getDevicePath(); -- if (devicePath != null && vlmData.exists()) -+ // Check if device path physically exists before calling lsblk -+ // This is important for DRBD devices which might be temporarily unavailable during adjust -+ // (drbdadm adjust brings devices down/up, and kernel might not have created the device node yet) -+ if (devicePath != null && vlmData.exists() && Files.exists(Paths.get(devicePath))) - { - if (vlmData.getDiscGran() == VlmProviderObject.UNINITIALIZED_SIZE) - { diff --git a/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java b/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java -index 01967a3..871d830 100644 +index 01967a31f..871d830d1 100644 --- a/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java +++ b/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java @@ -592,7 +592,29 @@ public class DrbdLayer implements DeviceLayer @@ -116,3 +113,148 @@ index 01967a3..871d830 100644 } } +-- +2.39.5 (Apple Git-154) + + +From afe51ea674c4a350c27d1f2cacfecf6fe42b8a7a Mon Sep 17 00:00:00 2001 +From: Andrei Kvapil +Date: Mon, 12 Jan 2026 14:27:52 +0100 +Subject: [PATCH 2/3] fix(satellite): skip lsblk when device path doesn't + physically exist + +Add physical device path existence check before calling lsblk in updateDiscGran(). +This prevents race condition when drbdadm adjust temporarily brings devices down/up +and the kernel hasn't created the device node yet. + +Issue: After satellite restart with patched code, some DRBD resources ended up in +Unknown state because: +1. drbdadm adjust successfully completes (brings devices up) +2. updateDiscGran() immediately tries to check discard granularity +3. /dev/drbd* device node doesn't exist yet (kernel hasn't created it) +4. lsblk fails with exit code 32 "not a block device" +5. StorageException interrupts DeviceManager cycle +6. DRBD device remains in incomplete state + +Solution: Check Files.exists(devicePath) before calling lsblk. If device doesn't +exist yet, skip the check - it will be retried in the next DeviceManager cycle +when the device node is available. + +Co-Authored-By: Claude +Signed-off-by: Andrei Kvapil +--- + .../linbit/linstor/core/devmgr/DeviceHandlerImpl.java | 9 +++++++-- + 1 file changed, 7 insertions(+), 2 deletions(-) + +diff --git a/satellite/src/main/java/com/linbit/linstor/core/devmgr/DeviceHandlerImpl.java b/satellite/src/main/java/com/linbit/linstor/core/devmgr/DeviceHandlerImpl.java +index 49138a8fd..1c13cfc9d 100644 +--- a/satellite/src/main/java/com/linbit/linstor/core/devmgr/DeviceHandlerImpl.java ++++ b/satellite/src/main/java/com/linbit/linstor/core/devmgr/DeviceHandlerImpl.java +@@ -68,6 +68,8 @@ import javax.inject.Inject; + import javax.inject.Provider; + import javax.inject.Singleton; + ++import java.nio.file.Files; ++import java.nio.file.Paths; + import java.util.ArrayList; + import java.util.Collection; + import java.util.Collections; +@@ -1645,8 +1647,11 @@ public class DeviceHandlerImpl implements DeviceHandler + + private void updateDiscGran(VlmProviderObject vlmData) throws DatabaseException, StorageException + { +- String devicePath = vlmData.getDevicePath(); +- if (devicePath != null && vlmData.exists()) ++ @Nullable String devicePath = vlmData.getDevicePath(); ++ // Check if device path physically exists before calling lsblk ++ // This is important for DRBD devices which might be temporarily unavailable during adjust ++ // (drbdadm adjust brings devices down/up, and kernel might not have created the device node yet) ++ if (devicePath != null && vlmData.exists() && Files.exists(Paths.get(devicePath))) + { + if (vlmData.getDiscGran() == VlmProviderObject.UNINITIALIZED_SIZE) + { +-- +2.39.5 (Apple Git-154) + + +From de1f22e7c008c5479f85a3b1ebdf8461944210f4 Mon Sep 17 00:00:00 2001 +From: Andrei Kvapil +Date: Mon, 12 Jan 2026 14:28:23 +0100 +Subject: [PATCH 3/3] fix(drbd): only check child devices when disk access is + actually needed +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The previous implementation blocked `drbdadm adjust` whenever child +device paths were unavailable, even for operations that don't require +disk access (like network reconnect from StandAlone to Connected). + +After node reboot, DRBD resources often remain in StandAlone state +because: +1. updateResourceToCurrentDrbdState() correctly detects StandAlone + and sets adjustRequired=true +2. However, canAdjust check fails because child volumes may have + devicePath=null (due to INACTIVE flag, cloning state, or + initialization race) +3. adjust is skipped → adjustRequired=false → resources stay StandAlone + +Root cause: The canAdjust check was added to protect LUKS deletion +scenarios but was applied to ALL cases, including network reconnect +which doesn't need disk access. + +Fix: Check child device accessibility only when disk access is actually +required (volume creation, resize, metadata operations). Network +reconnect operations (StandAlone → Connected) now proceed without +checking child devices. + +This ensures automatic DRBD reconnection after reboot while preserving +protection for LUKS deletion scenarios. + +Co-Authored-By: Claude +Signed-off-by: Andrei Kvapil +--- + .../linbit/linstor/layer/drbd/DrbdLayer.java | 27 ++++++++++++++++++- + 1 file changed, 26 insertions(+), 1 deletion(-) + +diff --git a/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java b/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java +index 871d830d1..78b8195a4 100644 +--- a/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java ++++ b/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java +@@ -792,7 +792,32 @@ public class DrbdLayer implements DeviceLayer + // This is important for encrypted resources (LUKS) where the device + // might be closed during deletion + boolean canAdjust = true; +- if (!skipDisk && !drbdRscData.getAbsResource().isDrbdDiskless(workerCtx)) ++ ++ // IMPORTANT: Check child volumes only when disk access is actually needed. ++ // For network reconnect (StandAlone -> Connected), disk access is not required. ++ boolean needsDiskAccess = false; ++ ++ // Check if there are pending operations that require disk access ++ for (DrbdVlmData drbdVlmData : drbdRscData.getVlmLayerObjects().values()) ++ { ++ Volume vlm = (Volume) drbdVlmData.getVolume(); ++ StateFlags vlmFlags = vlm.getFlags(); ++ ++ // Disk access is needed if: ++ // - creating a new volume ++ // - resizing ++ // - checking/creating metadata ++ if (!drbdVlmData.exists() || ++ drbdVlmData.checkMetaData() || ++ vlmFlags.isSomeSet(workerCtx, Volume.Flags.RESIZE, Volume.Flags.DRBD_RESIZE)) ++ { ++ needsDiskAccess = true; ++ break; ++ } ++ } ++ ++ // Check child volumes only if disk access is actually needed ++ if (needsDiskAccess && !skipDisk && !drbdRscData.getAbsResource().isDrbdDiskless(workerCtx)) + { + AbsRscLayerObject dataChild = drbdRscData.getChildBySuffix(RscLayerSuffixes.SUFFIX_DATA); + if (dataChild != null) +-- +2.39.5 (Apple Git-154) + From 74589ce9151706ee6687ad7b0233eafcbb987297 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Mon, 12 Jan 2026 22:24:54 +0100 Subject: [PATCH 012/125] fix(linstor): remove node-level RWX validation Remove node-level RWX block validation from linstor-csi as controller-level check is sufficient. The controller already validates that all pods attached to RWX block volume belong to the same VM by extracting vmName from pod owner references (VirtualMachineInstance). This simplifies the validation logic and fixes VM live migration issues. Update linstor-csi image tag with rebuilt image containing the fix. Co-Authored-By: Claude Signed-off-by: Andrei Kvapil --- .../patches/001-rwx-validation.diff | 62 ++----------------- 1 file changed, 4 insertions(+), 58 deletions(-) diff --git a/packages/system/linstor/images/linstor-csi/patches/001-rwx-validation.diff b/packages/system/linstor/images/linstor-csi/patches/001-rwx-validation.diff index f6f0d83d..9c26f565 100644 --- a/packages/system/linstor/images/linstor-csi/patches/001-rwx-validation.diff +++ b/packages/system/linstor/images/linstor-csi/patches/001-rwx-validation.diff @@ -1,25 +1,8 @@ diff --git a/pkg/driver/driver.go b/pkg/driver/driver.go -index bea69a8..848bd5b 100644 +index bea69a8..69e71a6 100644 --- a/pkg/driver/driver.go +++ b/pkg/driver/driver.go -@@ -401,6 +401,16 @@ func (d Driver) NodePublishVolume(ctx context.Context, req *csi.NodePublishVolum - } - } - -+ // Validate RWX block volumes on node side using local filesystem check -+ // This provides protection for the edge case where two pods from different VMs land on the same node -+ if req.GetVolumeCapability().GetBlock() != nil && -+ req.GetVolumeCapability().GetAccessMode().GetMode() == csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER { -+ if err := d.validateRWXBlockOnNode(ctx, req.GetVolumeId(), req.GetTargetPath()); err != nil { -+ return nil, status.Errorf(codes.FailedPrecondition, -+ "NodePublishVolume failed for %s: %v", req.GetVolumeId(), err) -+ } -+ } -+ - if block := req.GetVolumeCapability().GetBlock(); block != nil { - volCtx.MountOptions = []string{"bind"} - } -@@ -707,6 +717,255 @@ func (d Driver) DeleteVolume(ctx context.Context, req *csi.DeleteVolumeRequest) +@@ -707,6 +707,219 @@ func (d Driver) DeleteVolume(ctx context.Context, req *csi.DeleteVolumeRequest) return &csi.DeleteVolumeResponse{}, nil } @@ -235,54 +218,17 @@ index bea69a8..848bd5b 100644 + + return vmName, nil +} -+ -+// validateRWXBlockOnNode performs node-side validation of RWX block volumes using local filesystem check. -+// This provides protection against the edge case where two pods from different VMs land on the same node. -+// Since ControllerPublishVolume is called only once per (volumeID, nodeID) pair, not per pod, -+// we need to check if the volume is already mounted for another pod on this node. -+func (d Driver) validateRWXBlockOnNode(ctx context.Context, volumeID, targetPath string) error { -+ // Extract base directory for this volume (contains subdirectories per pod UID) -+ baseDir := filepath.Dir(targetPath) -+ -+ // List existing mounts for this volume -+ entries, err := os.ReadDir(baseDir) -+ if err != nil { -+ if os.IsNotExist(err) { -+ // Directory doesn't exist yet - first mount -+ return nil -+ } -+ -+ d.log.WithError(err).Warn("cannot check existing mounts for RWX validation") -+ -+ return nil -+ } -+ -+ // If there are already other mounts, block the second one -+ if len(entries) > 0 { -+ d.log.WithFields(logrus.Fields{ -+ "volumeID": volumeID, -+ "existingMounts": len(entries), -+ "baseDir": baseDir, -+ }).Warn("blocking RWX block volume mount: already mounted for another pod on this node") -+ -+ return fmt.Errorf("RWX block volume is already mounted for another pod on this node - " + -+ "multiple pods on the same node sharing a block device is not supported (only for live migration across nodes)") -+ } -+ -+ return nil -+} + // ControllerPublishVolume https://github.com/container-storage-interface/spec/blob/v1.9.0/spec.md#controllerpublishvolume func (d Driver) ControllerPublishVolume(ctx context.Context, req *csi.ControllerPublishVolumeRequest) (*csi.ControllerPublishVolumeResponse, error) { if req.GetVolumeId() == "" { -@@ -751,6 +1010,15 @@ func (d Driver) ControllerPublishVolume(ctx context.Context, req *csi.Controller +@@ -751,6 +964,14 @@ func (d Driver) ControllerPublishVolume(ctx context.Context, req *csi.Controller // ReadWriteMany block volume rwxBlock := req.VolumeCapability.AccessMode.GetMode() == csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER && req.VolumeCapability.GetBlock() != nil + // Validate RWX block attachment to prevent misuse of allow-two-primaries + if rwxBlock { -+ _, err := d.validateRWXBlockAttachment(ctx, req.GetVolumeId()) -+ if err != nil { ++ if _, err := d.validateRWXBlockAttachment(ctx, req.GetVolumeId()); err != nil { + return nil, status.Errorf(codes.FailedPrecondition, + "ControllerPublishVolume failed for %s: %v", req.GetVolumeId(), err) + } From dececc85878c55797ee3859968731e0cc20c3a6a Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Tue, 13 Jan 2026 08:04:35 +0100 Subject: [PATCH 013/125] Release v0.40.1 (#1855) This PR prepares the release `v0.40.1`. Signed-off-by: Andrei Kvapil --- packages/apps/kubernetes/images/cluster-autoscaler.tag | 2 +- packages/core/installer/values.yaml | 2 +- packages/core/platform/values.yaml | 2 +- packages/core/testing/values.yaml | 2 +- packages/extra/bootbox/images/matchbox.tag | 2 +- packages/extra/seaweedfs/images/objectstorage-sidecar.tag | 2 +- packages/system/cozystack-api/values.yaml | 2 +- packages/system/cozystack-controller/values.yaml | 4 ++-- packages/system/dashboard/templates/configmap.yaml | 2 +- packages/system/dashboard/values.yaml | 6 +++--- packages/system/kamaji/values.yaml | 4 ++-- packages/system/kubeovn-plunger/values.yaml | 2 +- packages/system/kubeovn-webhook/values.yaml | 2 +- packages/system/lineage-controller-webhook/values.yaml | 2 +- packages/system/linstor/values.yaml | 3 +-- packages/system/objectstorage-controller/values.yaml | 2 +- packages/system/seaweedfs/values.yaml | 2 +- 17 files changed, 21 insertions(+), 22 deletions(-) diff --git a/packages/apps/kubernetes/images/cluster-autoscaler.tag b/packages/apps/kubernetes/images/cluster-autoscaler.tag index d5ab33d1..03a5ad61 100644 --- a/packages/apps/kubernetes/images/cluster-autoscaler.tag +++ b/packages/apps/kubernetes/images/cluster-autoscaler.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/cluster-autoscaler:0.0.0@sha256:2d39989846c3579dd020b9f6c77e6e314cc81aa344eaac0f6d633e723c17196d +ghcr.io/cozystack/cozystack/cluster-autoscaler:0.0.0@sha256:6f2b1d6b0b2bdc66f1cbb30c59393369cbf070cb8f5fec748f176952273483cc diff --git a/packages/core/installer/values.yaml b/packages/core/installer/values.yaml index 0fcf6c25..61870b23 100644 --- a/packages/core/installer/values.yaml +++ b/packages/core/installer/values.yaml @@ -1,5 +1,5 @@ cozystack: - image: ghcr.io/cozystack/cozystack/installer:v0.38.2@sha256:9ff92b655de6f9bea3cba4cd42dcffabd9aace6966dcfb1cc02dda2420ea4a15 + image: ghcr.io/cozystack/cozystack/installer:v0.40.1@sha256:69a8f95b8ddcf122a8859a957017b3d9e33c5eca3bfbf30c112c109e355f4f1c cozystackOperator: enabled: false image: ghcr.io/cozystack/cozystack/cozystack-operator:latest@sha256:f7f6e0fd9e896b7bfa642d0bfa4378bc14e646bc5c2e86e2e09a82770ef33181 diff --git a/packages/core/platform/values.yaml b/packages/core/platform/values.yaml index 363d1921..84efcb78 100644 --- a/packages/core/platform/values.yaml +++ b/packages/core/platform/values.yaml @@ -1,2 +1,2 @@ assets: - image: ghcr.io/cozystack/cozystack/cozystack-assets:latest@sha256:19b166819d0205293c85d8351a3e038dc4c146b876a8e2ae21dce1d54f0b9e33 + image: ghcr.io/cozystack/cozystack/cozystack-assets:v0.40.1@sha256:ee1248692753820fe11139789680fd24dc4dbdba85a6cb70e086447d9b9b1d99 diff --git a/packages/core/testing/values.yaml b/packages/core/testing/values.yaml index de6bb330..90a9ed18 100644 --- a/packages/core/testing/values.yaml +++ b/packages/core/testing/values.yaml @@ -1,2 +1,2 @@ e2e: - image: ghcr.io/cozystack/cozystack/e2e-sandbox:v0.38.2@sha256:84be9e42bc2c04b0765c8b89e0a9728c49ebf4676a92522b007af96ae9aec68d + image: ghcr.io/cozystack/cozystack/e2e-sandbox:v0.40.1@sha256:78a556a059411cac89249c2e22596223128a1c74fbfcac4787fd74e4a66f66a6 diff --git a/packages/extra/bootbox/images/matchbox.tag b/packages/extra/bootbox/images/matchbox.tag index 2e63d30b..aa8ecbfd 100644 --- a/packages/extra/bootbox/images/matchbox.tag +++ b/packages/extra/bootbox/images/matchbox.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/matchbox:v0.38.2@sha256:9cd7f46fcae119a3f8e35b428b018d0cb6da7b0cdd2ce764cc9fbf6dcd903f27 +ghcr.io/cozystack/cozystack/matchbox:v0.40.1@sha256:293d562c134fc4ee0a9a756df32422b849715fe25a2120b69cb415ca98217363 diff --git a/packages/extra/seaweedfs/images/objectstorage-sidecar.tag b/packages/extra/seaweedfs/images/objectstorage-sidecar.tag index ec59113f..1724acc3 100644 --- a/packages/extra/seaweedfs/images/objectstorage-sidecar.tag +++ b/packages/extra/seaweedfs/images/objectstorage-sidecar.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/objectstorage-sidecar:v0.38.2@sha256:ff3281fe53a97d2cd5cd94bd4c4d8ff08189508729869bb39b3f60c80da5f919 +ghcr.io/cozystack/cozystack/objectstorage-sidecar:v0.40.1@sha256:4bb47c8adb34543403a16d1ff61b307939850e8cf5fc365388469e30dfb9681b diff --git a/packages/system/cozystack-api/values.yaml b/packages/system/cozystack-api/values.yaml index 8bf0a803..10a79b25 100644 --- a/packages/system/cozystack-api/values.yaml +++ b/packages/system/cozystack-api/values.yaml @@ -1,5 +1,5 @@ cozystackAPI: - image: ghcr.io/cozystack/cozystack/cozystack-api:v0.38.2@sha256:d17f1c59658731e5a2063c3db348adbc03b5cd31720052016b68449164cf2f14 + image: ghcr.io/cozystack/cozystack/cozystack-api:v0.40.1@sha256:408885b509d80ca30a85416f87d07112bc7f070374e71e80b64818fbb24ad1ee localK8sAPIEndpoint: enabled: true replicas: 2 diff --git a/packages/system/cozystack-controller/values.yaml b/packages/system/cozystack-controller/values.yaml index 1b365a43..a28c13c5 100644 --- a/packages/system/cozystack-controller/values.yaml +++ b/packages/system/cozystack-controller/values.yaml @@ -1,6 +1,6 @@ cozystackController: - image: ghcr.io/cozystack/cozystack/cozystack-controller:v0.38.2@sha256:468b2eccbc0aa00bd3d72d56624a46e6ba178fa279cdd19248af74d32ea7d319 + image: ghcr.io/cozystack/cozystack/cozystack-controller:v0.40.1@sha256:75636007f63e1b2cb51fc2e70782b707330d1e728c32642a4edc500b614f0781 debug: false disableTelemetry: false - cozystackVersion: "v0.38.2" + cozystackVersion: "v0.40.1" cozystackAPIKind: "DaemonSet" diff --git a/packages/system/dashboard/templates/configmap.yaml b/packages/system/dashboard/templates/configmap.yaml index f148109f..861d4eed 100644 --- a/packages/system/dashboard/templates/configmap.yaml +++ b/packages/system/dashboard/templates/configmap.yaml @@ -1,6 +1,6 @@ {{- $brandingConfig := .Values._cluster.branding | default dict }} -{{- $tenantText := "v0.38.2" }} +{{- $tenantText := "v0.40.1" }} {{- $footerText := "Cozystack" }} {{- $titleText := "Cozystack Dashboard" }} {{- $logoText := "" }} diff --git a/packages/system/dashboard/values.yaml b/packages/system/dashboard/values.yaml index fed8149b..94c395bb 100644 --- a/packages/system/dashboard/values.yaml +++ b/packages/system/dashboard/values.yaml @@ -1,6 +1,6 @@ openapiUI: - image: ghcr.io/cozystack/cozystack/openapi-ui:v0.38.2@sha256:5aafb6c864c5523418d021a9fe5b514990d36972b6f1de9c34a1cd41f9d8bf7e + image: ghcr.io/cozystack/cozystack/openapi-ui:v0.40.1@sha256:a427114308b9c87ab71b50980cba0d673a730791629164335d2046f7d1740327 openapiUIK8sBff: - image: ghcr.io/cozystack/cozystack/openapi-ui-k8s-bff:v0.38.2@sha256:7ffd8ae7b9da73fec7ae61a71c9c821a718d89a1b1df0197e09fda57678e1220 + image: ghcr.io/cozystack/cozystack/openapi-ui-k8s-bff:v0.40.1@sha256:fda379dce49c2cd8cb8d7d2a1d8ec6f7bedb3419c058c4355ecdece1c1e937f4 tokenProxy: - image: ghcr.io/cozystack/cozystack/token-proxy:v0.38.2@sha256:fad27112617bb17816702571e1f39d0ac3fe5283468d25eb12f79906cdab566b + image: ghcr.io/cozystack/cozystack/token-proxy:v0.40.1@sha256:4fc8a11f8a1a81aa0774ae2b1ed2e05d36d0b3ef1e37979cc4994e65114d93ae diff --git a/packages/system/kamaji/values.yaml b/packages/system/kamaji/values.yaml index b5b77213..35c15ef9 100644 --- a/packages/system/kamaji/values.yaml +++ b/packages/system/kamaji/values.yaml @@ -3,7 +3,7 @@ kamaji: deploy: false image: pullPolicy: IfNotPresent - tag: v0.38.2@sha256:13741b8f6dfede3ea0fd16d8bbebae810bc19254a81d7e5a139535efa17eabff + tag: v0.40.1@sha256:0bcfb2d376224b18a0627d7b18ba6202bb8a553f71796023e12740d9513740c7 repository: ghcr.io/cozystack/cozystack/kamaji resources: limits: @@ -13,4 +13,4 @@ kamaji: cpu: 100m memory: 100Mi extraArgs: - - --migrate-image=ghcr.io/cozystack/cozystack/kamaji:v0.38.2@sha256:13741b8f6dfede3ea0fd16d8bbebae810bc19254a81d7e5a139535efa17eabff + - --migrate-image=ghcr.io/cozystack/cozystack/kamaji:v0.40.1@sha256:0bcfb2d376224b18a0627d7b18ba6202bb8a553f71796023e12740d9513740c7 diff --git a/packages/system/kubeovn-plunger/values.yaml b/packages/system/kubeovn-plunger/values.yaml index 2e6fc195..10d8508a 100644 --- a/packages/system/kubeovn-plunger/values.yaml +++ b/packages/system/kubeovn-plunger/values.yaml @@ -1,4 +1,4 @@ portSecurity: true routes: "" -image: ghcr.io/cozystack/cozystack/kubeovn-plunger:v0.38.2@sha256:76c8af24cbec0261718c13c0150aa81c238a956626d4fd7baa8970b47fb3a6f0 +image: ghcr.io/cozystack/cozystack/kubeovn-plunger:v0.40.1@sha256:2a17dfa8eec0fc6b2adb564742e74f35bd68ac0d29e5ab1d007496aff3c0e97d ovnCentralName: ovn-central diff --git a/packages/system/kubeovn-webhook/values.yaml b/packages/system/kubeovn-webhook/values.yaml index 9304c59d..c60ff144 100644 --- a/packages/system/kubeovn-webhook/values.yaml +++ b/packages/system/kubeovn-webhook/values.yaml @@ -1,3 +1,3 @@ portSecurity: true routes: "" -image: ghcr.io/cozystack/cozystack/kubeovn-webhook:v0.38.2@sha256:8e67b2971f8c079a8b0636be1d091a9545d6cb653d745ff222a5966f56f903bd +image: ghcr.io/cozystack/cozystack/kubeovn-webhook:v0.40.1@sha256:e6334c29d3aaf0dea766c88e3e05b53ad623d1bb497b3c836e6f76adade45b29 diff --git a/packages/system/lineage-controller-webhook/values.yaml b/packages/system/lineage-controller-webhook/values.yaml index ab5ccbbf..55cb019b 100644 --- a/packages/system/lineage-controller-webhook/values.yaml +++ b/packages/system/lineage-controller-webhook/values.yaml @@ -1,5 +1,5 @@ lineageControllerWebhook: - image: ghcr.io/cozystack/cozystack/lineage-controller-webhook:v0.38.2@sha256:a5c750a0f46e8e25329b3ee2110d5dfb077c73e473195f1ed768d28d6f43902c + image: ghcr.io/cozystack/cozystack/lineage-controller-webhook:v0.40.1@sha256:00650eb92c8b93a0afc382a0c5e46ade453e430867be7dab9647a6fc2896f097 debug: false localK8sAPIEndpoint: enabled: true diff --git a/packages/system/linstor/values.yaml b/packages/system/linstor/values.yaml index 470cf2e5..173aacb4 100644 --- a/packages/system/linstor/values.yaml +++ b/packages/system/linstor/values.yaml @@ -1,8 +1,7 @@ piraeusServer: image: repository: ghcr.io/cozystack/cozystack/piraeus-server - tag: latest@sha256:417532baa2801288147cd9ac9ae260751c1a7754f0b829725d09b72a770c111a - + tag: 1.32.3@sha256:1138c8dc0a117360ef70e2e2ab97bc2696419b63f46358f7668c7e01a96c419b linstor: autoDiskful: enabled: true diff --git a/packages/system/objectstorage-controller/values.yaml b/packages/system/objectstorage-controller/values.yaml index 7a5b5c22..aba36be8 100644 --- a/packages/system/objectstorage-controller/values.yaml +++ b/packages/system/objectstorage-controller/values.yaml @@ -1,3 +1,3 @@ objectstorage: controller: - image: "ghcr.io/cozystack/cozystack/objectstorage-controller:v0.38.2@sha256:7d37495cce46d30d4613ecfacaa7b7f140e7ea8f3dbcc3e8c976e271de6cc71b" + image: "ghcr.io/cozystack/cozystack/objectstorage-controller:v0.40.1@sha256:e8f605a5ec4b801dfaf49aa7bce8d7a1ce28df578e23a0b859571f60f98efe8c" diff --git a/packages/system/seaweedfs/values.yaml b/packages/system/seaweedfs/values.yaml index 830c4faa..fb3defa6 100644 --- a/packages/system/seaweedfs/values.yaml +++ b/packages/system/seaweedfs/values.yaml @@ -177,7 +177,7 @@ seaweedfs: bucketClassName: "seaweedfs" region: "" sidecar: - image: "ghcr.io/cozystack/cozystack/objectstorage-sidecar:v0.38.2@sha256:ff3281fe53a97d2cd5cd94bd4c4d8ff08189508729869bb39b3f60c80da5f919" + image: "ghcr.io/cozystack/cozystack/objectstorage-sidecar:v0.40.1@sha256:4bb47c8adb34543403a16d1ff61b307939850e8cf5fc365388469e30dfb9681b" certificates: commonName: "SeaweedFS CA" ipAddresses: [] From 3c4f0cd952e91ec54ddb26aaae2f550ebd93a320 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Tue, 13 Jan 2026 13:18:13 +0100 Subject: [PATCH 014/125] [linstor] Refactor node-level RWX validation Signed-off-by: Andrei Kvapil --- .../patches/001-rwx-validation.diff | 386 ++++++++++++------ 1 file changed, 253 insertions(+), 133 deletions(-) diff --git a/packages/system/linstor/images/linstor-csi/patches/001-rwx-validation.diff b/packages/system/linstor/images/linstor-csi/patches/001-rwx-validation.diff index 9c26f565..7da0f137 100644 --- a/packages/system/linstor/images/linstor-csi/patches/001-rwx-validation.diff +++ b/packages/system/linstor/images/linstor-csi/patches/001-rwx-validation.diff @@ -1,100 +1,202 @@ +diff --git a/cmd/linstor-csi/linstor-csi.go b/cmd/linstor-csi/linstor-csi.go +index 143f6cee..bd28e06e 100644 +--- a/cmd/linstor-csi/linstor-csi.go ++++ b/cmd/linstor-csi/linstor-csi.go +@@ -41,22 +41,23 @@ import ( + + func main() { + var ( +- lsEndpoint = flag.String("linstor-endpoint", "", "Controller API endpoint for LINSTOR") +- lsSkipTLSVerification = flag.Bool("linstor-skip-tls-verification", false, "If true, do not verify tls") +- csiEndpoint = flag.String("csi-endpoint", "unix:///var/lib/kubelet/plugins/linstor.csi.linbit.com/csi.sock", "CSI endpoint") +- node = flag.String("node", "", "Node ID to pass to node service") +- logLevel = flag.String("log-level", "info", "Enable debug log output. Choose from: panic, fatal, error, warn, info, debug") +- rps = flag.Float64("linstor-api-requests-per-second", 0, "Maximum allowed number of LINSTOR API requests per second. Default: Unlimited") +- burst = flag.Int("linstor-api-burst", 1, "Maximum number of API requests allowed before being limited by requests-per-second. Default: 1 (no bursting)") +- bearerTokenFile = flag.String("bearer-token", "", "Read the bearer token from the given file and use it for authentication.") +- propNs = flag.String("property-namespace", linstor.NamespcAuxiliary, "Limit the reported topology keys to properties from the given namespace.") +- labelBySP = flag.Bool("label-by-storage-pool", true, "Set to false to disable labeling of nodes based on their configured storage pools.") +- nodeCacheTimeout = flag.Duration("node-cache-timeout", 1*time.Minute, "Duration for which the results of node and storage pool related API responses should be cached.") +- resourceCacheTimeout = flag.Duration("resource-cache-timeout", 30*time.Second, "Duration for which the results of resource related API responses should be cached.") +- resyncAfter = flag.Duration("resync-after", 5*time.Minute, "Duration after which reconciliations (such as for VolumeSnapshotClasses) should be rerun. Set to 0 to disable.") +- enableRWX = flag.Bool("enable-rwx", false, "Enable RWX support via NFS (requires running in Kubernetes).") +- namespace = flag.String("nfs-service-namespace", "", "The namespace the NFS service is running in.") +- reactorConfigMapName = flag.String("nfs-reactor-config-map-name", "linstor-csi-nfs-reactor-config", "Name of the config map used to store promoter configuration") ++ lsEndpoint = flag.String("linstor-endpoint", "", "Controller API endpoint for LINSTOR") ++ lsSkipTLSVerification = flag.Bool("linstor-skip-tls-verification", false, "If true, do not verify tls") ++ csiEndpoint = flag.String("csi-endpoint", "unix:///var/lib/kubelet/plugins/linstor.csi.linbit.com/csi.sock", "CSI endpoint") ++ node = flag.String("node", "", "Node ID to pass to node service") ++ logLevel = flag.String("log-level", "info", "Enable debug log output. Choose from: panic, fatal, error, warn, info, debug") ++ rps = flag.Float64("linstor-api-requests-per-second", 0, "Maximum allowed number of LINSTOR API requests per second. Default: Unlimited") ++ burst = flag.Int("linstor-api-burst", 1, "Maximum number of API requests allowed before being limited by requests-per-second. Default: 1 (no bursting)") ++ bearerTokenFile = flag.String("bearer-token", "", "Read the bearer token from the given file and use it for authentication.") ++ propNs = flag.String("property-namespace", linstor.NamespcAuxiliary, "Limit the reported topology keys to properties from the given namespace.") ++ labelBySP = flag.Bool("label-by-storage-pool", true, "Set to false to disable labeling of nodes based on their configured storage pools.") ++ nodeCacheTimeout = flag.Duration("node-cache-timeout", 1*time.Minute, "Duration for which the results of node and storage pool related API responses should be cached.") ++ resourceCacheTimeout = flag.Duration("resource-cache-timeout", 30*time.Second, "Duration for which the results of resource related API responses should be cached.") ++ resyncAfter = flag.Duration("resync-after", 5*time.Minute, "Duration after which reconciliations (such as for VolumeSnapshotClasses) should be rerun. Set to 0 to disable.") ++ enableRWX = flag.Bool("enable-rwx", false, "Enable RWX support via NFS (requires running in Kubernetes).") ++ namespace = flag.String("nfs-service-namespace", "", "The namespace the NFS service is running in.") ++ reactorConfigMapName = flag.String("nfs-reactor-config-map-name", "linstor-csi-nfs-reactor-config", "Name of the config map used to store promoter configuration") ++ disableRWXBlockValidation = flag.Bool("disable-rwx-block-validation", false, "Disable KubeVirt VM ownership validation for RWX block volumes.") + ) + + flag.Var(&volume.DefaultRemoteAccessPolicy, "default-remote-access-policy", "") +@@ -169,6 +170,10 @@ func main() { + opts = append(opts, driver.ConfigureRWX(*namespace, *reactorConfigMapName)) + } + ++ if *disableRWXBlockValidation { ++ opts = append(opts, driver.DisableRWXBlockValidation()) ++ } ++ + drv, err := driver.NewDriver(opts...) + if err != nil { + log.Fatal(err) diff --git a/pkg/driver/driver.go b/pkg/driver/driver.go -index bea69a8..69e71a6 100644 +index bea69a8b..a39674b6 100644 --- a/pkg/driver/driver.go +++ b/pkg/driver/driver.go -@@ -707,6 +707,219 @@ func (d Driver) DeleteVolume(ctx context.Context, req *csi.DeleteVolumeRequest) - return &csi.DeleteVolumeResponse{}, nil +@@ -83,6 +83,8 @@ type Driver struct { + topologyPrefix string + // resyncAfter is the interval after which reconciliations should be retried + resyncAfter time.Duration ++ // disableRWXBlockValidation disables KubeVirt VM ownership validation for RWX block volumes ++ disableRWXBlockValidation bool + + // Embed for forward compatibility. + csi.UnimplementedIdentityServer +@@ -300,6 +302,17 @@ func ResyncAfter(resyncAfter time.Duration) func(*Driver) error { + } } ++// DisableRWXBlockValidation disables the KubeVirt VM ownership validation for RWX block volumes. ++// When disabled, the driver will not check if multiple pods using the same RWX block volume ++// belong to the same VM. This may be needed in environments where the validation causes issues ++// or when using RWX block volumes outside of KubeVirt. ++func DisableRWXBlockValidation() func(*Driver) error { ++ return func(d *Driver) error { ++ d.disableRWXBlockValidation = true ++ return nil ++ } ++} ++ + // GetPluginInfo https://github.com/container-storage-interface/spec/blob/v1.9.0/spec.md#getplugininfo + func (d Driver) GetPluginInfo(ctx context.Context, req *csi.GetPluginInfoRequest) (*csi.GetPluginInfoResponse, error) { + return &csi.GetPluginInfoResponse{ +@@ -751,6 +764,14 @@ func (d Driver) ControllerPublishVolume(ctx context.Context, req *csi.Controller + // ReadWriteMany block volume + rwxBlock := req.VolumeCapability.AccessMode.GetMode() == csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER && req.VolumeCapability.GetBlock() != nil + ++ // Validate RWX block attachment to prevent misuse of allow-two-primaries ++ if rwxBlock && !d.disableRWXBlockValidation { ++ if _, err := utils.ValidateRWXBlockAttachment(ctx, d.kubeClient, d.log, req.GetVolumeId()); err != nil { ++ return nil, status.Errorf(codes.FailedPrecondition, ++ "ControllerPublishVolume failed for %s: %v", req.GetVolumeId(), err) ++ } ++ } ++ + devPath, err := d.Assignments.Attach(ctx, req.GetVolumeId(), req.GetNodeId(), rwxBlock) + if err != nil { + return nil, status.Errorf(codes.Internal, +diff --git a/pkg/utils/rwx_validation.go b/pkg/utils/rwx_validation.go +new file mode 100644 +index 00000000..9fe82768 +--- /dev/null ++++ b/pkg/utils/rwx_validation.go +@@ -0,0 +1,263 @@ ++/* ++CSI Driver for Linstor ++Copyright © 2018 LINBIT USA, LLC ++ ++This program is free software; you can redistribute it and/or modify ++it under the terms of the GNU General Public License as published by ++the Free Software Foundation; either version 2 of the License, or ++(at your option) any later version. ++ ++This program is distributed in the hope that it will be useful, ++but WITHOUT ANY WARRANTY; without even the implied warranty of ++MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++GNU General Public License for more details. ++ ++You should have received a copy of the GNU General Public License ++along with this program; if not, see . ++*/ ++ ++package utils ++ ++import ( ++ "context" ++ "fmt" ++ ++ "github.com/sirupsen/logrus" ++ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ++ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ++ "k8s.io/apimachinery/pkg/runtime/schema" ++ "k8s.io/client-go/dynamic" ++) ++ +// KubeVirtVMLabel is the label that KubeVirt adds to pods to identify the VM they belong to. +const KubeVirtVMLabel = "vm.kubevirt.io/name" + +// KubeVirtHotplugDiskLabel is the label that KubeVirt adds to hotplug disk pods. +const KubeVirtHotplugDiskLabel = "kubevirt.io" + -+// podGVR is the GroupVersionResource for pods. -+var podGVR = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"} ++// PodGVR is the GroupVersionResource for pods. ++var PodGVR = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"} + -+// pvGVR is the GroupVersionResource for persistent volumes. -+var pvGVR = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "persistentvolumes"} ++// PVGVR is the GroupVersionResource for persistent volumes. ++var PVGVR = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "persistentvolumes"} + -+// getVMNameFromPod extracts the VM name from a pod, handling both regular virt-launcher pods -+// and hotplug disk pods (which reference the virt-launcher pod via ownerReferences). -+func (d Driver) getVMNameFromPod(ctx context.Context, pod *unstructured.Unstructured) (string, error) { -+ labels := pod.GetLabels() -+ if labels == nil { -+ return "", nil -+ } -+ -+ // Direct case: pod has vm.kubevirt.io/name label (virt-launcher pod) -+ if vmName, ok := labels[KubeVirtVMLabel]; ok && vmName != "" { -+ return vmName, nil -+ } -+ -+ // Hotplug disk case: pod has kubevirt.io: hotplug-disk label -+ // Follow ownerReferences to find the virt-launcher pod -+ if hotplugValue, ok := labels[KubeVirtHotplugDiskLabel]; ok && hotplugValue == "hotplug-disk" { -+ ownerRefs := pod.GetOwnerReferences() -+ for _, owner := range ownerRefs { -+ if owner.Kind != "Pod" || owner.Controller == nil || !*owner.Controller { -+ continue -+ } -+ -+ // Get the owner pod (virt-launcher) -+ ownerPod, err := d.kubeClient.Resource(podGVR).Namespace(pod.GetNamespace()).Get(ctx, owner.Name, metav1.GetOptions{}) -+ if err != nil { -+ return "", fmt.Errorf("failed to get owner pod %s: %w", owner.Name, err) -+ } -+ -+ // Extract VM name from owner pod -+ ownerLabels := ownerPod.GetLabels() -+ if ownerLabels != nil { -+ if vmName, ok := ownerLabels[KubeVirtVMLabel]; ok && vmName != "" { -+ d.log.WithFields(logrus.Fields{ -+ "hotplugPod": pod.GetName(), -+ "virtLauncher": owner.Name, -+ "vmName": vmName, -+ }).Debug("resolved VM name from hotplug disk pod via owner reference") -+ -+ return vmName, nil -+ } -+ } -+ -+ return "", fmt.Errorf("owner pod %s does not have %s label", owner.Name, KubeVirtVMLabel) -+ } -+ -+ return "", fmt.Errorf("hotplug disk pod %s has no controller owner reference", pod.GetName()) -+ } -+ -+ return "", nil -+} -+ -+// validateRWXBlockAttachment checks that RWX block volumes are only used by pods belonging to the same VM. ++// ValidateRWXBlockAttachment checks that RWX block volumes are only used by pods belonging to the same VM. +// This prevents misuse of allow-two-primaries while still permitting live migration. +// Returns the VM name if validation passes, or an error if: +// - Multiple pods from different VMs are trying to use the same volume +// - A pod without the KubeVirt VM label is trying to use a volume already attached elsewhere (strict mode) +// Returns empty string for VM name when no pods are using the volume or validation is skipped. -+func (d Driver) validateRWXBlockAttachment(ctx context.Context, volumeID string) (string, error) { -+ d.log.WithField("volumeID", volumeID).Info("validateRWXBlockAttachment called") ++func ValidateRWXBlockAttachment(ctx context.Context, kubeClient dynamic.Interface, log *logrus.Entry, volumeID string) (string, error) { ++ log.WithField("volumeID", volumeID).Info("validateRWXBlockAttachment called") + -+ if d.kubeClient == nil { ++ if kubeClient == nil { + // Not running in Kubernetes, skip validation -+ d.log.Warn("validateRWXBlockAttachment: kubeClient is nil, skipping validation") ++ log.Warn("validateRWXBlockAttachment: kubeClient is nil, skipping validation") + return "", nil + } + -+ // Get PV to find PVC reference (volumeID == PV name in CSI) -+ pv, err := d.kubeClient.Resource(pvGVR).Get(ctx, volumeID, metav1.GetOptions{}) ++ // Get PV to find PVC reference ++ pv, err := kubeClient.Resource(PVGVR).Get(ctx, volumeID, metav1.GetOptions{}) + if err != nil { -+ d.log.WithError(err).Warn("cannot validate RWX attachment: failed to get PV") ++ log.WithError(err).Warn("cannot validate RWX attachment: failed to get PV") ++ return "", nil ++ } ++ ++ // Verify that PV's volumeHandle matches the volumeID ++ volumeHandle, found, err := unstructured.NestedString(pv.Object, "spec", "csi", "volumeHandle") ++ if err != nil { ++ log.WithError(err).Warnf("cannot validate RWX attachment: failed to read volumeHandle for PV %s", volumeID) ++ ++ return "", nil ++ } ++ ++ if !found { ++ log.Warnf("cannot validate RWX attachment: volumeHandle not found for PV %s", volumeID) ++ ++ return "", nil ++ } ++ ++ if volumeHandle != volumeID { ++ log.WithFields(logrus.Fields{ ++ "volumeID": volumeID, ++ "volumeHandle": volumeHandle, ++ }).Warn("cannot validate RWX attachment: PV volumeHandle does not match volumeID") ++ + return "", nil + } + + // Extract claimRef from PV + claimRef, found, _ := unstructured.NestedMap(pv.Object, "spec", "claimRef") + if !found { -+ d.log.Warn("cannot validate RWX attachment: PV has no claimRef") ++ log.Warn("cannot validate RWX attachment: PV has no claimRef") + return "", nil + } + @@ -102,12 +204,12 @@ index bea69a8..69e71a6 100644 + pvcNamespace, _, _ := unstructured.NestedString(claimRef, "namespace") + + if pvcNamespace == "" || pvcName == "" { -+ d.log.Warn("cannot validate RWX attachment: PVC name or namespace is empty in claimRef") ++ log.Warn("cannot validate RWX attachment: PVC name or namespace is empty in claimRef") + return "", nil + } + + // List all pods in the namespace -+ podList, err := d.kubeClient.Resource(podGVR).Namespace(pvcNamespace).List(ctx, metav1.ListOptions{}) ++ podList, err := kubeClient.Resource(PodGVR).Namespace(pvcNamespace).List(ctx, metav1.ListOptions{}) + if err != nil { + return "", fmt.Errorf("failed to list pods in namespace %s: %w", pvcNamespace, err) + } @@ -139,28 +241,25 @@ index bea69a8..69e71a6 100644 + continue + } + -+ pvc, found, _ := unstructured.NestedMap(volMap, "persistentVolumeClaim") -+ if !found { ++ claimName, found, _ := unstructured.NestedString(volMap, "persistentVolumeClaim", "claimName") ++ if !found || claimName != pvcName { + continue + } + -+ claimName, _, _ := unstructured.NestedString(pvc, "claimName") -+ if claimName == pvcName { -+ // Extract VM name, handling both regular and hotplug disk pods -+ vmName, err := d.getVMNameFromPod(ctx, &item) -+ if err != nil { -+ d.log.WithError(err).WithField("pod", item.GetName()).Warn("failed to get VM name from pod") -+ // Continue with empty vmName - will be caught by strict mode check -+ vmName = "" -+ } -+ -+ podsUsingPVC = append(podsUsingPVC, podInfo{ -+ name: item.GetName(), -+ vmName: vmName, -+ }) -+ -+ break ++ // Extract VM name, handling both regular and hotplug disk pods ++ vmName, err := GetVMNameFromPod(ctx, kubeClient, log, &item) ++ if err != nil { ++ log.WithError(err).WithField("pod", item.GetName()).Warn("failed to get VM name from pod") ++ // Continue with empty vmName - will be caught by strict mode check ++ vmName = "" + } ++ ++ podsUsingPVC = append(podsUsingPVC, podInfo{ ++ name: item.GetName(), ++ vmName: vmName, ++ }) ++ ++ break + } + } + @@ -168,7 +267,7 @@ index bea69a8..69e71a6 100644 + if len(podsUsingPVC) <= 1 { + // Return VM name if there's exactly one pod + if len(podsUsingPVC) == 1 { -+ d.log.WithFields(logrus.Fields{ ++ log.WithFields(logrus.Fields{ + "volumeID": volumeID, + "vmName": podsUsingPVC[0].vmName, + "podCount": 1, @@ -179,7 +278,7 @@ index bea69a8..69e71a6 100644 + return podsUsingPVC[0].vmName, nil + } + -+ d.log.WithFields(logrus.Fields{ ++ log.WithFields(logrus.Fields{ + "volumeID": volumeID, + "pvcNamespace": pvcNamespace, + "pvcName": pvcName, @@ -209,7 +308,7 @@ index bea69a8..69e71a6 100644 + } + } + -+ d.log.WithFields(logrus.Fields{ ++ log.WithFields(logrus.Fields{ + "pvcNamespace": pvcNamespace, + "pvcName": pvcName, + "vmName": vmName, @@ -219,30 +318,62 @@ index bea69a8..69e71a6 100644 + return vmName, nil +} + - // ControllerPublishVolume https://github.com/container-storage-interface/spec/blob/v1.9.0/spec.md#controllerpublishvolume - func (d Driver) ControllerPublishVolume(ctx context.Context, req *csi.ControllerPublishVolumeRequest) (*csi.ControllerPublishVolumeResponse, error) { - if req.GetVolumeId() == "" { -@@ -751,6 +964,14 @@ func (d Driver) ControllerPublishVolume(ctx context.Context, req *csi.Controller - // ReadWriteMany block volume - rwxBlock := req.VolumeCapability.AccessMode.GetMode() == csi.VolumeCapability_AccessMode_MULTI_NODE_MULTI_WRITER && req.VolumeCapability.GetBlock() != nil - -+ // Validate RWX block attachment to prevent misuse of allow-two-primaries -+ if rwxBlock { -+ if _, err := d.validateRWXBlockAttachment(ctx, req.GetVolumeId()); err != nil { -+ return nil, status.Errorf(codes.FailedPrecondition, -+ "ControllerPublishVolume failed for %s: %v", req.GetVolumeId(), err) -+ } ++// GetVMNameFromPod extracts the VM name from a pod, handling both regular virt-launcher pods ++// and hotplug disk pods (which reference the virt-launcher pod via ownerReferences). ++func GetVMNameFromPod(ctx context.Context, kubeClient dynamic.Interface, log *logrus.Entry, pod *unstructured.Unstructured) (string, error) { ++ labels := pod.GetLabels() ++ if labels == nil { ++ return "", nil + } + - devPath, err := d.Assignments.Attach(ctx, req.GetVolumeId(), req.GetNodeId(), rwxBlock) - if err != nil { - return nil, status.Errorf(codes.Internal, -diff --git a/pkg/driver/rwx_validation_test.go b/pkg/driver/rwx_validation_test.go ++ // Direct case: pod has vm.kubevirt.io/name label (virt-launcher pod) ++ if vmName, ok := labels[KubeVirtVMLabel]; ok && vmName != "" { ++ return vmName, nil ++ } ++ ++ // Hotplug disk case: pod has kubevirt.io: hotplug-disk label ++ // Follow ownerReferences to find the virt-launcher pod ++ if hotplugValue, ok := labels[KubeVirtHotplugDiskLabel]; ok && hotplugValue == "hotplug-disk" { ++ ownerRefs := pod.GetOwnerReferences() ++ for _, owner := range ownerRefs { ++ if owner.Kind != "Pod" || owner.Controller == nil || !*owner.Controller { ++ continue ++ } ++ ++ // Get the owner pod (virt-launcher) ++ ownerPod, err := kubeClient.Resource(PodGVR).Namespace(pod.GetNamespace()).Get(ctx, owner.Name, metav1.GetOptions{}) ++ if err != nil { ++ return "", fmt.Errorf("failed to get owner pod %s: %w", owner.Name, err) ++ } ++ ++ // Extract VM name from owner pod ++ ownerLabels := ownerPod.GetLabels() ++ if ownerLabels != nil { ++ if vmName, ok := ownerLabels[KubeVirtVMLabel]; ok && vmName != "" { ++ log.WithFields(logrus.Fields{ ++ "hotplugPod": pod.GetName(), ++ "virtLauncher": owner.Name, ++ "vmName": vmName, ++ }).Debug("resolved VM name from hotplug disk pod via owner reference") ++ ++ return vmName, nil ++ } ++ } ++ ++ return "", fmt.Errorf("owner pod %s does not have %s label", owner.Name, KubeVirtVMLabel) ++ } ++ ++ return "", fmt.Errorf("hotplug disk pod %s has no controller owner reference", pod.GetName()) ++ } ++ ++ return "", nil ++} +diff --git a/pkg/utils/rwx_validation_test.go b/pkg/utils/rwx_validation_test.go new file mode 100644 -index 0000000..92c1046 +index 00000000..d75690f9 --- /dev/null -+++ b/pkg/driver/rwx_validation_test.go -@@ -0,0 +1,353 @@ ++++ b/pkg/utils/rwx_validation_test.go +@@ -0,0 +1,342 @@ +/* +CSI Driver for Linstor +Copyright © 2018 LINBIT USA, LLC @@ -261,7 +392,7 @@ index 0000000..92c1046 +along with this program; if not, see . +*/ + -+package driver ++package utils + +import ( + "context" @@ -424,22 +555,17 @@ index 0000000..92c1046 + } + + gvrToListKind := map[schema.GroupVersionResource]string{ -+ podGVR: "PodList", -+ pvGVR: "PersistentVolumeList", ++ PodGVR: "PodList", ++ PVGVR: "PersistentVolumeList", + } + client := dynamicfake.NewSimpleDynamicClientWithCustomListKinds(scheme, gvrToListKind, objects...) + -+ // Create driver with fake client ++ // Create logger + logger := logrus.NewEntry(logrus.New()) + logger.Logger.SetLevel(logrus.DebugLevel) + -+ driver := &Driver{ -+ kubeClient: client, -+ log: logger, -+ } -+ + // Run validation -+ vmName, err := driver.validateRWXBlockAttachment(context.Background(), "test-volume-id") ++ vmName, err := ValidateRWXBlockAttachment(context.Background(), client, logger, "test-volume-id") + + if tc.expectError { + assert.Error(t, err) @@ -461,12 +587,8 @@ index 0000000..92c1046 +func TestValidateRWXBlockAttachmentNoKubeClient(t *testing.T) { + // When not running in Kubernetes (no client), validation should be skipped + logger := logrus.NewEntry(logrus.New()) -+ driver := &Driver{ -+ kubeClient: nil, -+ log: logger, -+ } + -+ vmName, err := driver.validateRWXBlockAttachment(context.Background(), "test-volume-id") ++ vmName, err := ValidateRWXBlockAttachment(context.Background(), nil, logger, "test-volume-id") + assert.NoError(t, err) + assert.Empty(t, vmName) +} @@ -476,20 +598,15 @@ index 0000000..92c1046 + scheme := runtime.NewScheme() + + gvrToListKind := map[schema.GroupVersionResource]string{ -+ podGVR: "PodList", -+ pvGVR: "PersistentVolumeList", ++ PodGVR: "PodList", ++ PVGVR: "PersistentVolumeList", + } + client := dynamicfake.NewSimpleDynamicClientWithCustomListKinds(scheme, gvrToListKind) + + logger := logrus.NewEntry(logrus.New()) + logger.Logger.SetLevel(logrus.DebugLevel) + -+ driver := &Driver{ -+ kubeClient: client, -+ log: logger, -+ } -+ -+ vmName, err := driver.validateRWXBlockAttachment(context.Background(), "non-existent-pv") ++ vmName, err := ValidateRWXBlockAttachment(context.Background(), client, logger, "non-existent-pv") + assert.NoError(t, err) + assert.Empty(t, vmName) +} @@ -538,6 +655,9 @@ index 0000000..92c1046 + "name": pvcName, + "namespace": pvcNamespace, + }, ++ "csi": map[string]interface{}{ ++ "volumeHandle": name, ++ }, + }, + }, + } From cf8ac03f45574b16e8399ce55f01da09aeb8c240 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Tue, 13 Jan 2026 17:22:13 +0100 Subject: [PATCH 015/125] Release v0.40.2 (#1858) This PR prepares the release `v0.40.2`. --- packages/apps/kubernetes/images/kubevirt-csi-driver.tag | 2 +- packages/core/installer/values.yaml | 2 +- packages/core/platform/values.yaml | 2 +- packages/core/testing/values.yaml | 2 +- packages/extra/bootbox/images/matchbox.tag | 2 +- packages/extra/seaweedfs/images/objectstorage-sidecar.tag | 2 +- packages/system/bucket/images/s3manager.tag | 2 +- packages/system/cozystack-api/values.yaml | 2 +- packages/system/cozystack-controller/values.yaml | 4 ++-- packages/system/dashboard/templates/configmap.yaml | 2 +- packages/system/dashboard/values.yaml | 6 +++--- packages/system/kamaji/values.yaml | 4 ++-- packages/system/kubeovn-plunger/values.yaml | 2 +- packages/system/kubeovn-webhook/values.yaml | 2 +- packages/system/kubevirt-csi-node/values.yaml | 2 +- packages/system/lineage-controller-webhook/values.yaml | 2 +- packages/system/linstor/values.yaml | 3 +-- packages/system/objectstorage-controller/values.yaml | 2 +- packages/system/seaweedfs/values.yaml | 2 +- 19 files changed, 23 insertions(+), 24 deletions(-) diff --git a/packages/apps/kubernetes/images/kubevirt-csi-driver.tag b/packages/apps/kubernetes/images/kubevirt-csi-driver.tag index 80d3d2e6..326da62f 100644 --- a/packages/apps/kubernetes/images/kubevirt-csi-driver.tag +++ b/packages/apps/kubernetes/images/kubevirt-csi-driver.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:d5c836ba33cf5dbed7e6f866784f668f80ffe69179e7c75847b680111984eefb +ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:0e5d8bbdc587a96e07bbf263f23d4d237dac8bd4528b34f44d31b042c6a0e495 diff --git a/packages/core/installer/values.yaml b/packages/core/installer/values.yaml index 61870b23..0379a40c 100644 --- a/packages/core/installer/values.yaml +++ b/packages/core/installer/values.yaml @@ -1,5 +1,5 @@ cozystack: - image: ghcr.io/cozystack/cozystack/installer:v0.40.1@sha256:69a8f95b8ddcf122a8859a957017b3d9e33c5eca3bfbf30c112c109e355f4f1c + image: ghcr.io/cozystack/cozystack/installer:v0.40.2@sha256:146e0de9d1208eba8342277fa08e0588d66e51bc637c6b3add8ef63cc54ef351 cozystackOperator: enabled: false image: ghcr.io/cozystack/cozystack/cozystack-operator:latest@sha256:f7f6e0fd9e896b7bfa642d0bfa4378bc14e646bc5c2e86e2e09a82770ef33181 diff --git a/packages/core/platform/values.yaml b/packages/core/platform/values.yaml index 84efcb78..26d0859c 100644 --- a/packages/core/platform/values.yaml +++ b/packages/core/platform/values.yaml @@ -1,2 +1,2 @@ assets: - image: ghcr.io/cozystack/cozystack/cozystack-assets:v0.40.1@sha256:ee1248692753820fe11139789680fd24dc4dbdba85a6cb70e086447d9b9b1d99 + image: ghcr.io/cozystack/cozystack/cozystack-assets:v0.40.2@sha256:02730e6a434a8367a970c00a1feaa31f19a856641f6c1d085afed88e9c0b8b2b diff --git a/packages/core/testing/values.yaml b/packages/core/testing/values.yaml index 90a9ed18..b00e9ea3 100644 --- a/packages/core/testing/values.yaml +++ b/packages/core/testing/values.yaml @@ -1,2 +1,2 @@ e2e: - image: ghcr.io/cozystack/cozystack/e2e-sandbox:v0.40.1@sha256:78a556a059411cac89249c2e22596223128a1c74fbfcac4787fd74e4a66f66a6 + image: ghcr.io/cozystack/cozystack/e2e-sandbox:v0.40.2@sha256:78a556a059411cac89249c2e22596223128a1c74fbfcac4787fd74e4a66f66a6 diff --git a/packages/extra/bootbox/images/matchbox.tag b/packages/extra/bootbox/images/matchbox.tag index aa8ecbfd..522bb744 100644 --- a/packages/extra/bootbox/images/matchbox.tag +++ b/packages/extra/bootbox/images/matchbox.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/matchbox:v0.40.1@sha256:293d562c134fc4ee0a9a756df32422b849715fe25a2120b69cb415ca98217363 +ghcr.io/cozystack/cozystack/matchbox:v0.40.2@sha256:f72d35fc691021d6b473360f2edaf0b1f176f737ca18f8cb9322d16c69705020 diff --git a/packages/extra/seaweedfs/images/objectstorage-sidecar.tag b/packages/extra/seaweedfs/images/objectstorage-sidecar.tag index 1724acc3..90d0d249 100644 --- a/packages/extra/seaweedfs/images/objectstorage-sidecar.tag +++ b/packages/extra/seaweedfs/images/objectstorage-sidecar.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/objectstorage-sidecar:v0.40.1@sha256:4bb47c8adb34543403a16d1ff61b307939850e8cf5fc365388469e30dfb9681b +ghcr.io/cozystack/cozystack/objectstorage-sidecar:v0.40.2@sha256:6735e6f050355355a13439ff815539a1b92eba09623e500e92dd7d4e600c59b4 diff --git a/packages/system/bucket/images/s3manager.tag b/packages/system/bucket/images/s3manager.tag index 1e12e68b..b71ecf9e 100644 --- a/packages/system/bucket/images/s3manager.tag +++ b/packages/system/bucket/images/s3manager.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/s3manager:v0.5.0@sha256:3825c9b4b6238f88f1b0de73bd18866a7e5f83f178d28fe2830f3bf24efb187d +ghcr.io/cozystack/cozystack/s3manager:v0.5.0@sha256:07f016720c4692fc270a415a327a88773b90d2821922098496ad1269beac6c94 diff --git a/packages/system/cozystack-api/values.yaml b/packages/system/cozystack-api/values.yaml index 10a79b25..04e86c7c 100644 --- a/packages/system/cozystack-api/values.yaml +++ b/packages/system/cozystack-api/values.yaml @@ -1,5 +1,5 @@ cozystackAPI: - image: ghcr.io/cozystack/cozystack/cozystack-api:v0.40.1@sha256:408885b509d80ca30a85416f87d07112bc7f070374e71e80b64818fbb24ad1ee + image: ghcr.io/cozystack/cozystack/cozystack-api:v0.40.2@sha256:408885b509d80ca30a85416f87d07112bc7f070374e71e80b64818fbb24ad1ee localK8sAPIEndpoint: enabled: true replicas: 2 diff --git a/packages/system/cozystack-controller/values.yaml b/packages/system/cozystack-controller/values.yaml index a28c13c5..de8eedd5 100644 --- a/packages/system/cozystack-controller/values.yaml +++ b/packages/system/cozystack-controller/values.yaml @@ -1,6 +1,6 @@ cozystackController: - image: ghcr.io/cozystack/cozystack/cozystack-controller:v0.40.1@sha256:75636007f63e1b2cb51fc2e70782b707330d1e728c32642a4edc500b614f0781 + image: ghcr.io/cozystack/cozystack/cozystack-controller:v0.40.2@sha256:1878af4389ee3ddc85f2079cc250bccc14b923fb74c665c9aa902a659a394ba6 debug: false disableTelemetry: false - cozystackVersion: "v0.40.1" + cozystackVersion: "v0.40.2" cozystackAPIKind: "DaemonSet" diff --git a/packages/system/dashboard/templates/configmap.yaml b/packages/system/dashboard/templates/configmap.yaml index 861d4eed..a929b0b1 100644 --- a/packages/system/dashboard/templates/configmap.yaml +++ b/packages/system/dashboard/templates/configmap.yaml @@ -1,6 +1,6 @@ {{- $brandingConfig := .Values._cluster.branding | default dict }} -{{- $tenantText := "v0.40.1" }} +{{- $tenantText := "v0.40.2" }} {{- $footerText := "Cozystack" }} {{- $titleText := "Cozystack Dashboard" }} {{- $logoText := "" }} diff --git a/packages/system/dashboard/values.yaml b/packages/system/dashboard/values.yaml index 94c395bb..091aaa4d 100644 --- a/packages/system/dashboard/values.yaml +++ b/packages/system/dashboard/values.yaml @@ -1,6 +1,6 @@ openapiUI: - image: ghcr.io/cozystack/cozystack/openapi-ui:v0.40.1@sha256:a427114308b9c87ab71b50980cba0d673a730791629164335d2046f7d1740327 + image: ghcr.io/cozystack/cozystack/openapi-ui:v0.40.2@sha256:a8d3770107c0279add903e6b5acdc5ca8a15551fb07514e40772f8010c5386ef openapiUIK8sBff: - image: ghcr.io/cozystack/cozystack/openapi-ui-k8s-bff:v0.40.1@sha256:fda379dce49c2cd8cb8d7d2a1d8ec6f7bedb3419c058c4355ecdece1c1e937f4 + image: ghcr.io/cozystack/cozystack/openapi-ui-k8s-bff:v0.40.2@sha256:3071dfc130dbaf185b1a46ed42f0800824e77c2f4e64c5ed87055311f3c5f2b1 tokenProxy: - image: ghcr.io/cozystack/cozystack/token-proxy:v0.40.1@sha256:4fc8a11f8a1a81aa0774ae2b1ed2e05d36d0b3ef1e37979cc4994e65114d93ae + image: ghcr.io/cozystack/cozystack/token-proxy:v0.40.2@sha256:4fc8a11f8a1a81aa0774ae2b1ed2e05d36d0b3ef1e37979cc4994e65114d93ae diff --git a/packages/system/kamaji/values.yaml b/packages/system/kamaji/values.yaml index 35c15ef9..4aea9c22 100644 --- a/packages/system/kamaji/values.yaml +++ b/packages/system/kamaji/values.yaml @@ -3,7 +3,7 @@ kamaji: deploy: false image: pullPolicy: IfNotPresent - tag: v0.40.1@sha256:0bcfb2d376224b18a0627d7b18ba6202bb8a553f71796023e12740d9513740c7 + tag: v0.40.2@sha256:cf9aae159deb586b1af5e5f6299247fe82f01646d58102bf5ca264c40f9edda2 repository: ghcr.io/cozystack/cozystack/kamaji resources: limits: @@ -13,4 +13,4 @@ kamaji: cpu: 100m memory: 100Mi extraArgs: - - --migrate-image=ghcr.io/cozystack/cozystack/kamaji:v0.40.1@sha256:0bcfb2d376224b18a0627d7b18ba6202bb8a553f71796023e12740d9513740c7 + - --migrate-image=ghcr.io/cozystack/cozystack/kamaji:v0.40.2@sha256:cf9aae159deb586b1af5e5f6299247fe82f01646d58102bf5ca264c40f9edda2 diff --git a/packages/system/kubeovn-plunger/values.yaml b/packages/system/kubeovn-plunger/values.yaml index 10d8508a..4229b116 100644 --- a/packages/system/kubeovn-plunger/values.yaml +++ b/packages/system/kubeovn-plunger/values.yaml @@ -1,4 +1,4 @@ portSecurity: true routes: "" -image: ghcr.io/cozystack/cozystack/kubeovn-plunger:v0.40.1@sha256:2a17dfa8eec0fc6b2adb564742e74f35bd68ac0d29e5ab1d007496aff3c0e97d +image: ghcr.io/cozystack/cozystack/kubeovn-plunger:v0.40.2@sha256:fdcc081d3c28a023a6950deac4d6124a0290d1bb4bd13c8c723569ac40d6874e ovnCentralName: ovn-central diff --git a/packages/system/kubeovn-webhook/values.yaml b/packages/system/kubeovn-webhook/values.yaml index c60ff144..6ec8f26e 100644 --- a/packages/system/kubeovn-webhook/values.yaml +++ b/packages/system/kubeovn-webhook/values.yaml @@ -1,3 +1,3 @@ portSecurity: true routes: "" -image: ghcr.io/cozystack/cozystack/kubeovn-webhook:v0.40.1@sha256:e6334c29d3aaf0dea766c88e3e05b53ad623d1bb497b3c836e6f76adade45b29 +image: ghcr.io/cozystack/cozystack/kubeovn-webhook:v0.40.2@sha256:e6334c29d3aaf0dea766c88e3e05b53ad623d1bb497b3c836e6f76adade45b29 diff --git a/packages/system/kubevirt-csi-node/values.yaml b/packages/system/kubevirt-csi-node/values.yaml index 8d2a7d45..d3fac521 100644 --- a/packages/system/kubevirt-csi-node/values.yaml +++ b/packages/system/kubevirt-csi-node/values.yaml @@ -1,3 +1,3 @@ storageClass: replicated csiDriver: - image: ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:d5c836ba33cf5dbed7e6f866784f668f80ffe69179e7c75847b680111984eefb + image: ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:0e5d8bbdc587a96e07bbf263f23d4d237dac8bd4528b34f44d31b042c6a0e495 diff --git a/packages/system/lineage-controller-webhook/values.yaml b/packages/system/lineage-controller-webhook/values.yaml index 55cb019b..554d9ef2 100644 --- a/packages/system/lineage-controller-webhook/values.yaml +++ b/packages/system/lineage-controller-webhook/values.yaml @@ -1,5 +1,5 @@ lineageControllerWebhook: - image: ghcr.io/cozystack/cozystack/lineage-controller-webhook:v0.40.1@sha256:00650eb92c8b93a0afc382a0c5e46ade453e430867be7dab9647a6fc2896f097 + image: ghcr.io/cozystack/cozystack/lineage-controller-webhook:v0.40.2@sha256:16a700334d4d9cebea2516874c04d0799a450052f3652a0c91668c37d1d0e227 debug: false localK8sAPIEndpoint: enabled: true diff --git a/packages/system/linstor/values.yaml b/packages/system/linstor/values.yaml index 173aacb4..da62e9da 100644 --- a/packages/system/linstor/values.yaml +++ b/packages/system/linstor/values.yaml @@ -7,8 +7,7 @@ linstor: enabled: true minutes: 30 allowCleanup: true - linstorCSI: image: repository: ghcr.io/cozystack/cozystack/linstor-csi - tag: latest@sha256:e8329b3e07c47ec73a7d9644535639fd80dbe5c27a7e03181b999ebe072a3a2e + tag: v1.10.5@sha256:276e35e644cc4f1a17bc3842ded84aef4d9a5d750b5bdf3fc7238e369e88ada8 diff --git a/packages/system/objectstorage-controller/values.yaml b/packages/system/objectstorage-controller/values.yaml index aba36be8..041c3ad2 100644 --- a/packages/system/objectstorage-controller/values.yaml +++ b/packages/system/objectstorage-controller/values.yaml @@ -1,3 +1,3 @@ objectstorage: controller: - image: "ghcr.io/cozystack/cozystack/objectstorage-controller:v0.40.1@sha256:e8f605a5ec4b801dfaf49aa7bce8d7a1ce28df578e23a0b859571f60f98efe8c" + image: "ghcr.io/cozystack/cozystack/objectstorage-controller:v0.40.2@sha256:f44845c034643c3b9040b70dc417e633d8ab0cc56a304a88dace733f2b4f1d70" diff --git a/packages/system/seaweedfs/values.yaml b/packages/system/seaweedfs/values.yaml index fb3defa6..fd8f7ff0 100644 --- a/packages/system/seaweedfs/values.yaml +++ b/packages/system/seaweedfs/values.yaml @@ -177,7 +177,7 @@ seaweedfs: bucketClassName: "seaweedfs" region: "" sidecar: - image: "ghcr.io/cozystack/cozystack/objectstorage-sidecar:v0.40.1@sha256:4bb47c8adb34543403a16d1ff61b307939850e8cf5fc365388469e30dfb9681b" + image: "ghcr.io/cozystack/cozystack/objectstorage-sidecar:v0.40.2@sha256:6735e6f050355355a13439ff815539a1b92eba09623e500e92dd7d4e600c59b4" certificates: commonName: "SeaweedFS CA" ipAddresses: [] From 909c3a5abca21948dd404ac8e5d1048b68193102 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Tue, 13 Jan 2026 17:31:36 +0100 Subject: [PATCH 016/125] [docs] Generate changelogs for v0.39.5, v0.40.1, and v0.40.2 Add missing changelog files for recent patch releases: - v0.39.5: LINSTOR critical patches backport - v0.40.1: LINSTOR critical patches backport - v0.40.2: LINSTOR RWX validation refactoring Co-Authored-By: Claude Signed-off-by: Andrei Kvapil --- docs/changelogs/v0.39.5.md | 11 +++++++++++ docs/changelogs/v0.40.1.md | 11 +++++++++++ docs/changelogs/v0.40.2.md | 15 +++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 docs/changelogs/v0.39.5.md create mode 100644 docs/changelogs/v0.40.1.md create mode 100644 docs/changelogs/v0.40.2.md diff --git a/docs/changelogs/v0.39.5.md b/docs/changelogs/v0.39.5.md new file mode 100644 index 00000000..de352b1c --- /dev/null +++ b/docs/changelogs/v0.39.5.md @@ -0,0 +1,11 @@ + + +## Fixes + +* **[linstor] Update piraeus-server patches with critical fixes**: Backported critical patches to piraeus-server that address storage stability issues and improve DRBD resource handling. These patches fix edge cases in device management and ensure more reliable storage operations ([**@kvaps**](https://github.com/kvaps) in #1850, #1853). + +--- + +**Full Changelog**: [v0.39.4...v0.39.5](https://github.com/cozystack/cozystack/compare/v0.39.4...v0.39.5) diff --git a/docs/changelogs/v0.40.1.md b/docs/changelogs/v0.40.1.md new file mode 100644 index 00000000..ee718b47 --- /dev/null +++ b/docs/changelogs/v0.40.1.md @@ -0,0 +1,11 @@ + + +## Fixes + +* **[linstor] Update piraeus-server patches with critical fixes**: Backported critical patches to piraeus-server that address storage stability issues and improve DRBD resource handling. These patches fix edge cases in device management and ensure more reliable storage operations ([**@kvaps**](https://github.com/kvaps) in #1850, #1852). + +--- + +**Full Changelog**: [v0.40.0...v0.40.1](https://github.com/cozystack/cozystack/compare/v0.40.0...v0.40.1) diff --git a/docs/changelogs/v0.40.2.md b/docs/changelogs/v0.40.2.md new file mode 100644 index 00000000..5c686b92 --- /dev/null +++ b/docs/changelogs/v0.40.2.md @@ -0,0 +1,15 @@ + + +## Improvements + +* **[linstor] Refactor node-level RWX validation**: Refactored the node-level ReadWriteMany (RWX) validation logic in LINSTOR CSI. The validation has been moved to the CSI driver level with a custom linstor-csi image build, providing more reliable RWX volume handling and clearer error messages when RWX requirements cannot be satisfied ([**@kvaps**](https://github.com/kvaps) in #1856, #1857). + +## Fixes + +* **[linstor] Remove node-level RWX validation**: Removed the problematic node-level RWX validation that was causing issues with volume provisioning. The validation logic has been refactored and moved to a more appropriate location in the LINSTOR CSI driver ([**@kvaps**](https://github.com/kvaps) in #1851). + +--- + +**Full Changelog**: [v0.40.1...v0.40.2](https://github.com/cozystack/cozystack/compare/v0.40.1...v0.40.2) From 74d71606ab4cd2d5db2adcd8dbba0831c4d1e5b2 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Sat, 3 Jan 2026 09:06:47 +0100 Subject: [PATCH 017/125] feat(api): show only hash in version column for applications and modules Fix getVersion to parse "0.1.4+abcdef" format (with "+" separator) instead of incorrectly looking for "sha256:" prefix. Co-Authored-By: Claude Signed-off-by: Andrei Kvapil --- pkg/registry/apps/application/rest.go | 10 +++++++++- pkg/registry/core/tenantmodule/rest.go | 11 ++++++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/pkg/registry/apps/application/rest.go b/pkg/registry/apps/application/rest.go index 4f3204d5..c46b5cad 100644 --- a/pkg/registry/apps/application/rest.go +++ b/pkg/registry/apps/application/rest.go @@ -1095,11 +1095,19 @@ func (r *REST) buildTableFromApplication(app appsv1alpha1.Application) metav1.Ta return table } -// getVersion returns the application version or a placeholder if unknown +// getVersion extracts and returns only the revision from the version string +// If version is in format "0.1.4+abcdef", returns "abcdef" +// Otherwise returns the original string or "" if empty func getVersion(version string) string { if version == "" { return "" } + // Check if version contains "+" separator + if idx := strings.LastIndex(version, "+"); idx >= 0 && idx < len(version)-1 { + // Return only the part after "+" + return version[idx+1:] + } + // If no "+" found, return original version return version } diff --git a/pkg/registry/core/tenantmodule/rest.go b/pkg/registry/core/tenantmodule/rest.go index b2cda2db..77fbaaa8 100644 --- a/pkg/registry/core/tenantmodule/rest.go +++ b/pkg/registry/core/tenantmodule/rest.go @@ -20,6 +20,7 @@ import ( "context" "fmt" "net/http" + "strings" "sync" "time" @@ -669,11 +670,19 @@ func (r *REST) buildTableFromTenantModule(module corev1alpha1.TenantModule) meta return table } -// getVersion returns the module version or a placeholder if unknown +// getVersion extracts and returns only the revision from the version string +// If version is in format "0.1.4+abcdef", returns "abcdef" +// Otherwise returns the original string or "" if empty func getVersion(version string) string { if version == "" { return "" } + // Check if version contains "+" separator + if idx := strings.LastIndex(version, "+"); idx >= 0 && idx < len(version)-1 { + // Return only the part after "+" + return version[idx+1:] + } + // If no "+" found, return original version return version } From 43da779eee58759e22188901c232c8618288f2ce Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Wed, 14 Jan 2026 15:41:05 +0100 Subject: [PATCH 018/125] feat(api): add chartRef to CozystackResourceDefinition Replace the chart field with chartRef for referencing Helm charts via ExternalArtifact resources. This enables the Package controller to manage chart sources centrally. Changes: - Add chartRef field to CozystackResourceDefinition spec - Remove chart field (deprecated) - Remove validation (moved to controller) - Update lineage mapper for new field structure - Regenerate openapi specs Co-Authored-By: Claude Signed-off-by: Andrei Kvapil --- .../cozystackresourcedefinitions_types.go | 24 ++------- api/v1alpha1/zz_generated.deepcopy.go | 38 +++----------- pkg/apis/apps/fuzzer/fuzzer.go | 4 +- pkg/apis/apps/validation/validation.go | 40 --------------- pkg/apis/core/fuzzer/fuzzer.go | 4 +- pkg/apis/core/validation/validation.go | 40 --------------- pkg/cmd/server/start.go | 11 ++--- pkg/config/config.go | 16 ++---- pkg/generated/openapi/zz_generated.openapi.go | 49 ++++++++++++++++--- pkg/lineage/lineage.go | 5 ++ pkg/lineage/lineage_test.go | 33 ++++++++++++- pkg/lineage/mapper.go | 49 ------------------- pkg/registry/apps/application/rest.go | 15 ++---- 13 files changed, 106 insertions(+), 222 deletions(-) delete mode 100644 pkg/apis/apps/validation/validation.go delete mode 100644 pkg/apis/core/validation/validation.go delete mode 100644 pkg/lineage/mapper.go diff --git a/api/v1alpha1/cozystackresourcedefinitions_types.go b/api/v1alpha1/cozystackresourcedefinitions_types.go index 2f25fb2d..04b24294 100644 --- a/api/v1alpha1/cozystackresourcedefinitions_types.go +++ b/api/v1alpha1/cozystackresourcedefinitions_types.go @@ -17,6 +17,7 @@ limitations under the License. package v1alpha1 import ( + helmv2 "github.com/fluxcd/helm-controller/api/v2" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -61,24 +62,6 @@ type CozystackResourceDefinitionSpec struct { Dashboard *CozystackResourceDefinitionDashboard `json:"dashboard,omitempty"` } -type CozystackResourceDefinitionChart struct { - // Name of the Helm chart - Name string `json:"name"` - // Source reference for the Helm chart - SourceRef SourceRef `json:"sourceRef"` -} - -type SourceRef struct { - // Kind of the source reference - // +kubebuilder:default:="HelmRepository" - Kind string `json:"kind"` - // Name of the source reference - Name string `json:"name"` - // Namespace of the source reference - // +kubebuilder:default:="cozy-public" - Namespace string `json:"namespace"` -} - type CozystackResourceDefinitionApplication struct { // Kind of the application, used for UI and API Kind string `json:"kind"` @@ -91,9 +74,8 @@ type CozystackResourceDefinitionApplication struct { } type CozystackResourceDefinitionRelease struct { - // Helm chart configuration - // +optional - Chart CozystackResourceDefinitionChart `json:"chart,omitempty"` + // Reference to the chart source + ChartRef *helmv2.CrossNamespaceSourceReference `json:"chartRef"` // Labels for the release Labels map[string]string `json:"labels,omitempty"` // Prefix for the release name diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 7fe4f3e2..f060990f 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -21,6 +21,7 @@ limitations under the License. package v1alpha1 import ( + "github.com/fluxcd/helm-controller/api/v2" "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -118,22 +119,6 @@ func (in *CozystackResourceDefinitionApplication) DeepCopy() *CozystackResourceD return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CozystackResourceDefinitionChart) DeepCopyInto(out *CozystackResourceDefinitionChart) { - *out = *in - out.SourceRef = in.SourceRef -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionChart. -func (in *CozystackResourceDefinitionChart) DeepCopy() *CozystackResourceDefinitionChart { - if in == nil { - return nil - } - out := new(CozystackResourceDefinitionChart) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CozystackResourceDefinitionDashboard) DeepCopyInto(out *CozystackResourceDefinitionDashboard) { *out = *in @@ -205,7 +190,11 @@ func (in *CozystackResourceDefinitionList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *CozystackResourceDefinitionRelease) DeepCopyInto(out *CozystackResourceDefinitionRelease) { *out = *in - out.Chart = in.Chart + if in.ChartRef != nil { + in, out := &in.ChartRef, &out.ChartRef + *out = new(v2.CrossNamespaceSourceReference) + **out = **in + } if in.Labels != nil { in, out := &in.Labels, &out.Labels *out = make(map[string]string, len(*in)) @@ -622,21 +611,6 @@ func (in Selector) DeepCopy() Selector { return *out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SourceRef) DeepCopyInto(out *SourceRef) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SourceRef. -func (in *SourceRef) DeepCopy() *SourceRef { - if in == nil { - return nil - } - out := new(SourceRef) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Variant) DeepCopyInto(out *Variant) { *out = *in diff --git a/pkg/apis/apps/fuzzer/fuzzer.go b/pkg/apis/apps/fuzzer/fuzzer.go index fd744ed6..c92c5799 100644 --- a/pkg/apis/apps/fuzzer/fuzzer.go +++ b/pkg/apis/apps/fuzzer/fuzzer.go @@ -17,7 +17,7 @@ limitations under the License. package fuzzer import ( - "github.com/cozystack/cozystack/pkg/apis/apps" + "github.com/cozystack/cozystack/pkg/apis/apps/v1alpha1" fuzz "github.com/google/gofuzz" runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer" @@ -26,7 +26,7 @@ import ( // Funcs returns the fuzzer functions for the apps api group. var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} { return []interface{}{ - func(s *apps.ApplicationSpec, c fuzz.Continue) { + func(s *v1alpha1.Application, c fuzz.Continue) { c.FuzzNoCustom(s) // fuzz self without calling this function again }, } diff --git a/pkg/apis/apps/validation/validation.go b/pkg/apis/apps/validation/validation.go deleted file mode 100644 index 84c20c54..00000000 --- a/pkg/apis/apps/validation/validation.go +++ /dev/null @@ -1,40 +0,0 @@ -/* -Copyright 2024 The Cozystack Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package validation - -import ( - "github.com/cozystack/cozystack/pkg/apis/apps" - "k8s.io/apimachinery/pkg/util/validation/field" -) - -// ValidateApplication validates a Application. -func ValidateApplication(f *apps.Application) field.ErrorList { - allErrs := field.ErrorList{} - - allErrs = append(allErrs, ValidateApplicationSpec(&f.Spec, field.NewPath("spec"))...) - - return allErrs -} - -// ValidateApplicationSpec validates a ApplicationSpec. -func ValidateApplicationSpec(s *apps.ApplicationSpec, fldPath *field.Path) field.ErrorList { - allErrs := field.ErrorList{} - - // TODO validation - - return allErrs -} diff --git a/pkg/apis/core/fuzzer/fuzzer.go b/pkg/apis/core/fuzzer/fuzzer.go index dbf3ca39..82a1b5ab 100644 --- a/pkg/apis/core/fuzzer/fuzzer.go +++ b/pkg/apis/core/fuzzer/fuzzer.go @@ -17,7 +17,7 @@ limitations under the License. package fuzzer import ( - "github.com/cozystack/cozystack/pkg/apis/core" + "github.com/cozystack/cozystack/pkg/apis/core/v1alpha1" fuzz "github.com/google/gofuzz" runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer" @@ -26,7 +26,7 @@ import ( // Funcs returns the fuzzer functions for the core api group. var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} { return []interface{}{ - func(s *core.TenantNamespaceSpec, c fuzz.Continue) { + func(s *v1alpha1.TenantNamespace, c fuzz.Continue) { c.FuzzNoCustom(s) // fuzz self without calling this function again }, } diff --git a/pkg/apis/core/validation/validation.go b/pkg/apis/core/validation/validation.go deleted file mode 100644 index 060067de..00000000 --- a/pkg/apis/core/validation/validation.go +++ /dev/null @@ -1,40 +0,0 @@ -/* -Copyright 2024 The Cozystack Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package validation - -import ( - "github.com/cozystack/cozystack/pkg/apis/core" - "k8s.io/apimachinery/pkg/util/validation/field" -) - -// ValidateTenantNamespace validates a TenantNamespace. -func ValidateTenantNamespace(f *core.TenantNamespace) field.ErrorList { - allErrs := field.ErrorList{} - - allErrs = append(allErrs, ValidateTenantNamespaceSpec(&f.Spec, field.NewPath("spec"))...) - - return allErrs -} - -// ValidateTenantNamespaceSpec validates a TenantNamespaceSpec. -func ValidateTenantNamespaceSpec(s *core.TenantNamespaceSpec, fldPath *field.Path) field.ErrorList { - allErrs := field.ErrorList{} - - // TODO validation - - return allErrs -} diff --git a/pkg/cmd/server/start.go b/pkg/cmd/server/start.go index 5da8254e..2826cfd5 100644 --- a/pkg/cmd/server/start.go +++ b/pkg/cmd/server/start.go @@ -169,13 +169,10 @@ func (o *CozyServerOptions) Complete() error { Release: config.ReleaseConfig{ Prefix: crd.Spec.Release.Prefix, Labels: crd.Spec.Release.Labels, - Chart: config.ChartConfig{ - Name: crd.Spec.Release.Chart.Name, - SourceRef: config.SourceRefConfig{ - Kind: crd.Spec.Release.Chart.SourceRef.Kind, - Name: crd.Spec.Release.Chart.SourceRef.Name, - Namespace: crd.Spec.Release.Chart.SourceRef.Namespace, - }, + ChartRef: config.ChartRefConfig{ + Kind: crd.Spec.Release.ChartRef.Kind, + Name: crd.Spec.Release.ChartRef.Name, + Namespace: crd.Spec.Release.ChartRef.Namespace, }, }, } diff --git a/pkg/config/config.go b/pkg/config/config.go index 390918a6..1e123e2c 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -38,19 +38,13 @@ type ApplicationConfig struct { // ReleaseConfig contains the release settings. type ReleaseConfig struct { - Prefix string `yaml:"prefix"` - Labels map[string]string `yaml:"labels"` - Chart ChartConfig `yaml:"chart"` + Prefix string `yaml:"prefix"` + Labels map[string]string `yaml:"labels"` + ChartRef ChartRefConfig `yaml:"chartRef"` } -// ChartConfig contains the chart settings. -type ChartConfig struct { - Name string `yaml:"name"` - SourceRef SourceRefConfig `yaml:"sourceRef"` -} - -// SourceRefConfig contains the reference to the chart source. -type SourceRefConfig struct { +// ChartRefConfig references a Flux source artifact for the Helm chart. +type ChartRefConfig struct { Kind string `yaml:"kind"` Name string `yaml:"name"` Namespace string `yaml:"namespace"` diff --git a/pkg/generated/openapi/zz_generated.openapi.go b/pkg/generated/openapi/zz_generated.openapi.go index 4202bbac..ba5ab71b 100644 --- a/pkg/generated/openapi/zz_generated.openapi.go +++ b/pkg/generated/openapi/zz_generated.openapi.go @@ -2714,6 +2714,13 @@ func schema_pkg_apis_meta_v1_DeleteOptions(ref common.ReferenceCallback) common. }, }, }, + "ignoreStoreReadErrorWithClusterBreakingPotential": { + SchemaProps: spec.SchemaProps{ + Description: "if set to true, it will trigger an unsafe deletion of the resource in case the normal deletion flow fails with a corrupt object error. A resource is considered corrupt if it can not be retrieved from the underlying storage successfully because of a) its data can not be transformed e.g. decryption failure, or b) it fails to decode into an object. NOTE: unsafe deletion ignores finalizer constraints, skips precondition checks, and removes the object from the storage. WARNING: This may potentially break the cluster if the workload associated with the resource being unsafe-deleted relies on normal deletion flow. Use only if you REALLY know what you are doing. The default value is false, and the user must opt in to enable it", + Type: []string{"boolean"}, + Format: "", + }, + }, }, }, }, @@ -4601,16 +4608,46 @@ func schema_k8sio_apimachinery_pkg_version_Info(ref common.ReferenceCallback) co Properties: map[string]spec.Schema{ "major": { SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", + Description: "Major is the major version of the binary version", + Default: "", + Type: []string{"string"}, + Format: "", }, }, "minor": { SchemaProps: spec.SchemaProps{ - Default: "", - Type: []string{"string"}, - Format: "", + Description: "Minor is the minor version of the binary version", + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "emulationMajor": { + SchemaProps: spec.SchemaProps{ + Description: "EmulationMajor is the major version of the emulation version", + Type: []string{"string"}, + Format: "", + }, + }, + "emulationMinor": { + SchemaProps: spec.SchemaProps{ + Description: "EmulationMinor is the minor version of the emulation version", + Type: []string{"string"}, + Format: "", + }, + }, + "minCompatibilityMajor": { + SchemaProps: spec.SchemaProps{ + Description: "MinCompatibilityMajor is the major version of the minimum compatibility version", + Type: []string{"string"}, + Format: "", + }, + }, + "minCompatibilityMinor": { + SchemaProps: spec.SchemaProps{ + Description: "MinCompatibilityMinor is the minor version of the minimum compatibility version", + Type: []string{"string"}, + Format: "", }, }, "gitVersion": { diff --git a/pkg/lineage/lineage.go b/pkg/lineage/lineage.go index 867e1aab..94be77b7 100644 --- a/pkg/lineage/lineage.go +++ b/pkg/lineage/lineage.go @@ -22,6 +22,11 @@ const ( HRLabel = "helm.toolkit.fluxcd.io/name" ) +// AppMapper maps HelmRelease to application metadata. +type AppMapper interface { + Map(*helmv2.HelmRelease) (apiVersion, kind, prefix string, err error) +} + type ObjectID struct { APIVersion string Kind string diff --git a/pkg/lineage/lineage_test.go b/pkg/lineage/lineage_test.go index a705f26c..7da09dfc 100644 --- a/pkg/lineage/lineage_test.go +++ b/pkg/lineage/lineage_test.go @@ -4,8 +4,10 @@ import ( "context" "fmt" "os" + "strings" "testing" + helmv2 "github.com/fluxcd/helm-controller/api/v2" "github.com/go-logr/logr" "github.com/go-logr/zapr" "go.uber.org/zap" @@ -41,12 +43,41 @@ func init() { ctx = logr.NewContext(context.Background(), l) } +// labelsMapper implements AppMapper using HelmRelease labels. +type labelsMapper struct{} + +func (m *labelsMapper) Map(hr *helmv2.HelmRelease) (string, string, string, error) { + if hr.Labels == nil { + return "", "", "", fmt.Errorf("cannot map helm release %s/%s: labels are nil", hr.Namespace, hr.Name) + } + + appKind, ok := hr.Labels["apps.cozystack.io/application.kind"] + if !ok { + return "", "", "", fmt.Errorf("cannot map helm release %s/%s: missing application.kind label", hr.Namespace, hr.Name) + } + + appGroup, ok := hr.Labels["apps.cozystack.io/application.group"] + if !ok { + return "", "", "", fmt.Errorf("cannot map helm release %s/%s: missing application.group label", hr.Namespace, hr.Name) + } + + appName, ok := hr.Labels["apps.cozystack.io/application.name"] + if !ok { + return "", "", "", fmt.Errorf("cannot map helm release %s/%s: missing application.name label", hr.Namespace, hr.Name) + } + + apiVersion := fmt.Sprintf("%s/v1alpha1", appGroup) + prefix := strings.TrimSuffix(hr.Name, appName) + + return apiVersion, appKind, prefix, nil +} + func TestWalkingOwnershipGraph(t *testing.T) { obj, err := dynClient.Resource(schema.GroupVersionResource{"", "v1", "pods"}).Namespace(os.Args[1]).Get(ctx, os.Args[2], metav1.GetOptions{}) if err != nil { t.Fatal(err) } - nodes := WalkOwnershipGraph(ctx, dynClient, mapper, &stubMapper{}, obj) + nodes := WalkOwnershipGraph(ctx, dynClient, mapper, &labelsMapper{}, obj) for _, node := range nodes { fmt.Printf("%#v\n", node) } diff --git a/pkg/lineage/mapper.go b/pkg/lineage/mapper.go deleted file mode 100644 index c424b288..00000000 --- a/pkg/lineage/mapper.go +++ /dev/null @@ -1,49 +0,0 @@ -package lineage - -import ( - "fmt" - "strings" - - helmv2 "github.com/fluxcd/helm-controller/api/v2" -) - -type AppMapper interface { - Map(*helmv2.HelmRelease) (apiVersion, kind, prefix string, err error) -} - -type stubMapper struct{} - -var stubMapperMap = map[string]string{ - "cozystack-extra/bootbox": "apps.cozystack.io/v1alpha1/BootBox/", - "cozystack-apps/bucket": "apps.cozystack.io/v1alpha1/Bucket/bucket-", - "cozystack-apps/clickhouse": "apps.cozystack.io/v1alpha1/ClickHouse/clickhouse-", - "cozystack-extra/etcd": "apps.cozystack.io/v1alpha1/Etcd/", - "cozystack-apps/ferretdb": "apps.cozystack.io/v1alpha1/FerretDB/ferretdb-", - "cozystack-apps/http-cache": "apps.cozystack.io/v1alpha1/HTTPCache/http-cache-", - "cozystack-extra/info": "apps.cozystack.io/v1alpha1/Info/", - "cozystack-extra/ingress": "apps.cozystack.io/v1alpha1/Ingress/", - "cozystack-apps/kafka": "apps.cozystack.io/v1alpha1/Kafka/kafka-", - "cozystack-apps/kubernetes": "apps.cozystack.io/v1alpha1/Kubernetes/kubernetes-", - "cozystack-extra/monitoring": "apps.cozystack.io/v1alpha1/Monitoring/", - "cozystack-apps/mysql": "apps.cozystack.io/v1alpha1/MySQL/mysql-", - "cozystack-apps/nats": "apps.cozystack.io/v1alpha1/NATS/nats-", - "cozystack-apps/postgres": "apps.cozystack.io/v1alpha1/Postgres/postgres-", - "cozystack-apps/rabbitmq": "apps.cozystack.io/v1alpha1/RabbitMQ/rabbitmq-", - "cozystack-apps/redis": "apps.cozystack.io/v1alpha1/Redis/redis-", - "cozystack-extra/seaweedfs": "apps.cozystack.io/v1alpha1/SeaweedFS/", - "cozystack-apps/tcp-balancer": "apps.cozystack.io/v1alpha1/TCPBalancer/tcp-balancer-", - "cozystack-apps/tenant": "apps.cozystack.io/v1alpha1/Tenant/tenant-", - "cozystack-apps/virtual-machine": "apps.cozystack.io/v1alpha1/VirtualMachine/virtual-machine-", - "cozystack-apps/vm-disk": "apps.cozystack.io/v1alpha1/VMDisk/vm-disk-", - "cozystack-apps/vm-instance": "apps.cozystack.io/v1alpha1/VMInstance/vm-instance-", - "cozystack-apps/vpn": "apps.cozystack.io/v1alpha1/VPN/vpn-", -} - -func (s *stubMapper) Map(hr *helmv2.HelmRelease) (string, string, string, error) { - val, ok := stubMapperMap[hr.Spec.Chart.Spec.SourceRef.Name+"/"+hr.Spec.Chart.Spec.Chart] - if !ok { - return "", "", "", fmt.Errorf("cannot map helm release %s/%s to dynamic app", hr.Namespace, hr.Name) - } - split := strings.Split(val, "/") - return strings.Join(split[:2], "/"), split[2], split[3], nil -} diff --git a/pkg/registry/apps/application/rest.go b/pkg/registry/apps/application/rest.go index c46b5cad..6e0ae048 100644 --- a/pkg/registry/apps/application/rest.go +++ b/pkg/registry/apps/application/rest.go @@ -973,17 +973,10 @@ func (r *REST) convertApplicationToHelmRelease(app *appsv1alpha1.Application) (* UID: app.UID, }, Spec: helmv2.HelmReleaseSpec{ - Chart: &helmv2.HelmChartTemplate{ - Spec: helmv2.HelmChartTemplateSpec{ - Chart: r.releaseConfig.Chart.Name, - Version: ">= 0.0.0-0", - ReconcileStrategy: "Revision", - SourceRef: helmv2.CrossNamespaceObjectReference{ - Kind: r.releaseConfig.Chart.SourceRef.Kind, - Name: r.releaseConfig.Chart.SourceRef.Name, - Namespace: r.releaseConfig.Chart.SourceRef.Namespace, - }, - }, + ChartRef: &helmv2.CrossNamespaceSourceReference{ + Kind: r.releaseConfig.ChartRef.Kind, + Name: r.releaseConfig.ChartRef.Name, + Namespace: r.releaseConfig.ChartRef.Namespace, }, Interval: metav1.Duration{Duration: 5 * time.Minute}, Install: &helmv2.Install{ From 99ac2fc7105e8c361273fb6988851207c3188fac Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Thu, 15 Jan 2026 14:14:48 +0100 Subject: [PATCH 019/125] refactor(installer): migrate installer to cozystack-operator Remove legacy installer components (cozystack-assets-server, installer.sh script, cozystack container image) in favor of cozystack-operator based deployment. Move migration scripts from scripts/migrations/ to packages/core/platform/images/migrations/ for containerized execution. Add grafana-dashboards image for centralized dashboard management. Co-Authored-By: Claude Signed-off-by: Andrei Kvapil --- Makefile | 13 +-- cmd/cozystack-assets-server/main.go | 29 ------- packages/core/installer/Makefile | 57 ++++++------- .../installer/images/cozystack/Dockerfile | 41 ---------- .../images/cozystack/Dockerfile.dockerignore | 1 - .../templates/cozystack-operator.yaml | 45 ----------- .../core/installer/templates/cozystack.yaml | 81 ------------------- packages/core/installer/templates/crds.yaml | 2 - .../installer/templates/packagesource.yaml | 43 ++++++++++ packages/core/installer/values.yaml | 9 +-- .../images/cozystack-assets/Dockerfile | 25 ------ .../platform/images/migrations/Dockerfile | 12 +++ .../platform/images/migrations}/migrations/1 | 0 .../platform/images/migrations}/migrations/10 | 0 .../platform/images/migrations}/migrations/11 | 0 .../platform/images/migrations}/migrations/12 | 0 .../platform/images/migrations}/migrations/13 | 0 .../platform/images/migrations}/migrations/14 | 0 .../platform/images/migrations}/migrations/15 | 0 .../platform/images/migrations}/migrations/16 | 0 .../platform/images/migrations}/migrations/17 | 0 .../platform/images/migrations}/migrations/18 | 0 .../platform/images/migrations}/migrations/19 | 0 .../platform/images/migrations}/migrations/2 | 0 .../platform/images/migrations}/migrations/20 | 0 .../platform/images/migrations}/migrations/21 | 0 .../platform/images/migrations}/migrations/22 | 0 .../platform/images/migrations}/migrations/3 | 0 .../platform/images/migrations}/migrations/4 | 0 .../platform/images/migrations}/migrations/5 | 0 .../platform/images/migrations}/migrations/6 | 0 .../platform/images/migrations}/migrations/7 | 0 .../platform/images/migrations}/migrations/8 | 0 .../platform/images/migrations}/migrations/9 | 0 .../images/migrations/run-migrations.sh | 41 ++++++++++ packages/system/grafana-operator/Makefile | 12 +++ .../images/grafana-dashboards.tag | 1 + .../images/grafana-dashboards/Dockerfile | 11 +++ .../Dockerfile.dockerignore | 3 + scripts/installer.sh | 71 ---------------- 40 files changed, 154 insertions(+), 343 deletions(-) delete mode 100644 cmd/cozystack-assets-server/main.go delete mode 100644 packages/core/installer/images/cozystack/Dockerfile delete mode 100644 packages/core/installer/images/cozystack/Dockerfile.dockerignore delete mode 100644 packages/core/installer/templates/cozystack.yaml create mode 100644 packages/core/installer/templates/packagesource.yaml delete mode 100644 packages/core/platform/images/cozystack-assets/Dockerfile create mode 100644 packages/core/platform/images/migrations/Dockerfile rename {scripts => packages/core/platform/images/migrations}/migrations/1 (100%) rename {scripts => packages/core/platform/images/migrations}/migrations/10 (100%) rename {scripts => packages/core/platform/images/migrations}/migrations/11 (100%) rename {scripts => packages/core/platform/images/migrations}/migrations/12 (100%) rename {scripts => packages/core/platform/images/migrations}/migrations/13 (100%) rename {scripts => packages/core/platform/images/migrations}/migrations/14 (100%) rename {scripts => packages/core/platform/images/migrations}/migrations/15 (100%) rename {scripts => packages/core/platform/images/migrations}/migrations/16 (100%) rename {scripts => packages/core/platform/images/migrations}/migrations/17 (100%) rename {scripts => packages/core/platform/images/migrations}/migrations/18 (100%) rename {scripts => packages/core/platform/images/migrations}/migrations/19 (100%) rename {scripts => packages/core/platform/images/migrations}/migrations/2 (100%) rename {scripts => packages/core/platform/images/migrations}/migrations/20 (100%) rename {scripts => packages/core/platform/images/migrations}/migrations/21 (100%) rename {scripts => packages/core/platform/images/migrations}/migrations/22 (100%) rename {scripts => packages/core/platform/images/migrations}/migrations/3 (100%) rename {scripts => packages/core/platform/images/migrations}/migrations/4 (100%) rename {scripts => packages/core/platform/images/migrations}/migrations/5 (100%) rename {scripts => packages/core/platform/images/migrations}/migrations/6 (100%) rename {scripts => packages/core/platform/images/migrations}/migrations/7 (100%) rename {scripts => packages/core/platform/images/migrations}/migrations/8 (100%) rename {scripts => packages/core/platform/images/migrations}/migrations/9 (100%) create mode 100755 packages/core/platform/images/migrations/run-migrations.sh create mode 100644 packages/system/grafana-operator/images/grafana-dashboards.tag create mode 100644 packages/system/grafana-operator/images/grafana-dashboards/Dockerfile create mode 100644 packages/system/grafana-operator/images/grafana-dashboards/Dockerfile.dockerignore delete mode 100755 scripts/installer.sh diff --git a/Makefile b/Makefile index d0f5dd69..971775cb 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -.PHONY: manifests repos assets unit-tests helm-unit-tests +.PHONY: manifests assets unit-tests helm-unit-tests build-deps: @command -V find docker skopeo jq gh helm > /dev/null @@ -16,6 +16,7 @@ build: build-deps make -C packages/system/cozystack-api image make -C packages/system/cozystack-controller image make -C packages/system/backup-controller image + make -C packages/system/backupstrategy-controller image make -C packages/system/lineage-controller-webhook image make -C packages/system/cilium image make -C packages/system/linstor image @@ -26,21 +27,15 @@ build: build-deps make -C packages/system/kamaji image make -C packages/system/bucket image make -C packages/system/objectstorage-controller image + make -C packages/system/grafana-operator image make -C packages/core/testing image make -C packages/core/talos image - make -C packages/core/platform image make -C packages/core/installer image make manifests -repos: - rm -rf _out - make -C packages/system repo - make -C packages/apps repo - make -C packages/extra repo - manifests: mkdir -p _out/assets - (cd packages/core/installer/; helm template -n cozy-installer installer .) > _out/assets/cozystack-installer.yaml + (cd packages/core/installer/; helm template --namespace cozy-installer installer .) > _out/assets/cozystack-installer.yaml assets: make -C packages/core/talos assets diff --git a/cmd/cozystack-assets-server/main.go b/cmd/cozystack-assets-server/main.go deleted file mode 100644 index 75563712..00000000 --- a/cmd/cozystack-assets-server/main.go +++ /dev/null @@ -1,29 +0,0 @@ -package main - -import ( - "flag" - "log" - "net/http" - "path/filepath" -) - -func main() { - addr := flag.String("address", ":8123", "Address to listen on") - dir := flag.String("dir", "/cozystack/assets", "Directory to serve files from") - flag.Parse() - - absDir, err := filepath.Abs(*dir) - if err != nil { - log.Fatalf("Error getting absolute path for %s: %v", *dir, err) - } - - fs := http.FileServer(http.Dir(absDir)) - http.Handle("/", fs) - - log.Printf("Server starting on %s, serving directory %s", *addr, absDir) - - err = http.ListenAndServe(*addr, nil) - if err != nil { - log.Fatalf("Server failed to start: %v", err) - } -} diff --git a/packages/core/installer/Makefile b/packages/core/installer/Makefile index 4c19c258..f4b527bf 100644 --- a/packages/core/installer/Makefile +++ b/packages/core/installer/Makefile @@ -7,51 +7,42 @@ pre-checks: ../../../hack/pre-checks.sh show: - cozyhr show -n $(NAMESPACE) $(NAME) --plain + cozyhr show --namespace $(NAMESPACE) $(NAME) --plain apply: - cozyhr show -n $(NAMESPACE) $(NAME) --plain | kubectl apply -f- + cozyhr show --namespace $(NAMESPACE) $(NAME) --plain | kubectl apply --filename - diff: - cozyhr show -n $(NAMESPACE) $(NAME) --plain | kubectl diff -f - + cozyhr show --namespace $(NAMESPACE) $(NAME) --plain | kubectl diff --filename - -image: pre-checks image-cozystack - -image-cozystack: - docker buildx build -f images/cozystack/Dockerfile ../../.. \ - --tag $(REGISTRY)/installer:$(call settag,$(TAG)) \ - --cache-from type=registry,ref=$(REGISTRY)/installer:latest \ - --cache-to type=inline \ - --metadata-file images/installer.json \ - $(BUILDX_ARGS) - IMAGE="$(REGISTRY)/installer:$(call settag,$(TAG))@$$(yq e '."containerimage.digest"' images/installer.json -o json -r)" \ - yq -i '.cozystack.image = strenv(IMAGE)' values.yaml - rm -f images/installer.json +image: pre-checks image-operator image-packages update-version: TAG="$(call settag,$(TAG))" \ - yq -i '.cozystackOperator.cozystackVersion = strenv(TAG)' values.yaml + yq --inplace '.cozystackOperator.cozystackVersion = strenv(TAG)' values.yaml image-operator: - docker buildx build -f images/cozystack-operator/Dockerfile ../../.. \ - --tag $(REGISTRY)/cozystack-operator:$(call settag,$(TAG)) \ - --cache-from type=registry,ref=$(REGISTRY)/cozystack-operator:latest \ - --cache-to type=inline \ - --metadata-file images/cozystack-operator.json \ - $(BUILDX_ARGS) - IMAGE="$(REGISTRY)/cozystack-operator:$(call settag,$(TAG))@$$(yq e '."containerimage.digest"' images/cozystack-operator.json -o json -r)" \ - yq -i '.cozystackOperator.image = strenv(IMAGE)' values.yaml + docker buildx build --file images/cozystack-operator/Dockerfile ../../.. \ + --tag $(REGISTRY)/cozystack-operator:$(call settag,$(TAG)) \ + --cache-from type=registry,ref=$(REGISTRY)/cozystack-operator:latest \ + --cache-to type=inline \ + --metadata-file images/cozystack-operator.json \ + $(BUILDX_ARGS) + IMAGE="$(REGISTRY)/cozystack-operator:$(call settag,$(TAG))@$$(yq -e '.["containerimage.digest"]' images/cozystack-operator.json -o json -r)" \ + yq --inplace '.cozystackOperator.image = strenv(IMAGE)' values.yaml rm -f images/cozystack-operator.json - image-packages: update-version mkdir -p ../../../_out/assets images flux push artifact \ - oci://$(REGISTRY)/platform-packages:$(call settag,$(TAG)) \ - --path=../../../packages \ - --source=https://github.com/cozystack/cozystack \ - --revision="$$(git describe --tags):$$(git rev-parse HEAD)" \ - 2>&1 | tee images/cozystack-packages.log - export REPO="oci://$(REGISTRY)/platform-packages"; \ - export DIGEST=$$(awk -F@ '/artifact successfully pushed/ {print $$2}' images/cozystack-packages.log; rm -f images/cozystack-packages.log); \ - test -n "$$DIGEST" && yq -i '.cozystackOperator.platformSource = (strenv(REPO) + "@" + strenv(DIGEST))' values.yaml + oci://$(REGISTRY)/cozystack-packages:$(call settag,$(TAG)) \ + --path=../../../packages \ + --source=https://github.com/cozystack/cozystack \ + --revision="$$(git describe --tags):$$(git rev-parse HEAD)" \ + 2>&1 | tee images/cozystack-packages.log + export REPO="oci://$(REGISTRY)/cozystack-packages" \ + export DIGEST=$$(awk --field-separator @ '/artifact successfully pushed/ {print $$2}' images/cozystack-packages.log) && \ + rm -f images/cozystack-packages.log && \ + test -n "$$DIGEST" && \ + yq --inplace '.cozystackOperator.platformSourceUrl = strenv(REPO)' values.yaml && \ + yq --inplace '.cozystackOperator.platformSourceRef = "digest=" + strenv(DIGEST)' values.yaml diff --git a/packages/core/installer/images/cozystack/Dockerfile b/packages/core/installer/images/cozystack/Dockerfile deleted file mode 100644 index 8e1ae219..00000000 --- a/packages/core/installer/images/cozystack/Dockerfile +++ /dev/null @@ -1,41 +0,0 @@ -FROM golang:1.24-alpine AS k8s-await-election-builder - -ARG K8S_AWAIT_ELECTION_GITREPO=https://github.com/LINBIT/k8s-await-election -ARG K8S_AWAIT_ELECTION_VERSION=0.4.1 - -# TARGETARCH is a docker special variable: https://docs.docker.com/engine/reference/builder/#automatic-platform-args-in-the-global-scope -ARG TARGETARCH - -RUN apk add --no-cache git make -RUN git clone ${K8S_AWAIT_ELECTION_GITREPO} /usr/local/go/k8s-await-election/ \ - && cd /usr/local/go/k8s-await-election \ - && git reset --hard v${K8S_AWAIT_ELECTION_VERSION} \ - && make \ - && mv ./out/k8s-await-election-${TARGETARCH} /k8s-await-election - -FROM golang:1.25-alpine AS builder - -ARG TARGETOS -ARG TARGETARCH - -RUN apk add --no-cache make git -RUN apk add helm --repository=https://dl-cdn.alpinelinux.org/alpine/edge/community - -COPY . /src/ -WORKDIR /src - -RUN go mod download - -FROM alpine:3.22 - -RUN wget -O- https://github.com/cozystack/cozyhr/raw/refs/heads/main/hack/install.sh | sh -s -- -v 1.5.0 - -RUN apk add --no-cache make kubectl helm coreutils git jq openssl - -COPY --from=builder /src/scripts /cozystack/scripts -COPY --from=builder /src/packages/core /cozystack/packages/core -COPY --from=builder /src/packages/system /cozystack/packages/system -COPY --from=k8s-await-election-builder /k8s-await-election /usr/bin/k8s-await-election - -WORKDIR /cozystack -ENTRYPOINT ["/usr/bin/k8s-await-election", "/cozystack/scripts/installer.sh" ] diff --git a/packages/core/installer/images/cozystack/Dockerfile.dockerignore b/packages/core/installer/images/cozystack/Dockerfile.dockerignore deleted file mode 100644 index c1d18d8a..00000000 --- a/packages/core/installer/images/cozystack/Dockerfile.dockerignore +++ /dev/null @@ -1 +0,0 @@ -_out diff --git a/packages/core/installer/templates/cozystack-operator.yaml b/packages/core/installer/templates/cozystack-operator.yaml index 514dcc32..7cc215b1 100644 --- a/packages/core/installer/templates/cozystack-operator.yaml +++ b/packages/core/installer/templates/cozystack-operator.yaml @@ -1,4 +1,3 @@ -{{- if .Values.cozystackOperator.enabled }} --- apiVersion: v1 kind: Namespace @@ -78,47 +77,3 @@ spec: - key: "node.cilium.io/agent-not-ready" operator: "Exists" effect: "NoSchedule" ---- -apiVersion: cozystack.io/v1alpha1 -kind: PackageSource -metadata: - name: cozystack.cozystack-platform - annotations: - operator.cozystack.io/skip-cozystack-values: "true" -spec: - sourceRef: - kind: OCIRepository - name: cozystack-packages - namespace: cozy-system - path: / - variants: - - name: default - components: - - install: - namespace: cozy-system - releaseName: cozystack-platform - name: cozystack-platform - path: core/platform - valuesFiles: - - values.yaml - - name: isp-full - components: - - install: - namespace: cozy-system - releaseName: cozystack-platform - name: cozystack-platform - path: core/platform - valuesFiles: - - values.yaml - - values-isp-full.yaml - - name: isp-hosted - components: - - install: - namespace: cozy-system - releaseName: cozystack-platform - name: cozystack-platform - path: core/platform - valuesFiles: - - values.yaml - - values-isp-hosted.yaml -{{- end }} diff --git a/packages/core/installer/templates/cozystack.yaml b/packages/core/installer/templates/cozystack.yaml deleted file mode 100644 index 24ca0557..00000000 --- a/packages/core/installer/templates/cozystack.yaml +++ /dev/null @@ -1,81 +0,0 @@ -{{- if not .Values.cozystackOperator.enabled }} ---- -apiVersion: v1 -kind: Namespace -metadata: - name: cozy-system - labels: - cozystack.io/system: "true" - pod-security.kubernetes.io/enforce: privileged ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: cozystack - namespace: cozy-system ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: cozystack -subjects: -- kind: ServiceAccount - name: cozystack - namespace: cozy-system -roleRef: - kind: ClusterRole - name: cluster-admin - apiGroup: rbac.authorization.k8s.io ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: cozystack - namespace: cozy-system -spec: - replicas: 1 - selector: - matchLabels: - app: cozystack - strategy: - type: RollingUpdate - rollingUpdate: - maxSurge: 0 - maxUnavailable: 1 - template: - metadata: - labels: - app: cozystack - spec: - hostNetwork: true - serviceAccountName: cozystack - containers: - - name: cozystack - image: "{{ .Values.cozystack.image }}" - env: - - name: KUBERNETES_SERVICE_HOST - value: localhost - - name: INSTALL_FLUX - value: "true" - - name: KUBERNETES_SERVICE_PORT - value: "7445" - - name: K8S_AWAIT_ELECTION_ENABLED - value: "1" - - name: K8S_AWAIT_ELECTION_NAME - value: cozystack - - name: K8S_AWAIT_ELECTION_LOCK_NAME - value: cozystack - - name: K8S_AWAIT_ELECTION_LOCK_NAMESPACE - value: cozy-system - - name: K8S_AWAIT_ELECTION_IDENTITY - valueFrom: - fieldRef: - fieldPath: metadata.name - tolerations: - - key: "node.kubernetes.io/not-ready" - operator: "Exists" - effect: "NoSchedule" - - key: "node.cilium.io/agent-not-ready" - operator: "Exists" - effect: "NoSchedule" -{{- end }} diff --git a/packages/core/installer/templates/crds.yaml b/packages/core/installer/templates/crds.yaml index cffc1205..32b438c1 100644 --- a/packages/core/installer/templates/crds.yaml +++ b/packages/core/installer/templates/crds.yaml @@ -1,6 +1,4 @@ -{{- if .Values.cozystackOperator.enabled }} {{- range $path, $_ := .Files.Glob "definitions/*.yaml" }} --- {{ $.Files.Get $path }} {{- end }} -{{- end }} diff --git a/packages/core/installer/templates/packagesource.yaml b/packages/core/installer/templates/packagesource.yaml new file mode 100644 index 00000000..7a3dd73e --- /dev/null +++ b/packages/core/installer/templates/packagesource.yaml @@ -0,0 +1,43 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.cozystack-platform + annotations: + operator.cozystack.io/skip-cozystack-values: "true" +spec: + sourceRef: + kind: OCIRepository + name: cozystack-platform + namespace: cozy-system + path: / + variants: + - name: default + components: + - install: + namespace: cozy-system + releaseName: cozystack-platform + name: platform + path: core/platform + valuesFiles: + - values.yaml + - name: isp-full + components: + - install: + namespace: cozy-system + releaseName: cozystack-platform + name: platform + path: core/platform + valuesFiles: + - values.yaml + - values-isp-full.yaml + - name: isp-hosted + components: + - install: + namespace: cozy-system + releaseName: cozystack-platform + name: platform + path: core/platform + valuesFiles: + - values.yaml + - values-isp-hosted.yaml diff --git a/packages/core/installer/values.yaml b/packages/core/installer/values.yaml index 0379a40c..e38b4dfb 100644 --- a/packages/core/installer/values.yaml +++ b/packages/core/installer/values.yaml @@ -1,8 +1,5 @@ -cozystack: - image: ghcr.io/cozystack/cozystack/installer:v0.40.2@sha256:146e0de9d1208eba8342277fa08e0588d66e51bc637c6b3add8ef63cc54ef351 cozystackOperator: - enabled: false - image: ghcr.io/cozystack/cozystack/cozystack-operator:latest@sha256:f7f6e0fd9e896b7bfa642d0bfa4378bc14e646bc5c2e86e2e09a82770ef33181 - platformSourceUrl: 'oci://ghcr.io/cozystack/cozystack/platform-packages' - platformSourceRef: 'digest=sha256:0576491291b33936cdf770a5c5b5692add97339c1505fc67a92df9d69dfbfdf6' + image: ghcr.io/cozystack/cozystack/cozystack-operator:latest@sha256:1e998a7cf2b9ed10b1320d9b3282e9362283fcda524f22fa0ad40579e7e464ff + platformSourceUrl: 'oci://ghcr.io/cozystack/cozystack/cozystack-packages' + platformSourceRef: 'digest=sha256:319e78e454cdc28e1d6edd701e4b09723bfb12a03c71b77271d136b09002ad7e' cozystackVersion: latest diff --git a/packages/core/platform/images/cozystack-assets/Dockerfile b/packages/core/platform/images/cozystack-assets/Dockerfile deleted file mode 100644 index bf284de3..00000000 --- a/packages/core/platform/images/cozystack-assets/Dockerfile +++ /dev/null @@ -1,25 +0,0 @@ -FROM golang:1.25-alpine AS builder - -ARG TARGETOS -ARG TARGETARCH - -RUN apk add --no-cache make git -RUN apk add helm --repository=https://dl-cdn.alpinelinux.org/alpine/edge/community - -COPY . /src/ -WORKDIR /src - -RUN go mod download - -RUN go build -o /cozystack-assets-server -ldflags '-extldflags "-static" -w -s' ./cmd/cozystack-assets-server - -RUN make repos - -FROM alpine:3.22 - -COPY --from=builder /src/_out/repos /cozystack/assets/repos -COPY --from=builder /cozystack-assets-server /usr/bin/cozystack-assets-server -COPY --from=builder /src/dashboards /cozystack/assets/dashboards - -WORKDIR /cozystack -ENTRYPOINT ["/usr/bin/cozystack-assets-server"] diff --git a/packages/core/platform/images/migrations/Dockerfile b/packages/core/platform/images/migrations/Dockerfile new file mode 100644 index 00000000..137c2768 --- /dev/null +++ b/packages/core/platform/images/migrations/Dockerfile @@ -0,0 +1,12 @@ +FROM alpine:3.22 + +RUN wget -O- https://github.com/cozystack/cozyhr/raw/refs/heads/main/hack/install.sh | sh -s -- -v 1.5.0 + +RUN apk add --no-cache kubectl helm coreutils git jq ca-certificates bash curl + +COPY migrations /migrations +COPY run-migrations.sh /usr/bin/run-migrations.sh + +WORKDIR /migrations + +ENTRYPOINT ["/usr/bin/run-migrations.sh"] diff --git a/scripts/migrations/1 b/packages/core/platform/images/migrations/migrations/1 similarity index 100% rename from scripts/migrations/1 rename to packages/core/platform/images/migrations/migrations/1 diff --git a/scripts/migrations/10 b/packages/core/platform/images/migrations/migrations/10 similarity index 100% rename from scripts/migrations/10 rename to packages/core/platform/images/migrations/migrations/10 diff --git a/scripts/migrations/11 b/packages/core/platform/images/migrations/migrations/11 similarity index 100% rename from scripts/migrations/11 rename to packages/core/platform/images/migrations/migrations/11 diff --git a/scripts/migrations/12 b/packages/core/platform/images/migrations/migrations/12 similarity index 100% rename from scripts/migrations/12 rename to packages/core/platform/images/migrations/migrations/12 diff --git a/scripts/migrations/13 b/packages/core/platform/images/migrations/migrations/13 similarity index 100% rename from scripts/migrations/13 rename to packages/core/platform/images/migrations/migrations/13 diff --git a/scripts/migrations/14 b/packages/core/platform/images/migrations/migrations/14 similarity index 100% rename from scripts/migrations/14 rename to packages/core/platform/images/migrations/migrations/14 diff --git a/scripts/migrations/15 b/packages/core/platform/images/migrations/migrations/15 similarity index 100% rename from scripts/migrations/15 rename to packages/core/platform/images/migrations/migrations/15 diff --git a/scripts/migrations/16 b/packages/core/platform/images/migrations/migrations/16 similarity index 100% rename from scripts/migrations/16 rename to packages/core/platform/images/migrations/migrations/16 diff --git a/scripts/migrations/17 b/packages/core/platform/images/migrations/migrations/17 similarity index 100% rename from scripts/migrations/17 rename to packages/core/platform/images/migrations/migrations/17 diff --git a/scripts/migrations/18 b/packages/core/platform/images/migrations/migrations/18 similarity index 100% rename from scripts/migrations/18 rename to packages/core/platform/images/migrations/migrations/18 diff --git a/scripts/migrations/19 b/packages/core/platform/images/migrations/migrations/19 similarity index 100% rename from scripts/migrations/19 rename to packages/core/platform/images/migrations/migrations/19 diff --git a/scripts/migrations/2 b/packages/core/platform/images/migrations/migrations/2 similarity index 100% rename from scripts/migrations/2 rename to packages/core/platform/images/migrations/migrations/2 diff --git a/scripts/migrations/20 b/packages/core/platform/images/migrations/migrations/20 similarity index 100% rename from scripts/migrations/20 rename to packages/core/platform/images/migrations/migrations/20 diff --git a/scripts/migrations/21 b/packages/core/platform/images/migrations/migrations/21 similarity index 100% rename from scripts/migrations/21 rename to packages/core/platform/images/migrations/migrations/21 diff --git a/scripts/migrations/22 b/packages/core/platform/images/migrations/migrations/22 similarity index 100% rename from scripts/migrations/22 rename to packages/core/platform/images/migrations/migrations/22 diff --git a/scripts/migrations/3 b/packages/core/platform/images/migrations/migrations/3 similarity index 100% rename from scripts/migrations/3 rename to packages/core/platform/images/migrations/migrations/3 diff --git a/scripts/migrations/4 b/packages/core/platform/images/migrations/migrations/4 similarity index 100% rename from scripts/migrations/4 rename to packages/core/platform/images/migrations/migrations/4 diff --git a/scripts/migrations/5 b/packages/core/platform/images/migrations/migrations/5 similarity index 100% rename from scripts/migrations/5 rename to packages/core/platform/images/migrations/migrations/5 diff --git a/scripts/migrations/6 b/packages/core/platform/images/migrations/migrations/6 similarity index 100% rename from scripts/migrations/6 rename to packages/core/platform/images/migrations/migrations/6 diff --git a/scripts/migrations/7 b/packages/core/platform/images/migrations/migrations/7 similarity index 100% rename from scripts/migrations/7 rename to packages/core/platform/images/migrations/migrations/7 diff --git a/scripts/migrations/8 b/packages/core/platform/images/migrations/migrations/8 similarity index 100% rename from scripts/migrations/8 rename to packages/core/platform/images/migrations/migrations/8 diff --git a/scripts/migrations/9 b/packages/core/platform/images/migrations/migrations/9 similarity index 100% rename from scripts/migrations/9 rename to packages/core/platform/images/migrations/migrations/9 diff --git a/packages/core/platform/images/migrations/run-migrations.sh b/packages/core/platform/images/migrations/run-migrations.sh new file mode 100755 index 00000000..c35ade21 --- /dev/null +++ b/packages/core/platform/images/migrations/run-migrations.sh @@ -0,0 +1,41 @@ +#!/bin/sh +set -euo pipefail + +NAMESPACE="${NAMESPACE:-cozy-system}" +CURRENT_VERSION="${CURRENT_VERSION:-0}" +TARGET_VERSION="${TARGET_VERSION:-0}" + +echo "Starting migrations from version $CURRENT_VERSION to $TARGET_VERSION" + +# Check if ConfigMap exists +if ! kubectl get configmap --namespace "$NAMESPACE" cozystack-version >/dev/null 2>&1; then + echo "ConfigMap cozystack-version does not exist, creating it with version $TARGET_VERSION" + kubectl create configmap --namespace "$NAMESPACE" cozystack-version \ + --from-literal=version="$TARGET_VERSION" \ + --dry-run=client --output yaml | kubectl apply --filename - + echo "ConfigMap created with version $TARGET_VERSION" + exit 0 +fi + +# If current version is already at target, nothing to do +if [ "$CURRENT_VERSION" -ge "$TARGET_VERSION" ]; then + echo "Current version $CURRENT_VERSION is already at or above target version $TARGET_VERSION" + exit 0 +fi + +# Run migrations sequentially from current version to target version +for i in $(seq $((CURRENT_VERSION + 1)) $TARGET_VERSION); do + if [ -f "/migrations/$i" ]; then + echo "Running migration $i" + chmod +x /migrations/$i + /migrations/$i || { + echo "Migration $i failed" + exit 1 + } + echo "Migration $i completed successfully" + else + echo "Migration $i not found, skipping" + fi +done + +echo "All migrations completed successfully" diff --git a/packages/system/grafana-operator/Makefile b/packages/system/grafana-operator/Makefile index 82c734d4..a752d25b 100644 --- a/packages/system/grafana-operator/Makefile +++ b/packages/system/grafana-operator/Makefile @@ -1,6 +1,7 @@ export NAME=grafana-operator export NAMESPACE=cozy-grafana-operator +include ../../../scripts/common-envs.mk include ../../../scripts/package.mk update: @@ -8,3 +9,14 @@ update: mkdir -p charts curl -sSL https://github.com/grafana-operator/grafana-operator/archive/refs/heads/master.tar.gz | \ tar xzvf - --strip 3 -C charts grafana-operator-master/deploy/helm/grafana-operator + +image: + docker buildx build --file images/grafana-dashboards/Dockerfile ../../.. \ + --tag $(REGISTRY)/grafana-dashboards:$(call settag,$(TAG)) \ + --cache-from type=registry,ref=$(REGISTRY)/grafana-dashboards:latest \ + --cache-to type=inline \ + --metadata-file images/grafana-dashboards.json \ + $(BUILDX_ARGS) + echo "$(REGISTRY)/grafana-dashboards:$(call settag,$(TAG))@$$(yq --exit-status '.["containerimage.digest"]' images/grafana-dashboards.json --output-format json -r)" \ + > images/grafana-dashboards.tag + rm -f images/grafana-dashboards.json diff --git a/packages/system/grafana-operator/images/grafana-dashboards.tag b/packages/system/grafana-operator/images/grafana-dashboards.tag new file mode 100644 index 00000000..bd0f3b14 --- /dev/null +++ b/packages/system/grafana-operator/images/grafana-dashboards.tag @@ -0,0 +1 @@ +ghcr.io/cozystack/cozystack/grafana-dashboards:latest diff --git a/packages/system/grafana-operator/images/grafana-dashboards/Dockerfile b/packages/system/grafana-operator/images/grafana-dashboards/Dockerfile new file mode 100644 index 00000000..8ea43e76 --- /dev/null +++ b/packages/system/grafana-operator/images/grafana-dashboards/Dockerfile @@ -0,0 +1,11 @@ +FROM alpine:3.22 + +RUN apk add --no-cache darkhttpd + +COPY dashboards /var/www/dashboards + +WORKDIR /var/www + +EXPOSE 8080 + +CMD ["darkhttpd", "/var/www/dashboards", "--port", "8080", "--addr", "0.0.0.0"] diff --git a/packages/system/grafana-operator/images/grafana-dashboards/Dockerfile.dockerignore b/packages/system/grafana-operator/images/grafana-dashboards/Dockerfile.dockerignore new file mode 100644 index 00000000..fa588871 --- /dev/null +++ b/packages/system/grafana-operator/images/grafana-dashboards/Dockerfile.dockerignore @@ -0,0 +1,3 @@ +# Exclude everything except dashboards directory +* +!dashboards/** diff --git a/scripts/installer.sh b/scripts/installer.sh deleted file mode 100755 index 042e895a..00000000 --- a/scripts/installer.sh +++ /dev/null @@ -1,71 +0,0 @@ -#!/bin/sh -set -o pipefail -set -e - -BUNDLE=$(set -x; kubectl get configmap -n cozy-system cozystack -o 'go-template={{index .data "bundle-name"}}') -VERSION=$(find scripts/migrations -mindepth 1 -maxdepth 1 -type f | sort -V | awk -F/ 'END {print $NF+1}') - -run_migrations() { - if ! kubectl get configmap -n cozy-system cozystack-version; then - kubectl create configmap -n cozy-system cozystack-version --from-literal=version="$VERSION" --dry-run=client -o yaml | kubectl create -f- - return - fi - current_version=$(kubectl get configmap -n cozy-system cozystack-version -o jsonpath='{.data.version}') || true - until [ "$current_version" = "$VERSION" ]; do - echo "run migration: $current_version --> $VERSION" - chmod +x scripts/migrations/$current_version - scripts/migrations/$current_version - current_version=$(kubectl get configmap -n cozy-system cozystack-version -o jsonpath='{.data.version}') - done -} - -install_flux() { - if [ "$INSTALL_FLUX" != "true" ]; then - return - fi - make -C packages/core/flux-aio apply - wait_for_crds helmreleases.helm.toolkit.fluxcd.io helmrepositories.source.toolkit.fluxcd.io -} - -wait_for_crds() { - timeout 60 sh -c "until kubectl get crd $*; do sleep 1; done" -} - -cd "$(dirname "$0")/.." - -# Run migrations -run_migrations - -# Install namespaces -make -C packages/core/platform namespaces-apply - -# Install fluxcd -install_flux - -# Install fluxcd certificates -./scripts/issue-flux-certificates.sh - -# Install platform chart -make -C packages/core/platform reconcile - -# Reconcile Helm repositories -kubectl annotate helmrepositories.source.toolkit.fluxcd.io -A -l cozystack.io/repository reconcile.fluxcd.io/requestedAt=$(date +"%Y-%m-%dT%H:%M:%SZ") --overwrite - -# Unsuspend all Cozystack managed charts -kubectl get hr -A -o go-template='{{ range .items }}{{ if .spec.suspend }}{{ .spec.chart.spec.sourceRef.namespace }}/{{ .spec.chart.spec.sourceRef.name }} {{ .metadata.namespace }} {{ .metadata.name }}{{ "\n" }}{{ end }}{{ end }}' | while read repo namespace name; do - case "$repo" in - cozy-system/cozystack-system|cozy-public/cozystack-extra|cozy-public/cozystack-apps) - kubectl patch hr -n "$namespace" "$name" -p '{"spec": {"suspend": null}}' --type=merge --field-manager=flux-client-side-apply - ;; - esac -done - -# Update all Cozystack managed charts to latest version -kubectl get hr -A -l cozystack.io/ui=true --no-headers | awk '{print "kubectl patch helmrelease -n " $1 " " $2 " --type=merge -p '\''{\"spec\":{\"chart\":{\"spec\":{\"version\":\">= 0.0.0-0\"}}}}'\'' "}' | sh -x - -# Reconcile platform chart -trap 'exit' INT TERM -while true; do - sleep 60 & wait - make -C packages/core/platform reconcile -done From 8e9c0dd5adbb03e83a131874c85d8d69cf1d80a0 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Thu, 15 Jan 2026 14:14:55 +0100 Subject: [PATCH 020/125] refactor(cozyrds): update chartRef to use ExternalArtifact Update all CozystackResourceDefinition files to use chartRef with ExternalArtifact instead of OCIRepository sourceRef. Co-Authored-By: Claude Signed-off-by: Andrei Kvapil --- hack/update-crd.sh | 21 +++++++++++++------ .../system/bootbox-rd/cozyrds/bootbox.yaml | 11 +++++----- packages/system/bucket-rd/cozyrds/bucket.yaml | 11 +++++----- .../clickhouse-rd/cozyrds/clickhouse.yaml | 11 +++++----- packages/system/etcd-rd/cozyrds/etcd.yaml | 11 +++++----- .../system/ferretdb-rd/cozyrds/ferretdb.yaml | 11 +++++----- .../foundationdb-rd/cozyrds/foundationdb.yaml | 11 +++++----- .../http-cache-rd/cozyrds/http-cache.yaml | 11 +++++----- packages/system/info-rd/cozyrds/info.yaml | 11 +++++----- .../system/ingress-rd/cozyrds/ingress.yaml | 11 +++++----- packages/system/kafka-rd/cozyrds/kafka.yaml | 11 +++++----- .../kubernetes-rd/cozyrds/kubernetes.yaml | 11 +++++----- .../monitoring-rd/cozyrds/monitoring.yaml | 11 +++++----- packages/system/mysql-rd/cozyrds/mysql.yaml | 11 +++++----- packages/system/nats-rd/cozyrds/nats.yaml | 11 +++++----- .../system/postgres-rd/cozyrds/postgres.yaml | 11 +++++----- .../system/rabbitmq-rd/cozyrds/rabbitmq.yaml | 11 +++++----- packages/system/redis-rd/cozyrds/redis.yaml | 11 +++++----- .../seaweedfs-rd/cozyrds/seaweedfs.yaml | 11 +++++----- .../tcp-balancer-rd/cozyrds/tcp-balancer.yaml | 11 +++++----- packages/system/tenant-rd/cozyrds/tenant.yaml | 11 +++++----- .../cozyrds/virtual-machine.yaml | 11 +++++----- .../cozyrds/virtualprivatecloud.yaml | 11 +++++----- .../system/vm-disk-rd/cozyrds/vm-disk.yaml | 11 +++++----- .../vm-instance-rd/cozyrds/vm-instance.yaml | 11 +++++----- packages/system/vpn-rd/cozyrds/vpn.yaml | 11 +++++----- 26 files changed, 140 insertions(+), 156 deletions(-) diff --git a/hack/update-crd.sh b/hack/update-crd.sh index b619b220..ce9d5d10 100755 --- a/hack/update-crd.sh +++ b/hack/update-crd.sh @@ -65,6 +65,15 @@ case "$PWD" in *"/extra/"*) SOURCE_NAME="cozystack-extra" ;; esac +# Determine variant from PackageSource file +# Look for packages/core/platform/sources/${NAME}-application.yaml +PACKAGE_SOURCE_FILE="../../core/platform/sources/${NAME}-application.yaml" +if [[ -f "$PACKAGE_SOURCE_FILE" ]]; then + VARIANT="$(yq -r '.spec.variants[0].name // "default"' "$PACKAGE_SOURCE_FILE")" +else + VARIANT="default" +fi + # If file doesn't exist, create a minimal skeleton OUT="${OUT:-$CRD_DIR/$NAME.yaml}" if [[ ! -f "$OUT" ]]; then @@ -86,6 +95,7 @@ fi export DESCRIPTION="$DESC" export ICON_B64="$ICON_B64" export SOURCE_NAME="$SOURCE_NAME" +export VARIANT="$VARIANT" export SCHEMA_JSON_MIN="$(jq -c . "$SCHEMA_JSON")" # Generate keysOrder from values.yaml @@ -118,8 +128,8 @@ export KEYS_ORDER="$( # Update only necessary fields in-place # - openAPISchema is loaded from file as a multi-line string (block scalar) # - labels ensure cozystack.io/ui: "true" -# - prefix = "-" -# - sourceRef derived from directory (apps|extra) +# - prefix = "-" or "" for extra +# - chartRef points to ExternalArtifact created by Package controller yq -i ' .apiVersion = (.apiVersion // "cozystack.io/v1alpha1") | .kind = (.kind // "CozystackResourceDefinition") | @@ -128,10 +138,9 @@ yq -i ' (.spec.application.openAPISchema style="literal") | .spec.release.prefix = (strenv(PREFIX)) | .spec.release.labels."cozystack.io/ui" = "true" | - .spec.release.chart.name = strenv(RES_NAME) | - .spec.release.chart.sourceRef.kind = "HelmRepository" | - .spec.release.chart.sourceRef.name = strenv(SOURCE_NAME) | - .spec.release.chart.sourceRef.namespace = "cozy-public" | + .spec.release.chartRef.kind = "ExternalArtifact" | + .spec.release.chartRef.name = ("cozystack-" + strenv(RES_NAME) + "-application-" + strenv(VARIANT) + "-" + strenv(RES_NAME)) | + .spec.release.chartRef.namespace = "cozy-system" | .spec.dashboard.description = strenv(DESCRIPTION) | .spec.dashboard.icon = strenv(ICON_B64) | .spec.dashboard.keysOrder = env(KEYS_ORDER) diff --git a/packages/system/bootbox-rd/cozyrds/bootbox.yaml b/packages/system/bootbox-rd/cozyrds/bootbox.yaml index f47bf8f6..7ed1f52f 100644 --- a/packages/system/bootbox-rd/cozyrds/bootbox.yaml +++ b/packages/system/bootbox-rd/cozyrds/bootbox.yaml @@ -13,12 +13,11 @@ spec: prefix: "" labels: cozystack.io/ui: "true" - chart: - name: bootbox - sourceRef: - kind: HelmRepository - name: cozystack-extra - namespace: cozy-public + sharding.fluxcd.io/key: tenants + chartRef: + kind: ExternalArtifact + name: cozystack-bootbox-application-default-bootbox + namespace: cozy-system dashboard: category: Administration singular: BootBox diff --git a/packages/system/bucket-rd/cozyrds/bucket.yaml b/packages/system/bucket-rd/cozyrds/bucket.yaml index 889a36b5..8596bf5a 100644 --- a/packages/system/bucket-rd/cozyrds/bucket.yaml +++ b/packages/system/bucket-rd/cozyrds/bucket.yaml @@ -13,12 +13,11 @@ spec: prefix: bucket- labels: cozystack.io/ui: "true" - chart: - name: bucket - sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public + sharding.fluxcd.io/key: tenants + chartRef: + kind: ExternalArtifact + name: cozystack-bucket-application-default-bucket + namespace: cozy-system dashboard: singular: Bucket plural: Buckets diff --git a/packages/system/clickhouse-rd/cozyrds/clickhouse.yaml b/packages/system/clickhouse-rd/cozyrds/clickhouse.yaml index 6948fce0..de35d626 100644 --- a/packages/system/clickhouse-rd/cozyrds/clickhouse.yaml +++ b/packages/system/clickhouse-rd/cozyrds/clickhouse.yaml @@ -13,12 +13,11 @@ spec: prefix: clickhouse- labels: cozystack.io/ui: "true" - chart: - name: clickhouse - sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public + sharding.fluxcd.io/key: tenants + chartRef: + kind: ExternalArtifact + name: cozystack-clickhouse-application-default-clickhouse + namespace: cozy-system dashboard: category: PaaS singular: ClickHouse diff --git a/packages/system/etcd-rd/cozyrds/etcd.yaml b/packages/system/etcd-rd/cozyrds/etcd.yaml index 7381469d..e4483602 100644 --- a/packages/system/etcd-rd/cozyrds/etcd.yaml +++ b/packages/system/etcd-rd/cozyrds/etcd.yaml @@ -13,13 +13,12 @@ spec: prefix: "" labels: cozystack.io/ui: "true" + sharding.fluxcd.io/key: tenants internal.cozystack.io/tenantmodule: "true" - chart: - name: etcd - sourceRef: - kind: HelmRepository - name: cozystack-extra - namespace: cozy-public + chartRef: + kind: ExternalArtifact + name: cozystack-etcd-application-default-etcd + namespace: cozy-system dashboard: category: Administration singular: Etcd diff --git a/packages/system/ferretdb-rd/cozyrds/ferretdb.yaml b/packages/system/ferretdb-rd/cozyrds/ferretdb.yaml index f7bdeb82..a3236914 100644 --- a/packages/system/ferretdb-rd/cozyrds/ferretdb.yaml +++ b/packages/system/ferretdb-rd/cozyrds/ferretdb.yaml @@ -13,12 +13,11 @@ spec: prefix: ferretdb- labels: cozystack.io/ui: "true" - chart: - name: ferretdb - sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public + sharding.fluxcd.io/key: tenants + chartRef: + kind: ExternalArtifact + name: cozystack-ferretdb-application-default-ferretdb + namespace: cozy-system dashboard: category: PaaS singular: FerretDB diff --git a/packages/system/foundationdb-rd/cozyrds/foundationdb.yaml b/packages/system/foundationdb-rd/cozyrds/foundationdb.yaml index e7759380..5cc8ccd6 100644 --- a/packages/system/foundationdb-rd/cozyrds/foundationdb.yaml +++ b/packages/system/foundationdb-rd/cozyrds/foundationdb.yaml @@ -13,12 +13,11 @@ spec: prefix: foundationdb- labels: cozystack.io/ui: "true" - chart: - name: foundationdb - sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public + sharding.fluxcd.io/key: tenants + chartRef: + kind: ExternalArtifact + name: cozystack-foundationdb-application-default-foundationdb + namespace: cozy-system dashboard: category: PaaS singular: FoundationDB diff --git a/packages/system/http-cache-rd/cozyrds/http-cache.yaml b/packages/system/http-cache-rd/cozyrds/http-cache.yaml index 70de0418..991e933f 100644 --- a/packages/system/http-cache-rd/cozyrds/http-cache.yaml +++ b/packages/system/http-cache-rd/cozyrds/http-cache.yaml @@ -13,12 +13,11 @@ spec: prefix: http-cache- labels: cozystack.io/ui: "true" - chart: - name: http-cache - sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public + sharding.fluxcd.io/key: tenants + chartRef: + kind: ExternalArtifact + name: cozystack-http-cache-application-default-http-cache + namespace: cozy-system dashboard: category: NaaS singular: HTTP Cache diff --git a/packages/system/info-rd/cozyrds/info.yaml b/packages/system/info-rd/cozyrds/info.yaml index 43cda091..68ed9b32 100644 --- a/packages/system/info-rd/cozyrds/info.yaml +++ b/packages/system/info-rd/cozyrds/info.yaml @@ -13,13 +13,12 @@ spec: prefix: "" labels: cozystack.io/ui: "true" + sharding.fluxcd.io/key: tenants internal.cozystack.io/tenantmodule: "true" - chart: - name: info - sourceRef: - kind: HelmRepository - name: cozystack-extra - namespace: cozy-public + chartRef: + kind: ExternalArtifact + name: cozystack-info-application-default-info + namespace: cozy-system dashboard: name: info category: Administration diff --git a/packages/system/ingress-rd/cozyrds/ingress.yaml b/packages/system/ingress-rd/cozyrds/ingress.yaml index 2e00f6ed..b547c28c 100644 --- a/packages/system/ingress-rd/cozyrds/ingress.yaml +++ b/packages/system/ingress-rd/cozyrds/ingress.yaml @@ -13,13 +13,12 @@ spec: prefix: "" labels: cozystack.io/ui: "true" + sharding.fluxcd.io/key: tenants internal.cozystack.io/tenantmodule: "true" - chart: - name: ingress - sourceRef: - kind: HelmRepository - name: cozystack-extra - namespace: cozy-public + chartRef: + kind: ExternalArtifact + name: cozystack-ingress-application-default-ingress + namespace: cozy-system dashboard: category: Administration singular: Ingress diff --git a/packages/system/kafka-rd/cozyrds/kafka.yaml b/packages/system/kafka-rd/cozyrds/kafka.yaml index 7009d241..5e0a7da1 100644 --- a/packages/system/kafka-rd/cozyrds/kafka.yaml +++ b/packages/system/kafka-rd/cozyrds/kafka.yaml @@ -13,12 +13,11 @@ spec: prefix: kafka- labels: cozystack.io/ui: "true" - chart: - name: kafka - sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public + sharding.fluxcd.io/key: tenants + chartRef: + kind: ExternalArtifact + name: cozystack-kafka-application-default-kafka + namespace: cozy-system dashboard: category: PaaS singular: Kafka diff --git a/packages/system/kubernetes-rd/cozyrds/kubernetes.yaml b/packages/system/kubernetes-rd/cozyrds/kubernetes.yaml index 05ed110f..c5516b91 100644 --- a/packages/system/kubernetes-rd/cozyrds/kubernetes.yaml +++ b/packages/system/kubernetes-rd/cozyrds/kubernetes.yaml @@ -13,12 +13,11 @@ spec: prefix: kubernetes- labels: cozystack.io/ui: "true" - chart: - name: kubernetes - sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public + sharding.fluxcd.io/key: tenants + chartRef: + kind: ExternalArtifact + name: cozystack-kubernetes-application-kubevirt-kubernetes + namespace: cozy-system dashboard: category: IaaS singular: Kubernetes diff --git a/packages/system/monitoring-rd/cozyrds/monitoring.yaml b/packages/system/monitoring-rd/cozyrds/monitoring.yaml index e3f098e4..e8a9a02b 100644 --- a/packages/system/monitoring-rd/cozyrds/monitoring.yaml +++ b/packages/system/monitoring-rd/cozyrds/monitoring.yaml @@ -13,13 +13,12 @@ spec: prefix: "" labels: cozystack.io/ui: "true" + sharding.fluxcd.io/key: tenants internal.cozystack.io/tenantmodule: "true" - chart: - name: monitoring - sourceRef: - kind: HelmRepository - name: cozystack-extra - namespace: cozy-public + chartRef: + kind: ExternalArtifact + name: cozystack-monitoring-application-default-monitoring + namespace: cozy-system dashboard: category: Administration singular: Monitoring diff --git a/packages/system/mysql-rd/cozyrds/mysql.yaml b/packages/system/mysql-rd/cozyrds/mysql.yaml index ba8251b8..bb9a170a 100644 --- a/packages/system/mysql-rd/cozyrds/mysql.yaml +++ b/packages/system/mysql-rd/cozyrds/mysql.yaml @@ -13,12 +13,11 @@ spec: prefix: mysql- labels: cozystack.io/ui: "true" - chart: - name: mysql - sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public + sharding.fluxcd.io/key: tenants + chartRef: + kind: ExternalArtifact + name: cozystack-mysql-application-default-mysql + namespace: cozy-system dashboard: category: PaaS singular: MySQL diff --git a/packages/system/nats-rd/cozyrds/nats.yaml b/packages/system/nats-rd/cozyrds/nats.yaml index 258f8f40..c9b44601 100644 --- a/packages/system/nats-rd/cozyrds/nats.yaml +++ b/packages/system/nats-rd/cozyrds/nats.yaml @@ -13,12 +13,11 @@ spec: prefix: nats- labels: cozystack.io/ui: "true" - chart: - name: nats - sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public + sharding.fluxcd.io/key: tenants + chartRef: + kind: ExternalArtifact + name: cozystack-nats-application-default-nats + namespace: cozy-system dashboard: category: PaaS singular: NATS diff --git a/packages/system/postgres-rd/cozyrds/postgres.yaml b/packages/system/postgres-rd/cozyrds/postgres.yaml index c3d16cb7..05d18b51 100644 --- a/packages/system/postgres-rd/cozyrds/postgres.yaml +++ b/packages/system/postgres-rd/cozyrds/postgres.yaml @@ -13,12 +13,11 @@ spec: prefix: postgres- labels: cozystack.io/ui: "true" - chart: - name: postgres - sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public + sharding.fluxcd.io/key: tenants + chartRef: + kind: ExternalArtifact + name: cozystack-postgres-application-default-postgres + namespace: cozy-system dashboard: category: PaaS singular: PostgreSQL diff --git a/packages/system/rabbitmq-rd/cozyrds/rabbitmq.yaml b/packages/system/rabbitmq-rd/cozyrds/rabbitmq.yaml index 092142ac..39c6962d 100644 --- a/packages/system/rabbitmq-rd/cozyrds/rabbitmq.yaml +++ b/packages/system/rabbitmq-rd/cozyrds/rabbitmq.yaml @@ -13,12 +13,11 @@ spec: prefix: rabbitmq- labels: cozystack.io/ui: "true" - chart: - name: rabbitmq - sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public + sharding.fluxcd.io/key: tenants + chartRef: + kind: ExternalArtifact + name: cozystack-rabbitmq-application-default-rabbitmq + namespace: cozy-system dashboard: category: PaaS singular: RabbitMQ diff --git a/packages/system/redis-rd/cozyrds/redis.yaml b/packages/system/redis-rd/cozyrds/redis.yaml index d23f8d2c..176743ff 100644 --- a/packages/system/redis-rd/cozyrds/redis.yaml +++ b/packages/system/redis-rd/cozyrds/redis.yaml @@ -13,12 +13,11 @@ spec: prefix: redis- labels: cozystack.io/ui: "true" - chart: - name: redis - sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public + sharding.fluxcd.io/key: tenants + chartRef: + kind: ExternalArtifact + name: cozystack-redis-application-default-redis + namespace: cozy-system dashboard: category: PaaS singular: Redis diff --git a/packages/system/seaweedfs-rd/cozyrds/seaweedfs.yaml b/packages/system/seaweedfs-rd/cozyrds/seaweedfs.yaml index 787b5448..b83c7b23 100644 --- a/packages/system/seaweedfs-rd/cozyrds/seaweedfs.yaml +++ b/packages/system/seaweedfs-rd/cozyrds/seaweedfs.yaml @@ -13,13 +13,12 @@ spec: prefix: "" labels: cozystack.io/ui: "true" + sharding.fluxcd.io/key: tenants internal.cozystack.io/tenantmodule: "true" - chart: - name: seaweedfs - sourceRef: - kind: HelmRepository - name: cozystack-extra - namespace: cozy-public + chartRef: + kind: ExternalArtifact + name: cozystack-seaweedfs-application-default-seaweedfs + namespace: cozy-system dashboard: category: Administration singular: SeaweedFS diff --git a/packages/system/tcp-balancer-rd/cozyrds/tcp-balancer.yaml b/packages/system/tcp-balancer-rd/cozyrds/tcp-balancer.yaml index 057bc922..b7ce2e7a 100644 --- a/packages/system/tcp-balancer-rd/cozyrds/tcp-balancer.yaml +++ b/packages/system/tcp-balancer-rd/cozyrds/tcp-balancer.yaml @@ -13,12 +13,11 @@ spec: prefix: tcp-balancer- labels: cozystack.io/ui: "true" - chart: - name: tcp-balancer - sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public + sharding.fluxcd.io/key: tenants + chartRef: + kind: ExternalArtifact + name: cozystack-tcp-balancer-application-default-tcp-balancer + namespace: cozy-system dashboard: category: NaaS singular: TCP Balancer diff --git a/packages/system/tenant-rd/cozyrds/tenant.yaml b/packages/system/tenant-rd/cozyrds/tenant.yaml index a5c497ac..08ed1336 100644 --- a/packages/system/tenant-rd/cozyrds/tenant.yaml +++ b/packages/system/tenant-rd/cozyrds/tenant.yaml @@ -13,12 +13,11 @@ spec: prefix: tenant- labels: cozystack.io/ui: "true" - chart: - name: tenant - sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public + sharding.fluxcd.io/key: tenants + chartRef: + kind: ExternalArtifact + name: cozystack-tenant-application-default-tenant + namespace: cozy-system dashboard: category: Administration singular: Tenant diff --git a/packages/system/virtual-machine-rd/cozyrds/virtual-machine.yaml b/packages/system/virtual-machine-rd/cozyrds/virtual-machine.yaml index a1e384c4..30b2abf2 100644 --- a/packages/system/virtual-machine-rd/cozyrds/virtual-machine.yaml +++ b/packages/system/virtual-machine-rd/cozyrds/virtual-machine.yaml @@ -13,12 +13,11 @@ spec: prefix: virtual-machine- labels: cozystack.io/ui: "true" - chart: - name: virtual-machine - sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public + sharding.fluxcd.io/key: tenants + chartRef: + kind: ExternalArtifact + name: cozystack-virtual-machine-application-kubevirt-virtual-machine + namespace: cozy-system dashboard: category: IaaS singular: Virtual Machine diff --git a/packages/system/virtualprivatecloud-rd/cozyrds/virtualprivatecloud.yaml b/packages/system/virtualprivatecloud-rd/cozyrds/virtualprivatecloud.yaml index 8d05a5b6..a39bf7a2 100644 --- a/packages/system/virtualprivatecloud-rd/cozyrds/virtualprivatecloud.yaml +++ b/packages/system/virtualprivatecloud-rd/cozyrds/virtualprivatecloud.yaml @@ -13,12 +13,11 @@ spec: prefix: "virtualprivatecloud-" labels: cozystack.io/ui: "true" - chart: - name: virtualprivatecloud - sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public + sharding.fluxcd.io/key: tenants + chartRef: + kind: ExternalArtifact + name: cozystack-virtualprivatecloud-application-kubevirt-virtualprivatecloud + namespace: cozy-system dashboard: category: IaaS singular: VPC diff --git a/packages/system/vm-disk-rd/cozyrds/vm-disk.yaml b/packages/system/vm-disk-rd/cozyrds/vm-disk.yaml index c3c1b830..5760b8fc 100644 --- a/packages/system/vm-disk-rd/cozyrds/vm-disk.yaml +++ b/packages/system/vm-disk-rd/cozyrds/vm-disk.yaml @@ -13,12 +13,11 @@ spec: prefix: vm-disk- labels: cozystack.io/ui: "true" - chart: - name: vm-disk - sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public + sharding.fluxcd.io/key: tenants + chartRef: + kind: ExternalArtifact + name: cozystack-vm-disk-application-kubevirt-vm-disk + namespace: cozy-system dashboard: category: IaaS singular: VM Disk diff --git a/packages/system/vm-instance-rd/cozyrds/vm-instance.yaml b/packages/system/vm-instance-rd/cozyrds/vm-instance.yaml index 58eb5f9a..aa89adbc 100644 --- a/packages/system/vm-instance-rd/cozyrds/vm-instance.yaml +++ b/packages/system/vm-instance-rd/cozyrds/vm-instance.yaml @@ -13,12 +13,11 @@ spec: prefix: vm-instance- labels: cozystack.io/ui: "true" - chart: - name: vm-instance - sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public + sharding.fluxcd.io/key: tenants + chartRef: + kind: ExternalArtifact + name: cozystack-vm-instance-application-kubevirt-vm-instance + namespace: cozy-system dashboard: category: IaaS singular: VM Instance diff --git a/packages/system/vpn-rd/cozyrds/vpn.yaml b/packages/system/vpn-rd/cozyrds/vpn.yaml index 59d940c0..4f889c1e 100644 --- a/packages/system/vpn-rd/cozyrds/vpn.yaml +++ b/packages/system/vpn-rd/cozyrds/vpn.yaml @@ -13,12 +13,11 @@ spec: prefix: vpn- labels: cozystack.io/ui: "true" - chart: - name: vpn - sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public + sharding.fluxcd.io/key: tenants + chartRef: + kind: ExternalArtifact + name: cozystack-vpn-application-default-vpn + namespace: cozy-system dashboard: category: NaaS singular: VPN From 2d022e38e31da71cdb2503a0e5d3d94538553850 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Thu, 15 Jan 2026 14:15:00 +0100 Subject: [PATCH 021/125] refactor(platform): restructure bundles and add PackageSources Restructure platform bundles from monolithic files to modular directory structure with separate applicationdefinitions. Add PackageSources for better dependency management and migrate from legacy HelmRepositories to new repository format. Co-Authored-By: Claude Signed-off-by: Andrei Kvapil --- packages/core/platform/Makefile | 33 +- .../core/platform/bundles/distro-full.yaml | 435 ------------- .../core/platform/bundles/distro-hosted.yaml | 186 ------ packages/core/platform/bundles/paas-full.yaml | 612 ------------------ .../core/platform/bundles/paas-hosted.yaml | 417 ------------ .../sources/backupstrategy-controller.yaml | 22 + ...l => capi-provider-bootstrap-kubeadm.yaml} | 13 +- ...ders-core.yaml => capi-provider-core.yaml} | 2 +- ...ider.yaml => capi-provider-cp-kamaji.yaml} | 14 +- ...yaml => capi-provider-infra-kubevirt.yaml} | 14 +- .../platform/sources/cozystack-engine.yaml | 1 + .../core/platform/sources/ingress-nginx.yaml | 22 + packages/core/platform/sources/keycloak.yaml | 4 + .../sources/kubernetes-application.yaml | 8 +- .../core/platform/sources/metrics-server.yaml | 22 + .../sources/monitoring-application.yaml | 1 + .../sources/postgres-application.yaml | 1 + packages/core/platform/templates/_helpers.tpl | 150 ++--- packages/core/platform/templates/apps.yaml | 109 +--- .../core/platform/templates/bundles/iaas.yaml | 20 + .../core/platform/templates/bundles/naas.yaml | 8 + .../core/platform/templates/bundles/paas.yaml | 20 + .../platform/templates/bundles/system.yaml | 72 +++ .../templates/containerd-registry-secret.yaml | 35 + .../platform/templates/cozystack-assets.yaml | 73 --- .../platform/templates/cozystack-version.yaml | 11 + .../core/platform/templates/helmreleases.yaml | 100 --- .../core/platform/templates/helmrepos.yaml | 40 -- .../platform/templates/migration-hook.yaml | 69 ++ .../core/platform/templates/namespaces.yaml | 76 --- .../core/platform/templates/repository.yaml | 17 + .../core/platform/templates/scheduling.yaml | 11 + packages/core/platform/templates/sources.yaml | 2 - packages/core/platform/values-isp-full.yaml | 12 + packages/core/platform/values-isp-hosted.yaml | 12 + packages/core/platform/values.yaml | 93 ++- 36 files changed, 568 insertions(+), 2169 deletions(-) delete mode 100644 packages/core/platform/bundles/distro-full.yaml delete mode 100644 packages/core/platform/bundles/distro-hosted.yaml delete mode 100644 packages/core/platform/bundles/paas-full.yaml delete mode 100644 packages/core/platform/bundles/paas-hosted.yaml create mode 100644 packages/core/platform/sources/backupstrategy-controller.yaml rename packages/core/platform/sources/{capi-providers-bootstrap.yaml => capi-provider-bootstrap-kubeadm.yaml} (58%) rename packages/core/platform/sources/{capi-providers-core.yaml => capi-provider-core.yaml} (92%) rename packages/core/platform/sources/{capi-providers-cpprovider.yaml => capi-provider-cp-kamaji.yaml} (58%) rename packages/core/platform/sources/{capi-providers-infraprovider.yaml => capi-provider-infra-kubevirt.yaml} (57%) create mode 100644 packages/core/platform/sources/ingress-nginx.yaml create mode 100644 packages/core/platform/sources/metrics-server.yaml create mode 100644 packages/core/platform/templates/bundles/iaas.yaml create mode 100644 packages/core/platform/templates/bundles/naas.yaml create mode 100644 packages/core/platform/templates/bundles/paas.yaml create mode 100644 packages/core/platform/templates/bundles/system.yaml create mode 100644 packages/core/platform/templates/containerd-registry-secret.yaml delete mode 100644 packages/core/platform/templates/cozystack-assets.yaml create mode 100644 packages/core/platform/templates/cozystack-version.yaml delete mode 100644 packages/core/platform/templates/helmreleases.yaml delete mode 100644 packages/core/platform/templates/helmrepos.yaml create mode 100644 packages/core/platform/templates/migration-hook.yaml delete mode 100644 packages/core/platform/templates/namespaces.yaml create mode 100644 packages/core/platform/templates/repository.yaml create mode 100644 packages/core/platform/templates/scheduling.yaml create mode 100644 packages/core/platform/values-isp-full.yaml create mode 100644 packages/core/platform/values-isp-hosted.yaml diff --git a/packages/core/platform/Makefile b/packages/core/platform/Makefile index 2ea59153..d5d63e6e 100644 --- a/packages/core/platform/Makefile +++ b/packages/core/platform/Makefile @@ -4,31 +4,26 @@ NAMESPACE=cozy-system include ../../../scripts/common-envs.mk show: - cozyhr show -n $(NAMESPACE) $(NAME) --plain + cozyhr show --namespace $(NAMESPACE) $(NAME) --plain apply: - cozyhr show -n $(NAMESPACE) $(NAME) --plain | kubectl apply -f- - kubectl delete helmreleases.helm.toolkit.fluxcd.io -l cozystack.io/marked-for-deletion=true -A + cozyhr show --namespace $(NAMESPACE) $(NAME) --plain | kubectl apply --filename - + kubectl delete helmreleases.helm.toolkit.fluxcd.io --selector cozystack.io/marked-for-deletion=true --all-namespaces reconcile: apply -namespaces-show: - cozyhr show -n $(NAMESPACE) $(NAME) --plain -s templates/namespaces.yaml - -namespaces-apply: - cozyhr show -n $(NAMESPACE) $(NAME) --plain -s templates/namespaces.yaml | kubectl apply -f- - diff: - cozyhr show -n $(NAMESPACE) $(NAME) --plain | kubectl diff -f- + cozyhr show --namespace $(NAMESPACE) $(NAME) --plain | kubectl diff --filename - -image: image-assets -image-assets: - docker buildx build -f images/cozystack-assets/Dockerfile ../../.. \ - --tag $(REGISTRY)/cozystack-assets:$(call settag,$(TAG)) \ - --cache-from type=registry,ref=$(REGISTRY)/cozystack-assets:latest \ +image: image-migrations + +image-migrations: + docker buildx build --file images/migrations/Dockerfile . \ + --tag $(REGISTRY)/platform-migrations:$(call settag,$(TAG)) \ + --cache-from type=registry,ref=$(REGISTRY)/platform-migrations:latest \ --cache-to type=inline \ - --metadata-file images/cozystack-assets.json \ + --metadata-file images/migrations.json \ $(BUILDX_ARGS) - IMAGE="$(REGISTRY)/cozystack-assets:$(call settag,$(TAG))@$$(yq e '."containerimage.digest"' images/cozystack-assets.json -o json -r)" \ - yq -i '.assets.image = strenv(IMAGE)' values.yaml - rm -f images/cozystack-assets.json + IMAGE="$(REGISTRY)/platform-migrations:$(call settag,$(TAG))@$$(yq --exit-status '.["containerimage.digest"]' images/migrations.json --output-format json --raw-output)" \ + yq --inplace '.migrations.image = strenv(IMAGE)' values.yaml + rm -f images/migrations.json diff --git a/packages/core/platform/bundles/distro-full.yaml b/packages/core/platform/bundles/distro-full.yaml deleted file mode 100644 index 13d58ded..00000000 --- a/packages/core/platform/bundles/distro-full.yaml +++ /dev/null @@ -1,435 +0,0 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $clusterDomain := (index $cozyConfig.data "cluster-domain") | default "cozy.local" }} - -releases: -- name: cilium - releaseName: cilium - chart: cozy-cilium - namespace: cozy-cilium - privileged: true - dependsOn: [] - valuesFiles: - - values.yaml - - values-talos.yaml - values: - cilium: - enableIPv4Masquerade: true - enableIdentityMark: true - ipv4NativeRoutingCIDR: "{{ index $cozyConfig.data "ipv4-pod-cidr" }}" - autoDirectNodeRoutes: true - routingMode: native - -- name: cilium-networkpolicy - releaseName: cilium-networkpolicy - chart: cozy-cilium-networkpolicy - namespace: cozy-cilium - privileged: true - dependsOn: [cilium] - -- name: cozy-proxy - releaseName: cozystack - chart: cozy-cozy-proxy - namespace: cozy-system - optional: true - dependsOn: [cilium] - -- name: cert-manager-crds - releaseName: cert-manager-crds - chart: cozy-cert-manager-crds - namespace: cozy-cert-manager - dependsOn: [cilium] - -- name: cozystack-controller - releaseName: cozystack-controller - chart: cozy-cozystack-controller - namespace: cozy-system - dependsOn: [cilium] - {{- if eq (index $cozyConfig.data "telemetry-enabled") "false" }} - values: - cozystackController: - disableTelemetry: true - {{- end }} - -- name: lineage-controller-webhook - releaseName: lineage-controller-webhook - chart: cozy-lineage-controller-webhook - namespace: cozy-system - dependsOn: [cozystack-controller,cilium,cert-manager] - -- name: cozystack-resource-definition-crd - releaseName: cozystack-resource-definition-crd - chart: cozystack-resource-definition-crd - namespace: cozy-system - dependsOn: [cilium] - -- name: bootbox-rd - releaseName: bootbox-rd - chart: bootbox-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: bucket-rd - releaseName: bucket-rd - chart: bucket-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: clickhouse-rd - releaseName: clickhouse-rd - chart: clickhouse-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: etcd-rd - releaseName: etcd-rd - chart: etcd-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: ferretdb-rd - releaseName: ferretdb-rd - chart: ferretdb-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: foundationdb-rd - releaseName: foundationdb-rd - chart: foundationdb-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: http-cache-rd - releaseName: http-cache-rd - chart: http-cache-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: info-rd - releaseName: info-rd - chart: info-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: ingress-rd - releaseName: ingress-rd - chart: ingress-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: kafka-rd - releaseName: kafka-rd - chart: kafka-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: kubernetes-rd - releaseName: kubernetes-rd - chart: kubernetes-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: monitoring-rd - releaseName: monitoring-rd - chart: monitoring-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: mysql-rd - releaseName: mysql-rd - chart: mysql-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: nats-rd - releaseName: nats-rd - chart: nats-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: postgres-rd - releaseName: postgres-rd - chart: postgres-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: rabbitmq-rd - releaseName: rabbitmq-rd - chart: rabbitmq-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: redis-rd - releaseName: redis-rd - chart: redis-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: seaweedfs-rd - releaseName: seaweedfs-rd - chart: seaweedfs-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: tcp-balancer-rd - releaseName: tcp-balancer-rd - chart: tcp-balancer-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: tenant-rd - releaseName: tenant-rd - chart: tenant-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: virtual-machine-rd - releaseName: virtual-machine-rd - chart: virtual-machine-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: virtualprivatecloud-rd - releaseName: virtualprivatecloud-rd - chart: virtualprivatecloud-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: vm-disk-rd - releaseName: vm-disk-rd - chart: vm-disk-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: vm-instance-rd - releaseName: vm-instance-rd - chart: vm-instance-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: vpn-rd - releaseName: vpn-rd - chart: vpn-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: cert-manager - releaseName: cert-manager - chart: cozy-cert-manager - namespace: cozy-cert-manager - dependsOn: [cert-manager-crds] - -- name: cert-manager-issuers - releaseName: cert-manager-issuers - chart: cozy-cert-manager-issuers - namespace: cozy-cert-manager - dependsOn: [cilium,cert-manager] - -- name: prometheus-operator-crds - releaseName: prometheus-operator-crds - chart: cozy-prometheus-operator-crds - namespace: cozy-victoria-metrics-operator - dependsOn: [] - -- name: metrics-server - releaseName: metrics-server - chart: cozy-metrics-server - namespace: cozy-monitoring - dependsOn: [cilium,prometheus-operator-crds] - -- name: victoria-metrics-operator - releaseName: victoria-metrics-operator - chart: cozy-victoria-metrics-operator - namespace: cozy-victoria-metrics-operator - optional: true - dependsOn: [cilium,cert-manager,prometheus-operator-crds] - -- name: monitoring-agents - releaseName: monitoring-agents - chart: cozy-monitoring-agents - namespace: cozy-monitoring - privileged: true - optional: true - dependsOn: [cilium,victoria-metrics-operator,metrics-server] - values: - scrapeRules: - etcd: - enabled: true - -- name: metallb - releaseName: metallb - chart: cozy-metallb - namespace: cozy-metallb - privileged: true - dependsOn: [cilium] - -- name: etcd-operator - releaseName: etcd-operator - chart: cozy-etcd-operator - namespace: cozy-etcd-operator - optional: true - dependsOn: [cilium,cert-manager] - -- name: grafana-operator - releaseName: grafana-operator - chart: cozy-grafana-operator - namespace: cozy-grafana-operator - optional: true - dependsOn: [cilium] - -- name: mariadb-operator - releaseName: mariadb-operator - chart: cozy-mariadb-operator - namespace: cozy-mariadb-operator - optional: true - dependsOn: [cilium,cert-manager,victoria-metrics-operator] - values: - mariadb-operator: - clusterName: {{ $clusterDomain }} - -- name: postgres-operator - releaseName: postgres-operator - chart: cozy-postgres-operator - namespace: cozy-postgres-operator - optional: true - dependsOn: [cilium,cert-manager,victoria-metrics-operator] - -- name: kafka-operator - releaseName: kafka-operator - chart: cozy-kafka-operator - namespace: cozy-kafka-operator - optional: true - dependsOn: [cilium,victoria-metrics-operator] - values: - strimzi-kafka-operator: - kubernetesServiceDnsDomain: {{ $clusterDomain }} - -- name: clickhouse-operator - releaseName: clickhouse-operator - chart: cozy-clickhouse-operator - namespace: cozy-clickhouse-operator - optional: true - dependsOn: [cilium,victoria-metrics-operator] - -- name: foundationdb-operator - releaseName: foundationdb-operator - chart: cozy-foundationdb-operator - namespace: cozy-foundationdb-operator - optional: true - dependsOn: [cilium,cert-manager] - -- name: rabbitmq-operator - releaseName: rabbitmq-operator - chart: cozy-rabbitmq-operator - namespace: cozy-rabbitmq-operator - optional: true - dependsOn: [cilium] - -- name: redis-operator - releaseName: redis-operator - chart: cozy-redis-operator - namespace: cozy-redis-operator - optional: true - dependsOn: [cilium] - -- name: piraeus-operator - releaseName: piraeus-operator - chart: cozy-piraeus-operator - namespace: cozy-linstor - dependsOn: [cilium,cert-manager] - -- name: snapshot-controller - releaseName: snapshot-controller - chart: cozy-snapshot-controller - namespace: cozy-snapshot-controller - dependsOn: [cilium,cert-manager-issuers] - -- name: objectstorage-controller - releaseName: objectstorage-controller - chart: cozy-objectstorage-controller - namespace: cozy-objectstorage-controller - optional: true - dependsOn: [cilium] - -- name: linstor - releaseName: linstor - chart: cozy-linstor - namespace: cozy-linstor - privileged: true - dependsOn: [piraeus-operator,cilium,cert-manager,snapshot-controller] - -- name: linstor-scheduler - releaseName: linstor-scheduler - chart: cozy-linstor-scheduler - namespace: cozy-linstor - dependsOn: [linstor,cert-manager] - -- name: nfs-driver - releaseName: nfs-driver - chart: cozy-nfs-driver - namespace: cozy-nfs-driver - privileged: true - dependsOn: [cilium] - optional: true - -- name: telepresence - releaseName: traffic-manager - chart: cozy-telepresence - namespace: cozy-telepresence - optional: true - dependsOn: [] - -- name: external-dns - releaseName: external-dns - chart: cozy-external-dns - namespace: cozy-external-dns - optional: true - dependsOn: [cilium] - -- name: external-secrets-operator - releaseName: external-secrets-operator - chart: cozy-external-secrets-operator - namespace: cozy-external-secrets-operator - optional: true - dependsOn: [cilium] - -- name: keycloak - releaseName: keycloak - chart: cozy-keycloak - namespace: cozy-keycloak - optional: true - dependsOn: [postgres-operator] - -- name: keycloak-operator - releaseName: keycloak-operator - chart: cozy-keycloak-operator - namespace: cozy-keycloak - optional: true - dependsOn: [keycloak] - -- name: bootbox - releaseName: bootbox - chart: cozy-bootbox - namespace: cozy-bootbox - privileged: true - optional: true - dependsOn: [cilium] - -- name: reloader - releaseName: reloader - chart: cozy-reloader - namespace: cozy-reloader - -- name: velero - releaseName: velero - chart: cozy-velero - namespace: cozy-velero - privileged: true - optional: true - dependsOn: [cilium] - -- name: hetzner-robotlb - releaseName: robotlb - optional: true - chart: cozy-hetzner-robotlb - namespace: cozy-hetzner-robotlb - dependsOn: [cilium] diff --git a/packages/core/platform/bundles/distro-hosted.yaml b/packages/core/platform/bundles/distro-hosted.yaml deleted file mode 100644 index e2aa5b03..00000000 --- a/packages/core/platform/bundles/distro-hosted.yaml +++ /dev/null @@ -1,186 +0,0 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $clusterDomain := (index $cozyConfig.data "cluster-domain") | default "cozy.local" }} - -releases: -- name: cert-manager-crds - releaseName: cert-manager-crds - chart: cozy-cert-manager-crds - namespace: cozy-cert-manager - dependsOn: [] - -- name: cozystack-controller - releaseName: cozystack-controller - chart: cozy-cozystack-controller - namespace: cozy-system - {{- if eq (index $cozyConfig.data "telemetry-enabled") "false" }} - values: - cozystackController: - disableTelemetry: true - {{- end }} - -- name: lineage-controller-webhook - releaseName: lineage-controller-webhook - chart: cozy-lineage-controller-webhook - namespace: cozy-system - dependsOn: [cozystack-controller,cert-manager] - -- name: cert-manager - releaseName: cert-manager - chart: cozy-cert-manager - namespace: cozy-cert-manager - dependsOn: [cert-manager-crds] - -- name: cert-manager-issuers - releaseName: cert-manager-issuers - chart: cozy-cert-manager-issuers - namespace: cozy-cert-manager - optional: true - dependsOn: [cert-manager] - -- name: prometheus-operator-crds - releaseName: prometheus-operator-crds - chart: cozy-prometheus-operator-crds - namespace: cozy-victoria-metrics-operator - dependsOn: [] - -- name: metrics-server - releaseName: metrics-server - chart: cozy-metrics-server - namespace: cozy-monitoring - dependsOn: [prometheus-operator-crds] - -- name: victoria-metrics-operator - releaseName: victoria-metrics-operator - chart: cozy-victoria-metrics-operator - namespace: cozy-victoria-metrics-operator - optional: true - dependsOn: [prometheus-operator-crds,cert-manager] - -- name: monitoring-agents - releaseName: monitoring-agents - chart: cozy-monitoring-agents - namespace: cozy-monitoring - privileged: true - optional: true - dependsOn: [victoria-metrics-operator, metrics-server] - values: - scrapeRules: - etcd: - enabled: true - -- name: etcd-operator - releaseName: etcd-operator - chart: cozy-etcd-operator - namespace: cozy-etcd-operator - optional: true - dependsOn: [cert-manager] - -- name: grafana-operator - releaseName: grafana-operator - chart: cozy-grafana-operator - namespace: cozy-grafana-operator - optional: true - dependsOn: [] - -- name: mariadb-operator - releaseName: mariadb-operator - chart: cozy-mariadb-operator - namespace: cozy-mariadb-operator - optional: true - dependsOn: [victoria-metrics-operator] - values: - mariadb-operator: - clusterName: {{ $clusterDomain }} - -- name: postgres-operator - releaseName: postgres-operator - chart: cozy-postgres-operator - namespace: cozy-postgres-operator - optional: true - dependsOn: [victoria-metrics-operator] - -- name: kafka-operator - releaseName: kafka-operator - chart: cozy-kafka-operator - namespace: cozy-kafka-operator - optional: true - dependsOn: [victoria-metrics-operator] - values: - strimzi-kafka-operator: - kubernetesServiceDnsDomain: {{ $clusterDomain }} - -- name: clickhouse-operator - releaseName: clickhouse-operator - chart: cozy-clickhouse-operator - namespace: cozy-clickhouse-operator - optional: true - dependsOn: [victoria-metrics-operator] - -- name: foundationdb-operator - releaseName: foundationdb-operator - chart: cozy-foundationdb-operator - namespace: cozy-foundationdb-operator - optional: true - dependsOn: [cert-manager] - -- name: rabbitmq-operator - releaseName: rabbitmq-operator - chart: cozy-rabbitmq-operator - namespace: cozy-rabbitmq-operator - optional: true - dependsOn: [] - -- name: redis-operator - releaseName: redis-operator - chart: cozy-redis-operator - namespace: cozy-redis-operator - optional: true - dependsOn: [] - -- name: telepresence - releaseName: traffic-manager - chart: cozy-telepresence - namespace: cozy-telepresence - optional: true - dependsOn: [] - -- name: external-dns - releaseName: external-dns - chart: cozy-external-dns - namespace: cozy-external-dns - optional: true - dependsOn: [] - -- name: external-secrets-operator - releaseName: external-secrets-operator - chart: cozy-external-secrets-operator - namespace: cozy-external-secrets-operator - optional: true - dependsOn: [] - -- name: keycloak - releaseName: keycloak - chart: cozy-keycloak - namespace: cozy-keycloak - optional: true - dependsOn: [postgres-operator] - -- name: keycloak-operator - releaseName: keycloak-operator - chart: cozy-keycloak-operator - namespace: cozy-keycloak - optional: true - dependsOn: [keycloak] - -- name: velero - releaseName: velero - chart: cozy-velero - namespace: cozy-velero - privileged: true - optional: true - -- name: hetzner-robotlb - releaseName: robotlb - optional: true - chart: cozy-hetzner-robotlb - namespace: cozy-hetzner-robotlb diff --git a/packages/core/platform/bundles/paas-full.yaml b/packages/core/platform/bundles/paas-full.yaml deleted file mode 100644 index d25d885c..00000000 --- a/packages/core/platform/bundles/paas-full.yaml +++ /dev/null @@ -1,612 +0,0 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $clusterDomain := (index $cozyConfig.data "cluster-domain") | default "cozy.local" }} -{{- $oidcEnabled := index $cozyConfig.data "oidc-enabled" }} -{{- $host := index $cozyConfig.data "root-host" }} -{{- if not $host }} -{{- fail "ERROR need root-host in cozystack ConfigMap" }} -{{- end }} -{{- $apiServerEndpoint := index $cozyConfig.data "api-server-endpoint" }} -{{- if not $apiServerEndpoint }} -{{- fail "ERROR need api-server-endpoint in cozystack ConfigMap" }} -{{- end }} - -releases: -- name: cilium - releaseName: cilium - chart: cozy-cilium - namespace: cozy-cilium - privileged: true - dependsOn: [] - valuesFiles: - - values.yaml - - values-talos.yaml - - values-kubeovn.yaml - -- name: cilium-networkpolicy - releaseName: cilium-networkpolicy - chart: cozy-cilium-networkpolicy - namespace: cozy-cilium - privileged: true - dependsOn: [cilium] - -- name: kubeovn - releaseName: kubeovn - chart: cozy-kubeovn - namespace: cozy-kubeovn - privileged: true - dependsOn: [cilium] - values: - cozystack: - nodesHash: {{ include "cozystack.master-node-ips" . | sha256sum }} - kube-ovn: - ipv4: - POD_CIDR: "{{ index $cozyConfig.data "ipv4-pod-cidr" }}" - POD_GATEWAY: "{{ index $cozyConfig.data "ipv4-pod-gateway" }}" - SVC_CIDR: "{{ index $cozyConfig.data "ipv4-svc-cidr" }}" - JOIN_CIDR: "{{ index $cozyConfig.data "ipv4-join-cidr" }}" - -- name: kubeovn-webhook - releaseName: kubeovn-webhook - chart: cozy-kubeovn-webhook - namespace: cozy-kubeovn - privileged: true - dependsOn: [cilium,kubeovn,cert-manager] - -- name: kubeovn-plunger - releaseName: kubeovn-plunger - chart: cozy-kubeovn-plunger - 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 - namespace: cozy-system - dependsOn: [cilium,kubeovn,multus] - -- name: cert-manager-crds - releaseName: cert-manager-crds - chart: cozy-cert-manager-crds - namespace: cozy-cert-manager - dependsOn: [] - -- name: cozystack-api - releaseName: cozystack-api - chart: cozy-cozystack-api - namespace: cozy-system - dependsOn: [cilium,kubeovn,multus,cozystack-controller] - -- name: cozystack-controller - releaseName: cozystack-controller - chart: cozy-cozystack-controller - namespace: cozy-system - dependsOn: [cilium,kubeovn,multus] - {{- if eq (index $cozyConfig.data "telemetry-enabled") "false" }} - values: - cozystackController: - disableTelemetry: true - {{- end }} - -- name: backup-controller - releaseName: backup-controller - chart: cozy-backup-controller - namespace: cozy-backup-controller - dependsOn: [cilium,kubeovn,multus] - -- name: lineage-controller-webhook - releaseName: lineage-controller-webhook - chart: cozy-lineage-controller-webhook - namespace: cozy-system - dependsOn: [cozystack-controller,cilium,kubeovn,multus,cert-manager] - -- name: cozystack-resource-definition-crd - releaseName: cozystack-resource-definition-crd - chart: cozystack-resource-definition-crd - namespace: cozy-system - dependsOn: [cilium,kubeovn,multus,cozystack-api,cozystack-controller] - -- name: bootbox-rd - releaseName: bootbox-rd - chart: bootbox-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: bucket-rd - releaseName: bucket-rd - chart: bucket-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: clickhouse-rd - releaseName: clickhouse-rd - chart: clickhouse-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: etcd-rd - releaseName: etcd-rd - chart: etcd-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: ferretdb-rd - releaseName: ferretdb-rd - chart: ferretdb-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: foundationdb-rd - releaseName: foundationdb-rd - chart: foundationdb-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: http-cache-rd - releaseName: http-cache-rd - chart: http-cache-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: info-rd - releaseName: info-rd - chart: info-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: ingress-rd - releaseName: ingress-rd - chart: ingress-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: kafka-rd - releaseName: kafka-rd - chart: kafka-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: kubernetes-rd - releaseName: kubernetes-rd - chart: kubernetes-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: monitoring-rd - releaseName: monitoring-rd - chart: monitoring-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: mysql-rd - releaseName: mysql-rd - chart: mysql-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: nats-rd - releaseName: nats-rd - chart: nats-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: postgres-rd - releaseName: postgres-rd - chart: postgres-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: rabbitmq-rd - releaseName: rabbitmq-rd - chart: rabbitmq-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: redis-rd - releaseName: redis-rd - chart: redis-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: seaweedfs-rd - releaseName: seaweedfs-rd - chart: seaweedfs-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: tcp-balancer-rd - releaseName: tcp-balancer-rd - chart: tcp-balancer-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: tenant-rd - releaseName: tenant-rd - chart: tenant-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: virtual-machine-rd - releaseName: virtual-machine-rd - chart: virtual-machine-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: virtualprivatecloud-rd - releaseName: virtualprivatecloud-rd - chart: virtualprivatecloud-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: vm-disk-rd - releaseName: vm-disk-rd - chart: vm-disk-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: vm-instance-rd - releaseName: vm-instance-rd - chart: vm-instance-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: vpn-rd - releaseName: vpn-rd - chart: vpn-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: cert-manager - releaseName: cert-manager - chart: cozy-cert-manager - namespace: cozy-cert-manager - dependsOn: [cilium,kubeovn,multus,cert-manager-crds] - -- name: cert-manager-issuers - releaseName: cert-manager-issuers - chart: cozy-cert-manager-issuers - namespace: cozy-cert-manager - dependsOn: [cert-manager] - -- name: prometheus-operator-crds - releaseName: prometheus-operator-crds - chart: cozy-prometheus-operator-crds - namespace: cozy-victoria-metrics-operator - dependsOn: [] - -- name: metrics-server - releaseName: metrics-server - chart: cozy-metrics-server - namespace: cozy-monitoring - dependsOn: [cilium,kubeovn,multus,prometheus-operator-crds] - -- name: victoria-metrics-operator - releaseName: victoria-metrics-operator - chart: cozy-victoria-metrics-operator - namespace: cozy-victoria-metrics-operator - dependsOn: [cilium,kubeovn,multus,cert-manager,prometheus-operator-crds] - -- name: monitoring-agents - releaseName: monitoring-agents - chart: cozy-monitoring-agents - namespace: cozy-monitoring - privileged: true - dependsOn: [victoria-metrics-operator,vertical-pod-autoscaler-crds,metrics-server] - values: - scrapeRules: - etcd: - enabled: true - -- name: kubevirt-operator - releaseName: kubevirt-operator - chart: cozy-kubevirt-operator - namespace: cozy-kubevirt - dependsOn: [cilium,kubeovn,multus,victoria-metrics-operator] - -- name: kubevirt - releaseName: kubevirt - chart: cozy-kubevirt - namespace: cozy-kubevirt - privileged: true - dependsOn: [cilium,kubeovn,multus,kubevirt-operator] - {{- $cpuAllocationRatio := index $cozyConfig.data "cpu-allocation-ratio" }} - {{- if $cpuAllocationRatio }} - values: - cpuAllocationRatio: {{ $cpuAllocationRatio }} - {{- end }} - -- name: kubevirt-instancetypes - releaseName: kubevirt-instancetypes - chart: cozy-kubevirt-instancetypes - namespace: cozy-kubevirt - dependsOn: [cilium,kubeovn,multus,kubevirt-operator,kubevirt] - -- name: kubevirt-cdi-operator - releaseName: kubevirt-cdi-operator - chart: cozy-kubevirt-cdi-operator - namespace: cozy-kubevirt-cdi - dependsOn: [cilium,kubeovn,multus] - -- name: kubevirt-cdi - releaseName: kubevirt-cdi - chart: cozy-kubevirt-cdi - namespace: cozy-kubevirt-cdi - dependsOn: [cilium,kubeovn,multus,kubevirt-cdi-operator] - -- name: gpu-operator - releaseName: gpu-operator - chart: cozy-gpu-operator - namespace: cozy-gpu-operator - privileged: true - optional: true - dependsOn: [cilium,kubeovn,multus] - valuesFiles: - - values.yaml - - values-talos.yaml - -- name: metallb - releaseName: metallb - chart: cozy-metallb - namespace: cozy-metallb - privileged: true - dependsOn: [cilium,kubeovn,multus] - -- name: etcd-operator - releaseName: etcd-operator - chart: cozy-etcd-operator - namespace: cozy-etcd-operator - dependsOn: [cilium,kubeovn,multus,cert-manager] - -- name: grafana-operator - releaseName: grafana-operator - chart: cozy-grafana-operator - namespace: cozy-grafana-operator - dependsOn: [cilium,kubeovn,multus] - -- name: mariadb-operator - releaseName: mariadb-operator - chart: cozy-mariadb-operator - namespace: cozy-mariadb-operator - dependsOn: [cilium,kubeovn,multus,cert-manager,victoria-metrics-operator] - values: - mariadb-operator: - clusterName: {{ $clusterDomain }} - -- name: postgres-operator - releaseName: postgres-operator - chart: cozy-postgres-operator - namespace: cozy-postgres-operator - dependsOn: [cilium,kubeovn,multus,cert-manager] - -- name: kafka-operator - releaseName: kafka-operator - chart: cozy-kafka-operator - namespace: cozy-kafka-operator - dependsOn: [cilium,kubeovn,multus,victoria-metrics-operator] - values: - strimzi-kafka-operator: - kubernetesServiceDnsDomain: {{ $clusterDomain }} - -- name: clickhouse-operator - releaseName: clickhouse-operator - chart: cozy-clickhouse-operator - namespace: cozy-clickhouse-operator - dependsOn: [cilium,kubeovn,multus,victoria-metrics-operator] - -- name: foundationdb-operator - releaseName: foundationdb-operator - chart: cozy-foundationdb-operator - namespace: cozy-foundationdb-operator - dependsOn: [cilium,kubeovn,multus,cert-manager] - -- name: rabbitmq-operator - releaseName: rabbitmq-operator - chart: cozy-rabbitmq-operator - namespace: cozy-rabbitmq-operator - dependsOn: [cilium,kubeovn,multus] - -- name: redis-operator - releaseName: redis-operator - chart: cozy-redis-operator - namespace: cozy-redis-operator - dependsOn: [cilium,kubeovn,multus] - -- name: piraeus-operator - releaseName: piraeus-operator - chart: cozy-piraeus-operator - namespace: cozy-linstor - dependsOn: [cilium,kubeovn,multus,cert-manager,victoria-metrics-operator] - -- name: linstor - releaseName: linstor - chart: cozy-linstor - namespace: cozy-linstor - privileged: true - dependsOn: [piraeus-operator,cilium,kubeovn,multus,cert-manager,snapshot-controller] - -- name: linstor-scheduler - releaseName: linstor-scheduler - chart: cozy-linstor-scheduler - namespace: cozy-linstor - dependsOn: [linstor,cert-manager] - -- name: nfs-driver - releaseName: nfs-driver - chart: cozy-nfs-driver - namespace: cozy-nfs-driver - privileged: true - dependsOn: [cilium,kubeovn,multus] - optional: true - -- name: snapshot-controller - releaseName: snapshot-controller - chart: cozy-snapshot-controller - namespace: cozy-snapshot-controller - dependsOn: [cilium,kubeovn,multus,cert-manager-issuers] - -- name: objectstorage-controller - releaseName: objectstorage-controller - chart: cozy-objectstorage-controller - namespace: cozy-objectstorage-controller - dependsOn: [cilium,kubeovn,multus] - -- name: telepresence - releaseName: traffic-manager - chart: cozy-telepresence - namespace: cozy-telepresence - optional: true - dependsOn: [cilium,kubeovn,multus] - -- name: dashboard - releaseName: dashboard - chart: cozy-dashboard - namespace: cozy-dashboard - values: - {{- $dashboardKCconfig := lookup "v1" "ConfigMap" "cozy-dashboard" "kubeapps-auth-config" }} - {{- $dashboardKCValues := dig "data" "values.yaml" "" $dashboardKCconfig | fromYaml }} - {{- toYaml (deepCopy $dashboardKCValues | mergeOverwrite (fromYaml (include "cozystack.defaultDashboardValues" .))) | nindent 4 }} - dependsOn: - - cilium - - kubeovn - - multus - {{- if eq $oidcEnabled "true" }} - - keycloak-configure - {{- end }} - -- name: kamaji - releaseName: kamaji - chart: cozy-kamaji - namespace: cozy-kamaji - dependsOn: [cilium,kubeovn,multus,cert-manager] - -- name: capi-operator - releaseName: capi-operator - chart: cozy-capi-operator - namespace: cozy-cluster-api - privileged: true - dependsOn: [cilium,kubeovn,multus,cert-manager] - -- name: capi-providers-bootstrap - releaseName: capi-providers-bootstrap - chart: cozy-capi-providers-bootstrap - namespace: cozy-cluster-api - privileged: true - dependsOn: [cilium,kubeovn,multus,capi-operator] - -- name: capi-providers-core - releaseName: capi-providers-core - chart: cozy-capi-providers-core - namespace: cozy-cluster-api - privileged: true - dependsOn: [cilium,kubeovn,multus,capi-operator] - -- name: capi-providers-cpprovider - releaseName: capi-providers-cpprovider - chart: cozy-capi-providers-cpprovider - namespace: cozy-cluster-api - privileged: true - dependsOn: [cilium,kubeovn,multus,capi-operator] - -- name: capi-providers-infraprovider - releaseName: capi-providers-infraprovider - chart: cozy-capi-providers-infraprovider - namespace: cozy-cluster-api - privileged: true - dependsOn: [cilium,kubeovn,multus,capi-operator] - -- name: external-dns - releaseName: external-dns - chart: cozy-external-dns - namespace: cozy-external-dns - optional: true - dependsOn: [cilium,kubeovn,multus] - -- name: external-secrets-operator - releaseName: external-secrets-operator - chart: cozy-external-secrets-operator - namespace: cozy-external-secrets-operator - optional: true - dependsOn: [cilium,kubeovn,multus] - -- name: bootbox - releaseName: bootbox - chart: cozy-bootbox - namespace: cozy-bootbox - privileged: true - optional: true - dependsOn: [cilium,kubeovn,multus] - -{{- if $oidcEnabled }} -- name: keycloak - releaseName: keycloak - chart: cozy-keycloak - namespace: cozy-keycloak - dependsOn: [postgres-operator] - -- name: keycloak-operator - releaseName: keycloak-operator - chart: cozy-keycloak-operator - namespace: cozy-keycloak - dependsOn: [keycloak] - -- name: keycloak-configure - releaseName: keycloak-configure - chart: cozy-keycloak-configure - namespace: cozy-keycloak - dependsOn: [keycloak-operator] - values: - cozystack: - configHash: {{ $cozyConfig | toJson | sha256sum }} -{{- end }} - -- name: goldpinger - releaseName: goldpinger - chart: cozy-goldpinger - namespace: cozy-goldpinger - privileged: true - dependsOn: [monitoring-agents] - -- name: vertical-pod-autoscaler - releaseName: vertical-pod-autoscaler - chart: cozy-vertical-pod-autoscaler - namespace: cozy-vertical-pod-autoscaler - privileged: true - dependsOn: [monitoring-agents] - values: - vertical-pod-autoscaler: - recommender: - extraArgs: - prometheus-address: http://vmselect-shortterm.tenant-root.svc.{{ $clusterDomain }}:8481/select/0/prometheus/ - -- name: vertical-pod-autoscaler-crds - releaseName: vertical-pod-autoscaler-crds - chart: cozy-vertical-pod-autoscaler-crds - namespace: cozy-vertical-pod-autoscaler - privileged: true - dependsOn: [] - -- name: reloader - releaseName: reloader - chart: cozy-reloader - namespace: cozy-reloader - -- name: velero - releaseName: velero - chart: cozy-velero - namespace: cozy-velero - privileged: true - optional: true - dependsOn: [monitoring-agents] - -- name: hetzner-robotlb - releaseName: robotlb - optional: true - chart: cozy-hetzner-robotlb - namespace: cozy-hetzner-robotlb - dependsOn: [cilium, kubeovn] diff --git a/packages/core/platform/bundles/paas-hosted.yaml b/packages/core/platform/bundles/paas-hosted.yaml deleted file mode 100644 index 6db50c7f..00000000 --- a/packages/core/platform/bundles/paas-hosted.yaml +++ /dev/null @@ -1,417 +0,0 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $clusterDomain := (index $cozyConfig.data "cluster-domain") | default "cozy.local" }} -{{- $oidcEnabled := index $cozyConfig.data "oidc-enabled" }} -{{- $host := index $cozyConfig.data "root-host" }} -{{- if not $host }} -{{- fail "ERROR need root-host in cozystack ConfigMap" }} -{{- end }} -{{- $apiServerEndpoint := index $cozyConfig.data "api-server-endpoint" }} -{{- if not $apiServerEndpoint }} -{{- fail "ERROR need api-server-endpoint in cozystack ConfigMap" }} -{{- end }} - -releases: -- name: cert-manager-crds - releaseName: cert-manager-crds - chart: cozy-cert-manager-crds - namespace: cozy-cert-manager - dependsOn: [] - -- name: cozystack-api - releaseName: cozystack-api - chart: cozy-cozystack-api - namespace: cozy-system - dependsOn: [cozystack-controller] - values: - cozystackAPI: - localK8sAPIEndpoint: - enabled: false - -- name: cozystack-controller - releaseName: cozystack-controller - chart: cozy-cozystack-controller - namespace: cozy-system - dependsOn: [] - {{- if eq (index $cozyConfig.data "telemetry-enabled") "false" }} - values: - cozystackController: - disableTelemetry: true - {{- end }} - -- name: backup-controller - releaseName: backup-controller - chart: cozy-backup-controller - namespace: cozy-backup-controller - -- name: lineage-controller-webhook - releaseName: lineage-controller-webhook - chart: cozy-lineage-controller-webhook - namespace: cozy-system - dependsOn: [cozystack-controller,cert-manager] - -- name: cozystack-resource-definition-crd - releaseName: cozystack-resource-definition-crd - chart: cozystack-resource-definition-crd - namespace: cozy-system - dependsOn: [cozystack-api,cozystack-controller] - -- name: bootbox-rd - releaseName: bootbox-rd - chart: bootbox-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: bucket-rd - releaseName: bucket-rd - chart: bucket-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: clickhouse-rd - releaseName: clickhouse-rd - chart: clickhouse-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: etcd-rd - releaseName: etcd-rd - chart: etcd-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: ferretdb-rd - releaseName: ferretdb-rd - chart: ferretdb-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: foundationdb-rd - releaseName: foundationdb-rd - chart: foundationdb-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: http-cache-rd - releaseName: http-cache-rd - chart: http-cache-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: info-rd - releaseName: info-rd - chart: info-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: ingress-rd - releaseName: ingress-rd - chart: ingress-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: kafka-rd - releaseName: kafka-rd - chart: kafka-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: kubernetes-rd - releaseName: kubernetes-rd - chart: kubernetes-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: monitoring-rd - releaseName: monitoring-rd - chart: monitoring-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: mysql-rd - releaseName: mysql-rd - chart: mysql-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: nats-rd - releaseName: nats-rd - chart: nats-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: postgres-rd - releaseName: postgres-rd - chart: postgres-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: rabbitmq-rd - releaseName: rabbitmq-rd - chart: rabbitmq-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: redis-rd - releaseName: redis-rd - chart: redis-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: seaweedfs-rd - releaseName: seaweedfs-rd - chart: seaweedfs-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: tcp-balancer-rd - releaseName: tcp-balancer-rd - chart: tcp-balancer-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: tenant-rd - releaseName: tenant-rd - chart: tenant-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: virtual-machine-rd - releaseName: virtual-machine-rd - chart: virtual-machine-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: virtualprivatecloud-rd - releaseName: virtualprivatecloud-rd - chart: virtualprivatecloud-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: vm-disk-rd - releaseName: vm-disk-rd - chart: vm-disk-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: vm-instance-rd - releaseName: vm-instance-rd - chart: vm-instance-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: vpn-rd - releaseName: vpn-rd - chart: vpn-rd - namespace: cozy-system - dependsOn: [cozystack-resource-definition-crd] - -- name: cert-manager - releaseName: cert-manager - chart: cozy-cert-manager - namespace: cozy-cert-manager - dependsOn: [cert-manager-crds] - -- name: cert-manager-issuers - releaseName: cert-manager-issuers - chart: cozy-cert-manager-issuers - namespace: cozy-cert-manager - dependsOn: [cert-manager] - -- name: prometheus-operator-crds - releaseName: prometheus-operator-crds - chart: cozy-prometheus-operator-crds - namespace: cozy-victoria-metrics-operator - dependsOn: [] - -- name: metrics-server - releaseName: metrics-server - chart: cozy-metrics-server - namespace: cozy-monitoring - dependsOn: [prometheus-operator-crds] - -- name: victoria-metrics-operator - releaseName: victoria-metrics-operator - chart: cozy-victoria-metrics-operator - namespace: cozy-victoria-metrics-operator - dependsOn: [cert-manager,prometheus-operator-crds] - -- name: monitoring-agents - releaseName: monitoring-agents - chart: cozy-monitoring-agents - namespace: cozy-monitoring - privileged: true - dependsOn: [victoria-metrics-operator, vertical-pod-autoscaler-crds, metrics-server] - values: - scrapeRules: - etcd: - enabled: true - -- name: etcd-operator - releaseName: etcd-operator - chart: cozy-etcd-operator - namespace: cozy-etcd-operator - dependsOn: [cert-manager] - -- name: grafana-operator - releaseName: grafana-operator - chart: cozy-grafana-operator - namespace: cozy-grafana-operator - dependsOn: [] - -- name: mariadb-operator - releaseName: mariadb-operator - chart: cozy-mariadb-operator - namespace: cozy-mariadb-operator - dependsOn: [cert-manager,victoria-metrics-operator] - values: - mariadb-operator: - clusterName: {{ $clusterDomain }} - -- name: postgres-operator - releaseName: postgres-operator - chart: cozy-postgres-operator - namespace: cozy-postgres-operator - dependsOn: [cert-manager,victoria-metrics-operator] - -- name: kafka-operator - releaseName: kafka-operator - chart: cozy-kafka-operator - namespace: cozy-kafka-operator - dependsOn: [victoria-metrics-operator] - values: - strimzi-kafka-operator: - kubernetesServiceDnsDomain: {{ $clusterDomain }} - -- name: clickhouse-operator - releaseName: clickhouse-operator - chart: cozy-clickhouse-operator - namespace: cozy-clickhouse-operator - dependsOn: [victoria-metrics-operator] - -- name: foundationdb-operator - releaseName: foundationdb-operator - chart: cozy-foundationdb-operator - namespace: cozy-foundationdb-operator - dependsOn: [cert-manager] - -- name: rabbitmq-operator - releaseName: rabbitmq-operator - chart: cozy-rabbitmq-operator - namespace: cozy-rabbitmq-operator - dependsOn: [] - -- name: redis-operator - releaseName: redis-operator - chart: cozy-redis-operator - namespace: cozy-redis-operator - dependsOn: [] - -- name: piraeus-operator - releaseName: piraeus-operator - chart: cozy-piraeus-operator - namespace: cozy-linstor - dependsOn: [cert-manager] - -- name: objectstorage-controller - releaseName: objectstorage-controller - chart: cozy-objectstorage-controller - namespace: cozy-objectstorage-controller - dependsOn: [] - -- name: telepresence - releaseName: traffic-manager - chart: cozy-telepresence - namespace: cozy-telepresence - optional: true - dependsOn: [] - -- name: external-dns - releaseName: external-dns - chart: cozy-external-dns - namespace: cozy-external-dns - optional: true - dependsOn: [] - -- name: external-secrets-operator - releaseName: external-secrets-operator - chart: cozy-external-secrets-operator - namespace: cozy-external-secrets-operator - optional: true - dependsOn: [] - -- name: dashboard - releaseName: dashboard - chart: cozy-dashboard - namespace: cozy-dashboard - values: - {{- $dashboardKCconfig := lookup "v1" "ConfigMap" "cozy-dashboard" "kubeapps-auth-config" }} - {{- $dashboardKCValues := dig "data" "values.yaml" (dict) $dashboardKCconfig }} - {{- toYaml (deepCopy $dashboardKCValues | mergeOverwrite (fromYaml (include "cozystack.defaultDashboardValues" .))) | nindent 4 }} - {{- if eq $oidcEnabled "true" }} - dependsOn: [keycloak-configure,cozystack-api] - {{- else }} - dependsOn: [] - {{- end }} - -{{- if $oidcEnabled }} -- name: keycloak - releaseName: keycloak - chart: cozy-keycloak - namespace: cozy-keycloak - dependsOn: [postgres-operator] - -- name: keycloak-operator - releaseName: keycloak-operator - chart: cozy-keycloak-operator - namespace: cozy-keycloak - dependsOn: [keycloak] - -- name: keycloak-configure - releaseName: keycloak-configure - chart: cozy-keycloak-configure - namespace: cozy-keycloak - dependsOn: [keycloak-operator] - values: - cozystack: - configHash: {{ $cozyConfig | toJson | sha256sum }} -{{- end }} - -- name: goldpinger - releaseName: goldpinger - chart: cozy-goldpinger - namespace: cozy-goldpinger - privileged: true - dependsOn: [monitoring-agents] - -- name: vertical-pod-autoscaler - releaseName: vertical-pod-autoscaler - chart: cozy-vertical-pod-autoscaler - namespace: cozy-vertical-pod-autoscaler - privileged: true - dependsOn: [monitoring-agents] - values: - vertical-pod-autoscaler: - recommender: - extraArgs: - prometheus-address: http://vmselect-shortterm.tenant-root.svc.{{ $clusterDomain }}:8481/select/0/prometheus/ - -- name: vertical-pod-autoscaler-crds - releaseName: vertical-pod-autoscaler-crds - chart: cozy-vertical-pod-autoscaler-crds - namespace: cozy-vertical-pod-autoscaler - privileged: true - dependsOn: [] - -- name: velero - releaseName: velero - chart: cozy-velero - namespace: cozy-velero - privileged: true - optional: true - dependsOn: [monitoring-agents] - -- name: hetzner-robotlb - releaseName: robotlb - optional: true - chart: cozy-hetzner-robotlb - namespace: cozy-hetzner-robotlb diff --git a/packages/core/platform/sources/backupstrategy-controller.yaml b/packages/core/platform/sources/backupstrategy-controller.yaml new file mode 100644 index 00000000..3647e8dc --- /dev/null +++ b/packages/core/platform/sources/backupstrategy-controller.yaml @@ -0,0 +1,22 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.backupstrategy-controller +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + components: + - name: backupstrategy-controller + path: system/backupstrategy-controller + install: + privileged: true + namespace: cozy-backupstrategy-controller + releaseName: backupstrategy-controller diff --git a/packages/core/platform/sources/capi-providers-bootstrap.yaml b/packages/core/platform/sources/capi-provider-bootstrap-kubeadm.yaml similarity index 58% rename from packages/core/platform/sources/capi-providers-bootstrap.yaml rename to packages/core/platform/sources/capi-provider-bootstrap-kubeadm.yaml index 0469b1bc..90cea107 100644 --- a/packages/core/platform/sources/capi-providers-bootstrap.yaml +++ b/packages/core/platform/sources/capi-provider-bootstrap-kubeadm.yaml @@ -2,7 +2,7 @@ apiVersion: cozystack.io/v1alpha1 kind: PackageSource metadata: - name: cozystack.capi-providers-bootstrap + name: cozystack.capi-provider-bootstrap-kubeadm spec: sourceRef: kind: OCIRepository @@ -10,6 +10,17 @@ spec: namespace: cozy-system path: / variants: + - name: default + dependsOn: + - cozystack.networking + - cozystack.capi-operator + components: + - name: capi-providers-bootstrap + path: system/capi-providers-bootstrap + install: + privileged: true + namespace: cozy-cluster-api + releaseName: capi-providers-bootstrap - name: kubevirt dependsOn: - cozystack.networking diff --git a/packages/core/platform/sources/capi-providers-core.yaml b/packages/core/platform/sources/capi-provider-core.yaml similarity index 92% rename from packages/core/platform/sources/capi-providers-core.yaml rename to packages/core/platform/sources/capi-provider-core.yaml index e5b6cc99..91af6617 100644 --- a/packages/core/platform/sources/capi-providers-core.yaml +++ b/packages/core/platform/sources/capi-provider-core.yaml @@ -2,7 +2,7 @@ apiVersion: cozystack.io/v1alpha1 kind: PackageSource metadata: - name: cozystack.capi-providers-core + name: cozystack.capi-provider-core spec: sourceRef: kind: OCIRepository diff --git a/packages/core/platform/sources/capi-providers-cpprovider.yaml b/packages/core/platform/sources/capi-provider-cp-kamaji.yaml similarity index 58% rename from packages/core/platform/sources/capi-providers-cpprovider.yaml rename to packages/core/platform/sources/capi-provider-cp-kamaji.yaml index 7b63260d..08c29b8f 100644 --- a/packages/core/platform/sources/capi-providers-cpprovider.yaml +++ b/packages/core/platform/sources/capi-provider-cp-kamaji.yaml @@ -2,7 +2,7 @@ apiVersion: cozystack.io/v1alpha1 kind: PackageSource metadata: - name: cozystack.capi-providers-cpprovider + name: cozystack.capi-provider-cp-kamaji spec: sourceRef: kind: OCIRepository @@ -10,6 +10,18 @@ spec: namespace: cozy-system path: / variants: + - name: default + dependsOn: + - cozystack.networking + - cozystack.capi-operator + - cozystack.kamaji + components: + - name: capi-providers-cpprovider + path: system/capi-providers-cpprovider + install: + privileged: true + namespace: cozy-cluster-api + releaseName: capi-providers-cpprovider - name: kamaji dependsOn: - cozystack.networking diff --git a/packages/core/platform/sources/capi-providers-infraprovider.yaml b/packages/core/platform/sources/capi-provider-infra-kubevirt.yaml similarity index 57% rename from packages/core/platform/sources/capi-providers-infraprovider.yaml rename to packages/core/platform/sources/capi-provider-infra-kubevirt.yaml index 88bebc09..341e1920 100644 --- a/packages/core/platform/sources/capi-providers-infraprovider.yaml +++ b/packages/core/platform/sources/capi-provider-infra-kubevirt.yaml @@ -2,7 +2,7 @@ apiVersion: cozystack.io/v1alpha1 kind: PackageSource metadata: - name: cozystack.capi-providers-infraprovider + name: cozystack.capi-provider-infra-kubevirt spec: sourceRef: kind: OCIRepository @@ -10,6 +10,18 @@ spec: namespace: cozy-system path: / variants: + - name: default + dependsOn: + - cozystack.networking + - cozystack.capi-operator + - cozystack.kubevirt + components: + - name: capi-providers-infraprovider + path: system/capi-providers-infraprovider + install: + privileged: true + namespace: cozy-cluster-api + releaseName: capi-providers-infraprovider - name: kubevirt dependsOn: - cozystack.networking diff --git a/packages/core/platform/sources/cozystack-engine.yaml b/packages/core/platform/sources/cozystack-engine.yaml index ce06b9d8..7d1b03c8 100644 --- a/packages/core/platform/sources/cozystack-engine.yaml +++ b/packages/core/platform/sources/cozystack-engine.yaml @@ -58,6 +58,7 @@ spec: dependsOn: - cozystack.networking - cozystack.keycloak + - cozystack.keycloak-operator libraries: - name: cozy-lib path: library/cozy-lib diff --git a/packages/core/platform/sources/ingress-nginx.yaml b/packages/core/platform/sources/ingress-nginx.yaml new file mode 100644 index 00000000..b51e096f --- /dev/null +++ b/packages/core/platform/sources/ingress-nginx.yaml @@ -0,0 +1,22 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.ingress-nginx +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + - cozystack.prometheus-operator-crds + components: + - name: ingress-nginx + path: system/ingress-nginx + install: + namespace: cozy-ingress-nginx + releaseName: ingress-nginx diff --git a/packages/core/platform/sources/keycloak.yaml b/packages/core/platform/sources/keycloak.yaml index a9dc64b4..545d70a5 100644 --- a/packages/core/platform/sources/keycloak.yaml +++ b/packages/core/platform/sources/keycloak.yaml @@ -14,9 +14,13 @@ spec: dependsOn: - cozystack.networking - cozystack.postgres-operator + libraries: + - name: cozy-lib + path: library/cozy-lib components: - name: keycloak path: system/keycloak + libraries: ["cozy-lib"] install: namespace: cozy-keycloak releaseName: keycloak diff --git a/packages/core/platform/sources/kubernetes-application.yaml b/packages/core/platform/sources/kubernetes-application.yaml index 0acde1c4..9787cb19 100644 --- a/packages/core/platform/sources/kubernetes-application.yaml +++ b/packages/core/platform/sources/kubernetes-application.yaml @@ -14,10 +14,10 @@ spec: dependsOn: - cozystack.networking - cozystack.capi-operator - - cozystack.capi-providers-bootstrap - - cozystack.capi-providers-core - - cozystack.capi-providers-cpprovider - - cozystack.capi-providers-infraprovider + - cozystack.capi-provider-bootstrap-kubeadm + - cozystack.capi-provider-core + - cozystack.capi-provider-cp-kamaji + - cozystack.capi-provider-infra-kubevirt libraries: - name: cozy-lib path: library/cozy-lib diff --git a/packages/core/platform/sources/metrics-server.yaml b/packages/core/platform/sources/metrics-server.yaml new file mode 100644 index 00000000..0bd17881 --- /dev/null +++ b/packages/core/platform/sources/metrics-server.yaml @@ -0,0 +1,22 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.metrics-server +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + - cozystack.prometheus-operator-crds + components: + - name: metrics-server + path: system/metrics-server + install: + namespace: cozy-metrics-server + releaseName: metrics-server diff --git a/packages/core/platform/sources/monitoring-application.yaml b/packages/core/platform/sources/monitoring-application.yaml index 0f01eddd..6ea5a2d0 100644 --- a/packages/core/platform/sources/monitoring-application.yaml +++ b/packages/core/platform/sources/monitoring-application.yaml @@ -13,6 +13,7 @@ spec: - name: default dependsOn: - cozystack.networking + - cozystack.postgres-operator libraries: - name: cozy-lib path: library/cozy-lib diff --git a/packages/core/platform/sources/postgres-application.yaml b/packages/core/platform/sources/postgres-application.yaml index b5953069..5d7b220b 100644 --- a/packages/core/platform/sources/postgres-application.yaml +++ b/packages/core/platform/sources/postgres-application.yaml @@ -13,6 +13,7 @@ spec: - name: default dependsOn: - cozystack.networking + - cozystack.postgres-operator libraries: - name: cozy-lib path: library/cozy-lib diff --git a/packages/core/platform/templates/_helpers.tpl b/packages/core/platform/templates/_helpers.tpl index c656c7f8..18105acf 100644 --- a/packages/core/platform/templates/_helpers.tpl +++ b/packages/core/platform/templates/_helpers.tpl @@ -1,108 +1,52 @@ -{{/* -Get IP-addresses of master nodes -*/}} -{{- define "cozystack.master-node-ips" -}} -{{- $nodes := lookup "v1" "Node" "" "" -}} -{{- $ips := list -}} -{{- range $node := $nodes.items -}} - {{- if eq (index $node.metadata.labels "node-role.kubernetes.io/control-plane") "" -}} - {{- range $address := $node.status.addresses -}} - {{- if eq $address.type "InternalIP" -}} - {{- $ips = append $ips $address.address -}} - {{- break -}} - {{- end -}} - {{- end -}} - {{- end -}} +{{- define "cozystack.platform.package" -}} +{{- $name := index . 0 -}} +{{- $variant := default "default" (index . 1) -}} +{{- $root := default $ (index . 2) -}} +{{- $components := dict -}} +{{- if gt (len .) 3 -}} +{{- $components = index . 3 -}} +{{- end -}} +{{- $disabled := default (list) $root.Values.bundles.disabledPackages -}} +{{- if not (has $name $disabled) -}} +--- +apiVersion: cozystack.io/v1alpha1 +kind: Package +metadata: + name: {{ $name }} +spec: + variant: {{ $variant }} +{{- if $components }} + components: +{{ toYaml $components | indent 4 }} +{{- end }} {{- end -}} -{{ join "," $ips }} {{- end -}} -{{/* -Get Kubernetes API Endpoint from cozystack deployment -Returns host:port format -*/}} -{{- define "cozystack.kubernetesAPIEndpoint" -}} -{{- $cozyDeployment := lookup "apps/v1" "Deployment" "cozy-system" "cozystack" }} -{{- $cozyContainers := dig "spec" "template" "spec" "containers" list $cozyDeployment }} -{{- $kubernetesServiceHost := "" }} -{{- $kubernetesServicePort := "" }} -{{- range $cozyContainers }} -{{- if eq .name "cozystack" }} -{{- range .env }} -{{- if eq .name "KUBERNETES_SERVICE_HOST" }} -{{- $kubernetesServiceHost = .value }} -{{- end }} -{{- if eq .name "KUBERNETES_SERVICE_PORT" }} -{{- $kubernetesServicePort = .value }} -{{- end }} -{{- end }} -{{- end }} -{{- end }} -{{- if eq $kubernetesServiceHost "" }} -{{- $kubernetesServiceHost = "kubernetes.default.svc" }} -{{- end }} -{{- if eq $kubernetesServicePort "" }} -{{- $kubernetesServicePort = "443" }} -{{- end }} -{{- printf "%s:%s" $kubernetesServiceHost $kubernetesServicePort }} +{{- define "cozystack.platform.package.default" -}} +{{- $name := index . 0 -}} +{{- $root := index . 1 -}} +{{- include "cozystack.platform.package" (list $name "default" $root) }} {{- end -}} -{{- define "cozystack.defaultDashboardValues" -}} -kubeapps: -{{- if .Capabilities.APIVersions.Has "source.toolkit.fluxcd.io/v1" }} -{{- with (lookup "source.toolkit.fluxcd.io/v1" "HelmRepository" "cozy-public" "").items }} - redis: - master: - podAnnotations: - {{- range $index, $repo := . }} - {{- with (($repo.status).artifact).revision }} - repository.cozystack.io/{{ $repo.metadata.name }}: {{ quote . }} - {{- end }} - {{- end }} -{{- end }} -{{- end }} - frontend: - resourcesPreset: "none" - dashboard: - resourcesPreset: "none" - {{- $cozystackBranding:= lookup "v1" "ConfigMap" "cozy-system" "cozystack-branding" }} - {{- $branding := dig "data" "branding" "" $cozystackBranding }} - {{- if $branding }} - customLocale: - "Kubeapps": {{ $branding }} - {{- end }} - customStyle: | - {{- $logoImage := dig "data" "logo" "" $cozystackBranding }} - {{- if $logoImage }} - .kubeapps-logo { - background-image: {{ $logoImage }} - } - {{- end }} - #serviceaccount-selector { - display: none; - } - .login-moreinfo { - display: none; - } - a[href="#/docs"] { - display: none; - } - .login-group .clr-form-control .clr-control-label { - display: none; - } - .appview-separator div.appview-first-row div.center { - display: none; - } - .appview-separator div.appview-first-row section[aria-labelledby="app-secrets"] { - display: none; - } - .appview-first-row section[aria-labelledby="access-urls-title"] { - width: 100%; - } - .header-version { - display: none; - } - .label.label-info-secondary { - display: none; - } -{{- end }} +{{- define "cozystack.platform.package.optional" -}} +{{- $name := index . 0 -}} +{{- $variant := default "default" (index . 1) -}} +{{- $root := default $ (index . 2) -}} +{{- $disabled := default (list) $root.Values.bundles.disabledPackages -}} +{{- $enabled := default (list) $root.Values.bundles.enabledPackages -}} +{{- if and (has $name $enabled) (not (has $name $disabled)) -}} +--- +apiVersion: cozystack.io/v1alpha1 +kind: Package +metadata: + name: {{ $name }} +spec: + variant: {{ $variant }} +{{- end -}} +{{- end -}} + +{{- define "cozystack.platform.package.optional.default" -}} +{{- $name := index . 0 -}} +{{- $root := index . 1 -}} +{{- include "cozystack.platform.package.optional" (list $name "default" $root) }} +{{- end -}} diff --git a/packages/core/platform/templates/apps.yaml b/packages/core/platform/templates/apps.yaml index b42aad87..d3941067 100644 --- a/packages/core/platform/templates/apps.yaml +++ b/packages/core/platform/templates/apps.yaml @@ -1,118 +1,35 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $cozystackBranding := lookup "v1" "ConfigMap" "cozy-system" "cozystack-branding" }} -{{- $cozystackScheduling := lookup "v1" "ConfigMap" "cozy-system" "cozystack-scheduling" }} {{- $kubeRootCa := lookup "v1" "ConfigMap" "kube-system" "kube-root-ca.crt" }} -{{- $bundleName := index $cozyConfig.data "bundle-name" }} +{{- $bundleName := .Values.bundles.system.variant }} {{- $bundle := tpl (.Files.Get (printf "bundles/%s.yaml" $bundleName)) . | fromYaml }} -{{/* Default values for _cluster config to ensure all required keys exist */}} -{{- $clusterDefaults := dict - "root-host" "" - "bundle-name" "" - "clusterissuer" "http01" - "oidc-enabled" "false" - "expose-services" "" - "expose-ingress" "tenant-root" - "expose-external-ips" "" - "cluster-domain" "cozy.local" - "api-server-endpoint" "" -}} -{{- $clusterConfig := mergeOverwrite $clusterDefaults ($cozyConfig.data | default dict) }} -{{- $host := "example.org" }} -{{- $host := "example.org" }} -{{- if $cozyConfig.data }} - {{- if hasKey $cozyConfig.data "root-host" }} - {{- $host = index $cozyConfig.data "root-host" }} - {{- end }} -{{- end }} -{{- $tenantRoot := dict }} -{{- if .Capabilities.APIVersions.Has "helm.toolkit.fluxcd.io/v2" }} -{{- $tenantRoot = lookup "helm.toolkit.fluxcd.io/v2" "HelmRelease" "tenant-root" "tenant-root" }} -{{- end }} -{{- if and $tenantRoot $tenantRoot.spec $tenantRoot.spec.values $tenantRoot.spec.values.host }} -{{- $host = $tenantRoot.spec.values.host }} -{{- else }} -{{- end }} ---- -apiVersion: v1 -kind: Namespace -metadata: - annotations: - helm.sh/resource-policy: keep - labels: - tenant.cozystack.io/tenant-root: "" - namespace.cozystack.io/etcd: tenant-root - namespace.cozystack.io/monitoring: tenant-root - namespace.cozystack.io/ingress: tenant-root - namespace.cozystack.io/seaweedfs: tenant-root - namespace.cozystack.io/host: "{{ $host }}" - name: tenant-root --- apiVersion: v1 kind: Secret metadata: name: cozystack-values - namespace: tenant-root + namespace: cozy-system labels: reconcile.fluxcd.io/watch: Enabled type: Opaque stringData: values.yaml: | _cluster: - {{- $clusterConfig | toYaml | nindent 6 }} - {{- with $cozystackBranding.data }} + root-host: {{ .Values.publishing.host | quote }} + bundle-name: {{ .Values.bundles.system.variant | quote }} + clusterissuer: {{ .Values.publishing.certificates.issuerType | quote }} + oidc-enabled: {{ .Values.authentication.oidc.enabled | quote }} + expose-services: {{ .Values.publishing.exposedServices | join "," | quote }} + expose-ingress: {{ .Values.publishing.ingressName | quote }} + expose-external-ips: {{ .Values.publishing.externalIPs | join "," | quote }} + cluster-domain: {{ .Values.networking.clusterDomain | quote }} + api-server-endpoint: {{ .Values.publishing.apiServerEndpoint | quote }} + {{- with .Values.branding }} branding: {{- . | toYaml | nindent 8 }} {{- end }} - {{- with $cozystackScheduling.data }} + {{- with .Values.scheduling }} scheduling: {{- . | toYaml | nindent 8 }} {{- end }} {{- with $kubeRootCa.data }} kube-root-ca: {{ index . "ca.crt" | b64enc | quote }} {{- end }} - _namespace: - etcd: tenant-root - monitoring: tenant-root - ingress: tenant-root - seaweedfs: tenant-root - host: {{ $host | quote }} ---- -apiVersion: helm.toolkit.fluxcd.io/v2 -kind: HelmRelease -metadata: - name: tenant-root - namespace: tenant-root - labels: - cozystack.io/ui: "true" - apps.cozystack.io/application.kind: Tenant - apps.cozystack.io/application.group: apps.cozystack.io - apps.cozystack.io/application.name: tenant-root -spec: - interval: 0s - releaseName: tenant-root - install: - remediation: - retries: -1 - upgrade: - remediation: - retries: -1 - chart: - spec: - chart: tenant - version: '>= 0.0.0-0' - sourceRef: - kind: HelmRepository - name: cozystack-apps - namespace: cozy-public - valuesFrom: - - kind: Secret - name: cozystack-values - values: - host: "{{ $host }}" - dependsOn: - {{- range $x := $bundle.releases }} - {{- if has $x.name (list "cilium" "kubeovn") }} - - name: {{ $x.name }} - namespace: {{ $x.namespace }} - {{- end }} - {{- end }} diff --git a/packages/core/platform/templates/bundles/iaas.yaml b/packages/core/platform/templates/bundles/iaas.yaml new file mode 100644 index 00000000..529ec67a --- /dev/null +++ b/packages/core/platform/templates/bundles/iaas.yaml @@ -0,0 +1,20 @@ +{{- if and .Values.bundles.iaas.enabled (ne .Values.bundles.system.variant "isp-full") }} +{{- fail "bundles.iaas.enabled can only be true when bundles.system.variant is 'isp-full'" }} +{{- end }} +{{- if and .Values.bundles.iaas.enabled (eq .Values.bundles.system.variant "isp-full") }} +{{include "cozystack.platform.package.default" (list "cozystack.kubevirt" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.kubevirt-cdi" $) }} +{{include "cozystack.platform.package.optional.default" (list "cozystack.gpu-operator" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.kamaji" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.capi-operator" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.capi-provider-bootstrap-kubeadm" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.capi-provider-core" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.capi-provider-cp-kamaji" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.capi-provider-infra-kubevirt" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.bucket-application" $) }} +{{include "cozystack.platform.package" (list "cozystack.kubernetes-application" "kubevirt" $) }} +{{include "cozystack.platform.package" (list "cozystack.virtual-machine-application" "kubevirt" $) }} +{{include "cozystack.platform.package" (list "cozystack.virtualprivatecloud-application" "kubevirt" $) }} +{{include "cozystack.platform.package" (list "cozystack.vm-disk-application" "kubevirt" $) }} +{{include "cozystack.platform.package" (list "cozystack.vm-instance-application" "kubevirt" $) }} +{{- end }} diff --git a/packages/core/platform/templates/bundles/naas.yaml b/packages/core/platform/templates/bundles/naas.yaml new file mode 100644 index 00000000..7c40a4aa --- /dev/null +++ b/packages/core/platform/templates/bundles/naas.yaml @@ -0,0 +1,8 @@ +{{- if and .Values.bundles.naas.enabled (not (or (eq .Values.bundles.system.variant "isp-full") (eq .Values.bundles.system.variant "isp-hosted"))) }} +{{- fail "bundles.naas.enabled can only be true when bundles.system.variant is 'isp-full' or 'isp-hosted'" }} +{{- end }} +{{- if and .Values.bundles.naas.enabled (or (eq .Values.bundles.system.variant "isp-full") (eq .Values.bundles.system.variant "isp-hosted")) }} +{{include "cozystack.platform.package.default" (list "cozystack.http-cache-application" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.tcp-balancer-application" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.vpn-application" $) }} +{{- end }} diff --git a/packages/core/platform/templates/bundles/paas.yaml b/packages/core/platform/templates/bundles/paas.yaml new file mode 100644 index 00000000..fbaa25a1 --- /dev/null +++ b/packages/core/platform/templates/bundles/paas.yaml @@ -0,0 +1,20 @@ +{{- if and .Values.bundles.paas.enabled (not (or (eq .Values.bundles.system.variant "isp-full") (eq .Values.bundles.system.variant "isp-hosted"))) }} +{{- fail "bundles.paas.enabled can only be true when bundles.system.variant is 'isp-full' or 'isp-hosted'" }} +{{- end }} +{{- if and .Values.bundles.paas.enabled (or (eq .Values.bundles.system.variant "isp-full") (eq .Values.bundles.system.variant "isp-hosted")) }} +{{include "cozystack.platform.package.default" (list "cozystack.mariadb-operator" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.kafka-operator" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.clickhouse-operator" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.foundationdb-operator" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.rabbitmq-operator" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.redis-operator" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.clickhouse-application" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.ferretdb-application" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.foundationdb-application" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.kafka-application" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.mysql-application" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.nats-application" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.postgres-application" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.rabbitmq-application" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.redis-application" $) }} +{{- end }} diff --git a/packages/core/platform/templates/bundles/system.yaml b/packages/core/platform/templates/bundles/system.yaml new file mode 100644 index 00000000..59e44263 --- /dev/null +++ b/packages/core/platform/templates/bundles/system.yaml @@ -0,0 +1,72 @@ +{{- if .Values.bundles.system.enabled }} + +# Networking +{{- if eq .Values.bundles.system.variant "isp-full" }} +{{- $networkingComponents := dict -}} +{{- if .Values.networking -}} +{{- $kubeovnValues := dict "kube-ovn" (dict + "ipv4" (dict + "POD_CIDR" .Values.networking.podCIDR + "POD_GATEWAY" .Values.networking.podGateway + "SVC_CIDR" .Values.networking.serviceCIDR + "JOIN_CIDR" .Values.networking.joinCIDR)) -}} +{{- $_ := set $networkingComponents "kubeovn" (dict "values" $kubeovnValues) -}} +{{- end -}} +{{include "cozystack.platform.package" (list "cozystack.networking" "kubeovn-cilium" $ $networkingComponents) }} +{{include "cozystack.platform.package.default" (list "cozystack.kubeovn-webhook" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.kubeovn-plunger" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.cozy-proxy" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.multus" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.metallb" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.reloader" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.linstor" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.snapshot-controller" $) }} +{{- end }} + +{{- if eq .Values.bundles.system.variant "isp-hosted" }} +{{include "cozystack.platform.package" (list "cozystack.networking" "noop" $) }} +{{- end }} + +# Cozystack Engine +{{- if .Values.authentication.oidc.enabled }} +{{include "cozystack.platform.package" (list "cozystack.cozystack-engine" "oidc" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.keycloak" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.keycloak-operator" $) }} +{{- else }} +{{include "cozystack.platform.package" (list "cozystack.cozystack-engine" "default" $) }} +{{- end }} + +# Common Packages +{{include "cozystack.platform.package.default" (list "cozystack.cert-manager" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.victoria-metrics-operator" $) }} +{{- $tenantComponents := dict -}} +{{- $tenantClusterValues := dict "_cluster" (dict "oidc-enabled" (ternary "true" "false" .Values.authentication.oidc.enabled)) -}} +{{- $_ := set $tenantComponents "tenant" (dict "values" $tenantClusterValues) }} +{{include "cozystack.platform.package" (list "cozystack.tenant-application" "default" $ $tenantComponents) }} +{{include "cozystack.platform.package.default" (list "cozystack.ingress-application" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.seaweedfs-application" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.info-application" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.monitoring-application" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.etcd-application" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.cozystack-basics" $) }} +{{include "cozystack.platform.package.optional.default" (list "cozystack.backupstrategy-controller" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.backup-controller" $) }} +{{include "cozystack.platform.package.optional.default" (list "cozystack.velero" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.vertical-pod-autoscaler" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.monitoring-agents" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.goldpinger" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.prometheus-operator-crds" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.grafana-operator" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.etcd-operator" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.postgres-operator" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.objectstorage-controller" $) }} + +# Optional System Packages (controlled via bundles.enabledPackages) +{{include "cozystack.platform.package.optional.default" (list "cozystack.nfs-driver" $) }} +{{include "cozystack.platform.package.optional.default" (list "cozystack.telepresence" $) }} +{{include "cozystack.platform.package.optional.default" (list "cozystack.external-dns" $) }} +{{include "cozystack.platform.package.optional.default" (list "cozystack.external-secrets-operator" $) }} +{{include "cozystack.platform.package.optional.default" (list "cozystack.bootbox" $) }} +{{include "cozystack.platform.package.optional.default" (list "cozystack.hetzner-robotlb" $) }} + +{{- end }} diff --git a/packages/core/platform/templates/containerd-registry-secret.yaml b/packages/core/platform/templates/containerd-registry-secret.yaml new file mode 100644 index 00000000..6e9fb640 --- /dev/null +++ b/packages/core/platform/templates/containerd-registry-secret.yaml @@ -0,0 +1,35 @@ +{{- if .Values.registries.mirrors }} +apiVersion: v1 +kind: Secret +metadata: + name: patch-containerd + namespace: cozy-system +type: Opaque +stringData: +{{- range $registry, $mirror := .Values.registries.mirrors }} +{{- if $mirror.endpoints }} + {{ $registry }}.toml: | + server = "https://{{ $registry }}" +{{- range $endpoint := $mirror.endpoints }} + [host."{{ $endpoint }}"] + capabilities = ["pull", "resolve"] +{{- $endpointConfig := index $.Values.registries.config $endpoint }} +{{- if $endpointConfig }} +{{- if $endpointConfig.tls }} +{{- if $endpointConfig.tls.insecureSkipVerify }} + skip_verify = true +{{- end }} +{{- end }} +{{- if $endpointConfig.auth }} + [host."{{ $endpoint }}".auth] + username = "{{ $endpointConfig.auth.username }}" + password = "{{ $endpointConfig.auth.password }}" +{{- end }} +{{- else }} + skip_verify = true +{{- end }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} + diff --git a/packages/core/platform/templates/cozystack-assets.yaml b/packages/core/platform/templates/cozystack-assets.yaml deleted file mode 100644 index 61ab8dca..00000000 --- a/packages/core/platform/templates/cozystack-assets.yaml +++ /dev/null @@ -1,73 +0,0 @@ -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: cozystack-assets - namespace: cozy-system - labels: - app: cozystack-assets -spec: - serviceName: cozystack-assets - replicas: 1 - selector: - matchLabels: - app: cozystack-assets - template: - metadata: - labels: - app: cozystack-assets - spec: - hostNetwork: true - containers: - - name: assets-server - image: "{{ .Values.assets.image }}" - args: - - "-dir=/cozystack/assets" - - "-address=:8123" - ports: - - name: http - containerPort: 8123 - hostPort: 8123 - tolerations: - - operator: Exists ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: cozystack-assets-reader - namespace: cozy-system -rules: - - apiGroups: [""] - resources: - - pods/proxy - resourceNames: - - cozystack-assets-0 - verbs: - - get ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: cozystack-assets-reader - namespace: cozy-system -subjects: - - kind: User - name: cozystack-assets-reader - apiGroup: rbac.authorization.k8s.io -roleRef: - kind: Role - name: cozystack-assets-reader - apiGroup: rbac.authorization.k8s.io ---- -apiVersion: v1 -kind: Service -metadata: - name: cozystack-assets - namespace: cozy-system -spec: - ports: - - name: http - port: 80 - targetPort: 8123 - selector: - app: cozystack-assets - type: ClusterIP diff --git a/packages/core/platform/templates/cozystack-version.yaml b/packages/core/platform/templates/cozystack-version.yaml new file mode 100644 index 00000000..8e3ff6c6 --- /dev/null +++ b/packages/core/platform/templates/cozystack-version.yaml @@ -0,0 +1,11 @@ +{{- $configMap := lookup "v1" "ConfigMap" .Release.Namespace "cozystack-version" }} +{{- if not $configMap }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: cozystack-version + namespace: {{ .Release.Namespace }} +data: + version: {{ .Values.migrations.targetVersion | quote }} +{{- end }} diff --git a/packages/core/platform/templates/helmreleases.yaml b/packages/core/platform/templates/helmreleases.yaml deleted file mode 100644 index e3118b70..00000000 --- a/packages/core/platform/templates/helmreleases.yaml +++ /dev/null @@ -1,100 +0,0 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $bundleName := index $cozyConfig.data "bundle-name" }} -{{- $bundle := tpl (.Files.Get (printf "bundles/%s.yaml" $bundleName)) . | fromYaml }} -{{- $dependencyNamespaces := dict }} -{{- $disabledComponents := splitList "," ((index $cozyConfig.data "bundle-disable") | default "") }} -{{- $enabledComponents := splitList "," ((index $cozyConfig.data "bundle-enable") | default "") }} -{{- $oidcEnabled := (index (default dict $cozyConfig.data) "oidc-enabled") | default "false" | eq "true" }} - -{{/* collect dependency namespaces from releases */}} -{{- range $x := $bundle.releases }} -{{- $_ := set $dependencyNamespaces $x.name $x.namespace }} -{{- end }} - -{{- range $x := $bundle.releases }} - -{{- $shouldInstall := true }} -{{- $shouldDelete := false }} -{{- $notEnabledOptionalComponent := and ($x.optional) (not (has $x.name $enabledComponents)) }} -{{- $disabledComponent := has $x.name $disabledComponents }} -{{- $isKeycloakComponent := or (eq $x.name "keycloak") (eq $x.name "keycloak-operator") (eq $x.name "keycloak-configure") }} - -{{- if and $isKeycloakComponent (not $oidcEnabled) }} -{{- $shouldInstall = false }} -{{- if $.Capabilities.APIVersions.Has "helm.toolkit.fluxcd.io/v2" }} -{{- if lookup "helm.toolkit.fluxcd.io/v2" "HelmRelease" $x.namespace $x.name }} -{{- $shouldDelete = true }} -{{- end }} -{{- end }} -{{- else if or $disabledComponent $notEnabledOptionalComponent }} -{{- $shouldInstall = false }} -{{- if $.Capabilities.APIVersions.Has "helm.toolkit.fluxcd.io/v2" }} -{{- if lookup "helm.toolkit.fluxcd.io/v2" "HelmRelease" $x.namespace $x.name }} -{{- $shouldDelete = true }} -{{- end }} -{{- end }} -{{- end }} - -{{- if or $shouldInstall $shouldDelete }} ---- -apiVersion: helm.toolkit.fluxcd.io/v2 -kind: HelmRelease -metadata: - name: {{ $x.name }} - namespace: {{ $x.namespace }} - labels: - cozystack.io/repository: system - cozystack.io/system-app: "true" - {{- if $shouldDelete }} - cozystack.io/marked-for-deletion: "true" - {{- end }} -spec: - interval: 5m - releaseName: {{ $x.releaseName | default $x.name }} - install: - crds: CreateReplace - remediation: - retries: -1 - upgrade: - crds: CreateReplace - remediation: - retries: -1 - chart: - spec: - chart: {{ $x.chart }} - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' - {{- with $x.valuesFiles }} - valuesFiles: - {{- toYaml $x.valuesFiles | nindent 6 }} - {{- end }} - {{- $values := dict }} - {{- with $x.values }} - {{- $values = merge . $values }} - {{- end }} - {{- with index $cozyConfig.data (printf "values-%s" $x.name) }} - {{- $values = mergeOverwrite $values (fromYaml .) }} - {{- end }} - {{- with $values }} - values: - {{- toYaml . | nindent 4}} - {{- end }} - valuesFrom: - - kind: Secret - name: cozystack-values - - {{- with $x.dependsOn }} - dependsOn: - {{- range $dep := . }} - {{- if not (has $dep $disabledComponents) }} - - name: {{ $dep }} - namespace: {{ index $dependencyNamespaces $dep }} - {{- end }} - {{- end }} - {{- end }} -{{- end }} -{{- end }} diff --git a/packages/core/platform/templates/helmrepos.yaml b/packages/core/platform/templates/helmrepos.yaml deleted file mode 100644 index 47954869..00000000 --- a/packages/core/platform/templates/helmrepos.yaml +++ /dev/null @@ -1,40 +0,0 @@ ---- -apiVersion: source.toolkit.fluxcd.io/v1 -kind: HelmRepository -metadata: - name: cozystack-system - namespace: cozy-system - labels: - cozystack.io/repository: system -spec: - interval: 5m0s - url: https://{{ include "cozystack.kubernetesAPIEndpoint" . }}/api/v1/namespaces/cozy-system/pods/cozystack-assets-0/proxy/repos/system - certSecretRef: - name: cozystack-assets-tls ---- -apiVersion: source.toolkit.fluxcd.io/v1 -kind: HelmRepository -metadata: - name: cozystack-apps - namespace: cozy-public - labels: - cozystack.io/ui: "true" - cozystack.io/repository: apps -spec: - interval: 5m0s - url: https://{{ include "cozystack.kubernetesAPIEndpoint" . }}/api/v1/namespaces/cozy-system/pods/cozystack-assets-0/proxy/repos/apps - certSecretRef: - name: cozystack-assets-tls ---- -apiVersion: source.toolkit.fluxcd.io/v1 -kind: HelmRepository -metadata: - name: cozystack-extra - namespace: cozy-public - labels: - cozystack.io/repository: extra -spec: - interval: 5m0s - url: https://{{ include "cozystack.kubernetesAPIEndpoint" . }}/api/v1/namespaces/cozy-system/pods/cozystack-assets-0/proxy/repos/extra - certSecretRef: - name: cozystack-assets-tls diff --git a/packages/core/platform/templates/migration-hook.yaml b/packages/core/platform/templates/migration-hook.yaml new file mode 100644 index 00000000..67b09820 --- /dev/null +++ b/packages/core/platform/templates/migration-hook.yaml @@ -0,0 +1,69 @@ +{{- if .Values.migrations.enabled }} +{{- $shouldRunMigrationHook := false }} +{{- $currentVersion := 0 }} +{{- $targetVersion := .Values.migrations.targetVersion | int }} +{{- $configMap := lookup "v1" "ConfigMap" .Release.Namespace "cozystack-version" }} +{{- if $configMap }} + {{- $currentVersion = dig "data" "version" "0" $configMap | int }} + {{- if lt $currentVersion $targetVersion }} + {{- $shouldRunMigrationHook = true }} + {{- end }} +{{- end }} + +{{- if $shouldRunMigrationHook }} +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: cozystack-migration-hook + annotations: + helm.sh/hook: pre-upgrade,pre-install + helm.sh/hook-weight: "1" + helm.sh/hook-delete-policy: before-hook-creation +spec: + backoffLimit: 3 + template: + metadata: + labels: + policy.cozystack.io/allow-to-apiserver: "true" + spec: + serviceAccountName: cozystack-migration-hook + containers: + - name: migration + image: {{ .Values.migrations.image }} + env: + - name: NAMESPACE + value: {{ .Release.Namespace | quote }} + - name: CURRENT_VERSION + value: {{ $currentVersion | quote }} + - name: TARGET_VERSION + value: {{ $targetVersion | quote }} + restartPolicy: Never +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + annotations: + helm.sh/hook: pre-upgrade,pre-install + helm.sh/hook-weight: "1" + helm.sh/hook-delete-policy: hook-succeeded,before-hook-creation + name: cozystack-migration-hook +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin +subjects: + - kind: ServiceAccount + name: cozystack-migration-hook + namespace: {{ .Release.Namespace | quote }} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: cozystack-migration-hook + annotations: + helm.sh/hook: pre-upgrade,pre-install + helm.sh/hook-weight: "1" + helm.sh/hook-delete-policy: hook-succeeded,before-hook-creation +{{- end }} +{{- end }} diff --git a/packages/core/platform/templates/namespaces.yaml b/packages/core/platform/templates/namespaces.yaml deleted file mode 100644 index 2afe3445..00000000 --- a/packages/core/platform/templates/namespaces.yaml +++ /dev/null @@ -1,76 +0,0 @@ -{{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} -{{- $cozystackBranding := lookup "v1" "ConfigMap" "cozy-system" "cozystack-branding" }} -{{- $cozystackScheduling := lookup "v1" "ConfigMap" "cozy-system" "cozystack-scheduling" }} -{{- $bundleName := index $cozyConfig.data "bundle-name" }} -{{/* Default values for _cluster config to ensure all required keys exist */}} -{{- $clusterDefaults := dict - "root-host" "" - "bundle-name" "" - "clusterissuer" "http01" - "oidc-enabled" "false" - "expose-services" "" - "expose-ingress" "tenant-root" - "expose-external-ips" "" - "cluster-domain" "cozy.local" - "api-server-endpoint" "" -}} -{{- $clusterConfig := mergeOverwrite $clusterDefaults ($cozyConfig.data | default dict) }} -{{- $bundle := tpl (.Files.Get (printf "bundles/%s.yaml" $bundleName)) . | fromYaml }} -{{- $disabledComponents := splitList "," ((index $cozyConfig.data "bundle-disable") | default "") }} -{{- $enabledComponents := splitList "," ((index $cozyConfig.data "bundle-enable") | default "") }} -{{- $namespaces := dict }} - -{{/* collect namespaces from releases */}} -{{- range $x := $bundle.releases }} - {{- if not (hasKey $namespaces $x.namespace) }} - {{- if not (has $x.name $disabledComponents) }} - {{- if or (not $x.optional) (and ($x.optional) (has $x.name $enabledComponents)) }} - {{- $_ := set $namespaces $x.namespace false }} - {{- end }} - {{- end }} - {{- end }} - {{/* if at least one release requires a privileged namespace, then it should be privileged */}} - {{- if or $x.privileged (index $namespaces $x.namespace) }} - {{- $_ := set $namespaces $x.namespace true }} - {{- end }} -{{- end }} - -{{/* Add extra namespaces */}} -{{- $_ := set $namespaces "cozy-system" true }} -{{- $_ := set $namespaces "cozy-public" false }} - -{{- range $namespace, $privileged := $namespaces }} ---- -apiVersion: v1 -kind: Namespace -metadata: - annotations: - "helm.sh/resource-policy": keep - labels: - cozystack.io/system: "true" - {{- if $privileged }} - pod-security.kubernetes.io/enforce: privileged - {{- end }} - name: {{ $namespace }} ---- -apiVersion: v1 -kind: Secret -metadata: - name: cozystack-values - namespace: {{ $namespace }} - labels: - reconcile.fluxcd.io/watch: Enabled -type: Opaque -stringData: - values.yaml: | - _cluster: - {{- $clusterConfig | toYaml | nindent 6 }} - {{- with $cozystackBranding.data }} - branding: - {{- . | toYaml | nindent 8 }} - {{- end }} - {{- with $cozystackScheduling.data }} - scheduling: - {{- . | toYaml | nindent 8 }} - {{- end }} -{{- end }} diff --git a/packages/core/platform/templates/repository.yaml b/packages/core/platform/templates/repository.yaml new file mode 100644 index 00000000..9bd9f5a1 --- /dev/null +++ b/packages/core/platform/templates/repository.yaml @@ -0,0 +1,17 @@ +{{- $sourceRef := .Values.sourceRef }} +{{- $apiVersion := "source.toolkit.fluxcd.io/v1" }} +{{- $kind := $sourceRef.kind }} +{{- $name := $sourceRef.name }} +{{- $namespace := $sourceRef.namespace }} +{{- $sourceRepo := lookup $apiVersion $kind $namespace $name }} +{{- if not $sourceRepo }} +{{- fail (printf "Source repository %s/%s of kind %s not found in namespace %s" $namespace $name $kind $namespace) }} +{{- end }} +--- +apiVersion: {{ $apiVersion }} +kind: {{ $kind }} +metadata: + name: cozystack-packages + namespace: cozy-system +spec: +{{- $sourceRepo.spec | toYaml | nindent 2 }} diff --git a/packages/core/platform/templates/scheduling.yaml b/packages/core/platform/templates/scheduling.yaml new file mode 100644 index 00000000..4486155a --- /dev/null +++ b/packages/core/platform/templates/scheduling.yaml @@ -0,0 +1,11 @@ +{{- if .Values.scheduling.globalAppTopologySpreadConstraints }} +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: cozystack-scheduling + namespace: cozy-system +data: + globalAppTopologySpreadConstraints: | + {{- toYaml .Values.scheduling.globalAppTopologySpreadConstraints | nindent 4 }} +{{- end }} diff --git a/packages/core/platform/templates/sources.yaml b/packages/core/platform/templates/sources.yaml index 8b010f10..9d4eca37 100644 --- a/packages/core/platform/templates/sources.yaml +++ b/packages/core/platform/templates/sources.yaml @@ -1,6 +1,4 @@ -{{/* {{- range $path, $_ := .Files.Glob "sources/*.yaml" }} --- {{ $.Files.Get $path }} {{- end }} -*/}} diff --git a/packages/core/platform/values-isp-full.yaml b/packages/core/platform/values-isp-full.yaml new file mode 100644 index 00000000..c79ff4ba --- /dev/null +++ b/packages/core/platform/values-isp-full.yaml @@ -0,0 +1,12 @@ +migrations: + enabled: true +bundles: + system: + enabled: true + variant: "isp-full" + iaas: + enabled: true + paas: + enabled: true + naas: + enabled: true diff --git a/packages/core/platform/values-isp-hosted.yaml b/packages/core/platform/values-isp-hosted.yaml new file mode 100644 index 00000000..c3af0deb --- /dev/null +++ b/packages/core/platform/values-isp-hosted.yaml @@ -0,0 +1,12 @@ +migrations: + enabled: true +bundles: + system: + enabled: false + variant: "isp-hosted" + iaas: + enabled: false + paas: + enabled: true + naas: + enabled: true diff --git a/packages/core/platform/values.yaml b/packages/core/platform/values.yaml index 26d0859c..70b8ff86 100644 --- a/packages/core/platform/values.yaml +++ b/packages/core/platform/values.yaml @@ -1,2 +1,91 @@ -assets: - image: ghcr.io/cozystack/cozystack/cozystack-assets:v0.40.2@sha256:02730e6a434a8367a970c00a1feaa31f19a856641f6c1d085afed88e9c0b8b2b +sourceRef: + kind: OCIRepository + name: cozystack-platform + namespace: cozy-system + path: / +migrations: + enabled: false + image: ghcr.io/cozystack/cozystack/platform-migrations:latest + targetVersion: 23 +# Bundle deployment configuration +bundles: + system: + enabled: false + variant: "isp-full" # Options: "isp-full", "isp-hosted", "distro-full" + iaas: + enabled: false + paas: + enabled: false + naas: + enabled: false + disabledPackages: [] + enabledPackages: [] +# Network configuration +networking: + clusterDomain: "cozy.local" + podCIDR: "10.244.0.0/16" + podGateway: "10.244.0.1" + serviceCIDR: "10.96.0.0/16" + joinCIDR: "100.64.0.0/16" +# Service publishing and ingress configuration +publishing: + host: "example.org" + ingressName: tenant-root + exposedServices: + - api + - dashboard + - vm-exportproxy + - cdi-uploadproxy + apiServerEndpoint: "" # example: "https://api.example.org" + externalIPs: [] + certificates: + issuerType: http01 # "http01" or "cloudflare" +# Authentication configuration +authentication: + oidc: + enabled: false + dashboard: + extraRedirectUris: [] +# Pod scheduling configuration +scheduling: + topologySpreadConstraints: [] +# UI branding configuration +branding: + dashboard: {} + keycloak: {} +# Container registry mirrors configuration +# +# Example: +# registries: +# mirrors: +# docker.io: +# endpoints: +# - http://10.0.0.1:8082 +# ghcr.io: +# endpoints: +# - http://10.0.0.1:8083 +# gcr.io: +# endpoints: +# - http://10.0.0.1:8084 +# registry.k8s.io: +# endpoints: +# - http://10.0.0.1:8085 +# quay.io: +# endpoints: +# - http://10.0.0.1:8086 +# cr.fluentbit.io: +# endpoints: +# - http://10.0.0.1:8087 +# docker-registry3.mariadb.com: +# endpoints: +# - http://10.0.0.1:8088 +# config: +# "10.0.0.1:8082": +# tls: +# insecureSkipVerify: true +registries: {} +# Resource allocation ratios +resources: + cpuAllocationRatio: 10 + memoryAllocationRatio: 1 + ephemeralStorageAllocationRatio: 40 From 22cd8f1dd14e40d600d5e11e9959c45f68de5e42 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Thu, 15 Jan 2026 14:15:09 +0100 Subject: [PATCH 022/125] feat(flux): implement flux sharding for tenant HelmReleases Add dedicated flux-tenants controller with label selector --watch-label-selector=sharding.fluxcd.io/key=tenants to handle tenant workloads separately from platform components. Update all kubernetes app HelmReleases to: - Use chartRef with ExternalArtifact instead of OCIRepository sourceRef - Add sharding.fluxcd.io/key=tenants label - Add cozystack.io/target-cluster-name label Update fluxinstall to parse multiple YAML manifest files and use flux service for storage-adv-addr. Add cozystack-basics package for core tenant/namespace setup. Co-Authored-By: Claude Signed-off-by: Andrei Kvapil --- hack/cozyreport.sh | 20 + hack/e2e-install-cozystack.bats | 46 +- hack/migrate-to-version-1.0.sh | 152 + ...ystackresourcedefinition_helmreconciler.go | 82 +- internal/fluxinstall/install.go | 49 +- .../fluxinstall/manifests/fluxcd-service.yaml | 18 + .../fluxinstall/manifests/fluxcd-tenants.yaml | 102 + internal/fluxinstall/manifests/fluxcd.yaml | 2 +- internal/operator/package_reconciler.go | 2 + .../apps/bucket/templates/helmrelease.yaml | 15 +- .../helmreleases/cert-manager-crds.yaml | 14 +- .../templates/helmreleases/cert-manager.yaml | 14 +- .../templates/helmreleases/cilium.yaml | 14 +- .../templates/helmreleases/coredns.yaml | 14 +- .../templates/helmreleases/csi.yaml | 14 +- .../templates/helmreleases/fluxcd.yaml | 28 +- .../helmreleases/gateway-api-crds.yaml | 14 +- .../templates/helmreleases/gpu-operator.yaml | 14 +- .../templates/helmreleases/ingress-nginx.yaml | 14 +- .../helmreleases/metrics-server.yaml | 14 +- .../helmreleases/monitoring-agents.yaml | 14 +- .../prometheus-operator-crds.yaml | 14 +- .../templates/helmreleases/velero.yaml | 14 +- .../vertical-pod-autoscaler-crds.yaml | 14 +- .../helmreleases/vertical-pod-autoscaler.yaml | 14 +- .../victoria-metrics-operator.yaml | 14 +- .../helmreleases/volumesnapshot-crd.yaml | 14 +- packages/apps/nats/templates/nats.yaml | 15 +- packages/apps/tenant/templates/etcd.yaml | 14 +- packages/apps/tenant/templates/info.yaml | 14 +- packages/apps/tenant/templates/ingress.yaml | 14 +- .../apps/tenant/templates/monitoring.yaml | 14 +- packages/apps/tenant/templates/seaweedfs.yaml | 14 +- packages/core/flux-aio/Makefile | 24 +- packages/core/flux-aio/manifests | 1 + packages/core/flux-aio/templates/_helpers.tpl | 13 - packages/core/flux-aio/templates/fluxcd.yaml | 11957 ---------------- .../ingress/templates/nginx-ingress.yaml | 15 +- .../monitoring/templates/dashboards.yaml | 4 +- .../extra/seaweedfs/templates/seaweedfs.yaml | 15 +- .../backupstrategy-controller/Chart.yaml | 2 +- ...trategy.backups.cozystack.io_veleroes.yaml | 496 + .../backupstrategy-controller/Dockerfile | 2 +- .../system/bootbox/templates/bootbox.yaml | 14 +- packages/system/cozystack-basics/Chart.yaml | 3 + packages/system/cozystack-basics/Makefile | 4 + .../templates/cozy-public.yaml | 4 + .../templates/cozystack-values-secret.yaml | 19 + .../templates/tenant-root.yaml | 30 + packages/system/cozystack-basics/values.yaml | 1 + ...stack.io_cozystackresourcedefinitions.yaml | 284 +- .../templates/dashboards-deployment.yaml | 41 + .../templates/dashboards-service.yaml | 19 + .../kube-ovn/templates/controller-deploy.yaml | 9 +- .../charts/kube-ovn/templates/ovncni-ds.yaml | 3 +- .../skip-adjust-when-device-inaccessible.diff | 260 - .../templates/vpa-for-vpa.yaml | 13 +- 57 files changed, 1292 insertions(+), 12766 deletions(-) create mode 100755 hack/migrate-to-version-1.0.sh create mode 100644 internal/fluxinstall/manifests/fluxcd-service.yaml create mode 100644 internal/fluxinstall/manifests/fluxcd-tenants.yaml create mode 120000 packages/core/flux-aio/manifests delete mode 100644 packages/core/flux-aio/templates/_helpers.tpl delete mode 100644 packages/core/flux-aio/templates/fluxcd.yaml create mode 100644 packages/system/cozystack-basics/Chart.yaml create mode 100644 packages/system/cozystack-basics/Makefile create mode 100644 packages/system/cozystack-basics/templates/cozy-public.yaml create mode 100644 packages/system/cozystack-basics/templates/cozystack-values-secret.yaml create mode 100644 packages/system/cozystack-basics/templates/tenant-root.yaml create mode 100644 packages/system/cozystack-basics/values.yaml create mode 100644 packages/system/grafana-operator/templates/dashboards-deployment.yaml create mode 100644 packages/system/grafana-operator/templates/dashboards-service.yaml delete mode 100644 packages/system/linstor/images/piraeus-server/patches/skip-adjust-when-device-inaccessible.diff diff --git a/hack/cozyreport.sh b/hack/cozyreport.sh index 3174ed50..28fddfe9 100755 --- a/hack/cozyreport.sh +++ b/hack/cozyreport.sh @@ -56,6 +56,26 @@ kubectl get hr -A --no-headers | awk '$4 != "True"' | \ kubectl describe hr -n $NAMESPACE $NAME > $DIR/describe.txt 2>&1 done +echo "Collecting packages..." +kubectl get packages -A > $REPORT_DIR/kubernetes/packages.txt 2>&1 +kubectl get packages -A --no-headers | awk '$4 != "True"' | \ + while read NAMESPACE NAME _; do + DIR=$REPORT_DIR/kubernetes/packages/$NAMESPACE/$NAME + mkdir -p $DIR + kubectl get package -n $NAMESPACE $NAME -o yaml > $DIR/package.yaml 2>&1 + kubectl describe package -n $NAMESPACE $NAME > $DIR/describe.txt 2>&1 + done + +echo "Collecting packagesources..." +kubectl get packagesources -A > $REPORT_DIR/kubernetes/packagesources.txt 2>&1 +kubectl get packagesources -A --no-headers | awk '$4 != "True"' | \ + while read NAMESPACE NAME _; do + DIR=$REPORT_DIR/kubernetes/packagesources/$NAMESPACE/$NAME + mkdir -p $DIR + kubectl get packagesource -n $NAMESPACE $NAME -o yaml > $DIR/packagesource.yaml 2>&1 + kubectl describe packagesource -n $NAMESPACE $NAME > $DIR/describe.txt 2>&1 + done + echo "Collecting pods..." kubectl get pod -A -o wide > $REPORT_DIR/kubernetes/pods.txt 2>&1 kubectl get pod -A --no-headers | awk '$4 !~ /Running|Succeeded|Completed/' | diff --git a/hack/e2e-install-cozystack.bats b/hack/e2e-install-cozystack.bats index da132132..124bd8bf 100644 --- a/hack/e2e-install-cozystack.bats +++ b/hack/e2e-install-cozystack.bats @@ -8,28 +8,40 @@ } @test "Install Cozystack" { - # Create namespace & configmap required by installer + # Create namespace kubectl create namespace cozy-system --dry-run=client -o yaml | kubectl apply -f - - kubectl create configmap cozystack -n cozy-system \ - --from-literal=bundle-name=paas-full \ - --from-literal=ipv4-pod-cidr=10.244.0.0/16 \ - --from-literal=ipv4-pod-gateway=10.244.0.1 \ - --from-literal=ipv4-svc-cidr=10.96.0.0/16 \ - --from-literal=ipv4-join-cidr=100.64.0.0/16 \ - --from-literal=root-host=example.org \ - --from-literal=api-server-endpoint=https://192.168.123.10:6443 \ - --dry-run=client -o yaml | kubectl apply -f - - # Apply installer manifests from file + # Apply installer manifests (CRDs + operator) kubectl apply -f _out/assets/cozystack-installer.yaml - # Wait for the installer deployment to become available - kubectl wait deployment/cozystack -n cozy-system --timeout=1m --for=condition=Available + # Wait for the operator deployment to become available + kubectl wait deployment/cozystack-operator -n cozy-system --timeout=1m --for=condition=Available + + # Create platform Package with isp-full variant + kubectl apply -f - </dev/null | wc -l) -gt 10 ]; do sleep 1; done' sleep 5 - kubectl get hr -A -l cozystack.io/system-app=true | awk 'NR>1 {print "kubectl wait --timeout=15m --for=condition=ready -n "$1" hr/"$2" &"} END {print "wait"}' | sh -ex + kubectl get hr -A | awk 'NR>1 {print "kubectl wait --timeout=15m --for=condition=ready -n "$1" hr/"$2" &"} END {print "wait"}' | sh -ex # Fail the test if any HelmRelease is not Ready if kubectl get hr -A | grep -v " True " | grep -v NAME; then @@ -142,7 +154,7 @@ EOF # Expose Cozystack services through ingress - kubectl patch configmap/cozystack -n cozy-system --type merge -p '{"data":{"expose-services":"api,dashboard,cdi-uploadproxy,vm-exportproxy,keycloak"}}' + kubectl patch package cozystack.cozystack-platform --type merge -p '{"spec":{"components":{"platform":{"values":{"publishing":{"exposedServices":["api","dashboard","cdi-uploadproxy","vm-exportproxy","keycloak"]}}}}}}' # NGINX ingress controller timeout 60 sh -ec 'until kubectl get deploy root-ingress-controller -n tenant-root >/dev/null 2>&1; do sleep 1; done' @@ -169,7 +181,7 @@ EOF } @test "Keycloak OIDC stack is healthy" { - kubectl patch configmap/cozystack -n cozy-system --type merge -p '{"data":{"oidc-enabled":"true"}}' + kubectl patch package cozystack.cozystack-platform --type merge -p '{"spec":{"components":{"platform":{"values":{"authentication":{"oidc":{"enabled":true}}}}}}}' timeout 120 sh -ec 'until kubectl get hr -n cozy-keycloak keycloak keycloak-configure keycloak-operator >/dev/null 2>&1; do sleep 1; done' kubectl wait hr/keycloak hr/keycloak-configure hr/keycloak-operator -n cozy-keycloak --timeout=10m --for=condition=ready diff --git a/hack/migrate-to-version-1.0.sh b/hack/migrate-to-version-1.0.sh new file mode 100755 index 00000000..3985d6f9 --- /dev/null +++ b/hack/migrate-to-version-1.0.sh @@ -0,0 +1,152 @@ +#!/bin/bash +# Migration script from Cozystack ConfigMaps to Package-based configuration +# This script converts cozystack, cozystack-branding, and cozystack-scheduling +# ConfigMaps into a Package resource with the new values structure. + +set -e + +NAMESPACE="cozy-system" + +echo "Cozystack Migration to v1.0" +echo "===========================" +echo "" +echo "This script will convert existing ConfigMaps to a Package resource." +echo "" + +# Check if kubectl is available +if ! command -v kubectl &> /dev/null; then + echo "Error: kubectl is not installed or not in PATH" + exit 1 +fi + +# Check if we can access the cluster +if ! kubectl get namespace "$NAMESPACE" &> /dev/null; then + echo "Error: Cannot access namespace $NAMESPACE" + exit 1 +fi + +# Read ConfigMap cozystack +echo "Reading ConfigMap cozystack..." +COZYSTACK_CM=$(kubectl get configmap -n "$NAMESPACE" cozystack -o json 2>/dev/null || echo "{}") + +# Read ConfigMap cozystack-branding +echo "Reading ConfigMap cozystack-branding..." +BRANDING_CM=$(kubectl get configmap -n "$NAMESPACE" cozystack-branding -o json 2>/dev/null || echo "{}") + +# Read ConfigMap cozystack-scheduling +echo "Reading ConfigMap cozystack-scheduling..." +SCHEDULING_CM=$(kubectl get configmap -n "$NAMESPACE" cozystack-scheduling -o json 2>/dev/null || echo "{}") + +# Extract values from cozystack ConfigMap +CLUSTER_DOMAIN=$(echo "$COZYSTACK_CM" | jq -r '.data["cluster-domain"] // "cozy.local"') +ROOT_HOST=$(echo "$COZYSTACK_CM" | jq -r '.data["root-host"] // "example.org"') +API_SERVER_ENDPOINT=$(echo "$COZYSTACK_CM" | jq -r '.data["api-server-endpoint"] // ""') +OIDC_ENABLED=$(echo "$COZYSTACK_CM" | jq -r '.data["oidc-enabled"] // "false"') +TELEMETRY_ENABLED=$(echo "$COZYSTACK_CM" | jq -r '.data["telemetry-enabled"] // "true"') +BUNDLE_NAME=$(echo "$COZYSTACK_CM" | jq -r '.data["bundle-name"] // "paas-full"') + +# Network configuration +POD_CIDR=$(echo "$COZYSTACK_CM" | jq -r '.data["ipv4-pod-cidr"] // "10.244.0.0/16"') +POD_GATEWAY=$(echo "$COZYSTACK_CM" | jq -r '.data["ipv4-pod-gateway"] // "10.244.0.1"') +SVC_CIDR=$(echo "$COZYSTACK_CM" | jq -r '.data["ipv4-svc-cidr"] // "10.96.0.0/16"') +JOIN_CIDR=$(echo "$COZYSTACK_CM" | jq -r '.data["ipv4-join-cidr"] // "100.64.0.0/16"') + +# Determine bundle type +case "$BUNDLE_NAME" in + paas-full|distro-full) + SYSTEM_ENABLED="true" + SYSTEM_TYPE="full" + ;; + paas-hosted|distro-hosted) + SYSTEM_ENABLED="false" + SYSTEM_TYPE="hosted" + ;; + *) + SYSTEM_ENABLED="false" + SYSTEM_TYPE="hosted" + ;; +esac + +# Extract branding if available +DASHBOARD_BRANDING=$(echo "$BRANDING_CM" | jq -r '.data["dashboard"] // "{}"') +KEYCLOAK_BRANDING=$(echo "$BRANDING_CM" | jq -r '.data["keycloak"] // "{}"') + +# Extract scheduling if available +SCHEDULING_CONSTRAINTS=$(echo "$SCHEDULING_CM" | jq -r '.data["topologySpreadConstraints"] // "[]"') + +echo "" +echo "Extracted configuration:" +echo " Cluster Domain: $CLUSTER_DOMAIN" +echo " Root Host: $ROOT_HOST" +echo " API Server Endpoint: $API_SERVER_ENDPOINT" +echo " OIDC Enabled: $OIDC_ENABLED" +echo " Bundle Name: $BUNDLE_NAME" +echo " System Enabled: $SYSTEM_ENABLED" +echo " System Type: $SYSTEM_TYPE" +echo "" + +# Generate Package YAML +PACKAGE_YAML=$(cat < 0 { + if hrCopy.Labels == nil { + hrCopy.Labels = make(map[string]string) + } + for key, value := range crd.Spec.Release.Labels { + if hrCopy.Labels[key] != value { + logger.V(4).Info("Updating HelmRelease label", "name", hr.Name, "namespace", hr.Namespace, "label", key, "value", value) + hrCopy.Labels[key] = value + updated = true + } + } + } + if updated { - logger.V(4).Info("Updating HelmRelease chart", "name", hr.Name, "namespace", hr.Namespace) + logger.V(4).Info("Updating HelmRelease", "name", hr.Name, "namespace", hr.Namespace) if err := r.Update(ctx, hrCopy); err != nil { return fmt.Errorf("failed to update HelmRelease: %w", err) } @@ -198,4 +187,3 @@ func (r *CozystackResourceDefinitionHelmReconciler) updateHelmReleaseChart(ctx c return nil } - diff --git a/internal/fluxinstall/install.go b/internal/fluxinstall/install.go index aa834404..2097ecfb 100644 --- a/internal/fluxinstall/install.go +++ b/internal/fluxinstall/install.go @@ -56,26 +56,31 @@ func Install(ctx context.Context, k8sClient client.Client, writeEmbeddedManifest return fmt.Errorf("failed to extract embedded manifests: %w", err) } - // Find the manifest file (should be fluxcd.yaml from cozypkg) - manifestPath := filepath.Join(manifestsDir, "fluxcd.yaml") - if _, err := os.Stat(manifestPath); err != nil { - // Try to find any YAML file if fluxcd.yaml doesn't exist - entries, err := os.ReadDir(manifestsDir) - if err != nil { - return fmt.Errorf("failed to read manifests directory: %w", err) - } - for _, entry := range entries { - if strings.HasSuffix(entry.Name(), ".yaml") { - manifestPath = filepath.Join(manifestsDir, entry.Name()) - break - } + // Find all YAML manifest files + entries, err := os.ReadDir(manifestsDir) + if err != nil { + return fmt.Errorf("failed to read manifests directory: %w", err) + } + + var manifestFiles []string + for _, entry := range entries { + if strings.HasSuffix(entry.Name(), ".yaml") { + manifestFiles = append(manifestFiles, filepath.Join(manifestsDir, entry.Name())) } } - // Parse and apply manifests - objects, err := parseManifests(manifestPath) - if err != nil { - return fmt.Errorf("failed to parse manifests: %w", err) + if len(manifestFiles) == 0 { + return fmt.Errorf("no YAML manifest files found in directory") + } + + // Parse all manifest files + var objects []*unstructured.Unstructured + for _, manifestPath := range manifestFiles { + objs, err := parseManifests(manifestPath) + if err != nil { + return fmt.Errorf("failed to parse manifests from %s: %w", manifestPath, err) + } + objects = append(objects, objs...) } if len(objects) == 0 { @@ -96,7 +101,7 @@ func Install(ctx context.Context, k8sClient client.Client, writeEmbeddedManifest logger.Info("Installing Flux components", "namespace", namespace) // Apply manifests using server-side apply - logger.Info("Applying Flux manifests", "count", len(objects), "manifest", manifestPath, "namespace", namespace) + logger.Info("Applying Flux manifests", "count", len(objects), "files", len(manifestFiles), "namespace", namespace) if err := applyManifests(ctx, k8sClient, objects); err != nil { return fmt.Errorf("failed to apply manifests: %w", err) } @@ -251,11 +256,17 @@ func injectKubernetesServiceEnv(objects []*unstructured.Unstructured) error { continue } - // Navigate to spec.template.spec.containers + // Navigate to spec.template.spec spec, found, err := unstructured.NestedMap(obj.Object, "spec", "template", "spec") if !found { continue } + + // Skip pods that don't use hostNetwork - they should use normal Kubernetes DNS + hostNetwork, _, _ := unstructured.NestedBool(spec, "hostNetwork") + if !hostNetwork { + continue + } if err != nil { if firstErr == nil { firstErr = fmt.Errorf("failed to get spec for %s/%s: %w", kind, obj.GetName(), err) diff --git a/internal/fluxinstall/manifests/fluxcd-service.yaml b/internal/fluxinstall/manifests/fluxcd-service.yaml new file mode 100644 index 00000000..87feaec4 --- /dev/null +++ b/internal/fluxinstall/manifests/fluxcd-service.yaml @@ -0,0 +1,18 @@ +--- +apiVersion: v1 +kind: Service +metadata: + labels: + app.kubernetes.io/name: flux + app.kubernetes.io/part-of: flux + name: flux + namespace: cozy-fluxcd +spec: + ports: + - name: http + port: 80 + protocol: TCP + targetPort: http-sc + selector: + app.kubernetes.io/name: flux + type: ClusterIP diff --git a/internal/fluxinstall/manifests/fluxcd-tenants.yaml b/internal/fluxinstall/manifests/fluxcd-tenants.yaml new file mode 100644 index 00000000..077cf34c --- /dev/null +++ b/internal/fluxinstall/manifests/fluxcd-tenants.yaml @@ -0,0 +1,102 @@ +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app.kubernetes.io/name: flux-tenants + app.kubernetes.io/part-of: flux + app.kubernetes.io/version: v2.7.3 + sharding.fluxcd.io/role: shard + name: flux-tenants + namespace: cozy-fluxcd +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: flux-tenants + strategy: + type: Recreate + template: + metadata: + annotations: + cluster-autoscaler.kubernetes.io/safe-to-evict: "true" + prometheus.io/scrape: "true" + labels: + app.kubernetes.io/name: flux-tenants + spec: + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/os + operator: In + values: + - linux + containers: + - args: + - --watch-all-namespaces + - --log-level=info + - --log-encoding=json + - --enable-leader-election=false + - --metrics-addr=:9795 + - --health-addr=:9796 + - --watch-label-selector=sharding.fluxcd.io/key=tenants + - --concurrent=5 + - --requeue-dependency=30s + - --feature-gates=ExternalArtifact=true + env: + - name: RUNTIME_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: GOMEMLIMIT + valueFrom: + resourceFieldRef: + resource: limits.memory + - name: TUF_ROOT + value: /tmp/.sigstore + image: "ghcr.io/fluxcd/helm-controller:v1.4.3" + imagePullPolicy: IfNotPresent + livenessProbe: + httpGet: + path: /healthz + port: healthz + name: helm-controller + ports: + - containerPort: 9795 + name: http-prom + protocol: TCP + - containerPort: 9796 + name: healthz + protocol: TCP + readinessProbe: + httpGet: + path: /readyz + port: healthz + resources: + limits: + memory: 1Gi + requests: + cpu: 100m + memory: 64Mi + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + volumeMounts: + - mountPath: /tmp + name: tmp + priorityClassName: system-cluster-critical + securityContext: + fsGroup: 1337 + serviceAccountName: flux + terminationGracePeriodSeconds: 60 + volumes: + - emptyDir: {} + name: tmp diff --git a/internal/fluxinstall/manifests/fluxcd.yaml b/internal/fluxinstall/manifests/fluxcd.yaml index 237db089..4689d612 100644 --- a/internal/fluxinstall/manifests/fluxcd.yaml +++ b/internal/fluxinstall/manifests/fluxcd.yaml @@ -11871,7 +11871,7 @@ spec: - --health-addr=:9693 - --storage-addr=:9691 - --storage-path=/data - - --storage-adv-addr=source-watcher.$(RUNTIME_NAMESPACE).svc + - --storage-adv-addr=flux.$(RUNTIME_NAMESPACE).svc - --events-addr=http://localhost:9690 env: - name: SOURCE_CONTROLLER_LOCALHOST diff --git a/internal/operator/package_reconciler.go b/internal/operator/package_reconciler.go index 07237fe5..79d78a87 100644 --- a/internal/operator/package_reconciler.go +++ b/internal/operator/package_reconciler.go @@ -211,11 +211,13 @@ func (r *PackageReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ct Namespace: "cozy-system", }, Install: &helmv2.Install{ + Timeout: &metav1.Duration{Duration: 10 * 60 * 1000000000}, // 10m Remediation: &helmv2.InstallRemediation{ Retries: -1, }, }, Upgrade: &helmv2.Upgrade{ + Timeout: &metav1.Duration{Duration: 10 * 60 * 1000000000}, // 10m Remediation: &helmv2.UpgradeRemediation{ Retries: -1, }, diff --git a/packages/apps/bucket/templates/helmrelease.yaml b/packages/apps/bucket/templates/helmrelease.yaml index 5d242f84..704470a8 100644 --- a/packages/apps/bucket/templates/helmrelease.yaml +++ b/packages/apps/bucket/templates/helmrelease.yaml @@ -2,16 +2,13 @@ apiVersion: helm.toolkit.fluxcd.io/v2 kind: HelmRelease metadata: name: {{ .Release.Name }}-system + labels: + sharding.fluxcd.io/key: tenants spec: - chart: - spec: - chart: cozy-bucket - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-bucket-application-default-bucket-system + namespace: cozy-system interval: 5m timeout: 10m install: diff --git a/packages/apps/kubernetes/templates/helmreleases/cert-manager-crds.yaml b/packages/apps/kubernetes/templates/helmreleases/cert-manager-crds.yaml index 73954e12..be07a8b9 100644 --- a/packages/apps/kubernetes/templates/helmreleases/cert-manager-crds.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/cert-manager-crds.yaml @@ -6,17 +6,13 @@ metadata: labels: cozystack.io/repository: system cozystack.io/target-cluster-name: {{ .Release.Name }} + sharding.fluxcd.io/key: tenants spec: releaseName: cert-manager-crds - chart: - spec: - chart: cozy-cert-manager-crds - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-kubernetes-application-kubevirt-kubernetes-cert-manager-crds + namespace: cozy-system kubeConfig: secretRef: name: {{ .Release.Name }}-admin-kubeconfig diff --git a/packages/apps/kubernetes/templates/helmreleases/cert-manager.yaml b/packages/apps/kubernetes/templates/helmreleases/cert-manager.yaml index e0caf4cb..991ed70f 100644 --- a/packages/apps/kubernetes/templates/helmreleases/cert-manager.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/cert-manager.yaml @@ -6,17 +6,13 @@ metadata: labels: cozystack.io/repository: system cozystack.io/target-cluster-name: {{ .Release.Name }} + sharding.fluxcd.io/key: tenants spec: releaseName: cert-manager - chart: - spec: - chart: cozy-cert-manager - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-kubernetes-application-kubevirt-kubernetes-cert-manager + namespace: cozy-system kubeConfig: secretRef: name: {{ .Release.Name }}-admin-kubeconfig diff --git a/packages/apps/kubernetes/templates/helmreleases/cilium.yaml b/packages/apps/kubernetes/templates/helmreleases/cilium.yaml index c356dc79..64027e94 100644 --- a/packages/apps/kubernetes/templates/helmreleases/cilium.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/cilium.yaml @@ -20,17 +20,13 @@ metadata: labels: cozystack.io/repository: system cozystack.io/target-cluster-name: {{ .Release.Name }} + sharding.fluxcd.io/key: tenants spec: releaseName: cilium - chart: - spec: - chart: cozy-cilium - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-kubernetes-application-kubevirt-kubernetes-cilium + namespace: cozy-system kubeConfig: secretRef: name: {{ .Release.Name }}-admin-kubeconfig diff --git a/packages/apps/kubernetes/templates/helmreleases/coredns.yaml b/packages/apps/kubernetes/templates/helmreleases/coredns.yaml index 37a09a0b..bdb6c682 100644 --- a/packages/apps/kubernetes/templates/helmreleases/coredns.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/coredns.yaml @@ -11,17 +11,13 @@ metadata: labels: cozystack.io/repository: system cozystack.io/target-cluster-name: {{ .Release.Name }} + sharding.fluxcd.io/key: tenants spec: releaseName: coredns - chart: - spec: - chart: cozy-coredns - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-kubernetes-application-kubevirt-kubernetes-coredns + namespace: cozy-system kubeConfig: secretRef: name: {{ .Release.Name }}-admin-kubeconfig diff --git a/packages/apps/kubernetes/templates/helmreleases/csi.yaml b/packages/apps/kubernetes/templates/helmreleases/csi.yaml index 3ecbf1eb..dd2c69a6 100644 --- a/packages/apps/kubernetes/templates/helmreleases/csi.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/csi.yaml @@ -5,18 +5,14 @@ metadata: labels: cozystack.io/repository: system cozystack.io/target-cluster-name: {{ .Release.Name }} + sharding.fluxcd.io/key: tenants spec: interval: 5m releaseName: csi - chart: - spec: - chart: cozy-kubevirt-csi-node - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-kubernetes-application-kubevirt-kubernetes-kubevirt-csi-node + namespace: cozy-system kubeConfig: secretRef: name: {{ .Release.Name }}-admin-kubeconfig diff --git a/packages/apps/kubernetes/templates/helmreleases/fluxcd.yaml b/packages/apps/kubernetes/templates/helmreleases/fluxcd.yaml index 7518601b..76499dfe 100644 --- a/packages/apps/kubernetes/templates/helmreleases/fluxcd.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/fluxcd.yaml @@ -6,17 +6,13 @@ metadata: labels: cozystack.io/repository: system cozystack.io/target-cluster-name: {{ .Release.Name }} + sharding.fluxcd.io/key: tenants spec: releaseName: fluxcd-operator - chart: - spec: - chart: cozy-fluxcd-operator - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-kubernetes-application-kubevirt-kubernetes-fluxcd-operator + namespace: cozy-system kubeConfig: secretRef: name: {{ .Release.Name }}-admin-kubeconfig @@ -53,18 +49,14 @@ metadata: labels: cozystack.io/repository: system cozystack.io/target-cluster-name: {{ .Release.Name }} + sharding.fluxcd.io/key: tenants spec: interval: 5m releaseName: fluxcd - chart: - spec: - chart: cozy-fluxcd - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-kubernetes-application-kubevirt-kubernetes-fluxcd + namespace: cozy-system kubeConfig: secretRef: name: {{ .Release.Name }}-kubeconfig diff --git a/packages/apps/kubernetes/templates/helmreleases/gateway-api-crds.yaml b/packages/apps/kubernetes/templates/helmreleases/gateway-api-crds.yaml index 48a20c5a..2bcc8d4d 100644 --- a/packages/apps/kubernetes/templates/helmreleases/gateway-api-crds.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/gateway-api-crds.yaml @@ -6,17 +6,13 @@ metadata: labels: cozystack.io/repository: system cozystack.io/target-cluster-name: {{ .Release.Name }} + sharding.fluxcd.io/key: tenants spec: releaseName: gateway-api-crds - chart: - spec: - chart: cozy-gateway-api-crds - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-kubernetes-application-kubevirt-kubernetes-gateway-api-crds + namespace: cozy-system kubeConfig: secretRef: name: {{ .Release.Name }}-admin-kubeconfig diff --git a/packages/apps/kubernetes/templates/helmreleases/gpu-operator.yaml b/packages/apps/kubernetes/templates/helmreleases/gpu-operator.yaml index fbee1724..5ef48912 100644 --- a/packages/apps/kubernetes/templates/helmreleases/gpu-operator.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/gpu-operator.yaml @@ -6,17 +6,13 @@ metadata: labels: cozystack.io/repository: system cozystack.io/target-cluster-name: {{ .Release.Name }} + sharding.fluxcd.io/key: tenants spec: releaseName: gpu-operator - chart: - spec: - chart: cozy-gpu-operator - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-kubernetes-application-kubevirt-kubernetes-gpu-operator + namespace: cozy-system kubeConfig: secretRef: name: {{ .Release.Name }}-admin-kubeconfig diff --git a/packages/apps/kubernetes/templates/helmreleases/ingress-nginx.yaml b/packages/apps/kubernetes/templates/helmreleases/ingress-nginx.yaml index f80dd7ae..6f7b0759 100644 --- a/packages/apps/kubernetes/templates/helmreleases/ingress-nginx.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/ingress-nginx.yaml @@ -25,17 +25,13 @@ metadata: labels: cozystack.io/repository: system cozystack.io/target-cluster-name: {{ .Release.Name }} + sharding.fluxcd.io/key: tenants spec: releaseName: ingress-nginx - chart: - spec: - chart: cozy-ingress-nginx - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-kubernetes-application-kubevirt-kubernetes-ingress-nginx + namespace: cozy-system kubeConfig: secretRef: name: {{ .Release.Name }}-admin-kubeconfig diff --git a/packages/apps/kubernetes/templates/helmreleases/metrics-server.yaml b/packages/apps/kubernetes/templates/helmreleases/metrics-server.yaml index 394e6bb4..3cc81a14 100644 --- a/packages/apps/kubernetes/templates/helmreleases/metrics-server.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/metrics-server.yaml @@ -5,17 +5,13 @@ metadata: labels: cozystack.io/repository: system cozystack.io/target-cluster-name: {{ .Release.Name }} + sharding.fluxcd.io/key: tenants spec: releaseName: metrics-server - chart: - spec: - chart: cozy-metrics-server - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-kubernetes-application-kubevirt-kubernetes-metrics-server + namespace: cozy-system kubeConfig: secretRef: name: {{ .Release.Name }}-admin-kubeconfig diff --git a/packages/apps/kubernetes/templates/helmreleases/monitoring-agents.yaml b/packages/apps/kubernetes/templates/helmreleases/monitoring-agents.yaml index 73c3a368..ff54b6e2 100644 --- a/packages/apps/kubernetes/templates/helmreleases/monitoring-agents.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/monitoring-agents.yaml @@ -7,17 +7,13 @@ metadata: labels: cozystack.io/repository: system cozystack.io/target-cluster-name: {{ .Release.Name }} + sharding.fluxcd.io/key: tenants spec: releaseName: cozy-monitoring-agents - chart: - spec: - chart: cozy-monitoring-agents - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-kubernetes-application-kubevirt-kubernetes-monitoring-agents + namespace: cozy-system kubeConfig: secretRef: name: {{ .Release.Name }}-admin-kubeconfig diff --git a/packages/apps/kubernetes/templates/helmreleases/prometheus-operator-crds.yaml b/packages/apps/kubernetes/templates/helmreleases/prometheus-operator-crds.yaml index 63972126..600a7994 100644 --- a/packages/apps/kubernetes/templates/helmreleases/prometheus-operator-crds.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/prometheus-operator-crds.yaml @@ -5,17 +5,13 @@ metadata: labels: cozystack.io/repository: system cozystack.io/target-cluster-name: {{ .Release.Name }} + sharding.fluxcd.io/key: tenants spec: releaseName: prometheus-operator-crds - chart: - spec: - chart: cozy-prometheus-operator-crds - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-kubernetes-application-kubevirt-kubernetes-prometheus-operator-crds + namespace: cozy-system kubeConfig: secretRef: name: {{ .Release.Name }}-admin-kubeconfig diff --git a/packages/apps/kubernetes/templates/helmreleases/velero.yaml b/packages/apps/kubernetes/templates/helmreleases/velero.yaml index 0c918da6..ad236d53 100644 --- a/packages/apps/kubernetes/templates/helmreleases/velero.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/velero.yaml @@ -6,17 +6,13 @@ metadata: labels: cozystack.io/repository: system cozystack.io/target-cluster-name: {{ .Release.Name }} + sharding.fluxcd.io/key: tenants spec: releaseName: velero - chart: - spec: - chart: cozy-velero - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-kubernetes-application-kubevirt-kubernetes-velero + namespace: cozy-system kubeConfig: secretRef: name: {{ .Release.Name }}-admin-kubeconfig diff --git a/packages/apps/kubernetes/templates/helmreleases/vertical-pod-autoscaler-crds.yaml b/packages/apps/kubernetes/templates/helmreleases/vertical-pod-autoscaler-crds.yaml index 71bdc9ee..a3b7a9b4 100644 --- a/packages/apps/kubernetes/templates/helmreleases/vertical-pod-autoscaler-crds.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/vertical-pod-autoscaler-crds.yaml @@ -6,18 +6,14 @@ metadata: labels: cozystack.io/repository: system cozystack.io/target-cluster-name: {{ .Release.Name }} + sharding.fluxcd.io/key: tenants spec: interval: 5m releaseName: vertical-pod-autoscaler-crds - chart: - spec: - chart: cozy-vertical-pod-autoscaler-crds - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-kubernetes-application-kubevirt-kubernetes-vertical-pod-autoscaler-crds + namespace: cozy-system kubeConfig: secretRef: name: {{ .Release.Name }}-admin-kubeconfig diff --git a/packages/apps/kubernetes/templates/helmreleases/vertical-pod-autoscaler.yaml b/packages/apps/kubernetes/templates/helmreleases/vertical-pod-autoscaler.yaml index 8a62288d..178df3e3 100644 --- a/packages/apps/kubernetes/templates/helmreleases/vertical-pod-autoscaler.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/vertical-pod-autoscaler.yaml @@ -32,17 +32,13 @@ metadata: labels: cozystack.io/repository: system cozystack.io/target-cluster-name: {{ .Release.Name }} + sharding.fluxcd.io/key: tenants spec: releaseName: vertical-pod-autoscaler - chart: - spec: - chart: cozy-vertical-pod-autoscaler - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-kubernetes-application-kubevirt-kubernetes-vertical-pod-autoscaler + namespace: cozy-system kubeConfig: secretRef: name: {{ .Release.Name }}-admin-kubeconfig diff --git a/packages/apps/kubernetes/templates/helmreleases/victoria-metrics-operator.yaml b/packages/apps/kubernetes/templates/helmreleases/victoria-metrics-operator.yaml index dbb4d8dc..99744277 100644 --- a/packages/apps/kubernetes/templates/helmreleases/victoria-metrics-operator.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/victoria-metrics-operator.yaml @@ -6,17 +6,13 @@ metadata: labels: cozystack.io/repository: system cozystack.io/target-cluster-name: {{ .Release.Name }} + sharding.fluxcd.io/key: tenants spec: releaseName: cozy-victoria-metrics-operator - chart: - spec: - chart: cozy-victoria-metrics-operator - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-kubernetes-application-kubevirt-kubernetes-victoria-metrics-operator + namespace: cozy-system kubeConfig: secretRef: name: {{ .Release.Name }}-admin-kubeconfig diff --git a/packages/apps/kubernetes/templates/helmreleases/volumesnapshot-crd.yaml b/packages/apps/kubernetes/templates/helmreleases/volumesnapshot-crd.yaml index 83fa32d1..025f01b7 100644 --- a/packages/apps/kubernetes/templates/helmreleases/volumesnapshot-crd.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/volumesnapshot-crd.yaml @@ -5,17 +5,13 @@ metadata: labels: cozystack.io/repository: system cozystack.io/target-cluster-name: {{ .Release.Name }} + sharding.fluxcd.io/key: tenants spec: releaseName: vsnap-crd - chart: - spec: - chart: cozy-vsnap-crd - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-kubernetes-application-kubevirt-kubernetes-volumesnapshot-crd + namespace: cozy-system kubeConfig: secretRef: name: {{ .Release.Name }}-admin-kubeconfig diff --git a/packages/apps/nats/templates/nats.yaml b/packages/apps/nats/templates/nats.yaml index 3e858fd5..4f52ff11 100644 --- a/packages/apps/nats/templates/nats.yaml +++ b/packages/apps/nats/templates/nats.yaml @@ -33,16 +33,13 @@ apiVersion: helm.toolkit.fluxcd.io/v2 kind: HelmRelease metadata: name: {{ .Release.Name }}-system + labels: + sharding.fluxcd.io/key: tenants spec: - chart: - spec: - chart: cozy-nats - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-nats-application-default-nats-system + namespace: cozy-system interval: 5m timeout: 10m install: diff --git a/packages/apps/tenant/templates/etcd.yaml b/packages/apps/tenant/templates/etcd.yaml index e67ab597..142f0ca7 100644 --- a/packages/apps/tenant/templates/etcd.yaml +++ b/packages/apps/tenant/templates/etcd.yaml @@ -6,6 +6,7 @@ metadata: namespace: {{ include "tenant.name" . }} labels: cozystack.io/ui: "true" + sharding.fluxcd.io/key: tenants internal.cozystack.io/tenantmodule: "true" app.kubernetes.io/instance: {{ .Release.Name }} app.kubernetes.io/managed-by: {{ .Release.Service }} @@ -13,15 +14,10 @@ metadata: apps.cozystack.io/application.group: apps.cozystack.io apps.cozystack.io/application.name: etcd spec: - chart: - spec: - chart: etcd - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-extra - namespace: cozy-public - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-etcd-application-default-etcd + namespace: cozy-system interval: 5m timeout: 10m install: diff --git a/packages/apps/tenant/templates/info.yaml b/packages/apps/tenant/templates/info.yaml index efa01b87..4c99d551 100644 --- a/packages/apps/tenant/templates/info.yaml +++ b/packages/apps/tenant/templates/info.yaml @@ -5,6 +5,7 @@ metadata: namespace: {{ include "tenant.name" . }} labels: cozystack.io/ui: "true" + sharding.fluxcd.io/key: tenants internal.cozystack.io/tenantmodule: "true" app.kubernetes.io/instance: {{ .Release.Name }} app.kubernetes.io/managed-by: {{ .Release.Service }} @@ -12,15 +13,10 @@ metadata: apps.cozystack.io/application.group: apps.cozystack.io apps.cozystack.io/application.name: info spec: - chart: - spec: - chart: info - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-extra - namespace: cozy-public - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-info-application-default-info + namespace: cozy-system interval: 5m timeout: 10m install: diff --git a/packages/apps/tenant/templates/ingress.yaml b/packages/apps/tenant/templates/ingress.yaml index 6e870043..33f138e5 100644 --- a/packages/apps/tenant/templates/ingress.yaml +++ b/packages/apps/tenant/templates/ingress.yaml @@ -6,6 +6,7 @@ metadata: namespace: {{ include "tenant.name" . }} labels: cozystack.io/ui: "true" + sharding.fluxcd.io/key: tenants internal.cozystack.io/tenantmodule: "true" app.kubernetes.io/instance: {{ .Release.Name }} app.kubernetes.io/managed-by: {{ .Release.Service }} @@ -13,15 +14,10 @@ metadata: apps.cozystack.io/application.group: apps.cozystack.io apps.cozystack.io/application.name: ingress spec: - chart: - spec: - chart: ingress - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-extra - namespace: cozy-public - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-ingress-application-default-ingress + namespace: cozy-system interval: 5m timeout: 10m install: diff --git a/packages/apps/tenant/templates/monitoring.yaml b/packages/apps/tenant/templates/monitoring.yaml index 625670e9..393ef702 100644 --- a/packages/apps/tenant/templates/monitoring.yaml +++ b/packages/apps/tenant/templates/monitoring.yaml @@ -6,6 +6,7 @@ metadata: namespace: {{ include "tenant.name" . }} labels: cozystack.io/ui: "true" + sharding.fluxcd.io/key: tenants internal.cozystack.io/tenantmodule: "true" app.kubernetes.io/instance: {{ .Release.Name }} app.kubernetes.io/managed-by: {{ .Release.Service }} @@ -13,15 +14,10 @@ metadata: apps.cozystack.io/application.group: apps.cozystack.io apps.cozystack.io/application.name: monitoring spec: - chart: - spec: - chart: monitoring - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-extra - namespace: cozy-public - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-monitoring-application-default-monitoring + namespace: cozy-system interval: 5m timeout: 10m install: diff --git a/packages/apps/tenant/templates/seaweedfs.yaml b/packages/apps/tenant/templates/seaweedfs.yaml index ff0db1e4..075856fd 100644 --- a/packages/apps/tenant/templates/seaweedfs.yaml +++ b/packages/apps/tenant/templates/seaweedfs.yaml @@ -6,6 +6,7 @@ metadata: namespace: {{ include "tenant.name" . }} labels: cozystack.io/ui: "true" + sharding.fluxcd.io/key: tenants internal.cozystack.io/tenantmodule: "true" app.kubernetes.io/instance: {{ .Release.Name }} app.kubernetes.io/managed-by: {{ .Release.Service }} @@ -13,15 +14,10 @@ metadata: apps.cozystack.io/application.group: apps.cozystack.io apps.cozystack.io/application.name: seaweedfs spec: - chart: - spec: - chart: seaweedfs - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-extra - namespace: cozy-public - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-seaweedfs-application-default-seaweedfs + namespace: cozy-system interval: 5m timeout: 10m install: diff --git a/packages/core/flux-aio/Makefile b/packages/core/flux-aio/Makefile index 2b3c1408..0121d6d4 100644 --- a/packages/core/flux-aio/Makefile +++ b/packages/core/flux-aio/Makefile @@ -12,23 +12,13 @@ apply: diff: cozyhr show -n $(NAMESPACE) $(NAME) --plain | kubectl diff -f- -update: update-old update-new +MANIFESTS_DIR=../../../internal/fluxinstall/manifests -# TODO: remove old manifest after migration to cozystack-operator -update-old: - timoni bundle build -f flux-aio.cue > templates/fluxcd.yaml - yq eval '(select(.kind == "Namespace") | .metadata.labels."pod-security.kubernetes.io/enforce") = "privileged"' -i templates/fluxcd.yaml - sed -i templates/fluxcd.yaml \ +update: + timoni bundle build -f flux-aio.cue > $(MANIFESTS_DIR)/fluxcd.yaml + yq eval '(select(.kind == "Namespace") | .metadata.labels."pod-security.kubernetes.io/enforce") = "privileged"' -i $(MANIFESTS_DIR)/fluxcd.yaml + sed -i $(MANIFESTS_DIR)/fluxcd.yaml \ -e '/timoni/d' \ -e 's|\.cluster\.local\.,||g' -e 's|\.cluster\.local\,||g' -e 's|\.cluster\.local\.||g' \ - -e '/value: .svc/a \ {{- include "cozy.kubernetes_envs" . | nindent 12 }}' \ - -e '/hostNetwork: true/i \ dnsPolicy: ClusterFirstWithHostNet' - -update-new: - timoni bundle build -f flux-aio.cue > ../../../internal/fluxinstall/manifests/fluxcd.yaml - yq eval '(select(.kind == "Namespace") | .metadata.labels."pod-security.kubernetes.io/enforce") = "privileged"' -i ../../../internal/fluxinstall/manifests/fluxcd.yaml - sed -i ../../../internal/fluxinstall/manifests/fluxcd.yaml \ - -e '/timoni/d' \ - -e 's|\.cluster\.local\.,||g' -e 's|\.cluster\.local\,||g' -e 's|\.cluster\.local\.||g' - # TODO: solve dns issue with hostNetwork for installing helmreleases in tenant k8s clusters - #-e '/hostNetwork: true/i \ dnsPolicy: ClusterFirstWithHostNet' + -e 's|--storage-adv-addr=source-watcher.$$(RUNTIME_NAMESPACE).svc|--storage-adv-addr=flux.$$(RUNTIME_NAMESPACE).svc|' + yq eval '.spec.template.spec.containers[0].image = "'"$$(yq eval 'select(.kind == "Deployment" and .metadata.name == "flux") | .spec.template.spec.containers[] | select(.name == "helm-controller").image' $(MANIFESTS_DIR)/fluxcd.yaml)"'"' -i $(MANIFESTS_DIR)/fluxcd-tenants.yaml diff --git a/packages/core/flux-aio/manifests b/packages/core/flux-aio/manifests new file mode 120000 index 00000000..f9bb5aad --- /dev/null +++ b/packages/core/flux-aio/manifests @@ -0,0 +1 @@ +../../../internal/fluxinstall/manifests \ No newline at end of file diff --git a/packages/core/flux-aio/templates/_helpers.tpl b/packages/core/flux-aio/templates/_helpers.tpl deleted file mode 100644 index e22979ba..00000000 --- a/packages/core/flux-aio/templates/_helpers.tpl +++ /dev/null @@ -1,13 +0,0 @@ -{{- define "cozy.kubernetes_envs" }} -{{- $cozyDeployment := lookup "apps/v1" "Deployment" "cozy-system" "cozystack" }} -{{- $cozyContainers := dig "spec" "template" "spec" "containers" dict $cozyDeployment }} -{{- range $cozyContainers }} -{{- if eq .name "cozystack" }} -{{- range .env }} -{{- if has .name (list "KUBERNETES_SERVICE_HOST" "KUBERNETES_SERVICE_PORT") }} -- {{ toJson . }} -{{- end }} -{{- end }} -{{- end }} -{{- end }} -{{- end }} diff --git a/packages/core/flux-aio/templates/fluxcd.yaml b/packages/core/flux-aio/templates/fluxcd.yaml deleted file mode 100644 index 71a354c6..00000000 --- a/packages/core/flux-aio/templates/fluxcd.yaml +++ /dev/null @@ -1,11957 +0,0 @@ ---- -# Instance: flux ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - app.kubernetes.io/role: cluster-admin - controller-gen.kubebuilder.io/version: v0.19.0 - labels: - app.kubernetes.io/name: flux - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.7.3 - name: alerts.notification.toolkit.fluxcd.io -spec: - group: notification.toolkit.fluxcd.io - names: - kind: Alert - listKind: AlertList - plural: alerts - singular: alert - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - deprecated: true - deprecationWarning: v1beta2 Alert is deprecated, upgrade to v1beta3 - name: v1beta2 - schema: - openAPIV3Schema: - description: Alert is the Schema for the alerts API - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: AlertSpec defines an alerting rule for events involving a list of objects. - properties: - eventMetadata: - additionalProperties: - type: string - description: |- - EventMetadata is an optional field for adding metadata to events dispatched by the - controller. This can be used for enhancing the context of the event. If a field - would override one already present on the original event as generated by the emitter, - then the override doesn't happen, i.e. the original value is preserved, and an info - log is printed. - type: object - eventSeverity: - default: info - description: |- - EventSeverity specifies how to filter events based on severity. - If set to 'info' no events will be filtered. - enum: - - info - - error - type: string - eventSources: - description: |- - EventSources specifies how to filter events based - on the involved object kind, name and namespace. - items: - description: |- - CrossNamespaceObjectReference contains enough information to let you locate the - typed referenced object at cluster level - properties: - apiVersion: - description: API version of the referent - type: string - kind: - description: Kind of the referent - enum: - - Bucket - - GitRepository - - Kustomization - - HelmRelease - - HelmChart - - HelmRepository - - ImageRepository - - ImagePolicy - - ImageUpdateAutomation - - OCIRepository - type: string - matchLabels: - additionalProperties: - type: string - description: |- - MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - MatchLabels requires the name to be set to `*`. - type: object - name: - description: |- - Name of the referent - If multiple resources are targeted `*` may be set. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: Namespace of the referent - maxLength: 253 - minLength: 1 - type: string - required: - - kind - - name - type: object - type: array - exclusionList: - description: |- - ExclusionList specifies a list of Golang regular expressions - to be used for excluding messages. - items: - type: string - type: array - inclusionList: - description: |- - InclusionList specifies a list of Golang regular expressions - to be used for including messages. - items: - type: string - type: array - providerRef: - description: ProviderRef specifies which Provider this Alert should use. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - summary: - description: Summary holds a short description of the impact and affected cluster. - maxLength: 255 - type: string - suspend: - description: |- - Suspend tells the controller to suspend subsequent - events handling for this Alert. - type: boolean - required: - - eventSources - - providerRef - type: object - status: - default: - observedGeneration: -1 - description: AlertStatus defines the observed state of the Alert. - properties: - conditions: - description: Conditions holds the conditions for the Alert. - items: - description: Condition contains details for one aspect of the current state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastHandledReconcileAt: - description: |- - LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value - can be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last observed generation. - format: int64 - type: integer - type: object - type: object - served: true - storage: false - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta3 - schema: - openAPIV3Schema: - description: Alert is the Schema for the alerts API - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: AlertSpec defines an alerting rule for events involving a list of objects. - properties: - eventMetadata: - additionalProperties: - type: string - description: |- - EventMetadata is an optional field for adding metadata to events dispatched by the - controller. This can be used for enhancing the context of the event. If a field - would override one already present on the original event as generated by the emitter, - then the override doesn't happen, i.e. the original value is preserved, and an info - log is printed. - type: object - eventSeverity: - default: info - description: |- - EventSeverity specifies how to filter events based on severity. - If set to 'info' no events will be filtered. - enum: - - info - - error - type: string - eventSources: - description: |- - EventSources specifies how to filter events based - on the involved object kind, name and namespace. - items: - description: |- - CrossNamespaceObjectReference contains enough information to let you locate the - typed referenced object at cluster level - properties: - apiVersion: - description: API version of the referent - type: string - kind: - description: Kind of the referent - enum: - - Bucket - - GitRepository - - Kustomization - - HelmRelease - - HelmChart - - HelmRepository - - ImageRepository - - ImagePolicy - - ImageUpdateAutomation - - OCIRepository - type: string - matchLabels: - additionalProperties: - type: string - description: |- - MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - MatchLabels requires the name to be set to `*`. - type: object - name: - description: |- - Name of the referent - If multiple resources are targeted `*` may be set. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: Namespace of the referent - maxLength: 253 - minLength: 1 - type: string - required: - - kind - - name - type: object - type: array - exclusionList: - description: |- - ExclusionList specifies a list of Golang regular expressions - to be used for excluding messages. - items: - type: string - type: array - inclusionList: - description: |- - InclusionList specifies a list of Golang regular expressions - to be used for including messages. - items: - type: string - type: array - providerRef: - description: ProviderRef specifies which Provider this Alert should use. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - summary: - description: |- - Summary holds a short description of the impact and affected cluster. - Deprecated: Use EventMetadata instead. - maxLength: 255 - type: string - suspend: - description: |- - Suspend tells the controller to suspend subsequent - events handling for this Alert. - type: boolean - required: - - eventSources - - providerRef - type: object - type: object - served: true - storage: true - subresources: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - app.kubernetes.io/role: cluster-admin - controller-gen.kubebuilder.io/version: v0.19.0 - labels: - app.kubernetes.io/name: flux - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.7.3 - name: artifactgenerators.source.extensions.fluxcd.io -spec: - group: source.extensions.fluxcd.io - names: - kind: ArtifactGenerator - listKind: ArtifactGeneratorList - plural: artifactgenerators - shortNames: - - ag - singular: artifactgenerator - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - name: v1beta1 - schema: - openAPIV3Schema: - description: ArtifactGenerator is the Schema for the artifactgenerators API. - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: ArtifactGeneratorSpec defines the desired state of ArtifactGenerator. - properties: - artifacts: - description: OutputArtifacts is a list of output artifacts to be generated. - items: - description: |- - OutputArtifact defines the desired state of an ExternalArtifact - generated by the ArtifactGenerator. - properties: - copy: - description: |- - Copy defines a list of copy operations to perform from the sources to the generated artifact. - The copy operations are performed in the order they are listed with existing files - being overwritten by later copy operations. - items: - properties: - exclude: - description: |- - Exclude specifies a list of glob patterns to exclude - files and dirs matched by the 'From' field. - items: - type: string - maxItems: 100 - type: array - from: - description: |- - From specifies the source (by alias) and the glob pattern to match files. - The format is "@/". - maxLength: 1024 - pattern: ^@([a-z0-9]([a-z0-9_-]*[a-z0-9])?)/(.*)$ - type: string - strategy: - description: |- - Strategy specifies the copy strategy to use. - 'Overwrite' will overwrite existing files in the destination. - 'Merge' is for merging YAML files using Helm values merge strategy. - If not specified, defaults to 'Overwrite'. - enum: - - Overwrite - - Merge - type: string - to: - description: |- - To specifies the destination path within the artifact. - The format is "@artifact/path", the alias "artifact" - refers to the root path of the generated artifact. - maxLength: 1024 - pattern: ^@(artifact)/(.*)$ - type: string - required: - - from - - to - type: object - minItems: 1 - type: array - name: - description: Name is the name of the generated artifact. - maxLength: 253 - pattern: ^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ - type: string - originRevision: - description: |- - OriginRevision is used to set the 'org.opencontainers.image.revision' - annotation on the generated artifact metadata. - If specified, it must point to an existing source alias in the format "@". - If the referenced source has an origin revision (e.g. a Git commit SHA), - it will be used to set the annotation on the generated artifact. - If the referenced source does not have an origin revision, the field is ignored. - maxLength: 64 - pattern: ^@([a-z0-9]([a-z0-9_-]*[a-z0-9])?)$ - type: string - revision: - description: |- - Revision is the revision of the generated artifact. - If specified, it must point to an existing source alias in the format "@". - If not specified, the revision is automatically set to the digest of the artifact content. - maxLength: 64 - pattern: ^@([a-z0-9]([a-z0-9_-]*[a-z0-9])?)$ - type: string - required: - - copy - - name - type: object - maxItems: 1000 - minItems: 1 - type: array - sources: - description: |- - Sources is a list of references to the Flux source-controller - resources that will be used to generate the artifact. - items: - description: SourceReference contains the reference to a Flux source-controller resource. - properties: - alias: - description: |- - Alias of the source within the ArtifactGenerator context. - The alias must be unique per ArtifactGenerator, and must consist - of lower case alphanumeric characters, underscores, and hyphens. - It must start and end with an alphanumeric character. - maxLength: 63 - pattern: ^[a-z0-9]([a-z0-9_-]*[a-z0-9])?$ - type: string - kind: - description: Kind of the source. - enum: - - Bucket - - GitRepository - - OCIRepository - type: string - name: - description: Name of the source. - maxLength: 253 - pattern: ^[a-z0-9]([a-z0-9-]*[a-z0-9])?$ - type: string - namespace: - description: |- - Namespace of the source. - If not provided, defaults to the same namespace as the ArtifactGenerator. - maxLength: 63 - minLength: 1 - pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$ - type: string - required: - - alias - - kind - - name - type: object - maxItems: 1000 - minItems: 1 - type: array - required: - - artifacts - - sources - type: object - status: - description: ArtifactGeneratorStatus defines the observed state of ArtifactGenerator. - properties: - conditions: - description: Conditions holds the conditions for the ArtifactGenerator. - items: - description: Condition contains details for one aspect of the current state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - inventory: - description: Inventory contains the list of generated ExternalArtifact references. - items: - description: |- - ExternalArtifactReference contains the reference to a - generated ExternalArtifact along with its digest. - properties: - digest: - description: Digest of the referent artifact. - type: string - filename: - description: Filename is the name of the artifact file. - type: string - name: - description: Name of the referent artifact. - type: string - namespace: - description: Namespace of the referent artifact. - type: string - required: - - digest - - filename - - name - - namespace - type: object - type: array - lastHandledReconcileAt: - description: |- - LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value - can be detected. - type: string - observedSourcesDigest: - description: |- - ObservedSourcesDigest is a hash representing the current state of - all the sources referenced by the ArtifactGenerator. - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - app.kubernetes.io/role: cluster-admin - controller-gen.kubebuilder.io/version: v0.19.0 - labels: - app.kubernetes.io/name: flux - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.7.3 - name: buckets.source.toolkit.fluxcd.io -spec: - group: source.toolkit.fluxcd.io - names: - kind: Bucket - listKind: BucketList - plural: buckets - singular: bucket - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.endpoint - name: Endpoint - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - name: v1 - schema: - openAPIV3Schema: - description: Bucket is the Schema for the buckets API. - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: |- - BucketSpec specifies the required configuration to produce an Artifact for - an object storage bucket. - properties: - bucketName: - description: BucketName is the name of the object storage bucket. - type: string - certSecretRef: - description: |- - CertSecretRef can be given the name of a Secret containing - either or both of - - - a PEM-encoded client certificate (`tls.crt`) and private - key (`tls.key`); - - a PEM-encoded CA certificate (`ca.crt`) - - and whichever are supplied, will be used for connecting to the - bucket. The client cert and key are useful if you are - authenticating with a certificate; the CA cert is useful if - you are using a self-signed server certificate. The Secret must - be of type `Opaque` or `kubernetes.io/tls`. - - This field is only supported for the `generic` provider. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - endpoint: - description: Endpoint is the object storage address the BucketName is located at. - type: string - ignore: - description: |- - Ignore overrides the set of excluded patterns in the .sourceignore format - (which is the same as .gitignore). If not provided, a default will be used, - consult the documentation for your version to find out what those are. - type: string - insecure: - description: Insecure allows connecting to a non-TLS HTTP Endpoint. - type: boolean - interval: - description: |- - Interval at which the Bucket Endpoint is checked for updates. - This interval is approximate and may be subject to jitter to ensure - efficient use of resources. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - prefix: - description: Prefix to use for server-side filtering of files in the Bucket. - type: string - provider: - default: generic - description: |- - Provider of the object storage bucket. - Defaults to 'generic', which expects an S3 (API) compatible object - storage. - enum: - - generic - - aws - - gcp - - azure - type: string - proxySecretRef: - description: |- - ProxySecretRef specifies the Secret containing the proxy configuration - to use while communicating with the Bucket server. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - region: - description: Region of the Endpoint where the BucketName is located in. - type: string - secretRef: - description: |- - SecretRef specifies the Secret containing authentication credentials - for the Bucket. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - serviceAccountName: - description: |- - ServiceAccountName is the name of the Kubernetes ServiceAccount used to authenticate - the bucket. This field is only supported for the 'gcp' and 'aws' providers. - For more information about workload identity: - https://fluxcd.io/flux/components/source/buckets/#workload-identity - type: string - sts: - description: |- - STS specifies the required configuration to use a Security Token - Service for fetching temporary credentials to authenticate in a - Bucket provider. - - This field is only supported for the `aws` and `generic` providers. - properties: - certSecretRef: - description: |- - CertSecretRef can be given the name of a Secret containing - either or both of - - - a PEM-encoded client certificate (`tls.crt`) and private - key (`tls.key`); - - a PEM-encoded CA certificate (`ca.crt`) - - and whichever are supplied, will be used for connecting to the - STS endpoint. The client cert and key are useful if you are - authenticating with a certificate; the CA cert is useful if - you are using a self-signed server certificate. The Secret must - be of type `Opaque` or `kubernetes.io/tls`. - - This field is only supported for the `ldap` provider. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - endpoint: - description: |- - Endpoint is the HTTP/S endpoint of the Security Token Service from - where temporary credentials will be fetched. - pattern: ^(http|https)://.*$ - type: string - provider: - description: Provider of the Security Token Service. - enum: - - aws - - ldap - type: string - secretRef: - description: |- - SecretRef specifies the Secret containing authentication credentials - for the STS endpoint. This Secret must contain the fields `username` - and `password` and is supported only for the `ldap` provider. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - required: - - endpoint - - provider - type: object - suspend: - description: |- - Suspend tells the controller to suspend the reconciliation of this - Bucket. - type: boolean - timeout: - default: 60s - description: Timeout for fetch operations, defaults to 60s. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m))+$ - type: string - required: - - bucketName - - endpoint - - interval - type: object - x-kubernetes-validations: - - message: STS configuration is only supported for the 'aws' and 'generic' Bucket providers - rule: self.provider == 'aws' || self.provider == 'generic' || !has(self.sts) - - message: '''aws'' is the only supported STS provider for the ''aws'' Bucket provider' - rule: self.provider != 'aws' || !has(self.sts) || self.sts.provider == 'aws' - - message: '''ldap'' is the only supported STS provider for the ''generic'' Bucket provider' - rule: self.provider != 'generic' || !has(self.sts) || self.sts.provider == 'ldap' - - message: spec.sts.secretRef is not required for the 'aws' STS provider - rule: '!has(self.sts) || self.sts.provider != ''aws'' || !has(self.sts.secretRef)' - - message: spec.sts.certSecretRef is not required for the 'aws' STS provider - rule: '!has(self.sts) || self.sts.provider != ''aws'' || !has(self.sts.certSecretRef)' - - message: ServiceAccountName is not supported for the 'generic' Bucket provider - rule: self.provider != 'generic' || !has(self.serviceAccountName) - - message: cannot set both .spec.secretRef and .spec.serviceAccountName - rule: '!has(self.secretRef) || !has(self.serviceAccountName)' - status: - default: - observedGeneration: -1 - description: BucketStatus records the observed state of a Bucket. - properties: - artifact: - description: Artifact represents the last successful Bucket reconciliation. - properties: - digest: - description: Digest is the digest of the file in the form of ':'. - pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$ - type: string - lastUpdateTime: - description: |- - LastUpdateTime is the timestamp corresponding to the last update of the - Artifact. - format: date-time - type: string - metadata: - additionalProperties: - type: string - description: Metadata holds upstream information such as OCI annotations. - type: object - path: - description: |- - Path is the relative file path of the Artifact. It can be used to locate - the file in the root of the Artifact storage on the local file system of - the controller managing the Source. - type: string - revision: - description: |- - Revision is a human-readable identifier traceable in the origin source - system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. - type: string - size: - description: Size is the number of bytes in the file. - format: int64 - type: integer - url: - description: |- - URL is the HTTP address of the Artifact as exposed by the controller - managing the Source. It can be used to retrieve the Artifact for - consumption, e.g. by another controller applying the Artifact contents. - type: string - required: - - digest - - lastUpdateTime - - path - - revision - - url - type: object - conditions: - description: Conditions holds the conditions for the Bucket. - items: - description: Condition contains details for one aspect of the current state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastHandledReconcileAt: - description: |- - LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value - can be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last observed generation of the Bucket object. - format: int64 - type: integer - observedIgnore: - description: |- - ObservedIgnore is the observed exclusion patterns used for constructing - the source artifact. - type: string - url: - description: |- - URL is the dynamic fetch link for the latest Artifact. - It is provided on a "best effort" basis, and using the precise - BucketStatus.Artifact data is recommended. - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .spec.endpoint - name: Endpoint - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - deprecated: true - deprecationWarning: v1beta2 Bucket is deprecated, upgrade to v1 - name: v1beta2 - schema: - openAPIV3Schema: - description: Bucket is the Schema for the buckets API. - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: |- - BucketSpec specifies the required configuration to produce an Artifact for - an object storage bucket. - properties: - accessFrom: - description: |- - AccessFrom specifies an Access Control List for allowing cross-namespace - references to this object. - NOTE: Not implemented, provisional as of https://github.com/fluxcd/flux2/pull/2092 - properties: - namespaceSelectors: - description: |- - NamespaceSelectors is the list of namespace selectors to which this ACL applies. - Items in this list are evaluated using a logical OR operation. - items: - description: |- - NamespaceSelector selects the namespaces to which this ACL applies. - An empty map of MatchLabels matches all namespaces in a cluster. - properties: - matchLabels: - additionalProperties: - type: string - description: |- - MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - type: array - required: - - namespaceSelectors - type: object - bucketName: - description: BucketName is the name of the object storage bucket. - type: string - certSecretRef: - description: |- - CertSecretRef can be given the name of a Secret containing - either or both of - - - a PEM-encoded client certificate (`tls.crt`) and private - key (`tls.key`); - - a PEM-encoded CA certificate (`ca.crt`) - - and whichever are supplied, will be used for connecting to the - bucket. The client cert and key are useful if you are - authenticating with a certificate; the CA cert is useful if - you are using a self-signed server certificate. The Secret must - be of type `Opaque` or `kubernetes.io/tls`. - - This field is only supported for the `generic` provider. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - endpoint: - description: Endpoint is the object storage address the BucketName is located at. - type: string - ignore: - description: |- - Ignore overrides the set of excluded patterns in the .sourceignore format - (which is the same as .gitignore). If not provided, a default will be used, - consult the documentation for your version to find out what those are. - type: string - insecure: - description: Insecure allows connecting to a non-TLS HTTP Endpoint. - type: boolean - interval: - description: |- - Interval at which the Bucket Endpoint is checked for updates. - This interval is approximate and may be subject to jitter to ensure - efficient use of resources. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - prefix: - description: Prefix to use for server-side filtering of files in the Bucket. - type: string - provider: - default: generic - description: |- - Provider of the object storage bucket. - Defaults to 'generic', which expects an S3 (API) compatible object - storage. - enum: - - generic - - aws - - gcp - - azure - type: string - proxySecretRef: - description: |- - ProxySecretRef specifies the Secret containing the proxy configuration - to use while communicating with the Bucket server. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - region: - description: Region of the Endpoint where the BucketName is located in. - type: string - secretRef: - description: |- - SecretRef specifies the Secret containing authentication credentials - for the Bucket. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - sts: - description: |- - STS specifies the required configuration to use a Security Token - Service for fetching temporary credentials to authenticate in a - Bucket provider. - - This field is only supported for the `aws` and `generic` providers. - properties: - certSecretRef: - description: |- - CertSecretRef can be given the name of a Secret containing - either or both of - - - a PEM-encoded client certificate (`tls.crt`) and private - key (`tls.key`); - - a PEM-encoded CA certificate (`ca.crt`) - - and whichever are supplied, will be used for connecting to the - STS endpoint. The client cert and key are useful if you are - authenticating with a certificate; the CA cert is useful if - you are using a self-signed server certificate. The Secret must - be of type `Opaque` or `kubernetes.io/tls`. - - This field is only supported for the `ldap` provider. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - endpoint: - description: |- - Endpoint is the HTTP/S endpoint of the Security Token Service from - where temporary credentials will be fetched. - pattern: ^(http|https)://.*$ - type: string - provider: - description: Provider of the Security Token Service. - enum: - - aws - - ldap - type: string - secretRef: - description: |- - SecretRef specifies the Secret containing authentication credentials - for the STS endpoint. This Secret must contain the fields `username` - and `password` and is supported only for the `ldap` provider. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - required: - - endpoint - - provider - type: object - suspend: - description: |- - Suspend tells the controller to suspend the reconciliation of this - Bucket. - type: boolean - timeout: - default: 60s - description: Timeout for fetch operations, defaults to 60s. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m))+$ - type: string - required: - - bucketName - - endpoint - - interval - type: object - x-kubernetes-validations: - - message: STS configuration is only supported for the 'aws' and 'generic' Bucket providers - rule: self.provider == 'aws' || self.provider == 'generic' || !has(self.sts) - - message: '''aws'' is the only supported STS provider for the ''aws'' Bucket provider' - rule: self.provider != 'aws' || !has(self.sts) || self.sts.provider == 'aws' - - message: '''ldap'' is the only supported STS provider for the ''generic'' Bucket provider' - rule: self.provider != 'generic' || !has(self.sts) || self.sts.provider == 'ldap' - - message: spec.sts.secretRef is not required for the 'aws' STS provider - rule: '!has(self.sts) || self.sts.provider != ''aws'' || !has(self.sts.secretRef)' - - message: spec.sts.certSecretRef is not required for the 'aws' STS provider - rule: '!has(self.sts) || self.sts.provider != ''aws'' || !has(self.sts.certSecretRef)' - status: - default: - observedGeneration: -1 - description: BucketStatus records the observed state of a Bucket. - properties: - artifact: - description: Artifact represents the last successful Bucket reconciliation. - properties: - digest: - description: Digest is the digest of the file in the form of ':'. - pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$ - type: string - lastUpdateTime: - description: |- - LastUpdateTime is the timestamp corresponding to the last update of the - Artifact. - format: date-time - type: string - metadata: - additionalProperties: - type: string - description: Metadata holds upstream information such as OCI annotations. - type: object - path: - description: |- - Path is the relative file path of the Artifact. It can be used to locate - the file in the root of the Artifact storage on the local file system of - the controller managing the Source. - type: string - revision: - description: |- - Revision is a human-readable identifier traceable in the origin source - system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. - type: string - size: - description: Size is the number of bytes in the file. - format: int64 - type: integer - url: - description: |- - URL is the HTTP address of the Artifact as exposed by the controller - managing the Source. It can be used to retrieve the Artifact for - consumption, e.g. by another controller applying the Artifact contents. - type: string - required: - - digest - - lastUpdateTime - - path - - revision - - url - type: object - conditions: - description: Conditions holds the conditions for the Bucket. - items: - description: Condition contains details for one aspect of the current state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastHandledReconcileAt: - description: |- - LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value - can be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last observed generation of the Bucket object. - format: int64 - type: integer - observedIgnore: - description: |- - ObservedIgnore is the observed exclusion patterns used for constructing - the source artifact. - type: string - url: - description: |- - URL is the dynamic fetch link for the latest Artifact. - It is provided on a "best effort" basis, and using the precise - BucketStatus.Artifact data is recommended. - type: string - type: object - type: object - served: true - storage: false - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - app.kubernetes.io/role: cluster-admin - controller-gen.kubebuilder.io/version: v0.19.0 - labels: - app.kubernetes.io/name: flux - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.7.3 - name: externalartifacts.source.toolkit.fluxcd.io -spec: - group: source.toolkit.fluxcd.io - names: - kind: ExternalArtifact - listKind: ExternalArtifactList - plural: externalartifacts - singular: externalartifact - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - - jsonPath: .spec.sourceRef.name - name: Source - type: string - name: v1 - schema: - openAPIV3Schema: - description: ExternalArtifact is the Schema for the external artifacts API - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: ExternalArtifactSpec defines the desired state of ExternalArtifact - properties: - sourceRef: - description: |- - SourceRef points to the Kubernetes custom resource for - which the artifact is generated. - properties: - apiVersion: - description: API version of the referent, if not specified the Kubernetes preferred version will be used. - type: string - kind: - description: Kind of the referent. - type: string - name: - description: Name of the referent. - type: string - namespace: - description: Namespace of the referent, when not specified it acts as LocalObjectReference. - type: string - required: - - kind - - name - type: object - type: object - status: - description: ExternalArtifactStatus defines the observed state of ExternalArtifact - properties: - artifact: - description: Artifact represents the output of an ExternalArtifact reconciliation. - properties: - digest: - description: Digest is the digest of the file in the form of ':'. - pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$ - type: string - lastUpdateTime: - description: |- - LastUpdateTime is the timestamp corresponding to the last update of the - Artifact. - format: date-time - type: string - metadata: - additionalProperties: - type: string - description: Metadata holds upstream information such as OCI annotations. - type: object - path: - description: |- - Path is the relative file path of the Artifact. It can be used to locate - the file in the root of the Artifact storage on the local file system of - the controller managing the Source. - type: string - revision: - description: |- - Revision is a human-readable identifier traceable in the origin source - system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. - type: string - size: - description: Size is the number of bytes in the file. - format: int64 - type: integer - url: - description: |- - URL is the HTTP address of the Artifact as exposed by the controller - managing the Source. It can be used to retrieve the Artifact for - consumption, e.g. by another controller applying the Artifact contents. - type: string - required: - - digest - - lastUpdateTime - - path - - revision - - url - type: object - conditions: - description: Conditions holds the conditions for the ExternalArtifact. - items: - description: Condition contains details for one aspect of the current state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - type: object - type: object - served: true - storage: true - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - app.kubernetes.io/role: cluster-admin - controller-gen.kubebuilder.io/version: v0.19.0 - labels: - app.kubernetes.io/name: flux - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.7.3 - name: gitrepositories.source.toolkit.fluxcd.io -spec: - group: source.toolkit.fluxcd.io - names: - kind: GitRepository - listKind: GitRepositoryList - plural: gitrepositories - shortNames: - - gitrepo - singular: gitrepository - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.url - name: URL - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - name: v1 - schema: - openAPIV3Schema: - description: GitRepository is the Schema for the gitrepositories API. - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: |- - GitRepositorySpec specifies the required configuration to produce an - Artifact for a Git repository. - properties: - ignore: - description: |- - Ignore overrides the set of excluded patterns in the .sourceignore format - (which is the same as .gitignore). If not provided, a default will be used, - consult the documentation for your version to find out what those are. - type: string - include: - description: |- - Include specifies a list of GitRepository resources which Artifacts - should be included in the Artifact produced for this GitRepository. - items: - description: |- - GitRepositoryInclude specifies a local reference to a GitRepository which - Artifact (sub-)contents must be included, and where they should be placed. - properties: - fromPath: - description: |- - FromPath specifies the path to copy contents from, defaults to the root - of the Artifact. - type: string - repository: - description: |- - GitRepositoryRef specifies the GitRepository which Artifact contents - must be included. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - toPath: - description: |- - ToPath specifies the path to copy contents to, defaults to the name of - the GitRepositoryRef. - type: string - required: - - repository - type: object - type: array - interval: - description: |- - Interval at which the GitRepository URL is checked for updates. - This interval is approximate and may be subject to jitter to ensure - efficient use of resources. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - provider: - description: |- - Provider used for authentication, can be 'azure', 'github', 'generic'. - When not specified, defaults to 'generic'. - enum: - - generic - - azure - - github - type: string - proxySecretRef: - description: |- - ProxySecretRef specifies the Secret containing the proxy configuration - to use while communicating with the Git server. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - recurseSubmodules: - description: |- - RecurseSubmodules enables the initialization of all submodules within - the GitRepository as cloned from the URL, using their default settings. - type: boolean - ref: - description: |- - Reference specifies the Git reference to resolve and monitor for - changes, defaults to the 'master' branch. - properties: - branch: - description: Branch to check out, defaults to 'master' if no other field is defined. - type: string - commit: - description: |- - Commit SHA to check out, takes precedence over all reference fields. - - This can be combined with Branch to shallow clone the branch, in which - the commit is expected to exist. - type: string - name: - description: |- - Name of the reference to check out; takes precedence over Branch, Tag and SemVer. - - It must be a valid Git reference: https://git-scm.com/docs/git-check-ref-format#_description - Examples: "refs/heads/main", "refs/tags/v0.1.0", "refs/pull/420/head", "refs/merge-requests/1/head" - type: string - semver: - description: SemVer tag expression to check out, takes precedence over Tag. - type: string - tag: - description: Tag to check out, takes precedence over Branch. - type: string - type: object - secretRef: - description: |- - SecretRef specifies the Secret containing authentication credentials for - the GitRepository. - For HTTPS repositories the Secret must contain 'username' and 'password' - fields for basic auth or 'bearerToken' field for token auth. - For SSH repositories the Secret must contain 'identity' - and 'known_hosts' fields. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - serviceAccountName: - description: |- - ServiceAccountName is the name of the Kubernetes ServiceAccount used to - authenticate to the GitRepository. This field is only supported for 'azure' provider. - type: string - sparseCheckout: - description: |- - SparseCheckout specifies a list of directories to checkout when cloning - the repository. If specified, only these directories are included in the - Artifact produced for this GitRepository. - items: - type: string - type: array - suspend: - description: |- - Suspend tells the controller to suspend the reconciliation of this - GitRepository. - type: boolean - timeout: - default: 60s - description: Timeout for Git operations like cloning, defaults to 60s. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m))+$ - type: string - url: - description: URL specifies the Git repository URL, it can be an HTTP/S or SSH address. - pattern: ^(http|https|ssh)://.*$ - type: string - verify: - description: |- - Verification specifies the configuration to verify the Git commit - signature(s). - properties: - mode: - default: HEAD - description: |- - Mode specifies which Git object(s) should be verified. - - The variants "head" and "HEAD" both imply the same thing, i.e. verify - the commit that the HEAD of the Git repository points to. The variant - "head" solely exists to ensure backwards compatibility. - enum: - - head - - HEAD - - Tag - - TagAndHEAD - type: string - secretRef: - description: |- - SecretRef specifies the Secret containing the public keys of trusted Git - authors. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - required: - - secretRef - type: object - required: - - interval - - url - type: object - x-kubernetes-validations: - - message: serviceAccountName can only be set when provider is 'azure' - rule: '!has(self.serviceAccountName) || (has(self.provider) && self.provider == ''azure'')' - status: - default: - observedGeneration: -1 - description: GitRepositoryStatus records the observed state of a Git repository. - properties: - artifact: - description: Artifact represents the last successful GitRepository reconciliation. - properties: - digest: - description: Digest is the digest of the file in the form of ':'. - pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$ - type: string - lastUpdateTime: - description: |- - LastUpdateTime is the timestamp corresponding to the last update of the - Artifact. - format: date-time - type: string - metadata: - additionalProperties: - type: string - description: Metadata holds upstream information such as OCI annotations. - type: object - path: - description: |- - Path is the relative file path of the Artifact. It can be used to locate - the file in the root of the Artifact storage on the local file system of - the controller managing the Source. - type: string - revision: - description: |- - Revision is a human-readable identifier traceable in the origin source - system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. - type: string - size: - description: Size is the number of bytes in the file. - format: int64 - type: integer - url: - description: |- - URL is the HTTP address of the Artifact as exposed by the controller - managing the Source. It can be used to retrieve the Artifact for - consumption, e.g. by another controller applying the Artifact contents. - type: string - required: - - digest - - lastUpdateTime - - path - - revision - - url - type: object - conditions: - description: Conditions holds the conditions for the GitRepository. - items: - description: Condition contains details for one aspect of the current state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - includedArtifacts: - description: |- - IncludedArtifacts contains a list of the last successfully included - Artifacts as instructed by GitRepositorySpec.Include. - items: - description: Artifact represents the output of a Source reconciliation. - properties: - digest: - description: Digest is the digest of the file in the form of ':'. - pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$ - type: string - lastUpdateTime: - description: |- - LastUpdateTime is the timestamp corresponding to the last update of the - Artifact. - format: date-time - type: string - metadata: - additionalProperties: - type: string - description: Metadata holds upstream information such as OCI annotations. - type: object - path: - description: |- - Path is the relative file path of the Artifact. It can be used to locate - the file in the root of the Artifact storage on the local file system of - the controller managing the Source. - type: string - revision: - description: |- - Revision is a human-readable identifier traceable in the origin source - system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. - type: string - size: - description: Size is the number of bytes in the file. - format: int64 - type: integer - url: - description: |- - URL is the HTTP address of the Artifact as exposed by the controller - managing the Source. It can be used to retrieve the Artifact for - consumption, e.g. by another controller applying the Artifact contents. - type: string - required: - - digest - - lastUpdateTime - - path - - revision - - url - type: object - type: array - lastHandledReconcileAt: - description: |- - LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value - can be detected. - type: string - observedGeneration: - description: |- - ObservedGeneration is the last observed generation of the GitRepository - object. - format: int64 - type: integer - observedIgnore: - description: |- - ObservedIgnore is the observed exclusion patterns used for constructing - the source artifact. - type: string - observedInclude: - description: |- - ObservedInclude is the observed list of GitRepository resources used to - produce the current Artifact. - items: - description: |- - GitRepositoryInclude specifies a local reference to a GitRepository which - Artifact (sub-)contents must be included, and where they should be placed. - properties: - fromPath: - description: |- - FromPath specifies the path to copy contents from, defaults to the root - of the Artifact. - type: string - repository: - description: |- - GitRepositoryRef specifies the GitRepository which Artifact contents - must be included. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - toPath: - description: |- - ToPath specifies the path to copy contents to, defaults to the name of - the GitRepositoryRef. - type: string - required: - - repository - type: object - type: array - observedRecurseSubmodules: - description: |- - ObservedRecurseSubmodules is the observed resource submodules - configuration used to produce the current Artifact. - type: boolean - observedSparseCheckout: - description: |- - ObservedSparseCheckout is the observed list of directories used to - produce the current Artifact. - items: - type: string - type: array - sourceVerificationMode: - description: |- - SourceVerificationMode is the last used verification mode indicating - which Git object(s) have been verified. - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .spec.url - name: URL - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - deprecated: true - deprecationWarning: v1beta2 GitRepository is deprecated, upgrade to v1 - name: v1beta2 - schema: - openAPIV3Schema: - description: GitRepository is the Schema for the gitrepositories API. - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: |- - GitRepositorySpec specifies the required configuration to produce an - Artifact for a Git repository. - properties: - accessFrom: - description: |- - AccessFrom specifies an Access Control List for allowing cross-namespace - references to this object. - NOTE: Not implemented, provisional as of https://github.com/fluxcd/flux2/pull/2092 - properties: - namespaceSelectors: - description: |- - NamespaceSelectors is the list of namespace selectors to which this ACL applies. - Items in this list are evaluated using a logical OR operation. - items: - description: |- - NamespaceSelector selects the namespaces to which this ACL applies. - An empty map of MatchLabels matches all namespaces in a cluster. - properties: - matchLabels: - additionalProperties: - type: string - description: |- - MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - type: array - required: - - namespaceSelectors - type: object - gitImplementation: - default: go-git - description: |- - GitImplementation specifies which Git client library implementation to - use. Defaults to 'go-git', valid values are ('go-git', 'libgit2'). - Deprecated: gitImplementation is deprecated now that 'go-git' is the - only supported implementation. - enum: - - go-git - - libgit2 - type: string - ignore: - description: |- - Ignore overrides the set of excluded patterns in the .sourceignore format - (which is the same as .gitignore). If not provided, a default will be used, - consult the documentation for your version to find out what those are. - type: string - include: - description: |- - Include specifies a list of GitRepository resources which Artifacts - should be included in the Artifact produced for this GitRepository. - items: - description: |- - GitRepositoryInclude specifies a local reference to a GitRepository which - Artifact (sub-)contents must be included, and where they should be placed. - properties: - fromPath: - description: |- - FromPath specifies the path to copy contents from, defaults to the root - of the Artifact. - type: string - repository: - description: |- - GitRepositoryRef specifies the GitRepository which Artifact contents - must be included. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - toPath: - description: |- - ToPath specifies the path to copy contents to, defaults to the name of - the GitRepositoryRef. - type: string - required: - - repository - type: object - type: array - interval: - description: Interval at which to check the GitRepository for updates. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - recurseSubmodules: - description: |- - RecurseSubmodules enables the initialization of all submodules within - the GitRepository as cloned from the URL, using their default settings. - type: boolean - ref: - description: |- - Reference specifies the Git reference to resolve and monitor for - changes, defaults to the 'master' branch. - properties: - branch: - description: Branch to check out, defaults to 'master' if no other field is defined. - type: string - commit: - description: |- - Commit SHA to check out, takes precedence over all reference fields. - - This can be combined with Branch to shallow clone the branch, in which - the commit is expected to exist. - type: string - name: - description: |- - Name of the reference to check out; takes precedence over Branch, Tag and SemVer. - - It must be a valid Git reference: https://git-scm.com/docs/git-check-ref-format#_description - Examples: "refs/heads/main", "refs/tags/v0.1.0", "refs/pull/420/head", "refs/merge-requests/1/head" - type: string - semver: - description: SemVer tag expression to check out, takes precedence over Tag. - type: string - tag: - description: Tag to check out, takes precedence over Branch. - type: string - type: object - secretRef: - description: |- - SecretRef specifies the Secret containing authentication credentials for - the GitRepository. - For HTTPS repositories the Secret must contain 'username' and 'password' - fields for basic auth or 'bearerToken' field for token auth. - For SSH repositories the Secret must contain 'identity' - and 'known_hosts' fields. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - suspend: - description: |- - Suspend tells the controller to suspend the reconciliation of this - GitRepository. - type: boolean - timeout: - default: 60s - description: Timeout for Git operations like cloning, defaults to 60s. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m))+$ - type: string - url: - description: URL specifies the Git repository URL, it can be an HTTP/S or SSH address. - pattern: ^(http|https|ssh)://.*$ - type: string - verify: - description: |- - Verification specifies the configuration to verify the Git commit - signature(s). - properties: - mode: - description: Mode specifies what Git object should be verified, currently ('head'). - enum: - - head - type: string - secretRef: - description: |- - SecretRef specifies the Secret containing the public keys of trusted Git - authors. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - required: - - mode - - secretRef - type: object - required: - - interval - - url - type: object - status: - default: - observedGeneration: -1 - description: GitRepositoryStatus records the observed state of a Git repository. - properties: - artifact: - description: Artifact represents the last successful GitRepository reconciliation. - properties: - digest: - description: Digest is the digest of the file in the form of ':'. - pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$ - type: string - lastUpdateTime: - description: |- - LastUpdateTime is the timestamp corresponding to the last update of the - Artifact. - format: date-time - type: string - metadata: - additionalProperties: - type: string - description: Metadata holds upstream information such as OCI annotations. - type: object - path: - description: |- - Path is the relative file path of the Artifact. It can be used to locate - the file in the root of the Artifact storage on the local file system of - the controller managing the Source. - type: string - revision: - description: |- - Revision is a human-readable identifier traceable in the origin source - system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. - type: string - size: - description: Size is the number of bytes in the file. - format: int64 - type: integer - url: - description: |- - URL is the HTTP address of the Artifact as exposed by the controller - managing the Source. It can be used to retrieve the Artifact for - consumption, e.g. by another controller applying the Artifact contents. - type: string - required: - - digest - - lastUpdateTime - - path - - revision - - url - type: object - conditions: - description: Conditions holds the conditions for the GitRepository. - items: - description: Condition contains details for one aspect of the current state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - contentConfigChecksum: - description: |- - ContentConfigChecksum is a checksum of all the configurations related to - the content of the source artifact: - - .spec.ignore - - .spec.recurseSubmodules - - .spec.included and the checksum of the included artifacts - observed in .status.observedGeneration version of the object. This can - be used to determine if the content of the included repository has - changed. - It has the format of `:`, for example: `sha256:`. - - Deprecated: Replaced with explicit fields for observed artifact content - config in the status. - type: string - includedArtifacts: - description: |- - IncludedArtifacts contains a list of the last successfully included - Artifacts as instructed by GitRepositorySpec.Include. - items: - description: Artifact represents the output of a Source reconciliation. - properties: - digest: - description: Digest is the digest of the file in the form of ':'. - pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$ - type: string - lastUpdateTime: - description: |- - LastUpdateTime is the timestamp corresponding to the last update of the - Artifact. - format: date-time - type: string - metadata: - additionalProperties: - type: string - description: Metadata holds upstream information such as OCI annotations. - type: object - path: - description: |- - Path is the relative file path of the Artifact. It can be used to locate - the file in the root of the Artifact storage on the local file system of - the controller managing the Source. - type: string - revision: - description: |- - Revision is a human-readable identifier traceable in the origin source - system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. - type: string - size: - description: Size is the number of bytes in the file. - format: int64 - type: integer - url: - description: |- - URL is the HTTP address of the Artifact as exposed by the controller - managing the Source. It can be used to retrieve the Artifact for - consumption, e.g. by another controller applying the Artifact contents. - type: string - required: - - digest - - lastUpdateTime - - path - - revision - - url - type: object - type: array - lastHandledReconcileAt: - description: |- - LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value - can be detected. - type: string - observedGeneration: - description: |- - ObservedGeneration is the last observed generation of the GitRepository - object. - format: int64 - type: integer - observedIgnore: - description: |- - ObservedIgnore is the observed exclusion patterns used for constructing - the source artifact. - type: string - observedInclude: - description: |- - ObservedInclude is the observed list of GitRepository resources used to - to produce the current Artifact. - items: - description: |- - GitRepositoryInclude specifies a local reference to a GitRepository which - Artifact (sub-)contents must be included, and where they should be placed. - properties: - fromPath: - description: |- - FromPath specifies the path to copy contents from, defaults to the root - of the Artifact. - type: string - repository: - description: |- - GitRepositoryRef specifies the GitRepository which Artifact contents - must be included. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - toPath: - description: |- - ToPath specifies the path to copy contents to, defaults to the name of - the GitRepositoryRef. - type: string - required: - - repository - type: object - type: array - observedRecurseSubmodules: - description: |- - ObservedRecurseSubmodules is the observed resource submodules - configuration used to produce the current Artifact. - type: boolean - url: - description: |- - URL is the dynamic fetch link for the latest Artifact. - It is provided on a "best effort" basis, and using the precise - GitRepositoryStatus.Artifact data is recommended. - type: string - type: object - type: object - served: true - storage: false - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - app.kubernetes.io/role: cluster-admin - controller-gen.kubebuilder.io/version: v0.19.0 - labels: - app.kubernetes.io/name: flux - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.7.3 - name: helmcharts.source.toolkit.fluxcd.io -spec: - group: source.toolkit.fluxcd.io - names: - kind: HelmChart - listKind: HelmChartList - plural: helmcharts - shortNames: - - hc - singular: helmchart - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.chart - name: Chart - type: string - - jsonPath: .spec.version - name: Version - type: string - - jsonPath: .spec.sourceRef.kind - name: Source Kind - type: string - - jsonPath: .spec.sourceRef.name - name: Source Name - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - name: v1 - schema: - openAPIV3Schema: - description: HelmChart is the Schema for the helmcharts API. - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: HelmChartSpec specifies the desired state of a Helm chart. - properties: - chart: - description: |- - Chart is the name or path the Helm chart is available at in the - SourceRef. - type: string - ignoreMissingValuesFiles: - description: |- - IgnoreMissingValuesFiles controls whether to silently ignore missing values - files rather than failing. - type: boolean - interval: - description: |- - Interval at which the HelmChart SourceRef is checked for updates. - This interval is approximate and may be subject to jitter to ensure - efficient use of resources. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - reconcileStrategy: - default: ChartVersion - description: |- - ReconcileStrategy determines what enables the creation of a new artifact. - Valid values are ('ChartVersion', 'Revision'). - See the documentation of the values for an explanation on their behavior. - Defaults to ChartVersion when omitted. - enum: - - ChartVersion - - Revision - type: string - sourceRef: - description: SourceRef is the reference to the Source the chart is available at. - properties: - apiVersion: - description: APIVersion of the referent. - type: string - kind: - description: |- - Kind of the referent, valid values are ('HelmRepository', 'GitRepository', - 'Bucket'). - enum: - - HelmRepository - - GitRepository - - Bucket - type: string - name: - description: Name of the referent. - type: string - required: - - kind - - name - type: object - suspend: - description: |- - Suspend tells the controller to suspend the reconciliation of this - source. - type: boolean - valuesFiles: - description: |- - ValuesFiles is an alternative list of values files to use as the chart - values (values.yaml is not included by default), expected to be a - relative path in the SourceRef. - Values files are merged in the order of this list with the last file - overriding the first. Ignored when omitted. - items: - type: string - type: array - verify: - description: |- - Verify contains the secret name containing the trusted public keys - used to verify the signature and specifies which provider to use to check - whether OCI image is authentic. - This field is only supported when using HelmRepository source with spec.type 'oci'. - Chart dependencies, which are not bundled in the umbrella chart artifact, are not verified. - properties: - matchOIDCIdentity: - description: |- - MatchOIDCIdentity specifies the identity matching criteria to use - while verifying an OCI artifact which was signed using Cosign keyless - signing. The artifact's identity is deemed to be verified if any of the - specified matchers match against the identity. - items: - description: |- - OIDCIdentityMatch specifies options for verifying the certificate identity, - i.e. the issuer and the subject of the certificate. - properties: - issuer: - description: |- - Issuer specifies the regex pattern to match against to verify - the OIDC issuer in the Fulcio certificate. The pattern must be a - valid Go regular expression. - type: string - subject: - description: |- - Subject specifies the regex pattern to match against to verify - the identity subject in the Fulcio certificate. The pattern must - be a valid Go regular expression. - type: string - required: - - issuer - - subject - type: object - type: array - provider: - default: cosign - description: Provider specifies the technology used to sign the OCI Artifact. - enum: - - cosign - - notation - type: string - secretRef: - description: |- - SecretRef specifies the Kubernetes Secret containing the - trusted public keys. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - required: - - provider - type: object - version: - default: '*' - description: |- - Version is the chart version semver expression, ignored for charts from - GitRepository and Bucket sources. Defaults to latest when omitted. - type: string - required: - - chart - - interval - - sourceRef - type: object - status: - default: - observedGeneration: -1 - description: HelmChartStatus records the observed state of the HelmChart. - properties: - artifact: - description: Artifact represents the output of the last successful reconciliation. - properties: - digest: - description: Digest is the digest of the file in the form of ':'. - pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$ - type: string - lastUpdateTime: - description: |- - LastUpdateTime is the timestamp corresponding to the last update of the - Artifact. - format: date-time - type: string - metadata: - additionalProperties: - type: string - description: Metadata holds upstream information such as OCI annotations. - type: object - path: - description: |- - Path is the relative file path of the Artifact. It can be used to locate - the file in the root of the Artifact storage on the local file system of - the controller managing the Source. - type: string - revision: - description: |- - Revision is a human-readable identifier traceable in the origin source - system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. - type: string - size: - description: Size is the number of bytes in the file. - format: int64 - type: integer - url: - description: |- - URL is the HTTP address of the Artifact as exposed by the controller - managing the Source. It can be used to retrieve the Artifact for - consumption, e.g. by another controller applying the Artifact contents. - type: string - required: - - digest - - lastUpdateTime - - path - - revision - - url - type: object - conditions: - description: Conditions holds the conditions for the HelmChart. - items: - description: Condition contains details for one aspect of the current state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastHandledReconcileAt: - description: |- - LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value - can be detected. - type: string - observedChartName: - description: |- - ObservedChartName is the last observed chart name as specified by the - resolved chart reference. - type: string - observedGeneration: - description: |- - ObservedGeneration is the last observed generation of the HelmChart - object. - format: int64 - type: integer - observedSourceArtifactRevision: - description: |- - ObservedSourceArtifactRevision is the last observed Artifact.Revision - of the HelmChartSpec.SourceRef. - type: string - observedValuesFiles: - description: |- - ObservedValuesFiles are the observed value files of the last successful - reconciliation. - It matches the chart in the last successfully reconciled artifact. - items: - type: string - type: array - url: - description: |- - URL is the dynamic fetch link for the latest Artifact. - It is provided on a "best effort" basis, and using the precise - BucketStatus.Artifact data is recommended. - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .spec.chart - name: Chart - type: string - - jsonPath: .spec.version - name: Version - type: string - - jsonPath: .spec.sourceRef.kind - name: Source Kind - type: string - - jsonPath: .spec.sourceRef.name - name: Source Name - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - deprecated: true - deprecationWarning: v1beta2 HelmChart is deprecated, upgrade to v1 - name: v1beta2 - schema: - openAPIV3Schema: - description: HelmChart is the Schema for the helmcharts API. - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: HelmChartSpec specifies the desired state of a Helm chart. - properties: - accessFrom: - description: |- - AccessFrom specifies an Access Control List for allowing cross-namespace - references to this object. - NOTE: Not implemented, provisional as of https://github.com/fluxcd/flux2/pull/2092 - properties: - namespaceSelectors: - description: |- - NamespaceSelectors is the list of namespace selectors to which this ACL applies. - Items in this list are evaluated using a logical OR operation. - items: - description: |- - NamespaceSelector selects the namespaces to which this ACL applies. - An empty map of MatchLabels matches all namespaces in a cluster. - properties: - matchLabels: - additionalProperties: - type: string - description: |- - MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - type: array - required: - - namespaceSelectors - type: object - chart: - description: |- - Chart is the name or path the Helm chart is available at in the - SourceRef. - type: string - ignoreMissingValuesFiles: - description: |- - IgnoreMissingValuesFiles controls whether to silently ignore missing values - files rather than failing. - type: boolean - interval: - description: |- - Interval at which the HelmChart SourceRef is checked for updates. - This interval is approximate and may be subject to jitter to ensure - efficient use of resources. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - reconcileStrategy: - default: ChartVersion - description: |- - ReconcileStrategy determines what enables the creation of a new artifact. - Valid values are ('ChartVersion', 'Revision'). - See the documentation of the values for an explanation on their behavior. - Defaults to ChartVersion when omitted. - enum: - - ChartVersion - - Revision - type: string - sourceRef: - description: SourceRef is the reference to the Source the chart is available at. - properties: - apiVersion: - description: APIVersion of the referent. - type: string - kind: - description: |- - Kind of the referent, valid values are ('HelmRepository', 'GitRepository', - 'Bucket'). - enum: - - HelmRepository - - GitRepository - - Bucket - type: string - name: - description: Name of the referent. - type: string - required: - - kind - - name - type: object - suspend: - description: |- - Suspend tells the controller to suspend the reconciliation of this - source. - type: boolean - valuesFile: - description: |- - ValuesFile is an alternative values file to use as the default chart - values, expected to be a relative path in the SourceRef. Deprecated in - favor of ValuesFiles, for backwards compatibility the file specified here - is merged before the ValuesFiles items. Ignored when omitted. - type: string - valuesFiles: - description: |- - ValuesFiles is an alternative list of values files to use as the chart - values (values.yaml is not included by default), expected to be a - relative path in the SourceRef. - Values files are merged in the order of this list with the last file - overriding the first. Ignored when omitted. - items: - type: string - type: array - verify: - description: |- - Verify contains the secret name containing the trusted public keys - used to verify the signature and specifies which provider to use to check - whether OCI image is authentic. - This field is only supported when using HelmRepository source with spec.type 'oci'. - Chart dependencies, which are not bundled in the umbrella chart artifact, are not verified. - properties: - matchOIDCIdentity: - description: |- - MatchOIDCIdentity specifies the identity matching criteria to use - while verifying an OCI artifact which was signed using Cosign keyless - signing. The artifact's identity is deemed to be verified if any of the - specified matchers match against the identity. - items: - description: |- - OIDCIdentityMatch specifies options for verifying the certificate identity, - i.e. the issuer and the subject of the certificate. - properties: - issuer: - description: |- - Issuer specifies the regex pattern to match against to verify - the OIDC issuer in the Fulcio certificate. The pattern must be a - valid Go regular expression. - type: string - subject: - description: |- - Subject specifies the regex pattern to match against to verify - the identity subject in the Fulcio certificate. The pattern must - be a valid Go regular expression. - type: string - required: - - issuer - - subject - type: object - type: array - provider: - default: cosign - description: Provider specifies the technology used to sign the OCI Artifact. - enum: - - cosign - - notation - type: string - secretRef: - description: |- - SecretRef specifies the Kubernetes Secret containing the - trusted public keys. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - required: - - provider - type: object - version: - default: '*' - description: |- - Version is the chart version semver expression, ignored for charts from - GitRepository and Bucket sources. Defaults to latest when omitted. - type: string - required: - - chart - - interval - - sourceRef - type: object - status: - default: - observedGeneration: -1 - description: HelmChartStatus records the observed state of the HelmChart. - properties: - artifact: - description: Artifact represents the output of the last successful reconciliation. - properties: - digest: - description: Digest is the digest of the file in the form of ':'. - pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$ - type: string - lastUpdateTime: - description: |- - LastUpdateTime is the timestamp corresponding to the last update of the - Artifact. - format: date-time - type: string - metadata: - additionalProperties: - type: string - description: Metadata holds upstream information such as OCI annotations. - type: object - path: - description: |- - Path is the relative file path of the Artifact. It can be used to locate - the file in the root of the Artifact storage on the local file system of - the controller managing the Source. - type: string - revision: - description: |- - Revision is a human-readable identifier traceable in the origin source - system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. - type: string - size: - description: Size is the number of bytes in the file. - format: int64 - type: integer - url: - description: |- - URL is the HTTP address of the Artifact as exposed by the controller - managing the Source. It can be used to retrieve the Artifact for - consumption, e.g. by another controller applying the Artifact contents. - type: string - required: - - digest - - lastUpdateTime - - path - - revision - - url - type: object - conditions: - description: Conditions holds the conditions for the HelmChart. - items: - description: Condition contains details for one aspect of the current state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastHandledReconcileAt: - description: |- - LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value - can be detected. - type: string - observedChartName: - description: |- - ObservedChartName is the last observed chart name as specified by the - resolved chart reference. - type: string - observedGeneration: - description: |- - ObservedGeneration is the last observed generation of the HelmChart - object. - format: int64 - type: integer - observedSourceArtifactRevision: - description: |- - ObservedSourceArtifactRevision is the last observed Artifact.Revision - of the HelmChartSpec.SourceRef. - type: string - observedValuesFiles: - description: |- - ObservedValuesFiles are the observed value files of the last successful - reconciliation. - It matches the chart in the last successfully reconciled artifact. - items: - type: string - type: array - url: - description: |- - URL is the dynamic fetch link for the latest Artifact. - It is provided on a "best effort" basis, and using the precise - BucketStatus.Artifact data is recommended. - type: string - type: object - type: object - served: true - storage: false - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - app.kubernetes.io/role: cluster-admin - controller-gen.kubebuilder.io/version: v0.19.0 - labels: - app.kubernetes.io/name: flux - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.7.3 - name: helmreleases.helm.toolkit.fluxcd.io -spec: - group: helm.toolkit.fluxcd.io - names: - kind: HelmRelease - listKind: HelmReleaseList - plural: helmreleases - shortNames: - - hr - singular: helmrelease - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - name: v2 - schema: - openAPIV3Schema: - description: HelmRelease is the Schema for the helmreleases API - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: HelmReleaseSpec defines the desired state of a Helm release. - properties: - chart: - description: |- - Chart defines the template of the v1.HelmChart that should be created - for this HelmRelease. - properties: - metadata: - description: ObjectMeta holds the template for metadata like labels and annotations. - properties: - annotations: - additionalProperties: - type: string - description: |- - Annotations is an unstructured key value map stored with a resource that may be - set by external tools to store and retrieve arbitrary metadata. They are not - queryable and should be preserved when modifying objects. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ - type: object - labels: - additionalProperties: - type: string - description: |- - Map of string keys and values that can be used to organize and categorize - (scope and select) objects. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ - type: object - type: object - spec: - description: Spec holds the template for the v1.HelmChartSpec for this HelmRelease. - properties: - chart: - description: The name or path the Helm chart is available at in the SourceRef. - maxLength: 2048 - minLength: 1 - type: string - ignoreMissingValuesFiles: - description: IgnoreMissingValuesFiles controls whether to silently ignore missing values files rather than failing. - type: boolean - interval: - description: |- - Interval at which to check the v1.Source for updates. Defaults to - 'HelmReleaseSpec.Interval'. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - reconcileStrategy: - default: ChartVersion - description: |- - Determines what enables the creation of a new artifact. Valid values are - ('ChartVersion', 'Revision'). - See the documentation of the values for an explanation on their behavior. - Defaults to ChartVersion when omitted. - enum: - - ChartVersion - - Revision - type: string - sourceRef: - description: The name and namespace of the v1.Source the chart is available at. - properties: - apiVersion: - description: APIVersion of the referent. - type: string - kind: - description: Kind of the referent. - enum: - - HelmRepository - - GitRepository - - Bucket - type: string - name: - description: Name of the referent. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: Namespace of the referent. - maxLength: 63 - minLength: 1 - type: string - required: - - kind - - name - type: object - valuesFiles: - description: |- - Alternative list of values files to use as the chart values (values.yaml - is not included by default), expected to be a relative path in the SourceRef. - Values files are merged in the order of this list with the last file overriding - the first. Ignored when omitted. - items: - type: string - type: array - verify: - description: |- - Verify contains the secret name containing the trusted public keys - used to verify the signature and specifies which provider to use to check - whether OCI image is authentic. - This field is only supported for OCI sources. - Chart dependencies, which are not bundled in the umbrella chart artifact, - are not verified. - properties: - provider: - default: cosign - description: Provider specifies the technology used to sign the OCI Helm chart. - enum: - - cosign - - notation - type: string - secretRef: - description: |- - SecretRef specifies the Kubernetes Secret containing the - trusted public keys. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - required: - - provider - type: object - version: - default: '*' - description: |- - Version semver expression, ignored for charts from v1.GitRepository and - v1beta2.Bucket sources. Defaults to latest when omitted. - type: string - required: - - chart - - sourceRef - type: object - required: - - spec - type: object - chartRef: - description: |- - ChartRef holds a reference to a source controller resource containing the - Helm chart artifact. - properties: - apiVersion: - description: APIVersion of the referent. - type: string - kind: - description: Kind of the referent. - enum: - - OCIRepository - - HelmChart - - ExternalArtifact - type: string - name: - description: Name of the referent. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: |- - Namespace of the referent, defaults to the namespace of the Kubernetes - resource object that contains the reference. - maxLength: 63 - minLength: 1 - type: string - required: - - kind - - name - type: object - commonMetadata: - description: |- - CommonMetadata specifies the common labels and annotations that are - applied to all resources. Any existing label or annotation will be - overridden if its key matches a common one. - properties: - annotations: - additionalProperties: - type: string - description: Annotations to be added to the object's metadata. - type: object - labels: - additionalProperties: - type: string - description: Labels to be added to the object's metadata. - type: object - type: object - dependsOn: - description: |- - DependsOn may contain a DependencyReference slice with - references to HelmRelease resources that must be ready before this HelmRelease - can be reconciled. - items: - description: DependencyReference defines a HelmRelease dependency on another HelmRelease resource. - properties: - name: - description: Name of the referent. - type: string - namespace: - description: |- - Namespace of the referent, defaults to the namespace of the HelmRelease - resource object that contains the reference. - type: string - readyExpr: - description: |- - ReadyExpr is a CEL expression that can be used to assess the readiness - of a dependency. When specified, the built-in readiness check - is replaced by the logic defined in the CEL expression. - To make the CEL expression additive to the built-in readiness check, - the feature gate `AdditiveCELDependencyCheck` must be set to `true`. - type: string - required: - - name - type: object - type: array - driftDetection: - description: |- - DriftDetection holds the configuration for detecting and handling - differences between the manifest in the Helm storage and the resources - currently existing in the cluster. - properties: - ignore: - description: |- - Ignore contains a list of rules for specifying which changes to ignore - during diffing. - items: - description: |- - IgnoreRule defines a rule to selectively disregard specific changes during - the drift detection process. - properties: - paths: - description: |- - Paths is a list of JSON Pointer (RFC 6901) paths to be excluded from - consideration in a Kubernetes object. - items: - type: string - type: array - target: - description: |- - Target is a selector for specifying Kubernetes objects to which this - rule applies. - If Target is not set, the Paths will be ignored for all Kubernetes - objects within the manifest of the Helm release. - properties: - annotationSelector: - description: |- - AnnotationSelector is a string that follows the label selection expression - https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource annotations. - type: string - group: - description: |- - Group is the API group to select resources from. - Together with Version and Kind it is capable of unambiguously identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - kind: - description: |- - Kind of the API Group to select resources from. - Together with Group and Version it is capable of unambiguously - identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - labelSelector: - description: |- - LabelSelector is a string that follows the label selection expression - https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource labels. - type: string - name: - description: Name to match resources with. - type: string - namespace: - description: Namespace to select resources from. - type: string - version: - description: |- - Version of the API Group to select resources from. - Together with Group and Kind it is capable of unambiguously identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - type: object - required: - - paths - type: object - type: array - mode: - description: |- - Mode defines how differences should be handled between the Helm manifest - and the manifest currently applied to the cluster. - If not explicitly set, it defaults to DiffModeDisabled. - enum: - - enabled - - warn - - disabled - type: string - type: object - install: - description: Install holds the configuration for Helm install actions for this HelmRelease. - properties: - crds: - description: |- - CRDs upgrade CRDs from the Helm Chart's crds directory according - to the CRD upgrade policy provided here. Valid values are `Skip`, - `Create` or `CreateReplace`. Default is `Create` and if omitted - CRDs are installed but not updated. - - Skip: do neither install nor replace (update) any CRDs. - - Create: new CRDs are created, existing CRDs are neither updated nor deleted. - - CreateReplace: new CRDs are created, existing CRDs are updated (replaced) - but not deleted. - - By default, CRDs are applied (installed) during Helm install action. - With this option users can opt in to CRD replace existing CRDs on Helm - install actions, which is not (yet) natively supported by Helm. - https://helm.sh/docs/chart_best_practices/custom_resource_definitions. - enum: - - Skip - - Create - - CreateReplace - type: string - createNamespace: - description: |- - CreateNamespace tells the Helm install action to create the - HelmReleaseSpec.TargetNamespace if it does not exist yet. - On uninstall, the namespace will not be garbage collected. - type: boolean - disableHooks: - description: DisableHooks prevents hooks from running during the Helm install action. - type: boolean - disableOpenAPIValidation: - description: |- - DisableOpenAPIValidation prevents the Helm install action from validating - rendered templates against the Kubernetes OpenAPI Schema. - type: boolean - disableSchemaValidation: - description: |- - DisableSchemaValidation prevents the Helm install action from validating - the values against the JSON Schema. - type: boolean - disableTakeOwnership: - description: |- - DisableTakeOwnership disables taking ownership of existing resources - during the Helm install action. Defaults to false. - type: boolean - disableWait: - description: |- - DisableWait disables the waiting for resources to be ready after a Helm - install has been performed. - type: boolean - disableWaitForJobs: - description: |- - DisableWaitForJobs disables waiting for jobs to complete after a Helm - install has been performed. - type: boolean - remediation: - description: |- - Remediation holds the remediation configuration for when the Helm install - action for the HelmRelease fails. The default is to not perform any action. - properties: - ignoreTestFailures: - description: |- - IgnoreTestFailures tells the controller to skip remediation when the Helm - tests are run after an install action but fail. Defaults to - 'Test.IgnoreFailures'. - type: boolean - remediateLastFailure: - description: |- - RemediateLastFailure tells the controller to remediate the last failure, when - no retries remain. Defaults to 'false'. - type: boolean - retries: - description: |- - Retries is the number of retries that should be attempted on failures before - bailing. Remediation, using an uninstall, is performed between each attempt. - Defaults to '0', a negative integer equals to unlimited retries. - type: integer - type: object - replace: - description: |- - Replace tells the Helm install action to re-use the 'ReleaseName', but only - if that name is a deleted release which remains in the history. - type: boolean - skipCRDs: - description: |- - SkipCRDs tells the Helm install action to not install any CRDs. By default, - CRDs are installed if not already present. - - Deprecated use CRD policy (`crds`) attribute with value `Skip` instead. - type: boolean - strategy: - description: |- - Strategy defines the install strategy to use for this HelmRelease. - Defaults to 'RemediateOnFailure'. - properties: - name: - description: Name of the install strategy. - enum: - - RemediateOnFailure - - RetryOnFailure - type: string - retryInterval: - description: |- - RetryInterval is the interval at which to retry a failed install. - Can be used only when Name is set to RetryOnFailure. - Defaults to '5m'. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - required: - - name - type: object - x-kubernetes-validations: - - message: .retryInterval cannot be set when .name is 'RemediateOnFailure' - rule: '!has(self.retryInterval) || self.name != ''RemediateOnFailure''' - timeout: - description: |- - Timeout is the time to wait for any individual Kubernetes operation (like - Jobs for hooks) during the performance of a Helm install action. Defaults to - 'HelmReleaseSpec.Timeout'. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - type: object - interval: - description: Interval at which to reconcile the Helm release. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - kubeConfig: - description: |- - KubeConfig for reconciling the HelmRelease on a remote cluster. - When used in combination with HelmReleaseSpec.ServiceAccountName, - forces the controller to act on behalf of that Service Account at the - target cluster. - If the --default-service-account flag is set, its value will be used as - a controller level fallback for when HelmReleaseSpec.ServiceAccountName - is empty. - properties: - configMapRef: - description: |- - ConfigMapRef holds an optional name of a ConfigMap that contains - the following keys: - - - `provider`: the provider to use. One of `aws`, `azure`, `gcp`, or - `generic`. Required. - - `cluster`: the fully qualified resource name of the Kubernetes - cluster in the cloud provider API. Not used by the `generic` - provider. Required when one of `address` or `ca.crt` is not set. - - `address`: the address of the Kubernetes API server. Required - for `generic`. For the other providers, if not specified, the - first address in the cluster resource will be used, and if - specified, it must match one of the addresses in the cluster - resource. - If audiences is not set, will be used as the audience for the - `generic` provider. - - `ca.crt`: the optional PEM-encoded CA certificate for the - Kubernetes API server. If not set, the controller will use the - CA certificate from the cluster resource. - - `audiences`: the optional audiences as a list of - line-break-separated strings for the Kubernetes ServiceAccount - token. Defaults to the `address` for the `generic` provider, or - to specific values for the other providers depending on the - provider. - - `serviceAccountName`: the optional name of the Kubernetes - ServiceAccount in the same namespace that should be used - for authentication. If not specified, the controller - ServiceAccount will be used. - - Mutually exclusive with SecretRef. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - secretRef: - description: |- - SecretRef holds an optional name of a secret that contains a key with - the kubeconfig file as the value. If no key is set, the key will default - to 'value'. Mutually exclusive with ConfigMapRef. - It is recommended that the kubeconfig is self-contained, and the secret - is regularly updated if credentials such as a cloud-access-token expire. - Cloud specific `cmd-path` auth helpers will not function without adding - binaries and credentials to the Pod that is responsible for reconciling - Kubernetes resources. Supported only for the generic provider. - properties: - key: - description: Key in the Secret, when not specified an implementation-specific default key is used. - type: string - name: - description: Name of the Secret. - type: string - required: - - name - type: object - type: object - x-kubernetes-validations: - - message: exactly one of spec.kubeConfig.configMapRef or spec.kubeConfig.secretRef must be specified - rule: has(self.configMapRef) || has(self.secretRef) - - message: exactly one of spec.kubeConfig.configMapRef or spec.kubeConfig.secretRef must be specified - rule: '!has(self.configMapRef) || !has(self.secretRef)' - maxHistory: - description: |- - MaxHistory is the number of revisions saved by Helm for this HelmRelease. - Use '0' for an unlimited number of revisions; defaults to '5'. - type: integer - persistentClient: - description: |- - PersistentClient tells the controller to use a persistent Kubernetes - client for this release. When enabled, the client will be reused for the - duration of the reconciliation, instead of being created and destroyed - for each (step of a) Helm action. - - This can improve performance, but may cause issues with some Helm charts - that for example do create Custom Resource Definitions during installation - outside Helm's CRD lifecycle hooks, which are then not observed to be - available by e.g. post-install hooks. - - If not set, it defaults to true. - type: boolean - postRenderers: - description: |- - PostRenderers holds an array of Helm PostRenderers, which will be applied in order - of their definition. - items: - description: PostRenderer contains a Helm PostRenderer specification. - properties: - kustomize: - description: Kustomization to apply as PostRenderer. - properties: - images: - description: |- - Images is a list of (image name, new name, new tag or digest) - for changing image names, tags or digests. This can also be achieved with a - patch, but this operator is simpler to specify. - items: - description: Image contains an image name, a new name, a new tag or digest, which will replace the original name and tag. - properties: - digest: - description: |- - Digest is the value used to replace the original image tag. - If digest is present NewTag value is ignored. - type: string - name: - description: Name is a tag-less image name. - type: string - newName: - description: NewName is the value used to replace the original name. - type: string - newTag: - description: NewTag is the value used to replace the original tag. - type: string - required: - - name - type: object - type: array - patches: - description: |- - Strategic merge and JSON patches, defined as inline YAML objects, - capable of targeting objects based on kind, label and annotation selectors. - items: - description: |- - Patch contains an inline StrategicMerge or JSON6902 patch, and the target the patch should - be applied to. - properties: - patch: - description: |- - Patch contains an inline StrategicMerge patch or an inline JSON6902 patch with - an array of operation objects. - type: string - target: - description: Target points to the resources that the patch document should be applied to. - properties: - annotationSelector: - description: |- - AnnotationSelector is a string that follows the label selection expression - https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource annotations. - type: string - group: - description: |- - Group is the API group to select resources from. - Together with Version and Kind it is capable of unambiguously identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - kind: - description: |- - Kind of the API Group to select resources from. - Together with Group and Version it is capable of unambiguously - identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - labelSelector: - description: |- - LabelSelector is a string that follows the label selection expression - https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource labels. - type: string - name: - description: Name to match resources with. - type: string - namespace: - description: Namespace to select resources from. - type: string - version: - description: |- - Version of the API Group to select resources from. - Together with Group and Kind it is capable of unambiguously identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - type: object - required: - - patch - type: object - type: array - type: object - type: object - type: array - releaseName: - description: |- - ReleaseName used for the Helm release. Defaults to a composition of - '[TargetNamespace-]Name'. - maxLength: 53 - minLength: 1 - type: string - rollback: - description: Rollback holds the configuration for Helm rollback actions for this HelmRelease. - properties: - cleanupOnFail: - description: |- - CleanupOnFail allows deletion of new resources created during the Helm - rollback action when it fails. - type: boolean - disableHooks: - description: DisableHooks prevents hooks from running during the Helm rollback action. - type: boolean - disableWait: - description: |- - DisableWait disables the waiting for resources to be ready after a Helm - rollback has been performed. - type: boolean - disableWaitForJobs: - description: |- - DisableWaitForJobs disables waiting for jobs to complete after a Helm - rollback has been performed. - type: boolean - force: - description: Force forces resource updates through a replacement strategy. - type: boolean - recreate: - description: Recreate performs pod restarts for the resource if applicable. - type: boolean - timeout: - description: |- - Timeout is the time to wait for any individual Kubernetes operation (like - Jobs for hooks) during the performance of a Helm rollback action. Defaults to - 'HelmReleaseSpec.Timeout'. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - type: object - serviceAccountName: - description: |- - The name of the Kubernetes service account to impersonate - when reconciling this HelmRelease. - maxLength: 253 - minLength: 1 - type: string - storageNamespace: - description: |- - StorageNamespace used for the Helm storage. - Defaults to the namespace of the HelmRelease. - maxLength: 63 - minLength: 1 - type: string - suspend: - description: |- - Suspend tells the controller to suspend reconciliation for this HelmRelease, - it does not apply to already started reconciliations. Defaults to false. - type: boolean - targetNamespace: - description: |- - TargetNamespace to target when performing operations for the HelmRelease. - Defaults to the namespace of the HelmRelease. - maxLength: 63 - minLength: 1 - type: string - test: - description: Test holds the configuration for Helm test actions for this HelmRelease. - properties: - enable: - description: |- - Enable enables Helm test actions for this HelmRelease after an Helm install - or upgrade action has been performed. - type: boolean - filters: - description: Filters is a list of tests to run or exclude from running. - items: - description: Filter holds the configuration for individual Helm test filters. - properties: - exclude: - description: Exclude specifies whether the named test should be excluded. - type: boolean - name: - description: Name is the name of the test. - maxLength: 253 - minLength: 1 - type: string - required: - - name - type: object - type: array - ignoreFailures: - description: |- - IgnoreFailures tells the controller to skip remediation when the Helm tests - are run but fail. Can be overwritten for tests run after install or upgrade - actions in 'Install.IgnoreTestFailures' and 'Upgrade.IgnoreTestFailures'. - type: boolean - timeout: - description: |- - Timeout is the time to wait for any individual Kubernetes operation during - the performance of a Helm test action. Defaults to 'HelmReleaseSpec.Timeout'. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - type: object - timeout: - description: |- - Timeout is the time to wait for any individual Kubernetes operation (like Jobs - for hooks) during the performance of a Helm action. Defaults to '5m0s'. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - uninstall: - description: Uninstall holds the configuration for Helm uninstall actions for this HelmRelease. - properties: - deletionPropagation: - default: background - description: |- - DeletionPropagation specifies the deletion propagation policy when - a Helm uninstall is performed. - enum: - - background - - foreground - - orphan - type: string - disableHooks: - description: DisableHooks prevents hooks from running during the Helm rollback action. - type: boolean - disableWait: - description: |- - DisableWait disables waiting for all the resources to be deleted after - a Helm uninstall is performed. - type: boolean - keepHistory: - description: |- - KeepHistory tells Helm to remove all associated resources and mark the - release as deleted, but retain the release history. - type: boolean - timeout: - description: |- - Timeout is the time to wait for any individual Kubernetes operation (like - Jobs for hooks) during the performance of a Helm uninstall action. Defaults - to 'HelmReleaseSpec.Timeout'. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - type: object - upgrade: - description: Upgrade holds the configuration for Helm upgrade actions for this HelmRelease. - properties: - cleanupOnFail: - description: |- - CleanupOnFail allows deletion of new resources created during the Helm - upgrade action when it fails. - type: boolean - crds: - description: |- - CRDs upgrade CRDs from the Helm Chart's crds directory according - to the CRD upgrade policy provided here. Valid values are `Skip`, - `Create` or `CreateReplace`. Default is `Skip` and if omitted - CRDs are neither installed nor upgraded. - - Skip: do neither install nor replace (update) any CRDs. - - Create: new CRDs are created, existing CRDs are neither updated nor deleted. - - CreateReplace: new CRDs are created, existing CRDs are updated (replaced) - but not deleted. - - By default, CRDs are not applied during Helm upgrade action. With this - option users can opt-in to CRD upgrade, which is not (yet) natively supported by Helm. - https://helm.sh/docs/chart_best_practices/custom_resource_definitions. - enum: - - Skip - - Create - - CreateReplace - type: string - disableHooks: - description: DisableHooks prevents hooks from running during the Helm upgrade action. - type: boolean - disableOpenAPIValidation: - description: |- - DisableOpenAPIValidation prevents the Helm upgrade action from validating - rendered templates against the Kubernetes OpenAPI Schema. - type: boolean - disableSchemaValidation: - description: |- - DisableSchemaValidation prevents the Helm upgrade action from validating - the values against the JSON Schema. - type: boolean - disableTakeOwnership: - description: |- - DisableTakeOwnership disables taking ownership of existing resources - during the Helm upgrade action. Defaults to false. - type: boolean - disableWait: - description: |- - DisableWait disables the waiting for resources to be ready after a Helm - upgrade has been performed. - type: boolean - disableWaitForJobs: - description: |- - DisableWaitForJobs disables waiting for jobs to complete after a Helm - upgrade has been performed. - type: boolean - force: - description: Force forces resource updates through a replacement strategy. - type: boolean - preserveValues: - description: |- - PreserveValues will make Helm reuse the last release's values and merge in - overrides from 'Values'. Setting this flag makes the HelmRelease - non-declarative. - type: boolean - remediation: - description: |- - Remediation holds the remediation configuration for when the Helm upgrade - action for the HelmRelease fails. The default is to not perform any action. - properties: - ignoreTestFailures: - description: |- - IgnoreTestFailures tells the controller to skip remediation when the Helm - tests are run after an upgrade action but fail. - Defaults to 'Test.IgnoreFailures'. - type: boolean - remediateLastFailure: - description: |- - RemediateLastFailure tells the controller to remediate the last failure, when - no retries remain. Defaults to 'false' unless 'Retries' is greater than 0. - type: boolean - retries: - description: |- - Retries is the number of retries that should be attempted on failures before - bailing. Remediation, using 'Strategy', is performed between each attempt. - Defaults to '0', a negative integer equals to unlimited retries. - type: integer - strategy: - description: Strategy to use for failure remediation. Defaults to 'rollback'. - enum: - - rollback - - uninstall - type: string - type: object - strategy: - description: |- - Strategy defines the upgrade strategy to use for this HelmRelease. - Defaults to 'RemediateOnFailure'. - properties: - name: - description: Name of the upgrade strategy. - enum: - - RemediateOnFailure - - RetryOnFailure - type: string - retryInterval: - description: |- - RetryInterval is the interval at which to retry a failed upgrade. - Can be used only when Name is set to RetryOnFailure. - Defaults to '5m'. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - required: - - name - type: object - x-kubernetes-validations: - - message: .retryInterval can only be set when .name is 'RetryOnFailure' - rule: '!has(self.retryInterval) || self.name == ''RetryOnFailure''' - timeout: - description: |- - Timeout is the time to wait for any individual Kubernetes operation (like - Jobs for hooks) during the performance of a Helm upgrade action. Defaults to - 'HelmReleaseSpec.Timeout'. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - type: object - values: - description: Values holds the values for this Helm release. - x-kubernetes-preserve-unknown-fields: true - valuesFrom: - description: |- - ValuesFrom holds references to resources containing Helm values for this HelmRelease, - and information about how they should be merged. - items: - description: |- - ValuesReference contains a reference to a resource containing Helm values, - and optionally the key they can be found at. - properties: - kind: - description: Kind of the values referent, valid values are ('Secret', 'ConfigMap'). - enum: - - Secret - - ConfigMap - type: string - name: - description: |- - Name of the values referent. Should reside in the same namespace as the - referring resource. - maxLength: 253 - minLength: 1 - type: string - optional: - description: |- - Optional marks this ValuesReference as optional. When set, a not found error - for the values reference is ignored, but any ValuesKey, TargetPath or - transient error will still result in a reconciliation failure. - type: boolean - targetPath: - description: |- - TargetPath is the YAML dot notation path the value should be merged at. When - set, the ValuesKey is expected to be a single flat value. Defaults to 'None', - which results in the values getting merged at the root. - maxLength: 250 - pattern: ^([a-zA-Z0-9_\-.\\\/]|\[[0-9]{1,5}\])+$ - type: string - valuesKey: - description: |- - ValuesKey is the data key where the values.yaml or a specific value can be - found at. Defaults to 'values.yaml'. - maxLength: 253 - pattern: ^[\-._a-zA-Z0-9]+$ - type: string - required: - - kind - - name - type: object - type: array - required: - - interval - type: object - x-kubernetes-validations: - - message: either chart or chartRef must be set - rule: (has(self.chart) && !has(self.chartRef)) || (!has(self.chart) && has(self.chartRef)) - status: - default: - observedGeneration: -1 - description: HelmReleaseStatus defines the observed state of a HelmRelease. - properties: - conditions: - description: Conditions holds the conditions for the HelmRelease. - items: - description: Condition contains details for one aspect of the current state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - failures: - description: |- - Failures is the reconciliation failure count against the latest desired - state. It is reset after a successful reconciliation. - format: int64 - type: integer - helmChart: - description: |- - HelmChart is the namespaced name of the HelmChart resource created by - the controller for the HelmRelease. - type: string - history: - description: |- - History holds the history of Helm releases performed for this HelmRelease - up to the last successfully completed release. - items: - description: |- - Snapshot captures a point-in-time copy of the status information for a Helm release, - as managed by the controller. - properties: - apiVersion: - description: |- - APIVersion is the API version of the Snapshot. - Provisional: when the calculation method of the Digest field is changed, - this field will be used to distinguish between the old and new methods. - type: string - appVersion: - description: AppVersion is the chart app version of the release object in storage. - type: string - chartName: - description: ChartName is the chart name of the release object in storage. - type: string - chartVersion: - description: |- - ChartVersion is the chart version of the release object in - storage. - type: string - configDigest: - description: |- - ConfigDigest is the checksum of the config (better known as - "values") of the release object in storage. - It has the format of `:`. - type: string - deleted: - description: Deleted is when the release was deleted. - format: date-time - type: string - digest: - description: |- - Digest is the checksum of the release object in storage. - It has the format of `:`. - type: string - firstDeployed: - description: FirstDeployed is when the release was first deployed. - format: date-time - type: string - lastDeployed: - description: LastDeployed is when the release was last deployed. - format: date-time - type: string - name: - description: Name is the name of the release. - type: string - namespace: - description: Namespace is the namespace the release is deployed to. - type: string - ociDigest: - description: OCIDigest is the digest of the OCI artifact associated with the release. - type: string - status: - description: Status is the current state of the release. - type: string - testHooks: - additionalProperties: - description: |- - TestHookStatus holds the status information for a test hook as observed - to be run by the controller. - properties: - lastCompleted: - description: LastCompleted is the time the test hook last completed. - format: date-time - type: string - lastStarted: - description: LastStarted is the time the test hook was last started. - format: date-time - type: string - phase: - description: Phase the test hook was observed to be in. - type: string - type: object - description: |- - TestHooks is the list of test hooks for the release as observed to be - run by the controller. - type: object - version: - description: Version is the version of the release object in storage. - type: integer - required: - - chartName - - chartVersion - - configDigest - - digest - - firstDeployed - - lastDeployed - - name - - namespace - - status - - version - type: object - type: array - installFailures: - description: |- - InstallFailures is the install failure count against the latest desired - state. It is reset after a successful reconciliation. - format: int64 - type: integer - lastAttemptedConfigDigest: - description: |- - LastAttemptedConfigDigest is the digest for the config (better known as - "values") of the last reconciliation attempt. - type: string - lastAttemptedGeneration: - description: |- - LastAttemptedGeneration is the last generation the controller attempted - to reconcile. - format: int64 - type: integer - lastAttemptedReleaseAction: - description: |- - LastAttemptedReleaseAction is the last release action performed for this - HelmRelease. It is used to determine the active retry or remediation - strategy. - enum: - - install - - upgrade - type: string - lastAttemptedReleaseActionDuration: - description: |- - LastAttemptedReleaseActionDuration is the duration of the last - release action performed for this HelmRelease. - type: string - lastAttemptedRevision: - description: |- - LastAttemptedRevision is the Source revision of the last reconciliation - attempt. For OCIRepository sources, the 12 first characters of the digest are - appended to the chart version e.g. "1.2.3+1234567890ab". - type: string - lastAttemptedRevisionDigest: - description: |- - LastAttemptedRevisionDigest is the digest of the last reconciliation attempt. - This is only set for OCIRepository sources. - type: string - lastAttemptedValuesChecksum: - description: |- - LastAttemptedValuesChecksum is the SHA1 checksum for the values of the last - reconciliation attempt. - - Deprecated: Use LastAttemptedConfigDigest instead. - type: string - lastHandledForceAt: - description: |- - LastHandledForceAt holds the value of the most recent - force request value, so a change of the annotation value - can be detected. - type: string - lastHandledReconcileAt: - description: |- - LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value - can be detected. - type: string - lastHandledResetAt: - description: |- - LastHandledResetAt holds the value of the most recent reset request - value, so a change of the annotation value can be detected. - type: string - lastReleaseRevision: - description: |- - LastReleaseRevision is the revision of the last successful Helm release. - - Deprecated: Use History instead. - type: integer - observedCommonMetadataDigest: - description: |- - ObservedCommonMetadataDigest is the digest for the common metadata of - the last successful reconciliation attempt. - type: string - observedGeneration: - description: ObservedGeneration is the last observed generation. - format: int64 - type: integer - observedPostRenderersDigest: - description: |- - ObservedPostRenderersDigest is the digest for the post-renderers of - the last successful reconciliation attempt. - type: string - storageNamespace: - description: |- - StorageNamespace is the namespace of the Helm release storage for the - current release. - maxLength: 63 - minLength: 1 - type: string - upgradeFailures: - description: |- - UpgradeFailures is the upgrade failure count against the latest desired - state. It is reset after a successful reconciliation. - format: int64 - type: integer - type: object - type: object - served: true - storage: true - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - deprecated: true - deprecationWarning: v2beta2 HelmRelease is deprecated, upgrade to v2 - name: v2beta2 - schema: - openAPIV3Schema: - description: HelmRelease is the Schema for the helmreleases API - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: HelmReleaseSpec defines the desired state of a Helm release. - properties: - chart: - description: |- - Chart defines the template of the v1beta2.HelmChart that should be created - for this HelmRelease. - properties: - metadata: - description: ObjectMeta holds the template for metadata like labels and annotations. - properties: - annotations: - additionalProperties: - type: string - description: |- - Annotations is an unstructured key value map stored with a resource that may be - set by external tools to store and retrieve arbitrary metadata. They are not - queryable and should be preserved when modifying objects. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ - type: object - labels: - additionalProperties: - type: string - description: |- - Map of string keys and values that can be used to organize and categorize - (scope and select) objects. - More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ - type: object - type: object - spec: - description: Spec holds the template for the v1beta2.HelmChartSpec for this HelmRelease. - properties: - chart: - description: The name or path the Helm chart is available at in the SourceRef. - maxLength: 2048 - minLength: 1 - type: string - ignoreMissingValuesFiles: - description: IgnoreMissingValuesFiles controls whether to silently ignore missing values files rather than failing. - type: boolean - interval: - description: |- - Interval at which to check the v1.Source for updates. Defaults to - 'HelmReleaseSpec.Interval'. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - reconcileStrategy: - default: ChartVersion - description: |- - Determines what enables the creation of a new artifact. Valid values are - ('ChartVersion', 'Revision'). - See the documentation of the values for an explanation on their behavior. - Defaults to ChartVersion when omitted. - enum: - - ChartVersion - - Revision - type: string - sourceRef: - description: The name and namespace of the v1.Source the chart is available at. - properties: - apiVersion: - description: APIVersion of the referent. - type: string - kind: - description: Kind of the referent. - enum: - - HelmRepository - - GitRepository - - Bucket - type: string - name: - description: Name of the referent. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: Namespace of the referent. - maxLength: 63 - minLength: 1 - type: string - required: - - kind - - name - type: object - valuesFile: - description: |- - Alternative values file to use as the default chart values, expected to - be a relative path in the SourceRef. Deprecated in favor of ValuesFiles, - for backwards compatibility the file defined here is merged before the - ValuesFiles items. Ignored when omitted. - type: string - valuesFiles: - description: |- - Alternative list of values files to use as the chart values (values.yaml - is not included by default), expected to be a relative path in the SourceRef. - Values files are merged in the order of this list with the last file overriding - the first. Ignored when omitted. - items: - type: string - type: array - verify: - description: |- - Verify contains the secret name containing the trusted public keys - used to verify the signature and specifies which provider to use to check - whether OCI image is authentic. - This field is only supported for OCI sources. - Chart dependencies, which are not bundled in the umbrella chart artifact, - are not verified. - properties: - provider: - default: cosign - description: Provider specifies the technology used to sign the OCI Helm chart. - enum: - - cosign - - notation - type: string - secretRef: - description: |- - SecretRef specifies the Kubernetes Secret containing the - trusted public keys. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - required: - - provider - type: object - version: - default: '*' - description: |- - Version semver expression, ignored for charts from v1beta2.GitRepository and - v1beta2.Bucket sources. Defaults to latest when omitted. - type: string - required: - - chart - - sourceRef - type: object - required: - - spec - type: object - chartRef: - description: |- - ChartRef holds a reference to a source controller resource containing the - Helm chart artifact. - - Note: this field is provisional to the v2 API, and not actively used - by v2beta2 HelmReleases. - properties: - apiVersion: - description: APIVersion of the referent. - type: string - kind: - description: Kind of the referent. - enum: - - OCIRepository - - HelmChart - type: string - name: - description: Name of the referent. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: |- - Namespace of the referent, defaults to the namespace of the Kubernetes - resource object that contains the reference. - maxLength: 63 - minLength: 1 - type: string - required: - - kind - - name - type: object - dependsOn: - description: |- - DependsOn may contain a meta.NamespacedObjectReference slice with - references to HelmRelease resources that must be ready before this HelmRelease - can be reconciled. - items: - description: |- - NamespacedObjectReference contains enough information to locate the referenced Kubernetes resource object in any - namespace. - properties: - name: - description: Name of the referent. - type: string - namespace: - description: Namespace of the referent, when not specified it acts as LocalObjectReference. - type: string - required: - - name - type: object - type: array - driftDetection: - description: |- - DriftDetection holds the configuration for detecting and handling - differences between the manifest in the Helm storage and the resources - currently existing in the cluster. - properties: - ignore: - description: |- - Ignore contains a list of rules for specifying which changes to ignore - during diffing. - items: - description: |- - IgnoreRule defines a rule to selectively disregard specific changes during - the drift detection process. - properties: - paths: - description: |- - Paths is a list of JSON Pointer (RFC 6901) paths to be excluded from - consideration in a Kubernetes object. - items: - type: string - type: array - target: - description: |- - Target is a selector for specifying Kubernetes objects to which this - rule applies. - If Target is not set, the Paths will be ignored for all Kubernetes - objects within the manifest of the Helm release. - properties: - annotationSelector: - description: |- - AnnotationSelector is a string that follows the label selection expression - https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource annotations. - type: string - group: - description: |- - Group is the API group to select resources from. - Together with Version and Kind it is capable of unambiguously identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - kind: - description: |- - Kind of the API Group to select resources from. - Together with Group and Version it is capable of unambiguously - identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - labelSelector: - description: |- - LabelSelector is a string that follows the label selection expression - https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource labels. - type: string - name: - description: Name to match resources with. - type: string - namespace: - description: Namespace to select resources from. - type: string - version: - description: |- - Version of the API Group to select resources from. - Together with Group and Kind it is capable of unambiguously identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - type: object - required: - - paths - type: object - type: array - mode: - description: |- - Mode defines how differences should be handled between the Helm manifest - and the manifest currently applied to the cluster. - If not explicitly set, it defaults to DiffModeDisabled. - enum: - - enabled - - warn - - disabled - type: string - type: object - install: - description: Install holds the configuration for Helm install actions for this HelmRelease. - properties: - crds: - description: |- - CRDs upgrade CRDs from the Helm Chart's crds directory according - to the CRD upgrade policy provided here. Valid values are `Skip`, - `Create` or `CreateReplace`. Default is `Create` and if omitted - CRDs are installed but not updated. - - Skip: do neither install nor replace (update) any CRDs. - - Create: new CRDs are created, existing CRDs are neither updated nor deleted. - - CreateReplace: new CRDs are created, existing CRDs are updated (replaced) - but not deleted. - - By default, CRDs are applied (installed) during Helm install action. - With this option users can opt in to CRD replace existing CRDs on Helm - install actions, which is not (yet) natively supported by Helm. - https://helm.sh/docs/chart_best_practices/custom_resource_definitions. - enum: - - Skip - - Create - - CreateReplace - type: string - createNamespace: - description: |- - CreateNamespace tells the Helm install action to create the - HelmReleaseSpec.TargetNamespace if it does not exist yet. - On uninstall, the namespace will not be garbage collected. - type: boolean - disableHooks: - description: DisableHooks prevents hooks from running during the Helm install action. - type: boolean - disableOpenAPIValidation: - description: |- - DisableOpenAPIValidation prevents the Helm install action from validating - rendered templates against the Kubernetes OpenAPI Schema. - type: boolean - disableWait: - description: |- - DisableWait disables the waiting for resources to be ready after a Helm - install has been performed. - type: boolean - disableWaitForJobs: - description: |- - DisableWaitForJobs disables waiting for jobs to complete after a Helm - install has been performed. - type: boolean - remediation: - description: |- - Remediation holds the remediation configuration for when the Helm install - action for the HelmRelease fails. The default is to not perform any action. - properties: - ignoreTestFailures: - description: |- - IgnoreTestFailures tells the controller to skip remediation when the Helm - tests are run after an install action but fail. Defaults to - 'Test.IgnoreFailures'. - type: boolean - remediateLastFailure: - description: |- - RemediateLastFailure tells the controller to remediate the last failure, when - no retries remain. Defaults to 'false'. - type: boolean - retries: - description: |- - Retries is the number of retries that should be attempted on failures before - bailing. Remediation, using an uninstall, is performed between each attempt. - Defaults to '0', a negative integer equals to unlimited retries. - type: integer - type: object - replace: - description: |- - Replace tells the Helm install action to re-use the 'ReleaseName', but only - if that name is a deleted release which remains in the history. - type: boolean - skipCRDs: - description: |- - SkipCRDs tells the Helm install action to not install any CRDs. By default, - CRDs are installed if not already present. - - Deprecated use CRD policy (`crds`) attribute with value `Skip` instead. - type: boolean - timeout: - description: |- - Timeout is the time to wait for any individual Kubernetes operation (like - Jobs for hooks) during the performance of a Helm install action. Defaults to - 'HelmReleaseSpec.Timeout'. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - type: object - interval: - description: Interval at which to reconcile the Helm release. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - kubeConfig: - description: |- - KubeConfig for reconciling the HelmRelease on a remote cluster. - When used in combination with HelmReleaseSpec.ServiceAccountName, - forces the controller to act on behalf of that Service Account at the - target cluster. - If the --default-service-account flag is set, its value will be used as - a controller level fallback for when HelmReleaseSpec.ServiceAccountName - is empty. - properties: - configMapRef: - description: |- - ConfigMapRef holds an optional name of a ConfigMap that contains - the following keys: - - - `provider`: the provider to use. One of `aws`, `azure`, `gcp`, or - `generic`. Required. - - `cluster`: the fully qualified resource name of the Kubernetes - cluster in the cloud provider API. Not used by the `generic` - provider. Required when one of `address` or `ca.crt` is not set. - - `address`: the address of the Kubernetes API server. Required - for `generic`. For the other providers, if not specified, the - first address in the cluster resource will be used, and if - specified, it must match one of the addresses in the cluster - resource. - If audiences is not set, will be used as the audience for the - `generic` provider. - - `ca.crt`: the optional PEM-encoded CA certificate for the - Kubernetes API server. If not set, the controller will use the - CA certificate from the cluster resource. - - `audiences`: the optional audiences as a list of - line-break-separated strings for the Kubernetes ServiceAccount - token. Defaults to the `address` for the `generic` provider, or - to specific values for the other providers depending on the - provider. - - `serviceAccountName`: the optional name of the Kubernetes - ServiceAccount in the same namespace that should be used - for authentication. If not specified, the controller - ServiceAccount will be used. - - Mutually exclusive with SecretRef. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - secretRef: - description: |- - SecretRef holds an optional name of a secret that contains a key with - the kubeconfig file as the value. If no key is set, the key will default - to 'value'. Mutually exclusive with ConfigMapRef. - It is recommended that the kubeconfig is self-contained, and the secret - is regularly updated if credentials such as a cloud-access-token expire. - Cloud specific `cmd-path` auth helpers will not function without adding - binaries and credentials to the Pod that is responsible for reconciling - Kubernetes resources. Supported only for the generic provider. - properties: - key: - description: Key in the Secret, when not specified an implementation-specific default key is used. - type: string - name: - description: Name of the Secret. - type: string - required: - - name - type: object - type: object - x-kubernetes-validations: - - message: exactly one of spec.kubeConfig.configMapRef or spec.kubeConfig.secretRef must be specified - rule: has(self.configMapRef) || has(self.secretRef) - - message: exactly one of spec.kubeConfig.configMapRef or spec.kubeConfig.secretRef must be specified - rule: '!has(self.configMapRef) || !has(self.secretRef)' - maxHistory: - description: |- - MaxHistory is the number of revisions saved by Helm for this HelmRelease. - Use '0' for an unlimited number of revisions; defaults to '5'. - type: integer - persistentClient: - description: |- - PersistentClient tells the controller to use a persistent Kubernetes - client for this release. When enabled, the client will be reused for the - duration of the reconciliation, instead of being created and destroyed - for each (step of a) Helm action. - - This can improve performance, but may cause issues with some Helm charts - that for example do create Custom Resource Definitions during installation - outside Helm's CRD lifecycle hooks, which are then not observed to be - available by e.g. post-install hooks. - - If not set, it defaults to true. - type: boolean - postRenderers: - description: |- - PostRenderers holds an array of Helm PostRenderers, which will be applied in order - of their definition. - items: - description: PostRenderer contains a Helm PostRenderer specification. - properties: - kustomize: - description: Kustomization to apply as PostRenderer. - properties: - images: - description: |- - Images is a list of (image name, new name, new tag or digest) - for changing image names, tags or digests. This can also be achieved with a - patch, but this operator is simpler to specify. - items: - description: Image contains an image name, a new name, a new tag or digest, which will replace the original name and tag. - properties: - digest: - description: |- - Digest is the value used to replace the original image tag. - If digest is present NewTag value is ignored. - type: string - name: - description: Name is a tag-less image name. - type: string - newName: - description: NewName is the value used to replace the original name. - type: string - newTag: - description: NewTag is the value used to replace the original tag. - type: string - required: - - name - type: object - type: array - patches: - description: |- - Strategic merge and JSON patches, defined as inline YAML objects, - capable of targeting objects based on kind, label and annotation selectors. - items: - description: |- - Patch contains an inline StrategicMerge or JSON6902 patch, and the target the patch should - be applied to. - properties: - patch: - description: |- - Patch contains an inline StrategicMerge patch or an inline JSON6902 patch with - an array of operation objects. - type: string - target: - description: Target points to the resources that the patch document should be applied to. - properties: - annotationSelector: - description: |- - AnnotationSelector is a string that follows the label selection expression - https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource annotations. - type: string - group: - description: |- - Group is the API group to select resources from. - Together with Version and Kind it is capable of unambiguously identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - kind: - description: |- - Kind of the API Group to select resources from. - Together with Group and Version it is capable of unambiguously - identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - labelSelector: - description: |- - LabelSelector is a string that follows the label selection expression - https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource labels. - type: string - name: - description: Name to match resources with. - type: string - namespace: - description: Namespace to select resources from. - type: string - version: - description: |- - Version of the API Group to select resources from. - Together with Group and Kind it is capable of unambiguously identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - type: object - required: - - patch - type: object - type: array - patchesJson6902: - description: |- - JSON 6902 patches, defined as inline YAML objects. - - Deprecated: use Patches instead. - items: - description: JSON6902Patch contains a JSON6902 patch and the target the patch should be applied to. - properties: - patch: - description: Patch contains the JSON6902 patch document with an array of operation objects. - items: - description: |- - JSON6902 is a JSON6902 operation object. - https://datatracker.ietf.org/doc/html/rfc6902#section-4 - properties: - from: - description: |- - From contains a JSON-pointer value that references a location within the target document where the operation is - performed. The meaning of the value depends on the value of Op, and is NOT taken into account by all operations. - type: string - op: - description: |- - Op indicates the operation to perform. Its value MUST be one of "add", "remove", "replace", "move", "copy", or - "test". - https://datatracker.ietf.org/doc/html/rfc6902#section-4 - enum: - - test - - remove - - add - - replace - - move - - copy - type: string - path: - description: |- - Path contains the JSON-pointer value that references a location within the target document where the operation - is performed. The meaning of the value depends on the value of Op. - type: string - value: - description: |- - Value contains a valid JSON structure. The meaning of the value depends on the value of Op, and is NOT taken into - account by all operations. - x-kubernetes-preserve-unknown-fields: true - required: - - op - - path - type: object - type: array - target: - description: Target points to the resources that the patch document should be applied to. - properties: - annotationSelector: - description: |- - AnnotationSelector is a string that follows the label selection expression - https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource annotations. - type: string - group: - description: |- - Group is the API group to select resources from. - Together with Version and Kind it is capable of unambiguously identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - kind: - description: |- - Kind of the API Group to select resources from. - Together with Group and Version it is capable of unambiguously - identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - labelSelector: - description: |- - LabelSelector is a string that follows the label selection expression - https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource labels. - type: string - name: - description: Name to match resources with. - type: string - namespace: - description: Namespace to select resources from. - type: string - version: - description: |- - Version of the API Group to select resources from. - Together with Group and Kind it is capable of unambiguously identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - type: object - required: - - patch - - target - type: object - type: array - patchesStrategicMerge: - description: |- - Strategic merge patches, defined as inline YAML objects. - - Deprecated: use Patches instead. - items: - x-kubernetes-preserve-unknown-fields: true - type: array - type: object - type: object - type: array - releaseName: - description: |- - ReleaseName used for the Helm release. Defaults to a composition of - '[TargetNamespace-]Name'. - maxLength: 53 - minLength: 1 - type: string - rollback: - description: Rollback holds the configuration for Helm rollback actions for this HelmRelease. - properties: - cleanupOnFail: - description: |- - CleanupOnFail allows deletion of new resources created during the Helm - rollback action when it fails. - type: boolean - disableHooks: - description: DisableHooks prevents hooks from running during the Helm rollback action. - type: boolean - disableWait: - description: |- - DisableWait disables the waiting for resources to be ready after a Helm - rollback has been performed. - type: boolean - disableWaitForJobs: - description: |- - DisableWaitForJobs disables waiting for jobs to complete after a Helm - rollback has been performed. - type: boolean - force: - description: Force forces resource updates through a replacement strategy. - type: boolean - recreate: - description: Recreate performs pod restarts for the resource if applicable. - type: boolean - timeout: - description: |- - Timeout is the time to wait for any individual Kubernetes operation (like - Jobs for hooks) during the performance of a Helm rollback action. Defaults to - 'HelmReleaseSpec.Timeout'. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - type: object - serviceAccountName: - description: |- - The name of the Kubernetes service account to impersonate - when reconciling this HelmRelease. - maxLength: 253 - minLength: 1 - type: string - storageNamespace: - description: |- - StorageNamespace used for the Helm storage. - Defaults to the namespace of the HelmRelease. - maxLength: 63 - minLength: 1 - type: string - suspend: - description: |- - Suspend tells the controller to suspend reconciliation for this HelmRelease, - it does not apply to already started reconciliations. Defaults to false. - type: boolean - targetNamespace: - description: |- - TargetNamespace to target when performing operations for the HelmRelease. - Defaults to the namespace of the HelmRelease. - maxLength: 63 - minLength: 1 - type: string - test: - description: Test holds the configuration for Helm test actions for this HelmRelease. - properties: - enable: - description: |- - Enable enables Helm test actions for this HelmRelease after an Helm install - or upgrade action has been performed. - type: boolean - filters: - description: Filters is a list of tests to run or exclude from running. - items: - description: Filter holds the configuration for individual Helm test filters. - properties: - exclude: - description: Exclude specifies whether the named test should be excluded. - type: boolean - name: - description: Name is the name of the test. - maxLength: 253 - minLength: 1 - type: string - required: - - name - type: object - type: array - ignoreFailures: - description: |- - IgnoreFailures tells the controller to skip remediation when the Helm tests - are run but fail. Can be overwritten for tests run after install or upgrade - actions in 'Install.IgnoreTestFailures' and 'Upgrade.IgnoreTestFailures'. - type: boolean - timeout: - description: |- - Timeout is the time to wait for any individual Kubernetes operation during - the performance of a Helm test action. Defaults to 'HelmReleaseSpec.Timeout'. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - type: object - timeout: - description: |- - Timeout is the time to wait for any individual Kubernetes operation (like Jobs - for hooks) during the performance of a Helm action. Defaults to '5m0s'. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - uninstall: - description: Uninstall holds the configuration for Helm uninstall actions for this HelmRelease. - properties: - deletionPropagation: - default: background - description: |- - DeletionPropagation specifies the deletion propagation policy when - a Helm uninstall is performed. - enum: - - background - - foreground - - orphan - type: string - disableHooks: - description: DisableHooks prevents hooks from running during the Helm rollback action. - type: boolean - disableWait: - description: |- - DisableWait disables waiting for all the resources to be deleted after - a Helm uninstall is performed. - type: boolean - keepHistory: - description: |- - KeepHistory tells Helm to remove all associated resources and mark the - release as deleted, but retain the release history. - type: boolean - timeout: - description: |- - Timeout is the time to wait for any individual Kubernetes operation (like - Jobs for hooks) during the performance of a Helm uninstall action. Defaults - to 'HelmReleaseSpec.Timeout'. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - type: object - upgrade: - description: Upgrade holds the configuration for Helm upgrade actions for this HelmRelease. - properties: - cleanupOnFail: - description: |- - CleanupOnFail allows deletion of new resources created during the Helm - upgrade action when it fails. - type: boolean - crds: - description: |- - CRDs upgrade CRDs from the Helm Chart's crds directory according - to the CRD upgrade policy provided here. Valid values are `Skip`, - `Create` or `CreateReplace`. Default is `Skip` and if omitted - CRDs are neither installed nor upgraded. - - Skip: do neither install nor replace (update) any CRDs. - - Create: new CRDs are created, existing CRDs are neither updated nor deleted. - - CreateReplace: new CRDs are created, existing CRDs are updated (replaced) - but not deleted. - - By default, CRDs are not applied during Helm upgrade action. With this - option users can opt-in to CRD upgrade, which is not (yet) natively supported by Helm. - https://helm.sh/docs/chart_best_practices/custom_resource_definitions. - enum: - - Skip - - Create - - CreateReplace - type: string - disableHooks: - description: DisableHooks prevents hooks from running during the Helm upgrade action. - type: boolean - disableOpenAPIValidation: - description: |- - DisableOpenAPIValidation prevents the Helm upgrade action from validating - rendered templates against the Kubernetes OpenAPI Schema. - type: boolean - disableWait: - description: |- - DisableWait disables the waiting for resources to be ready after a Helm - upgrade has been performed. - type: boolean - disableWaitForJobs: - description: |- - DisableWaitForJobs disables waiting for jobs to complete after a Helm - upgrade has been performed. - type: boolean - force: - description: Force forces resource updates through a replacement strategy. - type: boolean - preserveValues: - description: |- - PreserveValues will make Helm reuse the last release's values and merge in - overrides from 'Values'. Setting this flag makes the HelmRelease - non-declarative. - type: boolean - remediation: - description: |- - Remediation holds the remediation configuration for when the Helm upgrade - action for the HelmRelease fails. The default is to not perform any action. - properties: - ignoreTestFailures: - description: |- - IgnoreTestFailures tells the controller to skip remediation when the Helm - tests are run after an upgrade action but fail. - Defaults to 'Test.IgnoreFailures'. - type: boolean - remediateLastFailure: - description: |- - RemediateLastFailure tells the controller to remediate the last failure, when - no retries remain. Defaults to 'false' unless 'Retries' is greater than 0. - type: boolean - retries: - description: |- - Retries is the number of retries that should be attempted on failures before - bailing. Remediation, using 'Strategy', is performed between each attempt. - Defaults to '0', a negative integer equals to unlimited retries. - type: integer - strategy: - description: Strategy to use for failure remediation. Defaults to 'rollback'. - enum: - - rollback - - uninstall - type: string - type: object - timeout: - description: |- - Timeout is the time to wait for any individual Kubernetes operation (like - Jobs for hooks) during the performance of a Helm upgrade action. Defaults to - 'HelmReleaseSpec.Timeout'. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - type: object - values: - description: Values holds the values for this Helm release. - x-kubernetes-preserve-unknown-fields: true - valuesFrom: - description: |- - ValuesFrom holds references to resources containing Helm values for this HelmRelease, - and information about how they should be merged. - items: - description: |- - ValuesReference contains a reference to a resource containing Helm values, - and optionally the key they can be found at. - properties: - kind: - description: Kind of the values referent, valid values are ('Secret', 'ConfigMap'). - enum: - - Secret - - ConfigMap - type: string - name: - description: |- - Name of the values referent. Should reside in the same namespace as the - referring resource. - maxLength: 253 - minLength: 1 - type: string - optional: - description: |- - Optional marks this ValuesReference as optional. When set, a not found error - for the values reference is ignored, but any ValuesKey, TargetPath or - transient error will still result in a reconciliation failure. - type: boolean - targetPath: - description: |- - TargetPath is the YAML dot notation path the value should be merged at. When - set, the ValuesKey is expected to be a single flat value. Defaults to 'None', - which results in the values getting merged at the root. - maxLength: 250 - pattern: ^([a-zA-Z0-9_\-.\\\/]|\[[0-9]{1,5}\])+$ - type: string - valuesKey: - description: |- - ValuesKey is the data key where the values.yaml or a specific value can be - found at. Defaults to 'values.yaml'. - maxLength: 253 - pattern: ^[\-._a-zA-Z0-9]+$ - type: string - required: - - kind - - name - type: object - type: array - required: - - interval - type: object - x-kubernetes-validations: - - message: either chart or chartRef must be set - rule: (has(self.chart) && !has(self.chartRef)) || (!has(self.chart) && has(self.chartRef)) - status: - default: - observedGeneration: -1 - description: HelmReleaseStatus defines the observed state of a HelmRelease. - properties: - conditions: - description: Conditions holds the conditions for the HelmRelease. - items: - description: Condition contains details for one aspect of the current state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - failures: - description: |- - Failures is the reconciliation failure count against the latest desired - state. It is reset after a successful reconciliation. - format: int64 - type: integer - helmChart: - description: |- - HelmChart is the namespaced name of the HelmChart resource created by - the controller for the HelmRelease. - type: string - history: - description: |- - History holds the history of Helm releases performed for this HelmRelease - up to the last successfully completed release. - items: - description: |- - Snapshot captures a point-in-time copy of the status information for a Helm release, - as managed by the controller. - properties: - apiVersion: - description: |- - APIVersion is the API version of the Snapshot. - Provisional: when the calculation method of the Digest field is changed, - this field will be used to distinguish between the old and new methods. - type: string - appVersion: - description: AppVersion is the chart app version of the release object in storage. - type: string - chartName: - description: ChartName is the chart name of the release object in storage. - type: string - chartVersion: - description: |- - ChartVersion is the chart version of the release object in - storage. - type: string - configDigest: - description: |- - ConfigDigest is the checksum of the config (better known as - "values") of the release object in storage. - It has the format of `:`. - type: string - deleted: - description: Deleted is when the release was deleted. - format: date-time - type: string - digest: - description: |- - Digest is the checksum of the release object in storage. - It has the format of `:`. - type: string - firstDeployed: - description: FirstDeployed is when the release was first deployed. - format: date-time - type: string - lastDeployed: - description: LastDeployed is when the release was last deployed. - format: date-time - type: string - name: - description: Name is the name of the release. - type: string - namespace: - description: Namespace is the namespace the release is deployed to. - type: string - ociDigest: - description: OCIDigest is the digest of the OCI artifact associated with the release. - type: string - status: - description: Status is the current state of the release. - type: string - testHooks: - additionalProperties: - description: |- - TestHookStatus holds the status information for a test hook as observed - to be run by the controller. - properties: - lastCompleted: - description: LastCompleted is the time the test hook last completed. - format: date-time - type: string - lastStarted: - description: LastStarted is the time the test hook was last started. - format: date-time - type: string - phase: - description: Phase the test hook was observed to be in. - type: string - type: object - description: |- - TestHooks is the list of test hooks for the release as observed to be - run by the controller. - type: object - version: - description: Version is the version of the release object in storage. - type: integer - required: - - chartName - - chartVersion - - configDigest - - digest - - firstDeployed - - lastDeployed - - name - - namespace - - status - - version - type: object - type: array - installFailures: - description: |- - InstallFailures is the install failure count against the latest desired - state. It is reset after a successful reconciliation. - format: int64 - type: integer - lastAppliedRevision: - description: |- - LastAppliedRevision is the revision of the last successfully applied - source. - - Deprecated: the revision can now be found in the History. - type: string - lastAttemptedConfigDigest: - description: |- - LastAttemptedConfigDigest is the digest for the config (better known as - "values") of the last reconciliation attempt. - type: string - lastAttemptedGeneration: - description: |- - LastAttemptedGeneration is the last generation the controller attempted - to reconcile. - format: int64 - type: integer - lastAttemptedReleaseAction: - description: |- - LastAttemptedReleaseAction is the last release action performed for this - HelmRelease. It is used to determine the active remediation strategy. - enum: - - install - - upgrade - type: string - lastAttemptedRevision: - description: |- - LastAttemptedRevision is the Source revision of the last reconciliation - attempt. For OCIRepository sources, the 12 first characters of the digest are - appended to the chart version e.g. "1.2.3+1234567890ab". - type: string - lastAttemptedRevisionDigest: - description: |- - LastAttemptedRevisionDigest is the digest of the last reconciliation attempt. - This is only set for OCIRepository sources. - type: string - lastAttemptedValuesChecksum: - description: |- - LastAttemptedValuesChecksum is the SHA1 checksum for the values of the last - reconciliation attempt. - - Deprecated: Use LastAttemptedConfigDigest instead. - type: string - lastHandledForceAt: - description: |- - LastHandledForceAt holds the value of the most recent force request - value, so a change of the annotation value can be detected. - type: string - lastHandledReconcileAt: - description: |- - LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value - can be detected. - type: string - lastHandledResetAt: - description: |- - LastHandledResetAt holds the value of the most recent reset request - value, so a change of the annotation value can be detected. - type: string - lastReleaseRevision: - description: |- - LastReleaseRevision is the revision of the last successful Helm release. - - Deprecated: Use History instead. - type: integer - observedGeneration: - description: ObservedGeneration is the last observed generation. - format: int64 - type: integer - observedPostRenderersDigest: - description: |- - ObservedPostRenderersDigest is the digest for the post-renderers of - the last successful reconciliation attempt. - type: string - storageNamespace: - description: |- - StorageNamespace is the namespace of the Helm release storage for the - current release. - maxLength: 63 - minLength: 1 - type: string - upgradeFailures: - description: |- - UpgradeFailures is the upgrade failure count against the latest desired - state. It is reset after a successful reconciliation. - format: int64 - type: integer - type: object - type: object - served: true - storage: false - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - app.kubernetes.io/role: cluster-admin - controller-gen.kubebuilder.io/version: v0.19.0 - labels: - app.kubernetes.io/name: flux - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.7.3 - name: helmrepositories.source.toolkit.fluxcd.io -spec: - group: source.toolkit.fluxcd.io - names: - kind: HelmRepository - listKind: HelmRepositoryList - plural: helmrepositories - shortNames: - - helmrepo - singular: helmrepository - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.url - name: URL - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - name: v1 - schema: - openAPIV3Schema: - description: HelmRepository is the Schema for the helmrepositories API. - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: |- - HelmRepositorySpec specifies the required configuration to produce an - Artifact for a Helm repository index YAML. - properties: - accessFrom: - description: |- - AccessFrom specifies an Access Control List for allowing cross-namespace - references to this object. - NOTE: Not implemented, provisional as of https://github.com/fluxcd/flux2/pull/2092 - properties: - namespaceSelectors: - description: |- - NamespaceSelectors is the list of namespace selectors to which this ACL applies. - Items in this list are evaluated using a logical OR operation. - items: - description: |- - NamespaceSelector selects the namespaces to which this ACL applies. - An empty map of MatchLabels matches all namespaces in a cluster. - properties: - matchLabels: - additionalProperties: - type: string - description: |- - MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - type: array - required: - - namespaceSelectors - type: object - certSecretRef: - description: |- - CertSecretRef can be given the name of a Secret containing - either or both of - - - a PEM-encoded client certificate (`tls.crt`) and private - key (`tls.key`); - - a PEM-encoded CA certificate (`ca.crt`) - - and whichever are supplied, will be used for connecting to the - registry. The client cert and key are useful if you are - authenticating with a certificate; the CA cert is useful if - you are using a self-signed server certificate. The Secret must - be of type `Opaque` or `kubernetes.io/tls`. - - It takes precedence over the values specified in the Secret referred - to by `.spec.secretRef`. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - insecure: - description: |- - Insecure allows connecting to a non-TLS HTTP container registry. - This field is only taken into account if the .spec.type field is set to 'oci'. - type: boolean - interval: - description: |- - Interval at which the HelmRepository URL is checked for updates. - This interval is approximate and may be subject to jitter to ensure - efficient use of resources. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - passCredentials: - description: |- - PassCredentials allows the credentials from the SecretRef to be passed - on to a host that does not match the host as defined in URL. - This may be required if the host of the advertised chart URLs in the - index differ from the defined URL. - Enabling this should be done with caution, as it can potentially result - in credentials getting stolen in a MITM-attack. - type: boolean - provider: - default: generic - description: |- - Provider used for authentication, can be 'aws', 'azure', 'gcp' or 'generic'. - This field is optional, and only taken into account if the .spec.type field is set to 'oci'. - When not specified, defaults to 'generic'. - enum: - - generic - - aws - - azure - - gcp - type: string - secretRef: - description: |- - SecretRef specifies the Secret containing authentication credentials - for the HelmRepository. - For HTTP/S basic auth the secret must contain 'username' and 'password' - fields. - Support for TLS auth using the 'certFile' and 'keyFile', and/or 'caFile' - keys is deprecated. Please use `.spec.certSecretRef` instead. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - suspend: - description: |- - Suspend tells the controller to suspend the reconciliation of this - HelmRepository. - type: boolean - timeout: - description: |- - Timeout is used for the index fetch operation for an HTTPS helm repository, - and for remote OCI Repository operations like pulling for an OCI helm - chart by the associated HelmChart. - Its default value is 60s. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m))+$ - type: string - type: - description: |- - Type of the HelmRepository. - When this field is set to "oci", the URL field value must be prefixed with "oci://". - enum: - - default - - oci - type: string - url: - description: |- - URL of the Helm repository, a valid URL contains at least a protocol and - host. - pattern: ^(http|https|oci)://.*$ - type: string - required: - - url - type: object - status: - default: - observedGeneration: -1 - description: HelmRepositoryStatus records the observed state of the HelmRepository. - properties: - artifact: - description: Artifact represents the last successful HelmRepository reconciliation. - properties: - digest: - description: Digest is the digest of the file in the form of ':'. - pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$ - type: string - lastUpdateTime: - description: |- - LastUpdateTime is the timestamp corresponding to the last update of the - Artifact. - format: date-time - type: string - metadata: - additionalProperties: - type: string - description: Metadata holds upstream information such as OCI annotations. - type: object - path: - description: |- - Path is the relative file path of the Artifact. It can be used to locate - the file in the root of the Artifact storage on the local file system of - the controller managing the Source. - type: string - revision: - description: |- - Revision is a human-readable identifier traceable in the origin source - system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. - type: string - size: - description: Size is the number of bytes in the file. - format: int64 - type: integer - url: - description: |- - URL is the HTTP address of the Artifact as exposed by the controller - managing the Source. It can be used to retrieve the Artifact for - consumption, e.g. by another controller applying the Artifact contents. - type: string - required: - - digest - - lastUpdateTime - - path - - revision - - url - type: object - conditions: - description: Conditions holds the conditions for the HelmRepository. - items: - description: Condition contains details for one aspect of the current state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastHandledReconcileAt: - description: |- - LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value - can be detected. - type: string - observedGeneration: - description: |- - ObservedGeneration is the last observed generation of the HelmRepository - object. - format: int64 - type: integer - url: - description: |- - URL is the dynamic fetch link for the latest Artifact. - It is provided on a "best effort" basis, and using the precise - HelmRepositoryStatus.Artifact data is recommended. - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .spec.url - name: URL - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - deprecated: true - deprecationWarning: v1beta2 HelmRepository is deprecated, upgrade to v1 - name: v1beta2 - schema: - openAPIV3Schema: - description: HelmRepository is the Schema for the helmrepositories API. - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: |- - HelmRepositorySpec specifies the required configuration to produce an - Artifact for a Helm repository index YAML. - properties: - accessFrom: - description: |- - AccessFrom specifies an Access Control List for allowing cross-namespace - references to this object. - NOTE: Not implemented, provisional as of https://github.com/fluxcd/flux2/pull/2092 - properties: - namespaceSelectors: - description: |- - NamespaceSelectors is the list of namespace selectors to which this ACL applies. - Items in this list are evaluated using a logical OR operation. - items: - description: |- - NamespaceSelector selects the namespaces to which this ACL applies. - An empty map of MatchLabels matches all namespaces in a cluster. - properties: - matchLabels: - additionalProperties: - type: string - description: |- - MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - type: array - required: - - namespaceSelectors - type: object - certSecretRef: - description: |- - CertSecretRef can be given the name of a Secret containing - either or both of - - - a PEM-encoded client certificate (`tls.crt`) and private - key (`tls.key`); - - a PEM-encoded CA certificate (`ca.crt`) - - and whichever are supplied, will be used for connecting to the - registry. The client cert and key are useful if you are - authenticating with a certificate; the CA cert is useful if - you are using a self-signed server certificate. The Secret must - be of type `Opaque` or `kubernetes.io/tls`. - - It takes precedence over the values specified in the Secret referred - to by `.spec.secretRef`. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - insecure: - description: |- - Insecure allows connecting to a non-TLS HTTP container registry. - This field is only taken into account if the .spec.type field is set to 'oci'. - type: boolean - interval: - description: |- - Interval at which the HelmRepository URL is checked for updates. - This interval is approximate and may be subject to jitter to ensure - efficient use of resources. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - passCredentials: - description: |- - PassCredentials allows the credentials from the SecretRef to be passed - on to a host that does not match the host as defined in URL. - This may be required if the host of the advertised chart URLs in the - index differ from the defined URL. - Enabling this should be done with caution, as it can potentially result - in credentials getting stolen in a MITM-attack. - type: boolean - provider: - default: generic - description: |- - Provider used for authentication, can be 'aws', 'azure', 'gcp' or 'generic'. - This field is optional, and only taken into account if the .spec.type field is set to 'oci'. - When not specified, defaults to 'generic'. - enum: - - generic - - aws - - azure - - gcp - type: string - secretRef: - description: |- - SecretRef specifies the Secret containing authentication credentials - for the HelmRepository. - For HTTP/S basic auth the secret must contain 'username' and 'password' - fields. - Support for TLS auth using the 'certFile' and 'keyFile', and/or 'caFile' - keys is deprecated. Please use `.spec.certSecretRef` instead. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - suspend: - description: |- - Suspend tells the controller to suspend the reconciliation of this - HelmRepository. - type: boolean - timeout: - description: |- - Timeout is used for the index fetch operation for an HTTPS helm repository, - and for remote OCI Repository operations like pulling for an OCI helm - chart by the associated HelmChart. - Its default value is 60s. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m))+$ - type: string - type: - description: |- - Type of the HelmRepository. - When this field is set to "oci", the URL field value must be prefixed with "oci://". - enum: - - default - - oci - type: string - url: - description: |- - URL of the Helm repository, a valid URL contains at least a protocol and - host. - pattern: ^(http|https|oci)://.*$ - type: string - required: - - url - type: object - status: - default: - observedGeneration: -1 - description: HelmRepositoryStatus records the observed state of the HelmRepository. - properties: - artifact: - description: Artifact represents the last successful HelmRepository reconciliation. - properties: - digest: - description: Digest is the digest of the file in the form of ':'. - pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$ - type: string - lastUpdateTime: - description: |- - LastUpdateTime is the timestamp corresponding to the last update of the - Artifact. - format: date-time - type: string - metadata: - additionalProperties: - type: string - description: Metadata holds upstream information such as OCI annotations. - type: object - path: - description: |- - Path is the relative file path of the Artifact. It can be used to locate - the file in the root of the Artifact storage on the local file system of - the controller managing the Source. - type: string - revision: - description: |- - Revision is a human-readable identifier traceable in the origin source - system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. - type: string - size: - description: Size is the number of bytes in the file. - format: int64 - type: integer - url: - description: |- - URL is the HTTP address of the Artifact as exposed by the controller - managing the Source. It can be used to retrieve the Artifact for - consumption, e.g. by another controller applying the Artifact contents. - type: string - required: - - digest - - lastUpdateTime - - path - - revision - - url - type: object - conditions: - description: Conditions holds the conditions for the HelmRepository. - items: - description: Condition contains details for one aspect of the current state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastHandledReconcileAt: - description: |- - LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value - can be detected. - type: string - observedGeneration: - description: |- - ObservedGeneration is the last observed generation of the HelmRepository - object. - format: int64 - type: integer - url: - description: |- - URL is the dynamic fetch link for the latest Artifact. - It is provided on a "best effort" basis, and using the precise - HelmRepositoryStatus.Artifact data is recommended. - type: string - type: object - type: object - served: true - storage: false - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - app.kubernetes.io/role: cluster-admin - controller-gen.kubebuilder.io/version: v0.19.0 - labels: - app.kubernetes.io/name: flux - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.7.3 - name: imagepolicies.image.toolkit.fluxcd.io -spec: - group: image.toolkit.fluxcd.io - names: - kind: ImagePolicy - listKind: ImagePolicyList - plural: imagepolicies - shortNames: - - imgpol - - imagepol - singular: imagepolicy - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.latestRef.name - name: Image - type: string - - jsonPath: .status.latestRef.tag - name: Tag - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1 - schema: - openAPIV3Schema: - description: ImagePolicy is the Schema for the imagepolicies API - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: |- - ImagePolicySpec defines the parameters for calculating the - ImagePolicy. - properties: - digestReflectionPolicy: - default: Never - description: |- - DigestReflectionPolicy governs the setting of the `.status.latestRef.digest` field. - - Never: The digest field will always be set to the empty string. - - IfNotPresent: The digest field will be set to the digest of the elected - latest image if the field is empty and the image did not change. - - Always: The digest field will always be set to the digest of the elected - latest image. - - Default: Never. - enum: - - Always - - IfNotPresent - - Never - type: string - filterTags: - description: |- - FilterTags enables filtering for only a subset of tags based on a set of - rules. If no rules are provided, all the tags from the repository will be - ordered and compared. - properties: - extract: - description: |- - Extract allows a capture group to be extracted from the specified regular - expression pattern, useful before tag evaluation. - type: string - pattern: - description: |- - Pattern specifies a regular expression pattern used to filter for image - tags. - type: string - type: object - imageRepositoryRef: - description: |- - ImageRepositoryRef points at the object specifying the image - being scanned - properties: - name: - description: Name of the referent. - type: string - namespace: - description: Namespace of the referent, when not specified it acts as LocalObjectReference. - type: string - required: - - name - type: object - interval: - description: |- - Interval is the length of time to wait between - refreshing the digest of the latest tag when the - reflection policy is set to "Always". - - Defaults to 10m. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - policy: - description: |- - Policy gives the particulars of the policy to be followed in - selecting the most recent image - properties: - alphabetical: - description: Alphabetical set of rules to use for alphabetical ordering of the tags. - properties: - order: - default: asc - description: |- - Order specifies the sorting order of the tags. Given the letters of the - alphabet as tags, ascending order would select Z, and descending order - would select A. - enum: - - asc - - desc - type: string - type: object - numerical: - description: Numerical set of rules to use for numerical ordering of the tags. - properties: - order: - default: asc - description: |- - Order specifies the sorting order of the tags. Given the integer values - from 0 to 9 as tags, ascending order would select 9, and descending order - would select 0. - enum: - - asc - - desc - type: string - type: object - semver: - description: |- - SemVer gives a semantic version range to check against the tags - available. - properties: - range: - description: |- - Range gives a semver range for the image tag; the highest - version within the range that's a tag yields the latest image. - type: string - required: - - range - type: object - type: object - suspend: - description: |- - This flag tells the controller to suspend subsequent policy reconciliations. - It does not apply to already started reconciliations. Defaults to false. - type: boolean - required: - - imageRepositoryRef - - policy - type: object - x-kubernetes-validations: - - message: spec.interval is only accepted when spec.digestReflectionPolicy is set to 'Always' - rule: '!has(self.interval) || (has(self.digestReflectionPolicy) && self.digestReflectionPolicy == ''Always'')' - - message: spec.interval must be set when spec.digestReflectionPolicy is set to 'Always' - rule: has(self.interval) || !has(self.digestReflectionPolicy) || self.digestReflectionPolicy != 'Always' - status: - default: - observedGeneration: -1 - description: ImagePolicyStatus defines the observed state of ImagePolicy - properties: - conditions: - items: - description: Condition contains details for one aspect of the current state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastHandledReconcileAt: - description: |- - LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value - can be detected. - type: string - latestRef: - description: |- - LatestRef gives the first in the list of images scanned by - the image repository, when filtered and ordered according - to the policy. - properties: - digest: - description: Digest is the image's digest. - type: string - name: - description: Name is the bare image's name. - type: string - tag: - description: Tag is the image's tag. - type: string - required: - - name - - tag - type: object - observedGeneration: - format: int64 - type: integer - observedPreviousRef: - description: |- - ObservedPreviousRef is the observed previous LatestRef. It is used - to keep track of the previous and current images. - properties: - digest: - description: Digest is the image's digest. - type: string - name: - description: Name is the bare image's name. - type: string - tag: - description: Tag is the image's tag. - type: string - required: - - name - - tag - type: object - type: object - type: object - served: true - storage: true - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .status.latestRef.name - name: Image - type: string - - jsonPath: .status.latestRef.tag - name: Tag - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - deprecated: true - deprecationWarning: v1beta2 ImagePolicy is deprecated, upgrade to v1 - name: v1beta2 - schema: - openAPIV3Schema: - description: ImagePolicy is the Schema for the imagepolicies API - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: |- - ImagePolicySpec defines the parameters for calculating the - ImagePolicy. - properties: - digestReflectionPolicy: - default: Never - description: |- - DigestReflectionPolicy governs the setting of the `.status.latestRef.digest` field. - - Never: The digest field will always be set to the empty string. - - IfNotPresent: The digest field will be set to the digest of the elected - latest image if the field is empty and the image did not change. - - Always: The digest field will always be set to the digest of the elected - latest image. - - Default: Never. - enum: - - Always - - IfNotPresent - - Never - type: string - filterTags: - description: |- - FilterTags enables filtering for only a subset of tags based on a set of - rules. If no rules are provided, all the tags from the repository will be - ordered and compared. - properties: - extract: - description: |- - Extract allows a capture group to be extracted from the specified regular - expression pattern, useful before tag evaluation. - type: string - pattern: - description: |- - Pattern specifies a regular expression pattern used to filter for image - tags. - type: string - type: object - imageRepositoryRef: - description: |- - ImageRepositoryRef points at the object specifying the image - being scanned - properties: - name: - description: Name of the referent. - type: string - namespace: - description: Namespace of the referent, when not specified it acts as LocalObjectReference. - type: string - required: - - name - type: object - interval: - description: |- - Interval is the length of time to wait between - refreshing the digest of the latest tag when the - reflection policy is set to "Always". - - Defaults to 10m. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - policy: - description: |- - Policy gives the particulars of the policy to be followed in - selecting the most recent image - properties: - alphabetical: - description: Alphabetical set of rules to use for alphabetical ordering of the tags. - properties: - order: - default: asc - description: |- - Order specifies the sorting order of the tags. Given the letters of the - alphabet as tags, ascending order would select Z, and descending order - would select A. - enum: - - asc - - desc - type: string - type: object - numerical: - description: Numerical set of rules to use for numerical ordering of the tags. - properties: - order: - default: asc - description: |- - Order specifies the sorting order of the tags. Given the integer values - from 0 to 9 as tags, ascending order would select 9, and descending order - would select 0. - enum: - - asc - - desc - type: string - type: object - semver: - description: |- - SemVer gives a semantic version range to check against the tags - available. - properties: - range: - description: |- - Range gives a semver range for the image tag; the highest - version within the range that's a tag yields the latest image. - type: string - required: - - range - type: object - type: object - suspend: - description: |- - This flag tells the controller to suspend subsequent policy reconciliations. - It does not apply to already started reconciliations. Defaults to false. - type: boolean - required: - - imageRepositoryRef - - policy - type: object - x-kubernetes-validations: - - message: spec.interval is only accepted when spec.digestReflectionPolicy is set to 'Always' - rule: '!has(self.interval) || (has(self.digestReflectionPolicy) && self.digestReflectionPolicy == ''Always'')' - - message: spec.interval must be set when spec.digestReflectionPolicy is set to 'Always' - rule: has(self.interval) || !has(self.digestReflectionPolicy) || self.digestReflectionPolicy != 'Always' - status: - default: - observedGeneration: -1 - description: ImagePolicyStatus defines the observed state of ImagePolicy - properties: - conditions: - items: - description: Condition contains details for one aspect of the current state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastHandledReconcileAt: - description: |- - LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value - can be detected. - type: string - latestRef: - description: |- - LatestRef gives the first in the list of images scanned by - the image repository, when filtered and ordered according - to the policy. - properties: - digest: - description: Digest is the image's digest. - type: string - name: - description: Name is the bare image's name. - type: string - tag: - description: Tag is the image's tag. - type: string - required: - - name - - tag - type: object - observedGeneration: - format: int64 - type: integer - observedPreviousRef: - description: |- - ObservedPreviousRef is the observed previous LatestRef. It is used - to keep track of the previous and current images. - properties: - digest: - description: Digest is the image's digest. - type: string - name: - description: Name is the bare image's name. - type: string - tag: - description: Tag is the image's tag. - type: string - required: - - name - - tag - type: object - type: object - type: object - served: true - storage: false - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - app.kubernetes.io/role: cluster-admin - controller-gen.kubebuilder.io/version: v0.19.0 - labels: - app.kubernetes.io/name: flux - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.7.3 - name: imagerepositories.image.toolkit.fluxcd.io -spec: - group: image.toolkit.fluxcd.io - names: - kind: ImageRepository - listKind: ImageRepositoryList - plural: imagerepositories - shortNames: - - imgrepo - - imagerepo - singular: imagerepository - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.image - name: Image - type: string - - jsonPath: .status.lastScanResult.tagCount - name: Tags - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - - jsonPath: .status.lastScanResult.scanTime - name: Last scan - priority: 1 - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1 - schema: - openAPIV3Schema: - description: ImageRepository is the Schema for the imagerepositories API - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: |- - ImageRepositorySpec defines the parameters for scanning an image - repository, e.g., `fluxcd/flux`. - properties: - accessFrom: - description: |- - AccessFrom defines an ACL for allowing cross-namespace references - to the ImageRepository object based on the caller's namespace labels. - properties: - namespaceSelectors: - description: |- - NamespaceSelectors is the list of namespace selectors to which this ACL applies. - Items in this list are evaluated using a logical OR operation. - items: - description: |- - NamespaceSelector selects the namespaces to which this ACL applies. - An empty map of MatchLabels matches all namespaces in a cluster. - properties: - matchLabels: - additionalProperties: - type: string - description: |- - MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - type: array - required: - - namespaceSelectors - type: object - certSecretRef: - description: |- - CertSecretRef can be given the name of a Secret containing - either or both of - - - a PEM-encoded client certificate (`tls.crt`) and private - key (`tls.key`); - - a PEM-encoded CA certificate (`ca.crt`) - - and whichever are supplied, will be used for connecting to the - registry. The client cert and key are useful if you are - authenticating with a certificate; the CA cert is useful if - you are using a self-signed server certificate. The Secret must - be of type `Opaque` or `kubernetes.io/tls`. - - Note: Support for the `caFile`, `certFile` and `keyFile` keys has - been deprecated. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - exclusionList: - default: - - ^.*\.sig$ - description: |- - ExclusionList is a list of regex strings used to exclude certain tags - from being stored in the database. - items: - type: string - maxItems: 25 - type: array - image: - description: Image is the name of the image repository - type: string - insecure: - description: Insecure allows connecting to a non-TLS HTTP container registry. - type: boolean - interval: - description: |- - Interval is the length of time to wait between - scans of the image repository. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - provider: - default: generic - description: |- - The provider used for authentication, can be 'aws', 'azure', 'gcp' or 'generic'. - When not specified, defaults to 'generic'. - enum: - - generic - - aws - - azure - - gcp - type: string - proxySecretRef: - description: |- - ProxySecretRef specifies the Secret containing the proxy configuration - to use while communicating with the container registry. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - secretRef: - description: |- - SecretRef can be given the name of a secret containing - credentials to use for the image registry. The secret should be - created with `kubectl create secret docker-registry`, or the - equivalent. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - serviceAccountName: - description: |- - ServiceAccountName is the name of the Kubernetes ServiceAccount used to authenticate - the image pull if the service account has attached pull secrets. - maxLength: 253 - type: string - suspend: - description: |- - This flag tells the controller to suspend subsequent image scans. - It does not apply to already started scans. Defaults to false. - type: boolean - timeout: - description: |- - Timeout for image scanning. - Defaults to 'Interval' duration. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m))+$ - type: string - required: - - image - - interval - type: object - status: - default: - observedGeneration: -1 - description: ImageRepositoryStatus defines the observed state of ImageRepository - properties: - canonicalImageName: - description: |- - CanonicalName is the name of the image repository with all the - implied bits made explicit; e.g., `docker.io/library/alpine` - rather than `alpine`. - type: string - conditions: - items: - description: Condition contains details for one aspect of the current state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastHandledReconcileAt: - description: |- - LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value - can be detected. - type: string - lastScanResult: - description: LastScanResult contains the number of fetched tags. - properties: - latestTags: - description: |- - LatestTags is a small sample of the tags found in the last scan. - It's the first 10 tags when sorting all the tags in descending - alphabetical order. - items: - type: string - type: array - revision: - description: Revision is a stable hash of the scanned tags. - type: string - scanTime: - description: ScanTime is the time when the last scan was performed. - format: date-time - type: string - tagCount: - description: TagCount is the number of tags found in the last scan. - type: integer - required: - - tagCount - type: object - observedExclusionList: - description: |- - ObservedExclusionList is a list of observed exclusion list. It reflects - the exclusion rules used for the observed scan result in - spec.lastScanResult. - items: - type: string - type: array - observedGeneration: - description: ObservedGeneration is the last reconciled generation. - format: int64 - type: integer - type: object - type: object - served: true - storage: true - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .spec.image - name: Image - type: string - - jsonPath: .status.lastScanResult.tagCount - name: Tags - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - - jsonPath: .status.lastScanResult.scanTime - name: Last scan - priority: 1 - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - deprecated: true - deprecationWarning: v1beta2 ImageRepository is deprecated, upgrade to v1 - name: v1beta2 - schema: - openAPIV3Schema: - description: ImageRepository is the Schema for the imagerepositories API - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: |- - ImageRepositorySpec defines the parameters for scanning an image - repository, e.g., `fluxcd/flux`. - properties: - accessFrom: - description: |- - AccessFrom defines an ACL for allowing cross-namespace references - to the ImageRepository object based on the caller's namespace labels. - properties: - namespaceSelectors: - description: |- - NamespaceSelectors is the list of namespace selectors to which this ACL applies. - Items in this list are evaluated using a logical OR operation. - items: - description: |- - NamespaceSelector selects the namespaces to which this ACL applies. - An empty map of MatchLabels matches all namespaces in a cluster. - properties: - matchLabels: - additionalProperties: - type: string - description: |- - MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - type: array - required: - - namespaceSelectors - type: object - certSecretRef: - description: |- - CertSecretRef can be given the name of a Secret containing - either or both of - - - a PEM-encoded client certificate (`tls.crt`) and private - key (`tls.key`); - - a PEM-encoded CA certificate (`ca.crt`) - - and whichever are supplied, will be used for connecting to the - registry. The client cert and key are useful if you are - authenticating with a certificate; the CA cert is useful if - you are using a self-signed server certificate. The Secret must - be of type `Opaque` or `kubernetes.io/tls`. - - Note: Support for the `caFile`, `certFile` and `keyFile` keys has - been deprecated. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - exclusionList: - default: - - ^.*\.sig$ - description: |- - ExclusionList is a list of regex strings used to exclude certain tags - from being stored in the database. - items: - type: string - maxItems: 25 - type: array - image: - description: Image is the name of the image repository - type: string - insecure: - description: Insecure allows connecting to a non-TLS HTTP container registry. - type: boolean - interval: - description: |- - Interval is the length of time to wait between - scans of the image repository. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - provider: - default: generic - description: |- - The provider used for authentication, can be 'aws', 'azure', 'gcp' or 'generic'. - When not specified, defaults to 'generic'. - enum: - - generic - - aws - - azure - - gcp - type: string - proxySecretRef: - description: |- - ProxySecretRef specifies the Secret containing the proxy configuration - to use while communicating with the container registry. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - secretRef: - description: |- - SecretRef can be given the name of a secret containing - credentials to use for the image registry. The secret should be - created with `kubectl create secret docker-registry`, or the - equivalent. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - serviceAccountName: - description: |- - ServiceAccountName is the name of the Kubernetes ServiceAccount used to authenticate - the image pull if the service account has attached pull secrets. - maxLength: 253 - type: string - suspend: - description: |- - This flag tells the controller to suspend subsequent image scans. - It does not apply to already started scans. Defaults to false. - type: boolean - timeout: - description: |- - Timeout for image scanning. - Defaults to 'Interval' duration. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m))+$ - type: string - required: - - image - - interval - type: object - status: - default: - observedGeneration: -1 - description: ImageRepositoryStatus defines the observed state of ImageRepository - properties: - canonicalImageName: - description: |- - CanonicalName is the name of the image repository with all the - implied bits made explicit; e.g., `docker.io/library/alpine` - rather than `alpine`. - type: string - conditions: - items: - description: Condition contains details for one aspect of the current state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastHandledReconcileAt: - description: |- - LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value - can be detected. - type: string - lastScanResult: - description: LastScanResult contains the number of fetched tags. - properties: - latestTags: - description: |- - LatestTags is a small sample of the tags found in the last scan. - It's the first 10 tags when sorting all the tags in descending - alphabetical order. - items: - type: string - type: array - revision: - description: Revision is a stable hash of the scanned tags. - type: string - scanTime: - description: ScanTime is the time when the last scan was performed. - format: date-time - type: string - tagCount: - description: TagCount is the number of tags found in the last scan. - type: integer - required: - - tagCount - type: object - observedExclusionList: - description: |- - ObservedExclusionList is a list of observed exclusion list. It reflects - the exclusion rules used for the observed scan result in - spec.lastScanResult. - items: - type: string - type: array - observedGeneration: - description: ObservedGeneration is the last reconciled generation. - format: int64 - type: integer - type: object - type: object - served: true - storage: false - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - app.kubernetes.io/role: cluster-admin - controller-gen.kubebuilder.io/version: v0.19.0 - labels: - app.kubernetes.io/name: flux - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.7.3 - name: imageupdateautomations.image.toolkit.fluxcd.io -spec: - group: image.toolkit.fluxcd.io - names: - kind: ImageUpdateAutomation - listKind: ImageUpdateAutomationList - plural: imageupdateautomations - shortNames: - - iua - - imgupd - - imgauto - singular: imageupdateautomation - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - - jsonPath: .status.lastAutomationRunTime - name: Last run - priority: 1 - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1 - schema: - openAPIV3Schema: - description: ImageUpdateAutomation is the Schema for the imageupdateautomations API - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: ImageUpdateAutomationSpec defines the desired state of ImageUpdateAutomation - properties: - git: - description: |- - GitSpec contains all the git-specific definitions. This is - technically optional, but in practice mandatory until there are - other kinds of source allowed. - properties: - checkout: - description: |- - Checkout gives the parameters for cloning the git repository, - ready to make changes. If not present, the `spec.ref` field from the - referenced `GitRepository` or its default will be used. - properties: - ref: - description: |- - Reference gives a branch, tag or commit to clone from the Git - repository. - properties: - branch: - description: Branch to check out, defaults to 'master' if no other field is defined. - type: string - commit: - description: |- - Commit SHA to check out, takes precedence over all reference fields. - - This can be combined with Branch to shallow clone the branch, in which - the commit is expected to exist. - type: string - name: - description: |- - Name of the reference to check out; takes precedence over Branch, Tag and SemVer. - - It must be a valid Git reference: https://git-scm.com/docs/git-check-ref-format#_description - Examples: "refs/heads/main", "refs/tags/v0.1.0", "refs/pull/420/head", "refs/merge-requests/1/head" - type: string - semver: - description: SemVer tag expression to check out, takes precedence over Tag. - type: string - tag: - description: Tag to check out, takes precedence over Branch. - type: string - type: object - required: - - ref - type: object - commit: - description: Commit specifies how to commit to the git repository. - properties: - author: - description: |- - Author gives the email and optionally the name to use as the - author of commits. - properties: - email: - description: Email gives the email to provide when making a commit. - type: string - name: - description: Name gives the name to provide when making a commit. - type: string - required: - - email - type: object - messageTemplate: - description: |- - MessageTemplate provides a template for the commit message, - into which will be interpolated the details of the change made. - Note: The `Updated` template field has been removed. Use `Changed` instead. - type: string - messageTemplateValues: - additionalProperties: - type: string - description: |- - MessageTemplateValues provides additional values to be available to the - templating rendering. - type: object - signingKey: - description: SigningKey provides the option to sign commits with a GPG key - properties: - secretRef: - description: |- - SecretRef holds the name to a secret that contains a 'git.asc' key - corresponding to the ASCII Armored file containing the GPG signing - keypair as the value. It must be in the same namespace as the - ImageUpdateAutomation. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - required: - - secretRef - type: object - required: - - author - type: object - push: - description: |- - Push specifies how and where to push commits made by the - automation. If missing, commits are pushed (back) to - `.spec.checkout.branch` or its default. - properties: - branch: - description: |- - Branch specifies that commits should be pushed to the branch - named. The branch is created using `.spec.checkout.branch` as the - starting point, if it doesn't already exist. - type: string - options: - additionalProperties: - type: string - description: |- - Options specifies the push options that are sent to the Git - server when performing a push operation. For details, see: - https://git-scm.com/docs/git-push#Documentation/git-push.txt---push-optionltoptiongt - type: object - refspec: - description: |- - Refspec specifies the Git Refspec to use for a push operation. - If both Branch and Refspec are provided, then the commit is pushed - to the branch and also using the specified refspec. - For more details about Git Refspecs, see: - https://git-scm.com/book/en/v2/Git-Internals-The-Refspec - type: string - type: object - required: - - commit - type: object - interval: - description: |- - Interval gives an lower bound for how often the automation - run should be attempted. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - policySelector: - description: |- - PolicySelector allows to filter applied policies based on labels. - By default includes all policies in namespace. - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - sourceRef: - description: |- - SourceRef refers to the resource giving access details - to a git repository. - properties: - apiVersion: - description: API version of the referent. - type: string - kind: - default: GitRepository - description: Kind of the referent. - enum: - - GitRepository - type: string - name: - description: Name of the referent. - type: string - namespace: - description: Namespace of the referent, defaults to the namespace of the Kubernetes resource object that contains the reference. - type: string - required: - - kind - - name - type: object - suspend: - description: |- - Suspend tells the controller to not run this automation, until - it is unset (or set to false). Defaults to false. - type: boolean - update: - default: - strategy: Setters - description: |- - Update gives the specification for how to update the files in - the repository. This can be left empty, to use the default - value. - properties: - path: - description: |- - Path to the directory containing the manifests to be updated. - Defaults to 'None', which translates to the root path - of the GitRepositoryRef. - type: string - strategy: - default: Setters - description: Strategy names the strategy to be used. - enum: - - Setters - type: string - type: object - required: - - interval - - sourceRef - type: object - status: - default: - observedGeneration: -1 - description: ImageUpdateAutomationStatus defines the observed state of ImageUpdateAutomation - properties: - conditions: - items: - description: Condition contains details for one aspect of the current state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastAutomationRunTime: - description: |- - LastAutomationRunTime records the last time the controller ran - this automation through to completion (even if no updates were - made). - format: date-time - type: string - lastHandledReconcileAt: - description: |- - LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value - can be detected. - type: string - lastPushCommit: - description: |- - LastPushCommit records the SHA1 of the last commit made by the - controller, for this automation object - type: string - lastPushTime: - description: LastPushTime records the time of the last pushed change. - format: date-time - type: string - observedGeneration: - format: int64 - type: integer - observedPolicies: - additionalProperties: - description: ImageRef represents an image reference. - properties: - digest: - description: Digest is the image's digest. - type: string - name: - description: Name is the bare image's name. - type: string - tag: - description: Tag is the image's tag. - type: string - required: - - name - - tag - type: object - description: |- - ObservedPolicies is the list of observed ImagePolicies that were - considered by the ImageUpdateAutomation update process. - type: object - observedSourceRevision: - description: |- - ObservedPolicies []ObservedPolicy `json:"observedPolicies,omitempty"` - ObservedSourceRevision is the last observed source revision. This can be - used to determine if the source has been updated since last observation. - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - - jsonPath: .status.lastAutomationRunTime - name: Last run - priority: 1 - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - deprecated: true - deprecationWarning: v1beta2 ImageUpdateAutomation is deprecated, upgrade to v1 - name: v1beta2 - schema: - openAPIV3Schema: - description: ImageUpdateAutomation is the Schema for the imageupdateautomations API - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: ImageUpdateAutomationSpec defines the desired state of ImageUpdateAutomation - properties: - git: - description: |- - GitSpec contains all the git-specific definitions. This is - technically optional, but in practice mandatory until there are - other kinds of source allowed. - properties: - checkout: - description: |- - Checkout gives the parameters for cloning the git repository, - ready to make changes. If not present, the `spec.ref` field from the - referenced `GitRepository` or its default will be used. - properties: - ref: - description: |- - Reference gives a branch, tag or commit to clone from the Git - repository. - properties: - branch: - description: Branch to check out, defaults to 'master' if no other field is defined. - type: string - commit: - description: |- - Commit SHA to check out, takes precedence over all reference fields. - - This can be combined with Branch to shallow clone the branch, in which - the commit is expected to exist. - type: string - name: - description: |- - Name of the reference to check out; takes precedence over Branch, Tag and SemVer. - - It must be a valid Git reference: https://git-scm.com/docs/git-check-ref-format#_description - Examples: "refs/heads/main", "refs/tags/v0.1.0", "refs/pull/420/head", "refs/merge-requests/1/head" - type: string - semver: - description: SemVer tag expression to check out, takes precedence over Tag. - type: string - tag: - description: Tag to check out, takes precedence over Branch. - type: string - type: object - required: - - ref - type: object - commit: - description: Commit specifies how to commit to the git repository. - properties: - author: - description: |- - Author gives the email and optionally the name to use as the - author of commits. - properties: - email: - description: Email gives the email to provide when making a commit. - type: string - name: - description: Name gives the name to provide when making a commit. - type: string - required: - - email - type: object - messageTemplate: - description: |- - MessageTemplate provides a template for the commit message, - into which will be interpolated the details of the change made. - Note: The `Updated` template field has been removed. Use `Changed` instead. - type: string - messageTemplateValues: - additionalProperties: - type: string - description: |- - MessageTemplateValues provides additional values to be available to the - templating rendering. - type: object - signingKey: - description: SigningKey provides the option to sign commits with a GPG key - properties: - secretRef: - description: |- - SecretRef holds the name to a secret that contains a 'git.asc' key - corresponding to the ASCII Armored file containing the GPG signing - keypair as the value. It must be in the same namespace as the - ImageUpdateAutomation. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - required: - - secretRef - type: object - required: - - author - type: object - push: - description: |- - Push specifies how and where to push commits made by the - automation. If missing, commits are pushed (back) to - `.spec.checkout.branch` or its default. - properties: - branch: - description: |- - Branch specifies that commits should be pushed to the branch - named. The branch is created using `.spec.checkout.branch` as the - starting point, if it doesn't already exist. - type: string - options: - additionalProperties: - type: string - description: |- - Options specifies the push options that are sent to the Git - server when performing a push operation. For details, see: - https://git-scm.com/docs/git-push#Documentation/git-push.txt---push-optionltoptiongt - type: object - refspec: - description: |- - Refspec specifies the Git Refspec to use for a push operation. - If both Branch and Refspec are provided, then the commit is pushed - to the branch and also using the specified refspec. - For more details about Git Refspecs, see: - https://git-scm.com/book/en/v2/Git-Internals-The-Refspec - type: string - type: object - required: - - commit - type: object - interval: - description: |- - Interval gives an lower bound for how often the automation - run should be attempted. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - policySelector: - description: |- - PolicySelector allows to filter applied policies based on labels. - By default includes all policies in namespace. - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - items: - description: |- - A label selector requirement is a selector that contains values, a key, and an operator that - relates the key and values. - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: |- - operator represents a key's relationship to a set of values. - Valid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: |- - values is an array of string values. If the operator is In or NotIn, - the values array must be non-empty. If the operator is Exists or DoesNotExist, - the values array must be empty. This array is replaced during a strategic - merge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: |- - matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - sourceRef: - description: |- - SourceRef refers to the resource giving access details - to a git repository. - properties: - apiVersion: - description: API version of the referent. - type: string - kind: - default: GitRepository - description: Kind of the referent. - enum: - - GitRepository - type: string - name: - description: Name of the referent. - type: string - namespace: - description: Namespace of the referent, defaults to the namespace of the Kubernetes resource object that contains the reference. - type: string - required: - - kind - - name - type: object - suspend: - description: |- - Suspend tells the controller to not run this automation, until - it is unset (or set to false). Defaults to false. - type: boolean - update: - default: - strategy: Setters - description: |- - Update gives the specification for how to update the files in - the repository. This can be left empty, to use the default - value. - properties: - path: - description: |- - Path to the directory containing the manifests to be updated. - Defaults to 'None', which translates to the root path - of the GitRepositoryRef. - type: string - strategy: - default: Setters - description: Strategy names the strategy to be used. - enum: - - Setters - type: string - type: object - required: - - interval - - sourceRef - type: object - status: - default: - observedGeneration: -1 - description: ImageUpdateAutomationStatus defines the observed state of ImageUpdateAutomation - properties: - conditions: - items: - description: Condition contains details for one aspect of the current state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastAutomationRunTime: - description: |- - LastAutomationRunTime records the last time the controller ran - this automation through to completion (even if no updates were - made). - format: date-time - type: string - lastHandledReconcileAt: - description: |- - LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value - can be detected. - type: string - lastPushCommit: - description: |- - LastPushCommit records the SHA1 of the last commit made by the - controller, for this automation object - type: string - lastPushTime: - description: LastPushTime records the time of the last pushed change. - format: date-time - type: string - observedGeneration: - format: int64 - type: integer - observedPolicies: - additionalProperties: - description: ImageRef represents an image reference. - properties: - digest: - description: Digest is the image's digest. - type: string - name: - description: Name is the bare image's name. - type: string - tag: - description: Tag is the image's tag. - type: string - required: - - name - - tag - type: object - description: |- - ObservedPolicies is the list of observed ImagePolicies that were - considered by the ImageUpdateAutomation update process. - type: object - observedSourceRevision: - description: |- - ObservedPolicies []ObservedPolicy `json:"observedPolicies,omitempty"` - ObservedSourceRevision is the last observed source revision. This can be - used to determine if the source has been updated since last observation. - type: string - type: object - type: object - served: true - storage: false - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - app.kubernetes.io/role: cluster-admin - controller-gen.kubebuilder.io/version: v0.19.0 - labels: - app.kubernetes.io/name: flux - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.7.3 - name: kustomizations.kustomize.toolkit.fluxcd.io -spec: - group: kustomize.toolkit.fluxcd.io - names: - kind: Kustomization - listKind: KustomizationList - plural: kustomizations - shortNames: - - ks - singular: kustomization - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - name: v1 - schema: - openAPIV3Schema: - description: Kustomization is the Schema for the kustomizations API. - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: |- - KustomizationSpec defines the configuration to calculate the desired state - from a Source using Kustomize. - properties: - commonMetadata: - description: |- - CommonMetadata specifies the common labels and annotations that are - applied to all resources. Any existing label or annotation will be - overridden if its key matches a common one. - properties: - annotations: - additionalProperties: - type: string - description: Annotations to be added to the object's metadata. - type: object - labels: - additionalProperties: - type: string - description: Labels to be added to the object's metadata. - type: object - type: object - components: - description: Components specifies relative paths to kustomize Components. - items: - type: string - type: array - decryption: - description: Decrypt Kubernetes secrets before applying them on the cluster. - properties: - provider: - description: Provider is the name of the decryption engine. - enum: - - sops - type: string - secretRef: - description: |- - The secret name containing the private OpenPGP keys used for decryption. - A static credential for a cloud provider defined inside the Secret - takes priority to secret-less authentication with the ServiceAccountName - field. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - serviceAccountName: - description: |- - ServiceAccountName is the name of the service account used to - authenticate with KMS services from cloud providers. If a - static credential for a given cloud provider is defined - inside the Secret referenced by SecretRef, that static - credential takes priority. - type: string - required: - - provider - type: object - deletionPolicy: - description: |- - DeletionPolicy can be used to control garbage collection when this - Kustomization is deleted. Valid values are ('MirrorPrune', 'Delete', - 'WaitForTermination', 'Orphan'). 'MirrorPrune' mirrors the Prune field - (orphan if false, delete if true). Defaults to 'MirrorPrune'. - enum: - - MirrorPrune - - Delete - - WaitForTermination - - Orphan - type: string - dependsOn: - description: |- - DependsOn may contain a DependencyReference slice - with references to Kustomization resources that must be ready before this - Kustomization can be reconciled. - items: - description: DependencyReference defines a Kustomization dependency on another Kustomization resource. - properties: - name: - description: Name of the referent. - type: string - namespace: - description: |- - Namespace of the referent, defaults to the namespace of the Kustomization - resource object that contains the reference. - type: string - readyExpr: - description: |- - ReadyExpr is a CEL expression that can be used to assess the readiness - of a dependency. When specified, the built-in readiness check - is replaced by the logic defined in the CEL expression. - To make the CEL expression additive to the built-in readiness check, - the feature gate `AdditiveCELDependencyCheck` must be set to `true`. - type: string - required: - - name - type: object - type: array - force: - default: false - description: |- - Force instructs the controller to recreate resources - when patching fails due to an immutable field change. - type: boolean - healthCheckExprs: - description: |- - HealthCheckExprs is a list of healthcheck expressions for evaluating the - health of custom resources using Common Expression Language (CEL). - The expressions are evaluated only when Wait or HealthChecks are specified. - items: - description: CustomHealthCheck defines the health check for custom resources. - properties: - apiVersion: - description: APIVersion of the custom resource under evaluation. - type: string - current: - description: |- - Current is the CEL expression that determines if the status - of the custom resource has reached the desired state. - type: string - failed: - description: |- - Failed is the CEL expression that determines if the status - of the custom resource has failed to reach the desired state. - type: string - inProgress: - description: |- - InProgress is the CEL expression that determines if the status - of the custom resource has not yet reached the desired state. - type: string - kind: - description: Kind of the custom resource under evaluation. - type: string - required: - - apiVersion - - current - - kind - type: object - type: array - healthChecks: - description: A list of resources to be included in the health assessment. - items: - description: |- - NamespacedObjectKindReference contains enough information to locate the typed referenced Kubernetes resource object - in any namespace. - properties: - apiVersion: - description: API version of the referent, if not specified the Kubernetes preferred version will be used. - type: string - kind: - description: Kind of the referent. - type: string - name: - description: Name of the referent. - type: string - namespace: - description: Namespace of the referent, when not specified it acts as LocalObjectReference. - type: string - required: - - kind - - name - type: object - type: array - ignoreMissingComponents: - description: |- - IgnoreMissingComponents instructs the controller to ignore Components paths - not found in source by removing them from the generated kustomization.yaml - before running kustomize build. - type: boolean - images: - description: |- - Images is a list of (image name, new name, new tag or digest) - for changing image names, tags or digests. This can also be achieved with a - patch, but this operator is simpler to specify. - items: - description: Image contains an image name, a new name, a new tag or digest, which will replace the original name and tag. - properties: - digest: - description: |- - Digest is the value used to replace the original image tag. - If digest is present NewTag value is ignored. - type: string - name: - description: Name is a tag-less image name. - type: string - newName: - description: NewName is the value used to replace the original name. - type: string - newTag: - description: NewTag is the value used to replace the original tag. - type: string - required: - - name - type: object - type: array - interval: - description: |- - The interval at which to reconcile the Kustomization. - This interval is approximate and may be subject to jitter to ensure - efficient use of resources. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - kubeConfig: - description: |- - The KubeConfig for reconciling the Kustomization on a remote cluster. - When used in combination with KustomizationSpec.ServiceAccountName, - forces the controller to act on behalf of that Service Account at the - target cluster. - If the --default-service-account flag is set, its value will be used as - a controller level fallback for when KustomizationSpec.ServiceAccountName - is empty. - properties: - configMapRef: - description: |- - ConfigMapRef holds an optional name of a ConfigMap that contains - the following keys: - - - `provider`: the provider to use. One of `aws`, `azure`, `gcp`, or - `generic`. Required. - - `cluster`: the fully qualified resource name of the Kubernetes - cluster in the cloud provider API. Not used by the `generic` - provider. Required when one of `address` or `ca.crt` is not set. - - `address`: the address of the Kubernetes API server. Required - for `generic`. For the other providers, if not specified, the - first address in the cluster resource will be used, and if - specified, it must match one of the addresses in the cluster - resource. - If audiences is not set, will be used as the audience for the - `generic` provider. - - `ca.crt`: the optional PEM-encoded CA certificate for the - Kubernetes API server. If not set, the controller will use the - CA certificate from the cluster resource. - - `audiences`: the optional audiences as a list of - line-break-separated strings for the Kubernetes ServiceAccount - token. Defaults to the `address` for the `generic` provider, or - to specific values for the other providers depending on the - provider. - - `serviceAccountName`: the optional name of the Kubernetes - ServiceAccount in the same namespace that should be used - for authentication. If not specified, the controller - ServiceAccount will be used. - - Mutually exclusive with SecretRef. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - secretRef: - description: |- - SecretRef holds an optional name of a secret that contains a key with - the kubeconfig file as the value. If no key is set, the key will default - to 'value'. Mutually exclusive with ConfigMapRef. - It is recommended that the kubeconfig is self-contained, and the secret - is regularly updated if credentials such as a cloud-access-token expire. - Cloud specific `cmd-path` auth helpers will not function without adding - binaries and credentials to the Pod that is responsible for reconciling - Kubernetes resources. Supported only for the generic provider. - properties: - key: - description: Key in the Secret, when not specified an implementation-specific default key is used. - type: string - name: - description: Name of the Secret. - type: string - required: - - name - type: object - type: object - x-kubernetes-validations: - - message: exactly one of spec.kubeConfig.configMapRef or spec.kubeConfig.secretRef must be specified - rule: has(self.configMapRef) || has(self.secretRef) - - message: exactly one of spec.kubeConfig.configMapRef or spec.kubeConfig.secretRef must be specified - rule: '!has(self.configMapRef) || !has(self.secretRef)' - namePrefix: - description: NamePrefix will prefix the names of all managed resources. - maxLength: 200 - minLength: 1 - type: string - nameSuffix: - description: NameSuffix will suffix the names of all managed resources. - maxLength: 200 - minLength: 1 - type: string - patches: - description: |- - Strategic merge and JSON patches, defined as inline YAML objects, - capable of targeting objects based on kind, label and annotation selectors. - items: - description: |- - Patch contains an inline StrategicMerge or JSON6902 patch, and the target the patch should - be applied to. - properties: - patch: - description: |- - Patch contains an inline StrategicMerge patch or an inline JSON6902 patch with - an array of operation objects. - type: string - target: - description: Target points to the resources that the patch document should be applied to. - properties: - annotationSelector: - description: |- - AnnotationSelector is a string that follows the label selection expression - https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource annotations. - type: string - group: - description: |- - Group is the API group to select resources from. - Together with Version and Kind it is capable of unambiguously identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - kind: - description: |- - Kind of the API Group to select resources from. - Together with Group and Version it is capable of unambiguously - identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - labelSelector: - description: |- - LabelSelector is a string that follows the label selection expression - https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource labels. - type: string - name: - description: Name to match resources with. - type: string - namespace: - description: Namespace to select resources from. - type: string - version: - description: |- - Version of the API Group to select resources from. - Together with Group and Kind it is capable of unambiguously identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - type: object - required: - - patch - type: object - type: array - path: - description: |- - Path to the directory containing the kustomization.yaml file, or the - set of plain YAMLs a kustomization.yaml should be generated for. - Defaults to 'None', which translates to the root path of the SourceRef. - type: string - postBuild: - description: |- - PostBuild describes which actions to perform on the YAML manifest - generated by building the kustomize overlay. - properties: - substitute: - additionalProperties: - type: string - description: |- - Substitute holds a map of key/value pairs. - The variables defined in your YAML manifests that match any of the keys - defined in the map will be substituted with the set value. - Includes support for bash string replacement functions - e.g. ${var:=default}, ${var:position} and ${var/substring/replacement}. - type: object - substituteFrom: - description: |- - SubstituteFrom holds references to ConfigMaps and Secrets containing - the variables and their values to be substituted in the YAML manifests. - The ConfigMap and the Secret data keys represent the var names, and they - must match the vars declared in the manifests for the substitution to - happen. - items: - description: |- - SubstituteReference contains a reference to a resource containing - the variables name and value. - properties: - kind: - description: Kind of the values referent, valid values are ('Secret', 'ConfigMap'). - enum: - - Secret - - ConfigMap - type: string - name: - description: |- - Name of the values referent. Should reside in the same namespace as the - referring resource. - maxLength: 253 - minLength: 1 - type: string - optional: - default: false - description: |- - Optional indicates whether the referenced resource must exist, or whether to - tolerate its absence. If true and the referenced resource is absent, proceed - as if the resource was present but empty, without any variables defined. - type: boolean - required: - - kind - - name - type: object - type: array - type: object - prune: - description: Prune enables garbage collection. - type: boolean - retryInterval: - description: |- - The interval at which to retry a previously failed reconciliation. - When not specified, the controller uses the KustomizationSpec.Interval - value to retry failures. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - serviceAccountName: - description: |- - The name of the Kubernetes service account to impersonate - when reconciling this Kustomization. - type: string - sourceRef: - description: Reference of the source where the kustomization file is. - properties: - apiVersion: - description: API version of the referent. - type: string - kind: - description: Kind of the referent. - enum: - - OCIRepository - - GitRepository - - Bucket - - ExternalArtifact - type: string - name: - description: Name of the referent. - type: string - namespace: - description: |- - Namespace of the referent, defaults to the namespace of the Kubernetes - resource object that contains the reference. - type: string - required: - - kind - - name - type: object - suspend: - description: |- - This flag tells the controller to suspend subsequent kustomize executions, - it does not apply to already started executions. Defaults to false. - type: boolean - targetNamespace: - description: |- - TargetNamespace sets or overrides the namespace in the - kustomization.yaml file. - maxLength: 63 - minLength: 1 - type: string - timeout: - description: |- - Timeout for validation, apply and health checking operations. - Defaults to 'Interval' duration. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - wait: - description: |- - Wait instructs the controller to check the health of all the reconciled - resources. When enabled, the HealthChecks are ignored. Defaults to false. - type: boolean - required: - - interval - - prune - - sourceRef - type: object - status: - default: - observedGeneration: -1 - description: KustomizationStatus defines the observed state of a kustomization. - properties: - conditions: - items: - description: Condition contains details for one aspect of the current state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - history: - description: |- - History contains a set of snapshots of the last reconciliation attempts - tracking the revision, the state and the duration of each attempt. - items: - description: |- - Snapshot represents a point-in-time record of a group of resources reconciliation, - including timing information, status, and a unique digest identifier. - properties: - digest: - description: Digest is the checksum in the format `:` of the resources in this snapshot. - type: string - firstReconciled: - description: FirstReconciled is the time when this revision was first reconciled to the cluster. - format: date-time - type: string - lastReconciled: - description: LastReconciled is the time when this revision was last reconciled to the cluster. - format: date-time - type: string - lastReconciledDuration: - description: LastReconciledDuration is time it took to reconcile the resources in this revision. - type: string - lastReconciledStatus: - description: LastReconciledStatus is the status of the last reconciliation. - type: string - metadata: - additionalProperties: - type: string - description: Metadata contains additional information about the snapshot. - type: object - totalReconciliations: - description: TotalReconciliations is the total number of reconciliations that have occurred for this snapshot. - format: int64 - type: integer - required: - - digest - - firstReconciled - - lastReconciled - - lastReconciledDuration - - lastReconciledStatus - - totalReconciliations - type: object - type: array - inventory: - description: |- - Inventory contains the list of Kubernetes resource object references that - have been successfully applied. - properties: - entries: - description: Entries of Kubernetes resource object references. - items: - description: ResourceRef contains the information necessary to locate a resource within a cluster. - properties: - id: - description: |- - ID is the string representation of the Kubernetes resource object's metadata, - in the format '___'. - type: string - v: - description: Version is the API version of the Kubernetes resource object's kind. - type: string - required: - - id - - v - type: object - type: array - required: - - entries - type: object - lastAppliedOriginRevision: - description: |- - The last successfully applied origin revision. - Equals the origin revision of the applied Artifact from the referenced Source. - Usually present on the Metadata of the applied Artifact and depends on the - Source type, e.g. for OCI it's the value associated with the key - "org.opencontainers.image.revision". - type: string - lastAppliedRevision: - description: |- - The last successfully applied revision. - Equals the Revision of the applied Artifact from the referenced Source. - type: string - lastAttemptedRevision: - description: LastAttemptedRevision is the revision of the last reconciliation attempt. - type: string - lastHandledReconcileAt: - description: |- - LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value - can be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last reconciled generation. - format: int64 - type: integer - type: object - type: object - served: true - storage: true - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - deprecated: true - deprecationWarning: v1beta2 Kustomization is deprecated, upgrade to v1 - name: v1beta2 - schema: - openAPIV3Schema: - description: Kustomization is the Schema for the kustomizations API. - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: KustomizationSpec defines the configuration to calculate the desired state from a Source using Kustomize. - properties: - commonMetadata: - description: |- - CommonMetadata specifies the common labels and annotations that are applied to all resources. - Any existing label or annotation will be overridden if its key matches a common one. - properties: - annotations: - additionalProperties: - type: string - description: Annotations to be added to the object's metadata. - type: object - labels: - additionalProperties: - type: string - description: Labels to be added to the object's metadata. - type: object - type: object - components: - description: Components specifies relative paths to specifications of other Components. - items: - type: string - type: array - decryption: - description: Decrypt Kubernetes secrets before applying them on the cluster. - properties: - provider: - description: Provider is the name of the decryption engine. - enum: - - sops - type: string - secretRef: - description: The secret name containing the private OpenPGP keys used for decryption. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - required: - - provider - type: object - dependsOn: - description: |- - DependsOn may contain a meta.NamespacedObjectReference slice - with references to Kustomization resources that must be ready before this - Kustomization can be reconciled. - items: - description: |- - NamespacedObjectReference contains enough information to locate the referenced Kubernetes resource object in any - namespace. - properties: - name: - description: Name of the referent. - type: string - namespace: - description: Namespace of the referent, when not specified it acts as LocalObjectReference. - type: string - required: - - name - type: object - type: array - force: - default: false - description: |- - Force instructs the controller to recreate resources - when patching fails due to an immutable field change. - type: boolean - healthChecks: - description: A list of resources to be included in the health assessment. - items: - description: |- - NamespacedObjectKindReference contains enough information to locate the typed referenced Kubernetes resource object - in any namespace. - properties: - apiVersion: - description: API version of the referent, if not specified the Kubernetes preferred version will be used. - type: string - kind: - description: Kind of the referent. - type: string - name: - description: Name of the referent. - type: string - namespace: - description: Namespace of the referent, when not specified it acts as LocalObjectReference. - type: string - required: - - kind - - name - type: object - type: array - images: - description: |- - Images is a list of (image name, new name, new tag or digest) - for changing image names, tags or digests. This can also be achieved with a - patch, but this operator is simpler to specify. - items: - description: Image contains an image name, a new name, a new tag or digest, which will replace the original name and tag. - properties: - digest: - description: |- - Digest is the value used to replace the original image tag. - If digest is present NewTag value is ignored. - type: string - name: - description: Name is a tag-less image name. - type: string - newName: - description: NewName is the value used to replace the original name. - type: string - newTag: - description: NewTag is the value used to replace the original tag. - type: string - required: - - name - type: object - type: array - interval: - description: The interval at which to reconcile the Kustomization. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - kubeConfig: - description: |- - The KubeConfig for reconciling the Kustomization on a remote cluster. - When used in combination with KustomizationSpec.ServiceAccountName, - forces the controller to act on behalf of that Service Account at the - target cluster. - If the --default-service-account flag is set, its value will be used as - a controller level fallback for when KustomizationSpec.ServiceAccountName - is empty. - properties: - configMapRef: - description: |- - ConfigMapRef holds an optional name of a ConfigMap that contains - the following keys: - - - `provider`: the provider to use. One of `aws`, `azure`, `gcp`, or - `generic`. Required. - - `cluster`: the fully qualified resource name of the Kubernetes - cluster in the cloud provider API. Not used by the `generic` - provider. Required when one of `address` or `ca.crt` is not set. - - `address`: the address of the Kubernetes API server. Required - for `generic`. For the other providers, if not specified, the - first address in the cluster resource will be used, and if - specified, it must match one of the addresses in the cluster - resource. - If audiences is not set, will be used as the audience for the - `generic` provider. - - `ca.crt`: the optional PEM-encoded CA certificate for the - Kubernetes API server. If not set, the controller will use the - CA certificate from the cluster resource. - - `audiences`: the optional audiences as a list of - line-break-separated strings for the Kubernetes ServiceAccount - token. Defaults to the `address` for the `generic` provider, or - to specific values for the other providers depending on the - provider. - - `serviceAccountName`: the optional name of the Kubernetes - ServiceAccount in the same namespace that should be used - for authentication. If not specified, the controller - ServiceAccount will be used. - - Mutually exclusive with SecretRef. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - secretRef: - description: |- - SecretRef holds an optional name of a secret that contains a key with - the kubeconfig file as the value. If no key is set, the key will default - to 'value'. Mutually exclusive with ConfigMapRef. - It is recommended that the kubeconfig is self-contained, and the secret - is regularly updated if credentials such as a cloud-access-token expire. - Cloud specific `cmd-path` auth helpers will not function without adding - binaries and credentials to the Pod that is responsible for reconciling - Kubernetes resources. Supported only for the generic provider. - properties: - key: - description: Key in the Secret, when not specified an implementation-specific default key is used. - type: string - name: - description: Name of the Secret. - type: string - required: - - name - type: object - type: object - x-kubernetes-validations: - - message: exactly one of spec.kubeConfig.configMapRef or spec.kubeConfig.secretRef must be specified - rule: has(self.configMapRef) || has(self.secretRef) - - message: exactly one of spec.kubeConfig.configMapRef or spec.kubeConfig.secretRef must be specified - rule: '!has(self.configMapRef) || !has(self.secretRef)' - patches: - description: |- - Strategic merge and JSON patches, defined as inline YAML objects, - capable of targeting objects based on kind, label and annotation selectors. - items: - description: |- - Patch contains an inline StrategicMerge or JSON6902 patch, and the target the patch should - be applied to. - properties: - patch: - description: |- - Patch contains an inline StrategicMerge patch or an inline JSON6902 patch with - an array of operation objects. - type: string - target: - description: Target points to the resources that the patch document should be applied to. - properties: - annotationSelector: - description: |- - AnnotationSelector is a string that follows the label selection expression - https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource annotations. - type: string - group: - description: |- - Group is the API group to select resources from. - Together with Version and Kind it is capable of unambiguously identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - kind: - description: |- - Kind of the API Group to select resources from. - Together with Group and Version it is capable of unambiguously - identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - labelSelector: - description: |- - LabelSelector is a string that follows the label selection expression - https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource labels. - type: string - name: - description: Name to match resources with. - type: string - namespace: - description: Namespace to select resources from. - type: string - version: - description: |- - Version of the API Group to select resources from. - Together with Group and Kind it is capable of unambiguously identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - type: object - required: - - patch - type: object - type: array - patchesJson6902: - description: |- - JSON 6902 patches, defined as inline YAML objects. - Deprecated: Use Patches instead. - items: - description: JSON6902Patch contains a JSON6902 patch and the target the patch should be applied to. - properties: - patch: - description: Patch contains the JSON6902 patch document with an array of operation objects. - items: - description: |- - JSON6902 is a JSON6902 operation object. - https://datatracker.ietf.org/doc/html/rfc6902#section-4 - properties: - from: - description: |- - From contains a JSON-pointer value that references a location within the target document where the operation is - performed. The meaning of the value depends on the value of Op, and is NOT taken into account by all operations. - type: string - op: - description: |- - Op indicates the operation to perform. Its value MUST be one of "add", "remove", "replace", "move", "copy", or - "test". - https://datatracker.ietf.org/doc/html/rfc6902#section-4 - enum: - - test - - remove - - add - - replace - - move - - copy - type: string - path: - description: |- - Path contains the JSON-pointer value that references a location within the target document where the operation - is performed. The meaning of the value depends on the value of Op. - type: string - value: - description: |- - Value contains a valid JSON structure. The meaning of the value depends on the value of Op, and is NOT taken into - account by all operations. - x-kubernetes-preserve-unknown-fields: true - required: - - op - - path - type: object - type: array - target: - description: Target points to the resources that the patch document should be applied to. - properties: - annotationSelector: - description: |- - AnnotationSelector is a string that follows the label selection expression - https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource annotations. - type: string - group: - description: |- - Group is the API group to select resources from. - Together with Version and Kind it is capable of unambiguously identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - kind: - description: |- - Kind of the API Group to select resources from. - Together with Group and Version it is capable of unambiguously - identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - labelSelector: - description: |- - LabelSelector is a string that follows the label selection expression - https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#api - It matches with the resource labels. - type: string - name: - description: Name to match resources with. - type: string - namespace: - description: Namespace to select resources from. - type: string - version: - description: |- - Version of the API Group to select resources from. - Together with Group and Kind it is capable of unambiguously identifying and/or selecting resources. - https://github.com/kubernetes/community/blob/master/contributors/design-proposals/api-machinery/api-group.md - type: string - type: object - required: - - patch - - target - type: object - type: array - patchesStrategicMerge: - description: |- - Strategic merge patches, defined as inline YAML objects. - Deprecated: Use Patches instead. - items: - x-kubernetes-preserve-unknown-fields: true - type: array - path: - description: |- - Path to the directory containing the kustomization.yaml file, or the - set of plain YAMLs a kustomization.yaml should be generated for. - Defaults to 'None', which translates to the root path of the SourceRef. - type: string - postBuild: - description: |- - PostBuild describes which actions to perform on the YAML manifest - generated by building the kustomize overlay. - properties: - substitute: - additionalProperties: - type: string - description: |- - Substitute holds a map of key/value pairs. - The variables defined in your YAML manifests - that match any of the keys defined in the map - will be substituted with the set value. - Includes support for bash string replacement functions - e.g. ${var:=default}, ${var:position} and ${var/substring/replacement}. - type: object - substituteFrom: - description: |- - SubstituteFrom holds references to ConfigMaps and Secrets containing - the variables and their values to be substituted in the YAML manifests. - The ConfigMap and the Secret data keys represent the var names and they - must match the vars declared in the manifests for the substitution to happen. - items: - description: |- - SubstituteReference contains a reference to a resource containing - the variables name and value. - properties: - kind: - description: Kind of the values referent, valid values are ('Secret', 'ConfigMap'). - enum: - - Secret - - ConfigMap - type: string - name: - description: |- - Name of the values referent. Should reside in the same namespace as the - referring resource. - maxLength: 253 - minLength: 1 - type: string - optional: - default: false - description: |- - Optional indicates whether the referenced resource must exist, or whether to - tolerate its absence. If true and the referenced resource is absent, proceed - as if the resource was present but empty, without any variables defined. - type: boolean - required: - - kind - - name - type: object - type: array - type: object - prune: - description: Prune enables garbage collection. - type: boolean - retryInterval: - description: |- - The interval at which to retry a previously failed reconciliation. - When not specified, the controller uses the KustomizationSpec.Interval - value to retry failures. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - serviceAccountName: - description: |- - The name of the Kubernetes service account to impersonate - when reconciling this Kustomization. - type: string - sourceRef: - description: Reference of the source where the kustomization file is. - properties: - apiVersion: - description: API version of the referent. - type: string - kind: - description: Kind of the referent. - enum: - - OCIRepository - - GitRepository - - Bucket - type: string - name: - description: Name of the referent. - type: string - namespace: - description: Namespace of the referent, defaults to the namespace of the Kubernetes resource object that contains the reference. - type: string - required: - - kind - - name - type: object - suspend: - description: |- - This flag tells the controller to suspend subsequent kustomize executions, - it does not apply to already started executions. Defaults to false. - type: boolean - targetNamespace: - description: |- - TargetNamespace sets or overrides the namespace in the - kustomization.yaml file. - maxLength: 63 - minLength: 1 - type: string - timeout: - description: |- - Timeout for validation, apply and health checking operations. - Defaults to 'Interval' duration. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - validation: - description: 'Deprecated: Not used in v1beta2.' - enum: - - none - - client - - server - type: string - wait: - description: |- - Wait instructs the controller to check the health of all the reconciled resources. - When enabled, the HealthChecks are ignored. Defaults to false. - type: boolean - required: - - interval - - prune - - sourceRef - type: object - status: - default: - observedGeneration: -1 - description: KustomizationStatus defines the observed state of a kustomization. - properties: - conditions: - items: - description: Condition contains details for one aspect of the current state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - inventory: - description: Inventory contains the list of Kubernetes resource object references that have been successfully applied. - properties: - entries: - description: Entries of Kubernetes resource object references. - items: - description: ResourceRef contains the information necessary to locate a resource within a cluster. - properties: - id: - description: |- - ID is the string representation of the Kubernetes resource object's metadata, - in the format '___'. - type: string - v: - description: Version is the API version of the Kubernetes resource object's kind. - type: string - required: - - id - - v - type: object - type: array - required: - - entries - type: object - lastAppliedRevision: - description: |- - The last successfully applied revision. - Equals the Revision of the applied Artifact from the referenced Source. - type: string - lastAttemptedRevision: - description: LastAttemptedRevision is the revision of the last reconciliation attempt. - type: string - lastHandledReconcileAt: - description: |- - LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value - can be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last reconciled generation. - format: int64 - type: integer - type: object - type: object - served: true - storage: false - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - app.kubernetes.io/role: cluster-admin - controller-gen.kubebuilder.io/version: v0.19.0 - labels: - app.kubernetes.io/name: flux - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.7.3 - name: ocirepositories.source.toolkit.fluxcd.io -spec: - group: source.toolkit.fluxcd.io - names: - kind: OCIRepository - listKind: OCIRepositoryList - plural: ocirepositories - shortNames: - - ocirepo - singular: ocirepository - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .spec.url - name: URL - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1 - schema: - openAPIV3Schema: - description: OCIRepository is the Schema for the ocirepositories API - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: OCIRepositorySpec defines the desired state of OCIRepository - properties: - certSecretRef: - description: |- - CertSecretRef can be given the name of a Secret containing - either or both of - - - a PEM-encoded client certificate (`tls.crt`) and private - key (`tls.key`); - - a PEM-encoded CA certificate (`ca.crt`) - - and whichever are supplied, will be used for connecting to the - registry. The client cert and key are useful if you are - authenticating with a certificate; the CA cert is useful if - you are using a self-signed server certificate. The Secret must - be of type `Opaque` or `kubernetes.io/tls`. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - ignore: - description: |- - Ignore overrides the set of excluded patterns in the .sourceignore format - (which is the same as .gitignore). If not provided, a default will be used, - consult the documentation for your version to find out what those are. - type: string - insecure: - description: Insecure allows connecting to a non-TLS HTTP container registry. - type: boolean - interval: - description: |- - Interval at which the OCIRepository URL is checked for updates. - This interval is approximate and may be subject to jitter to ensure - efficient use of resources. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - layerSelector: - description: |- - LayerSelector specifies which layer should be extracted from the OCI artifact. - When not specified, the first layer found in the artifact is selected. - properties: - mediaType: - description: |- - MediaType specifies the OCI media type of the layer - which should be extracted from the OCI Artifact. The - first layer matching this type is selected. - type: string - operation: - description: |- - Operation specifies how the selected layer should be processed. - By default, the layer compressed content is extracted to storage. - When the operation is set to 'copy', the layer compressed content - is persisted to storage as it is. - enum: - - extract - - copy - type: string - type: object - provider: - default: generic - description: |- - The provider used for authentication, can be 'aws', 'azure', 'gcp' or 'generic'. - When not specified, defaults to 'generic'. - enum: - - generic - - aws - - azure - - gcp - type: string - proxySecretRef: - description: |- - ProxySecretRef specifies the Secret containing the proxy configuration - to use while communicating with the container registry. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - ref: - description: |- - The OCI reference to pull and monitor for changes, - defaults to the latest tag. - properties: - digest: - description: |- - Digest is the image digest to pull, takes precedence over SemVer. - The value should be in the format 'sha256:'. - type: string - semver: - description: |- - SemVer is the range of tags to pull selecting the latest within - the range, takes precedence over Tag. - type: string - semverFilter: - description: SemverFilter is a regex pattern to filter the tags within the SemVer range. - type: string - tag: - description: Tag is the image tag to pull, defaults to latest. - type: string - type: object - secretRef: - description: |- - SecretRef contains the secret name containing the registry login - credentials to resolve image metadata. - The secret must be of type kubernetes.io/dockerconfigjson. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - serviceAccountName: - description: |- - ServiceAccountName is the name of the Kubernetes ServiceAccount used to authenticate - the image pull if the service account has attached pull secrets. For more information: - https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#add-imagepullsecrets-to-a-service-account - type: string - suspend: - description: This flag tells the controller to suspend the reconciliation of this source. - type: boolean - timeout: - default: 60s - description: The timeout for remote OCI Repository operations like pulling, defaults to 60s. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m))+$ - type: string - url: - description: |- - URL is a reference to an OCI artifact repository hosted - on a remote container registry. - pattern: ^oci://.*$ - type: string - verify: - description: |- - Verify contains the secret name containing the trusted public keys - used to verify the signature and specifies which provider to use to check - whether OCI image is authentic. - properties: - matchOIDCIdentity: - description: |- - MatchOIDCIdentity specifies the identity matching criteria to use - while verifying an OCI artifact which was signed using Cosign keyless - signing. The artifact's identity is deemed to be verified if any of the - specified matchers match against the identity. - items: - description: |- - OIDCIdentityMatch specifies options for verifying the certificate identity, - i.e. the issuer and the subject of the certificate. - properties: - issuer: - description: |- - Issuer specifies the regex pattern to match against to verify - the OIDC issuer in the Fulcio certificate. The pattern must be a - valid Go regular expression. - type: string - subject: - description: |- - Subject specifies the regex pattern to match against to verify - the identity subject in the Fulcio certificate. The pattern must - be a valid Go regular expression. - type: string - required: - - issuer - - subject - type: object - type: array - provider: - default: cosign - description: Provider specifies the technology used to sign the OCI Artifact. - enum: - - cosign - - notation - type: string - secretRef: - description: |- - SecretRef specifies the Kubernetes Secret containing the - trusted public keys. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - required: - - provider - type: object - required: - - interval - - url - type: object - status: - default: - observedGeneration: -1 - description: OCIRepositoryStatus defines the observed state of OCIRepository - properties: - artifact: - description: Artifact represents the output of the last successful OCI Repository sync. - properties: - digest: - description: Digest is the digest of the file in the form of ':'. - pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$ - type: string - lastUpdateTime: - description: |- - LastUpdateTime is the timestamp corresponding to the last update of the - Artifact. - format: date-time - type: string - metadata: - additionalProperties: - type: string - description: Metadata holds upstream information such as OCI annotations. - type: object - path: - description: |- - Path is the relative file path of the Artifact. It can be used to locate - the file in the root of the Artifact storage on the local file system of - the controller managing the Source. - type: string - revision: - description: |- - Revision is a human-readable identifier traceable in the origin source - system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. - type: string - size: - description: Size is the number of bytes in the file. - format: int64 - type: integer - url: - description: |- - URL is the HTTP address of the Artifact as exposed by the controller - managing the Source. It can be used to retrieve the Artifact for - consumption, e.g. by another controller applying the Artifact contents. - type: string - required: - - digest - - lastUpdateTime - - path - - revision - - url - type: object - conditions: - description: Conditions holds the conditions for the OCIRepository. - items: - description: Condition contains details for one aspect of the current state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastHandledReconcileAt: - description: |- - LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value - can be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last observed generation. - format: int64 - type: integer - observedIgnore: - description: |- - ObservedIgnore is the observed exclusion patterns used for constructing - the source artifact. - type: string - observedLayerSelector: - description: |- - ObservedLayerSelector is the observed layer selector used for constructing - the source artifact. - properties: - mediaType: - description: |- - MediaType specifies the OCI media type of the layer - which should be extracted from the OCI Artifact. The - first layer matching this type is selected. - type: string - operation: - description: |- - Operation specifies how the selected layer should be processed. - By default, the layer compressed content is extracted to storage. - When the operation is set to 'copy', the layer compressed content - is persisted to storage as it is. - enum: - - extract - - copy - type: string - type: object - url: - description: URL is the download link for the artifact output of the last OCI Repository sync. - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .spec.url - name: URL - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - deprecated: true - deprecationWarning: v1beta2 OCIRepository is deprecated, upgrade to v1 - name: v1beta2 - schema: - openAPIV3Schema: - description: OCIRepository is the Schema for the ocirepositories API - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: OCIRepositorySpec defines the desired state of OCIRepository - properties: - certSecretRef: - description: |- - CertSecretRef can be given the name of a Secret containing - either or both of - - - a PEM-encoded client certificate (`tls.crt`) and private - key (`tls.key`); - - a PEM-encoded CA certificate (`ca.crt`) - - and whichever are supplied, will be used for connecting to the - registry. The client cert and key are useful if you are - authenticating with a certificate; the CA cert is useful if - you are using a self-signed server certificate. The Secret must - be of type `Opaque` or `kubernetes.io/tls`. - - Note: Support for the `caFile`, `certFile` and `keyFile` keys have - been deprecated. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - ignore: - description: |- - Ignore overrides the set of excluded patterns in the .sourceignore format - (which is the same as .gitignore). If not provided, a default will be used, - consult the documentation for your version to find out what those are. - type: string - insecure: - description: Insecure allows connecting to a non-TLS HTTP container registry. - type: boolean - interval: - description: |- - Interval at which the OCIRepository URL is checked for updates. - This interval is approximate and may be subject to jitter to ensure - efficient use of resources. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - layerSelector: - description: |- - LayerSelector specifies which layer should be extracted from the OCI artifact. - When not specified, the first layer found in the artifact is selected. - properties: - mediaType: - description: |- - MediaType specifies the OCI media type of the layer - which should be extracted from the OCI Artifact. The - first layer matching this type is selected. - type: string - operation: - description: |- - Operation specifies how the selected layer should be processed. - By default, the layer compressed content is extracted to storage. - When the operation is set to 'copy', the layer compressed content - is persisted to storage as it is. - enum: - - extract - - copy - type: string - type: object - provider: - default: generic - description: |- - The provider used for authentication, can be 'aws', 'azure', 'gcp' or 'generic'. - When not specified, defaults to 'generic'. - enum: - - generic - - aws - - azure - - gcp - type: string - proxySecretRef: - description: |- - ProxySecretRef specifies the Secret containing the proxy configuration - to use while communicating with the container registry. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - ref: - description: |- - The OCI reference to pull and monitor for changes, - defaults to the latest tag. - properties: - digest: - description: |- - Digest is the image digest to pull, takes precedence over SemVer. - The value should be in the format 'sha256:'. - type: string - semver: - description: |- - SemVer is the range of tags to pull selecting the latest within - the range, takes precedence over Tag. - type: string - semverFilter: - description: SemverFilter is a regex pattern to filter the tags within the SemVer range. - type: string - tag: - description: Tag is the image tag to pull, defaults to latest. - type: string - type: object - secretRef: - description: |- - SecretRef contains the secret name containing the registry login - credentials to resolve image metadata. - The secret must be of type kubernetes.io/dockerconfigjson. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - serviceAccountName: - description: |- - ServiceAccountName is the name of the Kubernetes ServiceAccount used to authenticate - the image pull if the service account has attached pull secrets. For more information: - https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#add-imagepullsecrets-to-a-service-account - type: string - suspend: - description: This flag tells the controller to suspend the reconciliation of this source. - type: boolean - timeout: - default: 60s - description: The timeout for remote OCI Repository operations like pulling, defaults to 60s. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m))+$ - type: string - url: - description: |- - URL is a reference to an OCI artifact repository hosted - on a remote container registry. - pattern: ^oci://.*$ - type: string - verify: - description: |- - Verify contains the secret name containing the trusted public keys - used to verify the signature and specifies which provider to use to check - whether OCI image is authentic. - properties: - matchOIDCIdentity: - description: |- - MatchOIDCIdentity specifies the identity matching criteria to use - while verifying an OCI artifact which was signed using Cosign keyless - signing. The artifact's identity is deemed to be verified if any of the - specified matchers match against the identity. - items: - description: |- - OIDCIdentityMatch specifies options for verifying the certificate identity, - i.e. the issuer and the subject of the certificate. - properties: - issuer: - description: |- - Issuer specifies the regex pattern to match against to verify - the OIDC issuer in the Fulcio certificate. The pattern must be a - valid Go regular expression. - type: string - subject: - description: |- - Subject specifies the regex pattern to match against to verify - the identity subject in the Fulcio certificate. The pattern must - be a valid Go regular expression. - type: string - required: - - issuer - - subject - type: object - type: array - provider: - default: cosign - description: Provider specifies the technology used to sign the OCI Artifact. - enum: - - cosign - - notation - type: string - secretRef: - description: |- - SecretRef specifies the Kubernetes Secret containing the - trusted public keys. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - required: - - provider - type: object - required: - - interval - - url - type: object - status: - default: - observedGeneration: -1 - description: OCIRepositoryStatus defines the observed state of OCIRepository - properties: - artifact: - description: Artifact represents the output of the last successful OCI Repository sync. - properties: - digest: - description: Digest is the digest of the file in the form of ':'. - pattern: ^[a-z0-9]+(?:[.+_-][a-z0-9]+)*:[a-zA-Z0-9=_-]+$ - type: string - lastUpdateTime: - description: |- - LastUpdateTime is the timestamp corresponding to the last update of the - Artifact. - format: date-time - type: string - metadata: - additionalProperties: - type: string - description: Metadata holds upstream information such as OCI annotations. - type: object - path: - description: |- - Path is the relative file path of the Artifact. It can be used to locate - the file in the root of the Artifact storage on the local file system of - the controller managing the Source. - type: string - revision: - description: |- - Revision is a human-readable identifier traceable in the origin source - system. It can be a Git commit SHA, Git tag, a Helm chart version, etc. - type: string - size: - description: Size is the number of bytes in the file. - format: int64 - type: integer - url: - description: |- - URL is the HTTP address of the Artifact as exposed by the controller - managing the Source. It can be used to retrieve the Artifact for - consumption, e.g. by another controller applying the Artifact contents. - type: string - required: - - digest - - lastUpdateTime - - path - - revision - - url - type: object - conditions: - description: Conditions holds the conditions for the OCIRepository. - items: - description: Condition contains details for one aspect of the current state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - contentConfigChecksum: - description: |- - ContentConfigChecksum is a checksum of all the configurations related to - the content of the source artifact: - - .spec.ignore - - .spec.layerSelector - observed in .status.observedGeneration version of the object. This can - be used to determine if the content configuration has changed and the - artifact needs to be rebuilt. - It has the format of `:`, for example: `sha256:`. - - Deprecated: Replaced with explicit fields for observed artifact content - config in the status. - type: string - lastHandledReconcileAt: - description: |- - LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value - can be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last observed generation. - format: int64 - type: integer - observedIgnore: - description: |- - ObservedIgnore is the observed exclusion patterns used for constructing - the source artifact. - type: string - observedLayerSelector: - description: |- - ObservedLayerSelector is the observed layer selector used for constructing - the source artifact. - properties: - mediaType: - description: |- - MediaType specifies the OCI media type of the layer - which should be extracted from the OCI Artifact. The - first layer matching this type is selected. - type: string - operation: - description: |- - Operation specifies how the selected layer should be processed. - By default, the layer compressed content is extracted to storage. - When the operation is set to 'copy', the layer compressed content - is persisted to storage as it is. - enum: - - extract - - copy - type: string - type: object - url: - description: URL is the download link for the artifact output of the last OCI Repository sync. - type: string - type: object - type: object - served: true - storage: false - subresources: - status: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - app.kubernetes.io/role: cluster-admin - controller-gen.kubebuilder.io/version: v0.19.0 - labels: - app.kubernetes.io/name: flux - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.7.3 - name: providers.notification.toolkit.fluxcd.io -spec: - group: notification.toolkit.fluxcd.io - names: - kind: Provider - listKind: ProviderList - plural: providers - singular: provider - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - deprecated: true - deprecationWarning: v1beta2 Provider is deprecated, upgrade to v1beta3 - name: v1beta2 - schema: - openAPIV3Schema: - description: Provider is the Schema for the providers API. - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: ProviderSpec defines the desired state of the Provider. - properties: - address: - description: |- - Address specifies the endpoint, in a generic sense, to where alerts are sent. - What kind of endpoint depends on the specific Provider type being used. - For the generic Provider, for example, this is an HTTP/S address. - For other Provider types this could be a project ID or a namespace. - maxLength: 2048 - type: string - certSecretRef: - description: |- - CertSecretRef specifies the Secret containing - a PEM-encoded CA certificate (in the `ca.crt` key). - - Note: Support for the `caFile` key has - been deprecated. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - channel: - description: Channel specifies the destination channel where events should be posted. - maxLength: 2048 - type: string - interval: - description: Interval at which to reconcile the Provider with its Secret references. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - proxy: - description: Proxy the HTTP/S address of the proxy server. - maxLength: 2048 - pattern: ^(http|https)://.*$ - type: string - secretRef: - description: |- - SecretRef specifies the Secret containing the authentication - credentials for this Provider. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - suspend: - description: |- - Suspend tells the controller to suspend subsequent - events handling for this Provider. - type: boolean - timeout: - description: Timeout for sending alerts to the Provider. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m))+$ - type: string - type: - description: Type specifies which Provider implementation to use. - enum: - - slack - - discord - - msteams - - rocket - - generic - - generic-hmac - - github - - gitlab - - gitea - - bitbucketserver - - bitbucket - - azuredevops - - googlechat - - googlepubsub - - webex - - sentry - - azureeventhub - - telegram - - lark - - matrix - - opsgenie - - alertmanager - - grafana - - githubdispatch - - pagerduty - - datadog - type: string - username: - description: Username specifies the name under which events are posted. - maxLength: 2048 - type: string - required: - - type - type: object - status: - default: - observedGeneration: -1 - description: ProviderStatus defines the observed state of the Provider. - properties: - conditions: - description: Conditions holds the conditions for the Provider. - items: - description: Condition contains details for one aspect of the current state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastHandledReconcileAt: - description: |- - LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value - can be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last reconciled generation. - format: int64 - type: integer - type: object - type: object - served: true - storage: false - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - name: v1beta3 - schema: - openAPIV3Schema: - description: Provider is the Schema for the providers API - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: ProviderSpec defines the desired state of the Provider. - properties: - address: - description: |- - Address specifies the endpoint, in a generic sense, to where alerts are sent. - What kind of endpoint depends on the specific Provider type being used. - For the generic Provider, for example, this is an HTTP/S address. - For other Provider types this could be a project ID or a namespace. - maxLength: 2048 - type: string - certSecretRef: - description: |- - CertSecretRef specifies the Secret containing TLS certificates - for secure communication. - - Supported configurations: - - CA-only: Server authentication (provide ca.crt only) - - mTLS: Mutual authentication (provide ca.crt + tls.crt + tls.key) - - Client-only: Client authentication with system CA (provide tls.crt + tls.key only) - - Legacy keys "caFile", "certFile", "keyFile" are supported but deprecated. Use "ca.crt", "tls.crt", "tls.key" instead. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - channel: - description: Channel specifies the destination channel where events should be posted. - maxLength: 2048 - type: string - commitStatusExpr: - description: |- - CommitStatusExpr is a CEL expression that evaluates to a string value - that can be used to generate a custom commit status message for use - with eligible Provider types (github, gitlab, gitea, bitbucketserver, - bitbucket, azuredevops). Supported variables are: event, provider, - and alert. - type: string - interval: - description: |- - Interval at which to reconcile the Provider with its Secret references. - Deprecated and not used in v1beta3. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - proxy: - description: |- - Proxy the HTTP/S address of the proxy server. - Deprecated: Use ProxySecretRef instead. Will be removed in v1. - maxLength: 2048 - pattern: ^(http|https)://.*$ - type: string - proxySecretRef: - description: |- - ProxySecretRef specifies the Secret containing the proxy configuration - for this Provider. The Secret should contain an 'address' key with the - HTTP/S address of the proxy server. Optional 'username' and 'password' - keys can be provided for proxy authentication. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - secretRef: - description: |- - SecretRef specifies the Secret containing the authentication - credentials for this Provider. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - serviceAccountName: - description: |- - ServiceAccountName is the name of the Kubernetes ServiceAccount used to - authenticate with cloud provider services through workload identity. - This enables multi-tenant authentication without storing static credentials. - - Supported provider types: azureeventhub, azuredevops, googlepubsub - - When specified, the controller will: - 1. Create an OIDC token for the specified ServiceAccount - 2. Exchange it for cloud provider credentials via STS - 3. Use the obtained credentials for API authentication - - When unspecified, controller-level authentication is used (single-tenant). - - An error is thrown if static credentials are also defined in SecretRef. - This field requires the ObjectLevelWorkloadIdentity feature gate to be enabled. - type: string - suspend: - description: |- - Suspend tells the controller to suspend subsequent - events handling for this Provider. - type: boolean - timeout: - description: Timeout for sending alerts to the Provider. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m))+$ - type: string - type: - description: Type specifies which Provider implementation to use. - enum: - - slack - - discord - - msteams - - rocket - - generic - - generic-hmac - - github - - gitlab - - gitea - - bitbucketserver - - bitbucket - - azuredevops - - googlechat - - googlepubsub - - webex - - sentry - - azureeventhub - - telegram - - lark - - matrix - - opsgenie - - alertmanager - - grafana - - githubdispatch - - pagerduty - - datadog - - nats - - zulip - - otel - type: string - username: - description: Username specifies the name under which events are posted. - maxLength: 2048 - type: string - required: - - type - type: object - x-kubernetes-validations: - - message: spec.commitStatusExpr is only supported for the 'github', 'gitlab', 'gitea', 'bitbucketserver', 'bitbucket', 'azuredevops' provider types - rule: self.type == 'github' || self.type == 'gitlab' || self.type == 'gitea' || self.type == 'bitbucketserver' || self.type == 'bitbucket' || self.type == 'azuredevops' || !has(self.commitStatusExpr) - type: object - served: true - storage: true - subresources: {} ---- -apiVersion: apiextensions.k8s.io/v1 -kind: CustomResourceDefinition -metadata: - annotations: - app.kubernetes.io/role: cluster-admin - controller-gen.kubebuilder.io/version: v0.19.0 - labels: - app.kubernetes.io/name: flux - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.7.3 - name: receivers.notification.toolkit.fluxcd.io -spec: - group: notification.toolkit.fluxcd.io - names: - kind: Receiver - listKind: ReceiverList - plural: receivers - singular: receiver - scope: Namespaced - versions: - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - name: v1 - schema: - openAPIV3Schema: - description: Receiver is the Schema for the receivers API. - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: ReceiverSpec defines the desired state of the Receiver. - properties: - events: - description: |- - Events specifies the list of event types to handle, - e.g. 'push' for GitHub or 'Push Hook' for GitLab. - items: - type: string - type: array - interval: - default: 10m - description: Interval at which to reconcile the Receiver with its Secret references. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - resourceFilter: - description: |- - ResourceFilter is a CEL expression expected to return a boolean that is - evaluated for each resource referenced in the Resources field when a - webhook is received. If the expression returns false then the controller - will not request a reconciliation for the resource. - When the expression is specified the controller will parse it and mark - the object as terminally failed if the expression is invalid or does not - return a boolean. - type: string - resources: - description: A list of resources to be notified about changes. - items: - description: |- - CrossNamespaceObjectReference contains enough information to let you locate the - typed referenced object at cluster level - properties: - apiVersion: - description: API version of the referent - type: string - kind: - description: Kind of the referent - enum: - - Bucket - - GitRepository - - Kustomization - - HelmRelease - - HelmChart - - HelmRepository - - ImageRepository - - ImagePolicy - - ImageUpdateAutomation - - OCIRepository - type: string - matchLabels: - additionalProperties: - type: string - description: |- - MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - MatchLabels requires the name to be set to `*`. - type: object - name: - description: |- - Name of the referent - If multiple resources are targeted `*` may be set. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: Namespace of the referent - maxLength: 253 - minLength: 1 - type: string - required: - - kind - - name - type: object - type: array - secretRef: - description: |- - SecretRef specifies the Secret containing the token used - to validate the payload authenticity. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - suspend: - description: |- - Suspend tells the controller to suspend subsequent - events handling for this receiver. - type: boolean - type: - description: |- - Type of webhook sender, used to determine - the validation procedure and payload deserialization. - enum: - - generic - - generic-hmac - - github - - gitlab - - bitbucket - - harbor - - dockerhub - - quay - - gcr - - nexus - - acr - - cdevents - type: string - required: - - resources - - secretRef - - type - type: object - status: - default: - observedGeneration: -1 - description: ReceiverStatus defines the observed state of the Receiver. - properties: - conditions: - description: Conditions holds the conditions for the Receiver. - items: - description: Condition contains details for one aspect of the current state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastHandledReconcileAt: - description: |- - LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value - can be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last observed generation of the Receiver object. - format: int64 - type: integer - webhookPath: - description: |- - WebhookPath is the generated incoming webhook address in the format - of '/hook/sha256sum(token+name+namespace)'. - type: string - type: object - type: object - served: true - storage: true - subresources: - status: {} - - additionalPrinterColumns: - - jsonPath: .metadata.creationTimestamp - name: Age - type: date - - jsonPath: .status.conditions[?(@.type=="Ready")].status - name: Ready - type: string - - jsonPath: .status.conditions[?(@.type=="Ready")].message - name: Status - type: string - deprecated: true - deprecationWarning: v1beta2 Receiver is deprecated, upgrade to v1 - name: v1beta2 - schema: - openAPIV3Schema: - description: Receiver is the Schema for the receivers API. - properties: - apiVersion: - description: |- - APIVersion defines the versioned schema of this representation of an object. - Servers should convert recognized schemas to the latest internal value, and - may reject unrecognized values. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources - type: string - kind: - description: |- - Kind is a string value representing the REST resource this object represents. - Servers may infer this from the endpoint the client submits requests to. - Cannot be updated. - In CamelCase. - More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds - type: string - metadata: - type: object - spec: - description: ReceiverSpec defines the desired state of the Receiver. - properties: - events: - description: |- - Events specifies the list of event types to handle, - e.g. 'push' for GitHub or 'Push Hook' for GitLab. - items: - type: string - type: array - interval: - description: Interval at which to reconcile the Receiver with its Secret references. - pattern: ^([0-9]+(\.[0-9]+)?(ms|s|m|h))+$ - type: string - resources: - description: A list of resources to be notified about changes. - items: - description: |- - CrossNamespaceObjectReference contains enough information to let you locate the - typed referenced object at cluster level - properties: - apiVersion: - description: API version of the referent - type: string - kind: - description: Kind of the referent - enum: - - Bucket - - GitRepository - - Kustomization - - HelmRelease - - HelmChart - - HelmRepository - - ImageRepository - - ImagePolicy - - ImageUpdateAutomation - - OCIRepository - type: string - matchLabels: - additionalProperties: - type: string - description: |- - MatchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, whose key field is "key", the - operator is "In", and the values array contains only "value". The requirements are ANDed. - MatchLabels requires the name to be set to `*`. - type: object - name: - description: |- - Name of the referent - If multiple resources are targeted `*` may be set. - maxLength: 253 - minLength: 1 - type: string - namespace: - description: Namespace of the referent - maxLength: 253 - minLength: 1 - type: string - required: - - kind - - name - type: object - type: array - secretRef: - description: |- - SecretRef specifies the Secret containing the token used - to validate the payload authenticity. - properties: - name: - description: Name of the referent. - type: string - required: - - name - type: object - suspend: - description: |- - Suspend tells the controller to suspend subsequent - events handling for this receiver. - type: boolean - type: - description: |- - Type of webhook sender, used to determine - the validation procedure and payload deserialization. - enum: - - generic - - generic-hmac - - github - - gitlab - - bitbucket - - harbor - - dockerhub - - quay - - gcr - - nexus - - acr - type: string - required: - - resources - - secretRef - - type - type: object - status: - default: - observedGeneration: -1 - description: ReceiverStatus defines the observed state of the Receiver. - properties: - conditions: - description: Conditions holds the conditions for the Receiver. - items: - description: Condition contains details for one aspect of the current state of this API Resource. - properties: - lastTransitionTime: - description: |- - lastTransitionTime is the last time the condition transitioned from one status to another. - This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: |- - message is a human readable message indicating details about the transition. - This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: |- - observedGeneration represents the .metadata.generation that the condition was set based upon. - For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date - with respect to the current state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: |- - reason contains a programmatic identifier indicating the reason for the condition's last transition. - Producers of specific condition types may define expected values and meanings for this field, - and whether the values are considered a guaranteed API. - The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastHandledReconcileAt: - description: |- - LastHandledReconcileAt holds the value of the most recent - reconcile request value, so a change of the annotation value - can be detected. - type: string - observedGeneration: - description: ObservedGeneration is the last observed generation of the Receiver object. - format: int64 - type: integer - url: - description: |- - URL is the generated incoming webhook address in the format - of '/hook/sha256sum(token+name+namespace)'. - Deprecated: Replaced by WebhookPath. - type: string - webhookPath: - description: |- - WebhookPath is the generated incoming webhook address in the format - of '/hook/sha256sum(token+name+namespace)'. - type: string - type: object - type: object - served: true - storage: false - subresources: - status: {} ---- -apiVersion: v1 -kind: Namespace -metadata: - annotations: - app.kubernetes.io/role: cluster-admin - labels: - app.kubernetes.io/name: flux - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.7.3 - pod-security.kubernetes.io/enforce: privileged - name: cozy-fluxcd ---- -apiVersion: v1 -kind: ResourceQuota -metadata: - annotations: - app.kubernetes.io/role: cluster-admin - labels: - app.kubernetes.io/name: flux - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.7.3 - name: flux - namespace: cozy-fluxcd -spec: - hard: - pods: "1000" - scopeSelector: - matchExpressions: - - operator: In - scopeName: PriorityClass - values: - - system-node-critical - - system-cluster-critical ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - annotations: - app.kubernetes.io/role: cluster-admin - labels: - app.kubernetes.io/name: flux - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.7.3 - name: flux - namespace: cozy-fluxcd ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - annotations: - app.kubernetes.io/role: cluster-admin - labels: - app.kubernetes.io/name: flux - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.7.3 - rbac.authorization.k8s.io/aggregate-to-admin: "true" - rbac.authorization.k8s.io/aggregate-to-edit: "true" - rbac.authorization.k8s.io/aggregate-to-view: "true" - name: flux-view -rules: - - apiGroups: - - notification.toolkit.fluxcd.io - - source.toolkit.fluxcd.io - - helm.toolkit.fluxcd.io - - image.toolkit.fluxcd.io - - kustomize.toolkit.fluxcd.io - - source.extensions.fluxcd.io - resources: - - '*' - verbs: - - get - - list - - watch ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - annotations: - app.kubernetes.io/role: cluster-admin - labels: - app.kubernetes.io/name: flux - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.7.3 - name: flux -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: cluster-admin -subjects: - - kind: ServiceAccount - name: flux - namespace: cozy-fluxcd ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - annotations: - app.kubernetes.io/role: cluster-admin - labels: - app.kubernetes.io/name: flux - app.kubernetes.io/part-of: flux - app.kubernetes.io/version: v2.7.3 - name: flux - namespace: cozy-fluxcd -spec: - replicas: 1 - selector: - matchLabels: - app.kubernetes.io/name: flux - strategy: - type: Recreate - template: - metadata: - annotations: - cluster-autoscaler.kubernetes.io/safe-to-evict: "true" - prometheus.io/scrape: "true" - labels: - app.kubernetes.io/name: flux - spec: - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: kubernetes.io/os - operator: In - values: - - linux - podAntiAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - - labelSelector: - matchExpressions: - - key: app.kubernetes.io/name - operator: In - values: - - flux - topologyKey: kubernetes.io/hostname - containers: - - args: - - --watch-all-namespaces - - --log-level=info - - --log-encoding=json - - --enable-leader-election=false - - --metrics-addr=:9791 - - --health-addr=:9792 - - --storage-addr=:9790 - - --storage-path=/data - - --storage-adv-addr=flux.$(RUNTIME_NAMESPACE).svc - - --concurrent=5 - - --requeue-dependency=30s - - --watch-label-selector=!sharding.fluxcd.io/key - - --helm-cache-max-size=10 - - --helm-cache-ttl=60m - - --helm-cache-purge-interval=5m - - --events-addr=http://localhost:9690 - env: - - name: SOURCE_CONTROLLER_LOCALHOST - value: localhost:9790 - - name: SOURCE_WATCHER_LOCALHOST - value: localhost:9691 - - name: RUNTIME_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: GOMEMLIMIT - valueFrom: - resourceFieldRef: - resource: limits.memory - - name: TUF_ROOT - value: /tmp/.sigstore - - name: NO_PROXY - value: .svc - {{- include "cozy.kubernetes_envs" . | nindent 12 }} - image: ghcr.io/fluxcd/source-controller:v1.7.3 - imagePullPolicy: IfNotPresent - livenessProbe: - httpGet: - path: /healthz - port: healthz-sc - name: source-controller - ports: - - containerPort: 9790 - name: http-sc - protocol: TCP - - containerPort: 9791 - name: http-prom-sc - protocol: TCP - - containerPort: 9792 - name: healthz-sc - protocol: TCP - readinessProbe: - httpGet: - path: / - port: http-sc - resources: - limits: - memory: 1Gi - requests: - cpu: 100m - memory: 64Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - readOnlyRootFilesystem: true - runAsNonRoot: true - seccompProfile: - type: RuntimeDefault - volumeMounts: - - mountPath: /data - name: data - - mountPath: /tmp - name: tmp - - args: - - --watch-all-namespaces - - --log-level=info - - --log-encoding=json - - --enable-leader-election=false - - --metrics-addr=:9793 - - --health-addr=:9794 - - --watch-label-selector=!sharding.fluxcd.io/key - - --concurrent=5 - - --requeue-dependency=30s - - --events-addr=http://localhost:9690 - - --feature-gates=ExternalArtifact=true - env: - - name: SOURCE_CONTROLLER_LOCALHOST - value: localhost:9790 - - name: SOURCE_WATCHER_LOCALHOST - value: localhost:9691 - - name: RUNTIME_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: GOMEMLIMIT - valueFrom: - resourceFieldRef: - resource: limits.memory - - name: TUF_ROOT - value: /tmp/.sigstore - - name: NO_PROXY - value: .svc - {{- include "cozy.kubernetes_envs" . | nindent 12 }} - image: ghcr.io/fluxcd/kustomize-controller:v1.7.2 - imagePullPolicy: IfNotPresent - livenessProbe: - httpGet: - path: /healthz - port: healthz-kc - name: kustomize-controller - ports: - - containerPort: 9793 - name: http-prom-kc - protocol: TCP - - containerPort: 9794 - name: healthz-kc - protocol: TCP - readinessProbe: - httpGet: - path: /readyz - port: healthz-kc - resources: - limits: - memory: 1Gi - requests: - cpu: 100m - memory: 64Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - readOnlyRootFilesystem: true - runAsNonRoot: true - seccompProfile: - type: RuntimeDefault - volumeMounts: - - mountPath: /tmp - name: tmp - - args: - - --watch-all-namespaces - - --log-level=info - - --log-encoding=json - - --enable-leader-election=false - - --metrics-addr=:9795 - - --health-addr=:9796 - - --watch-label-selector=!sharding.fluxcd.io/key - - --concurrent=5 - - --requeue-dependency=30s - - --events-addr=http://localhost:9690 - - --feature-gates=ExternalArtifact=true - env: - - name: SOURCE_CONTROLLER_LOCALHOST - value: localhost:9790 - - name: SOURCE_WATCHER_LOCALHOST - value: localhost:9691 - - name: RUNTIME_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: GOMEMLIMIT - valueFrom: - resourceFieldRef: - resource: limits.memory - - name: TUF_ROOT - value: /tmp/.sigstore - - name: NO_PROXY - value: .svc - {{- include "cozy.kubernetes_envs" . | nindent 12 }} - image: ghcr.io/fluxcd/helm-controller:v1.4.3 - imagePullPolicy: IfNotPresent - livenessProbe: - httpGet: - path: /healthz - port: healthz-hc - name: helm-controller - ports: - - containerPort: 9795 - name: http-prom-hc - protocol: TCP - - containerPort: 9796 - name: healthz-hc - protocol: TCP - readinessProbe: - httpGet: - path: /readyz - port: healthz-hc - resources: - limits: - memory: 1Gi - requests: - cpu: 100m - memory: 64Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - readOnlyRootFilesystem: true - runAsNonRoot: true - seccompProfile: - type: RuntimeDefault - volumeMounts: - - mountPath: /tmp - name: tmp - - args: - - --watch-all-namespaces - - --log-level=info - - --log-encoding=json - - --enable-leader-election=false - - --receiverAddr=:9797 - - --metrics-addr=:9798 - - --health-addr=:9799 - - --events-addr=:9690 - env: - - name: SOURCE_CONTROLLER_LOCALHOST - value: localhost:9790 - - name: SOURCE_WATCHER_LOCALHOST - value: localhost:9691 - - name: RUNTIME_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: GOMEMLIMIT - valueFrom: - resourceFieldRef: - resource: limits.memory - - name: TUF_ROOT - value: /tmp/.sigstore - - name: NO_PROXY - value: .svc - {{- include "cozy.kubernetes_envs" . | nindent 12 }} - image: ghcr.io/fluxcd/notification-controller:v1.7.4 - imagePullPolicy: IfNotPresent - livenessProbe: - httpGet: - path: /healthz - port: healthz-nc - name: notification-controller - ports: - - containerPort: 9690 - name: http-nc - protocol: TCP - - containerPort: 9797 - name: http-webhook-nc - protocol: TCP - - containerPort: 9798 - name: http-prom-nc - protocol: TCP - - containerPort: 9799 - name: healthz-nc - protocol: TCP - readinessProbe: - httpGet: - path: /readyz - port: healthz-nc - resources: - limits: - memory: 1Gi - requests: - cpu: 100m - memory: 64Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - readOnlyRootFilesystem: true - runAsNonRoot: true - seccompProfile: - type: RuntimeDefault - volumeMounts: - - mountPath: /tmp - name: tmp - - args: - - --watch-all-namespaces - - --log-level=info - - --log-encoding=json - - --enable-leader-election=false - - --metrics-addr=:9692 - - --health-addr=:9693 - - --storage-addr=:9691 - - --storage-path=/data - - --storage-adv-addr=source-watcher.$(RUNTIME_NAMESPACE).svc - - --events-addr=http://localhost:9690 - env: - - name: SOURCE_CONTROLLER_LOCALHOST - value: localhost:9790 - - name: SOURCE_WATCHER_LOCALHOST - value: localhost:9691 - - name: RUNTIME_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: GOMEMLIMIT - valueFrom: - resourceFieldRef: - resource: limits.memory - - name: TUF_ROOT - value: /tmp/.sigstore - - name: NO_PROXY - value: .svc - {{- include "cozy.kubernetes_envs" . | nindent 12 }} - image: ghcr.io/fluxcd/source-watcher:v2.0.2 - imagePullPolicy: IfNotPresent - livenessProbe: - httpGet: - path: /healthz - port: healthz-sw - name: source-watcher - ports: - - containerPort: 9691 - name: http-sw - protocol: TCP - - containerPort: 9692 - name: http-prom-sw - protocol: TCP - - containerPort: 9693 - name: healthz-sw - protocol: TCP - readinessProbe: - httpGet: - path: / - port: http-sw - resources: - limits: - memory: 1Gi - requests: - cpu: 100m - memory: 64Mi - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - ALL - readOnlyRootFilesystem: true - runAsNonRoot: true - seccompProfile: - type: RuntimeDefault - volumeMounts: - - mountPath: /data - name: data - - mountPath: /tmp - name: tmp - dnsPolicy: ClusterFirstWithHostNet - hostNetwork: true - priorityClassName: system-cluster-critical - securityContext: - fsGroup: 1337 - serviceAccountName: flux - terminationGracePeriodSeconds: 120 - tolerations: - - key: node.kubernetes.io/not-ready - operator: Exists - - effect: NoExecute - key: node.kubernetes.io/unreachable - operator: Exists - tolerationSeconds: 300 - volumes: - - emptyDir: {} - name: data - - emptyDir: {} - name: tmp diff --git a/packages/extra/ingress/templates/nginx-ingress.yaml b/packages/extra/ingress/templates/nginx-ingress.yaml index d375be2c..ca50d276 100644 --- a/packages/extra/ingress/templates/nginx-ingress.yaml +++ b/packages/extra/ingress/templates/nginx-ingress.yaml @@ -4,16 +4,13 @@ apiVersion: helm.toolkit.fluxcd.io/v2 kind: HelmRelease metadata: name: ingress-nginx-system + labels: + sharding.fluxcd.io/key: tenants spec: - chart: - spec: - chart: cozy-ingress-nginx - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-ingress-application-default-ingress-system + namespace: cozy-system interval: 5m timeout: 10m install: diff --git a/packages/extra/monitoring/templates/dashboards.yaml b/packages/extra/monitoring/templates/dashboards.yaml index 522df88b..3872f9b7 100644 --- a/packages/extra/monitoring/templates/dashboards.yaml +++ b/packages/extra/monitoring/templates/dashboards.yaml @@ -1,6 +1,6 @@ {{- range (split "\n" (.Files.Get "dashboards.list")) }} {{- $parts := split "/" . }} -{{- if eq (len $parts) 2 }} +{{- if eq (len $parts) 2 }} --- apiVersion: grafana.integreatly.org/v1beta1 kind: GrafanaDashboard @@ -11,6 +11,6 @@ spec: instanceSelector: matchLabels: dashboards: grafana - url: http://cozystack-assets.cozy-system.svc/dashboards/{{ . }}.json + url: http://grafana-dashboards.cozy-grafana-operator.svc/{{ . }}.json {{- end }} {{- end }} diff --git a/packages/extra/seaweedfs/templates/seaweedfs.yaml b/packages/extra/seaweedfs/templates/seaweedfs.yaml index 747fc410..b04318bb 100644 --- a/packages/extra/seaweedfs/templates/seaweedfs.yaml +++ b/packages/extra/seaweedfs/templates/seaweedfs.yaml @@ -40,16 +40,13 @@ apiVersion: helm.toolkit.fluxcd.io/v2 kind: HelmRelease metadata: name: {{ .Release.Name }}-system + labels: + sharding.fluxcd.io/key: tenants spec: - chart: - spec: - chart: cozy-seaweedfs - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-seaweedfs-application-default-seaweedfs-system + namespace: cozy-system interval: 5m timeout: 10m install: diff --git a/packages/system/backupstrategy-controller/Chart.yaml b/packages/system/backupstrategy-controller/Chart.yaml index fd135712..bf556f3b 100644 --- a/packages/system/backupstrategy-controller/Chart.yaml +++ b/packages/system/backupstrategy-controller/Chart.yaml @@ -1,3 +1,3 @@ apiVersion: v2 -name: cozy-backup-controller +name: cozy-backupstrategy-controller version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process diff --git a/packages/system/backupstrategy-controller/definitions/strategy.backups.cozystack.io_veleroes.yaml b/packages/system/backupstrategy-controller/definitions/strategy.backups.cozystack.io_veleroes.yaml index 4433c7cb..e817de23 100644 --- a/packages/system/backupstrategy-controller/definitions/strategy.backups.cozystack.io_veleroes.yaml +++ b/packages/system/backupstrategy-controller/definitions/strategy.backups.cozystack.io_veleroes.yaml @@ -39,6 +39,502 @@ spec: spec: description: VeleroSpec specifies the desired strategy for backing up with Velero. + properties: + template: + description: |- + VeleroTemplate describes the data a backup.velero.io should have when + templated from a Velero backup strategy. + properties: + spec: + description: BackupSpec defines the specification for a Velero + backup. + properties: + csiSnapshotTimeout: + description: |- + CSISnapshotTimeout specifies the time used to wait for CSI VolumeSnapshot status turns to + ReadyToUse during creation, before returning error as timeout. + The default value is 10 minute. + type: string + datamover: + description: |- + DataMover specifies the data mover to be used by the backup. + If DataMover is "" or "velero", the built-in data mover will be used. + type: string + defaultVolumesToFsBackup: + description: |- + DefaultVolumesToFsBackup specifies whether pod volume file system backup should be used + for all volumes by default. + nullable: true + type: boolean + defaultVolumesToRestic: + description: |- + DefaultVolumesToRestic specifies whether restic should be used to take a + backup of all pod volumes by default. + + Deprecated: this field is no longer used and will be removed entirely in future. Use DefaultVolumesToFsBackup instead. + nullable: true + type: boolean + excludedClusterScopedResources: + description: |- + ExcludedClusterScopedResources is a slice of cluster-scoped + resource type names to exclude from the backup. + If set to "*", all cluster-scoped resource types are excluded. + The default value is empty. + items: + type: string + nullable: true + type: array + excludedNamespaceScopedResources: + description: |- + ExcludedNamespaceScopedResources is a slice of namespace-scoped + resource type names to exclude from the backup. + If set to "*", all namespace-scoped resource types are excluded. + The default value is empty. + items: + type: string + nullable: true + type: array + excludedNamespaces: + description: |- + ExcludedNamespaces contains a list of namespaces that are not + included in the backup. + items: + type: string + nullable: true + type: array + excludedResources: + description: |- + ExcludedResources is a slice of resource names that are not + included in the backup. + items: + type: string + nullable: true + type: array + hooks: + description: Hooks represent custom behaviors that should + be executed at different phases of the backup. + properties: + resources: + description: Resources are hooks that should be executed + when backing up individual instances of a resource. + items: + description: |- + BackupResourceHookSpec defines one or more BackupResourceHooks that should be executed based on + the rules defined for namespaces, resources, and label selector. + properties: + excludedNamespaces: + description: ExcludedNamespaces specifies the namespaces + to which this hook spec does not apply. + items: + type: string + nullable: true + type: array + excludedResources: + description: ExcludedResources specifies the resources + to which this hook spec does not apply. + items: + type: string + nullable: true + type: array + includedNamespaces: + description: |- + IncludedNamespaces specifies the namespaces to which this hook spec applies. If empty, it applies + to all namespaces. + items: + type: string + nullable: true + type: array + includedResources: + description: |- + IncludedResources specifies the resources to which this hook spec applies. If empty, it applies + to all resources. + items: + type: string + nullable: true + type: array + labelSelector: + description: LabelSelector, if specified, filters + the resources to which this hook spec applies. + nullable: true + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + name: + description: Name is the name of this hook. + type: string + post: + description: |- + PostHooks is a list of BackupResourceHooks to execute after storing the item in the backup. + These are executed after all "additional items" from item actions are processed. + items: + description: BackupResourceHook defines a hook + for a resource. + properties: + exec: + description: Exec defines an exec hook. + properties: + command: + description: Command is the command and + arguments to execute. + items: + type: string + minItems: 1 + type: array + container: + description: |- + Container is the container in the pod where the command should be executed. If not specified, + the pod's first container is used. + type: string + onError: + description: OnError specifies how Velero + should behave if it encounters an error + executing this hook. + enum: + - Continue + - Fail + type: string + timeout: + description: |- + Timeout defines the maximum amount of time Velero should wait for the hook to complete before + considering the execution a failure. + type: string + required: + - command + type: object + required: + - exec + type: object + type: array + pre: + description: |- + PreHooks is a list of BackupResourceHooks to execute prior to storing the item in the backup. + These are executed before any "additional items" from item actions are processed. + items: + description: BackupResourceHook defines a hook + for a resource. + properties: + exec: + description: Exec defines an exec hook. + properties: + command: + description: Command is the command and + arguments to execute. + items: + type: string + minItems: 1 + type: array + container: + description: |- + Container is the container in the pod where the command should be executed. If not specified, + the pod's first container is used. + type: string + onError: + description: OnError specifies how Velero + should behave if it encounters an error + executing this hook. + enum: + - Continue + - Fail + type: string + timeout: + description: |- + Timeout defines the maximum amount of time Velero should wait for the hook to complete before + considering the execution a failure. + type: string + required: + - command + type: object + required: + - exec + type: object + type: array + required: + - name + type: object + nullable: true + type: array + type: object + includeClusterResources: + description: |- + IncludeClusterResources specifies whether cluster-scoped resources + should be included for consideration in the backup. + nullable: true + type: boolean + includedClusterScopedResources: + description: |- + IncludedClusterScopedResources is a slice of cluster-scoped + resource type names to include in the backup. + If set to "*", all cluster-scoped resource types are included. + The default value is empty, which means only related + cluster-scoped resources are included. + items: + type: string + nullable: true + type: array + includedNamespaceScopedResources: + description: |- + IncludedNamespaceScopedResources is a slice of namespace-scoped + resource type names to include in the backup. + The default value is "*". + items: + type: string + nullable: true + type: array + includedNamespaces: + description: |- + IncludedNamespaces is a slice of namespace names to include objects + from. If empty, all namespaces are included. + items: + type: string + nullable: true + type: array + includedResources: + description: |- + IncludedResources is a slice of resource names to include + in the backup. If empty, all resources are included. + items: + type: string + nullable: true + type: array + itemOperationTimeout: + description: |- + ItemOperationTimeout specifies the time used to wait for asynchronous BackupItemAction operations + The default value is 4 hour. + type: string + labelSelector: + description: |- + LabelSelector is a metav1.LabelSelector to filter with + when adding individual objects to the backup. If empty + or nil, all objects are included. Optional. + nullable: true + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + metadata: + properties: + labels: + additionalProperties: + type: string + type: object + type: object + orLabelSelectors: + description: |- + OrLabelSelectors is list of metav1.LabelSelector to filter with + when adding individual objects to the backup. If multiple provided + they will be joined by the OR operator. LabelSelector as well as + OrLabelSelectors cannot co-exist in backup request, only one of them + can be used. + items: + description: |- + A label selector is a label query over a set of resources. The result of matchLabels and + matchExpressions are ANDed. An empty label selector matches all objects. A null + label selector matches no objects. + properties: + matchExpressions: + description: matchExpressions is a list of label selector + requirements. The requirements are ANDed. + items: + description: |- + A label selector requirement is a selector that contains values, a key, and an operator that + relates the key and values. + properties: + key: + description: key is the label key that the selector + applies to. + type: string + operator: + description: |- + operator represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: |- + values is an array of string values. If the operator is In or NotIn, + the values array must be non-empty. If the operator is Exists or DoesNotExist, + the values array must be empty. This array is replaced during a strategic + merge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: |- + matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, whose key field is "key", the + operator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + nullable: true + type: array + orderedResources: + additionalProperties: + type: string + description: |- + OrderedResources specifies the backup order of resources of specific Kind. + The map key is the resource name and value is a list of object names separated by commas. + Each resource name has format "namespace/objectname". For cluster resources, simply use "objectname". + nullable: true + type: object + resourcePolicy: + description: ResourcePolicy specifies the referenced resource + policies that backup should follow + properties: + apiGroup: + description: |- + APIGroup is the group for the resource being referenced. + If APIGroup is not specified, the specified Kind must be in the core API group. + For any other third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource being referenced + type: string + name: + description: Name is the name of resource being referenced + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + snapshotMoveData: + description: SnapshotMoveData specifies whether snapshot data + should be moved + nullable: true + type: boolean + snapshotVolumes: + description: |- + SnapshotVolumes specifies whether to take snapshots + of any PV's referenced in the set of objects included + in the Backup. + nullable: true + type: boolean + storageLocation: + description: StorageLocation is a string containing the name + of a BackupStorageLocation where the backup should be stored. + type: string + ttl: + description: |- + TTL is a time.Duration-parseable string describing how long + the Backup should be retained for. + type: string + uploaderConfig: + description: UploaderConfig specifies the configuration for + the uploader. + nullable: true + properties: + parallelFilesUpload: + description: ParallelFilesUpload is the number of files + parallel uploads to perform when using the uploader. + type: integer + type: object + volumeGroupSnapshotLabelKey: + description: VolumeGroupSnapshotLabelKey specifies the label + key to group PVCs under a VGS. + type: string + volumeSnapshotLocations: + description: VolumeSnapshotLocations is a list containing + names of VolumeSnapshotLocations associated with this backup. + items: + type: string + type: array + type: object + required: + - spec + type: object + required: + - template type: object status: properties: diff --git a/packages/system/backupstrategy-controller/images/backupstrategy-controller/Dockerfile b/packages/system/backupstrategy-controller/images/backupstrategy-controller/Dockerfile index e896f473..c4f60746 100644 --- a/packages/system/backupstrategy-controller/images/backupstrategy-controller/Dockerfile +++ b/packages/system/backupstrategy-controller/images/backupstrategy-controller/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.24-alpine AS builder +FROM golang:1.25-alpine AS builder ARG TARGETOS ARG TARGETARCH diff --git a/packages/system/bootbox/templates/bootbox.yaml b/packages/system/bootbox/templates/bootbox.yaml index 45cd07ce..332f30f4 100644 --- a/packages/system/bootbox/templates/bootbox.yaml +++ b/packages/system/bootbox/templates/bootbox.yaml @@ -5,20 +5,16 @@ metadata: helm.sh/resource-policy: keep labels: cozystack.io/ui: "true" + sharding.fluxcd.io/key: tenants apps.cozystack.io/application.kind: BootBox apps.cozystack.io/application.group: apps.cozystack.io apps.cozystack.io/application.name: bootbox name: bootbox namespace: tenant-root spec: - chart: - spec: - chart: bootbox - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-extra - namespace: cozy-public - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-bootbox-application-default-bootbox + namespace: cozy-system interval: 1m0s timeout: 5m0s diff --git a/packages/system/cozystack-basics/Chart.yaml b/packages/system/cozystack-basics/Chart.yaml new file mode 100644 index 00000000..8135baef --- /dev/null +++ b/packages/system/cozystack-basics/Chart.yaml @@ -0,0 +1,3 @@ +apiVersion: v2 +name: cozy-basics +version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process diff --git a/packages/system/cozystack-basics/Makefile b/packages/system/cozystack-basics/Makefile new file mode 100644 index 00000000..fe85b39b --- /dev/null +++ b/packages/system/cozystack-basics/Makefile @@ -0,0 +1,4 @@ +export NAME=tenant-root +export NAMESPACE=cozy-system + +include ../../../scripts/package.mk diff --git a/packages/system/cozystack-basics/templates/cozy-public.yaml b/packages/system/cozystack-basics/templates/cozy-public.yaml new file mode 100644 index 00000000..b76816b4 --- /dev/null +++ b/packages/system/cozystack-basics/templates/cozy-public.yaml @@ -0,0 +1,4 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: cozy-public diff --git a/packages/system/cozystack-basics/templates/cozystack-values-secret.yaml b/packages/system/cozystack-basics/templates/cozystack-values-secret.yaml new file mode 100644 index 00000000..b625435f --- /dev/null +++ b/packages/system/cozystack-basics/templates/cozystack-values-secret.yaml @@ -0,0 +1,19 @@ +--- +apiVersion: v1 +kind: Secret +metadata: + name: cozystack-values + namespace: tenant-root + labels: + reconcile.fluxcd.io/watch: Enabled +type: Opaque +stringData: + values.yaml: | + _cluster: + {{- .Values._cluster | toYaml | nindent 6 }} + _namespace: + host: {{ index .Values._cluster "root-host" | quote }} + etcd: tenant-root + ingress: tenant-root + monitoring: tenant-root + seaweedfs: tenant-root diff --git a/packages/system/cozystack-basics/templates/tenant-root.yaml b/packages/system/cozystack-basics/templates/tenant-root.yaml new file mode 100644 index 00000000..1d5586d9 --- /dev/null +++ b/packages/system/cozystack-basics/templates/tenant-root.yaml @@ -0,0 +1,30 @@ +--- +apiVersion: v1 +kind: Namespace +metadata: + name: tenant-root +--- +apiVersion: helm.toolkit.fluxcd.io/v2 +kind: HelmRelease +metadata: + annotations: + helm.sh/resource-policy: keep + labels: + cozystack.io/ui: "true" + sharding.fluxcd.io/key: tenants + apps.cozystack.io/application.kind: Tenant + apps.cozystack.io/application.group: apps.cozystack.io + apps.cozystack.io/application.name: tenant-root + name: tenant-root + namespace: tenant-root +spec: + chartRef: + kind: ExternalArtifact + name: cozystack-tenant-application-default-tenant + namespace: cozy-system + interval: 1m0s + timeout: 5m0s + values: + _cluster: + oidc-enabled: {{ .Values.oidcEnabled | quote }} + root-host: {{ .Values.rootHost | quote }} diff --git a/packages/system/cozystack-basics/values.yaml b/packages/system/cozystack-basics/values.yaml new file mode 100644 index 00000000..b8b5e457 --- /dev/null +++ b/packages/system/cozystack-basics/values.yaml @@ -0,0 +1 @@ +_cluster: {} diff --git a/packages/system/cozystack-resource-definition-crd/definition/cozystack.io_cozystackresourcedefinitions.yaml b/packages/system/cozystack-resource-definition-crd/definition/cozystack.io_cozystackresourcedefinitions.yaml index 685d4ac5..6c6c15f5 100644 --- a/packages/system/cozystack-resource-definition-crd/definition/cozystack.io_cozystackresourcedefinitions.yaml +++ b/packages/system/cozystack-resource-definition-crd/definition/cozystack.io_cozystackresourcedefinitions.yaml @@ -135,29 +135,22 @@ spec: If a resource matches the selector in any of the elements in the array, it is hidden from the user, regardless of the matches in the include array. items: - description: |- - CozystackResourceDefinitionResourceSelector extends metav1.LabelSelector with resourceNames support. - A resource matches this selector only if it satisfies ALL criteria: - - Label selector conditions (matchExpressions and matchLabels) - - AND has a name that matches one of the names in resourceNames (if specified) - - The resourceNames field supports Go templates with the following variables available: - - {{ .name }}: The name of the managing application (from apps.cozystack.io/application.name) - - {{ .kind }}: The lowercased kind of the managing application (from apps.cozystack.io/application.kind) - - {{ .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" + description: "CozystackResourceDefinitionResourceSelector extends + metav1.LabelSelector with resourceNames support.\nA resource + matches this selector only if it satisfies ALL criteria:\n- + Label selector conditions (matchExpressions and matchLabels)\n- + AND has a name that matches one of the names in resourceNames + (if specified)\n\nThe resourceNames field supports Go templates + with the following variables available:\n- {{ .name }}: The + name of the managing application (from apps.cozystack.io/application.name)\n- + {{ .kind }}: The lowercased kind of the managing application + (from apps.cozystack.io/application.kind)\n- {{ .namespace + }}: The namespace of the resource being processed\n\nExample + YAML:\n\n\tsecrets:\n\t include:\n\t - matchExpressions:\n\t + \ - key: badlabel\n\t operator: DoesNotExist\n\t matchLabels:\n\t + \ goodlabel: goodvalue\n\t resourceNames:\n\t - + \"{{ .name }}-secret\"\n\t - \"{{ .kind }}-{{ .name }}-tls\"\n\t + \ - \"specificname\"" properties: matchExpressions: description: matchExpressions is a list of label selector @@ -217,29 +210,22 @@ spec: matches none of the selectors in the exclude array that resource is marked as a tenant resource and is visible to users. items: - description: |- - CozystackResourceDefinitionResourceSelector extends metav1.LabelSelector with resourceNames support. - A resource matches this selector only if it satisfies ALL criteria: - - Label selector conditions (matchExpressions and matchLabels) - - AND has a name that matches one of the names in resourceNames (if specified) - - The resourceNames field supports Go templates with the following variables available: - - {{ .name }}: The name of the managing application (from apps.cozystack.io/application.name) - - {{ .kind }}: The lowercased kind of the managing application (from apps.cozystack.io/application.kind) - - {{ .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" + description: "CozystackResourceDefinitionResourceSelector extends + metav1.LabelSelector with resourceNames support.\nA resource + matches this selector only if it satisfies ALL criteria:\n- + Label selector conditions (matchExpressions and matchLabels)\n- + AND has a name that matches one of the names in resourceNames + (if specified)\n\nThe resourceNames field supports Go templates + with the following variables available:\n- {{ .name }}: The + name of the managing application (from apps.cozystack.io/application.name)\n- + {{ .kind }}: The lowercased kind of the managing application + (from apps.cozystack.io/application.kind)\n- {{ .namespace + }}: The namespace of the resource being processed\n\nExample + YAML:\n\n\tsecrets:\n\t include:\n\t - matchExpressions:\n\t + \ - key: badlabel\n\t operator: DoesNotExist\n\t matchLabels:\n\t + \ goodlabel: goodvalue\n\t resourceNames:\n\t - + \"{{ .name }}-secret\"\n\t - \"{{ .kind }}-{{ .name }}-tls\"\n\t + \ - \"specificname\"" properties: matchExpressions: description: matchExpressions is a list of label selector @@ -296,34 +282,34 @@ spec: release: description: Release configuration properties: - chart: - description: Helm chart configuration + chartRef: + description: Reference to the chart source properties: - name: - description: Name of the Helm chart + apiVersion: + description: APIVersion of the referent. + type: string + kind: + description: Kind of the referent. + enum: + - OCIRepository + - HelmChart + - ExternalArtifact + type: string + name: + description: Name of the referent. + maxLength: 253 + minLength: 1 + type: string + namespace: + description: |- + Namespace of the referent, defaults to the namespace of the Kubernetes + resource object that contains the reference. + maxLength: 63 + minLength: 1 type: string - sourceRef: - description: Source reference for the Helm chart - properties: - kind: - default: HelmRepository - description: Kind of the source reference - type: string - name: - description: Name of the source reference - type: string - namespace: - default: cozy-public - description: Namespace of the source reference - type: string - required: - - kind - - name - - namespace - type: object required: + - kind - name - - sourceRef type: object labels: additionalProperties: @@ -334,7 +320,7 @@ spec: description: Prefix for the release name type: string required: - - chart + - chartRef - prefix type: object secrets: @@ -346,29 +332,22 @@ spec: If a resource matches the selector in any of the elements in the array, it is hidden from the user, regardless of the matches in the include array. items: - description: |- - CozystackResourceDefinitionResourceSelector extends metav1.LabelSelector with resourceNames support. - A resource matches this selector only if it satisfies ALL criteria: - - Label selector conditions (matchExpressions and matchLabels) - - AND has a name that matches one of the names in resourceNames (if specified) - - The resourceNames field supports Go templates with the following variables available: - - {{ .name }}: The name of the managing application (from apps.cozystack.io/application.name) - - {{ .kind }}: The lowercased kind of the managing application (from apps.cozystack.io/application.kind) - - {{ .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" + description: "CozystackResourceDefinitionResourceSelector extends + metav1.LabelSelector with resourceNames support.\nA resource + matches this selector only if it satisfies ALL criteria:\n- + Label selector conditions (matchExpressions and matchLabels)\n- + AND has a name that matches one of the names in resourceNames + (if specified)\n\nThe resourceNames field supports Go templates + with the following variables available:\n- {{ .name }}: The + name of the managing application (from apps.cozystack.io/application.name)\n- + {{ .kind }}: The lowercased kind of the managing application + (from apps.cozystack.io/application.kind)\n- {{ .namespace + }}: The namespace of the resource being processed\n\nExample + YAML:\n\n\tsecrets:\n\t include:\n\t - matchExpressions:\n\t + \ - key: badlabel\n\t operator: DoesNotExist\n\t matchLabels:\n\t + \ goodlabel: goodvalue\n\t resourceNames:\n\t - + \"{{ .name }}-secret\"\n\t - \"{{ .kind }}-{{ .name }}-tls\"\n\t + \ - \"specificname\"" properties: matchExpressions: description: matchExpressions is a list of label selector @@ -428,29 +407,22 @@ spec: matches none of the selectors in the exclude array that resource is marked as a tenant resource and is visible to users. items: - description: |- - CozystackResourceDefinitionResourceSelector extends metav1.LabelSelector with resourceNames support. - A resource matches this selector only if it satisfies ALL criteria: - - Label selector conditions (matchExpressions and matchLabels) - - AND has a name that matches one of the names in resourceNames (if specified) - - The resourceNames field supports Go templates with the following variables available: - - {{ .name }}: The name of the managing application (from apps.cozystack.io/application.name) - - {{ .kind }}: The lowercased kind of the managing application (from apps.cozystack.io/application.kind) - - {{ .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" + description: "CozystackResourceDefinitionResourceSelector extends + metav1.LabelSelector with resourceNames support.\nA resource + matches this selector only if it satisfies ALL criteria:\n- + Label selector conditions (matchExpressions and matchLabels)\n- + AND has a name that matches one of the names in resourceNames + (if specified)\n\nThe resourceNames field supports Go templates + with the following variables available:\n- {{ .name }}: The + name of the managing application (from apps.cozystack.io/application.name)\n- + {{ .kind }}: The lowercased kind of the managing application + (from apps.cozystack.io/application.kind)\n- {{ .namespace + }}: The namespace of the resource being processed\n\nExample + YAML:\n\n\tsecrets:\n\t include:\n\t - matchExpressions:\n\t + \ - key: badlabel\n\t operator: DoesNotExist\n\t matchLabels:\n\t + \ goodlabel: goodvalue\n\t resourceNames:\n\t - + \"{{ .name }}-secret\"\n\t - \"{{ .kind }}-{{ .name }}-tls\"\n\t + \ - \"specificname\"" properties: matchExpressions: description: matchExpressions is a list of label selector @@ -513,29 +485,22 @@ spec: If a resource matches the selector in any of the elements in the array, it is hidden from the user, regardless of the matches in the include array. items: - description: |- - CozystackResourceDefinitionResourceSelector extends metav1.LabelSelector with resourceNames support. - A resource matches this selector only if it satisfies ALL criteria: - - Label selector conditions (matchExpressions and matchLabels) - - AND has a name that matches one of the names in resourceNames (if specified) - - The resourceNames field supports Go templates with the following variables available: - - {{ .name }}: The name of the managing application (from apps.cozystack.io/application.name) - - {{ .kind }}: The lowercased kind of the managing application (from apps.cozystack.io/application.kind) - - {{ .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" + description: "CozystackResourceDefinitionResourceSelector extends + metav1.LabelSelector with resourceNames support.\nA resource + matches this selector only if it satisfies ALL criteria:\n- + Label selector conditions (matchExpressions and matchLabels)\n- + AND has a name that matches one of the names in resourceNames + (if specified)\n\nThe resourceNames field supports Go templates + with the following variables available:\n- {{ .name }}: The + name of the managing application (from apps.cozystack.io/application.name)\n- + {{ .kind }}: The lowercased kind of the managing application + (from apps.cozystack.io/application.kind)\n- {{ .namespace + }}: The namespace of the resource being processed\n\nExample + YAML:\n\n\tsecrets:\n\t include:\n\t - matchExpressions:\n\t + \ - key: badlabel\n\t operator: DoesNotExist\n\t matchLabels:\n\t + \ goodlabel: goodvalue\n\t resourceNames:\n\t - + \"{{ .name }}-secret\"\n\t - \"{{ .kind }}-{{ .name }}-tls\"\n\t + \ - \"specificname\"" properties: matchExpressions: description: matchExpressions is a list of label selector @@ -595,29 +560,22 @@ spec: matches none of the selectors in the exclude array that resource is marked as a tenant resource and is visible to users. items: - description: |- - CozystackResourceDefinitionResourceSelector extends metav1.LabelSelector with resourceNames support. - A resource matches this selector only if it satisfies ALL criteria: - - Label selector conditions (matchExpressions and matchLabels) - - AND has a name that matches one of the names in resourceNames (if specified) - - The resourceNames field supports Go templates with the following variables available: - - {{ .name }}: The name of the managing application (from apps.cozystack.io/application.name) - - {{ .kind }}: The lowercased kind of the managing application (from apps.cozystack.io/application.kind) - - {{ .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" + description: "CozystackResourceDefinitionResourceSelector extends + metav1.LabelSelector with resourceNames support.\nA resource + matches this selector only if it satisfies ALL criteria:\n- + Label selector conditions (matchExpressions and matchLabels)\n- + AND has a name that matches one of the names in resourceNames + (if specified)\n\nThe resourceNames field supports Go templates + with the following variables available:\n- {{ .name }}: The + name of the managing application (from apps.cozystack.io/application.name)\n- + {{ .kind }}: The lowercased kind of the managing application + (from apps.cozystack.io/application.kind)\n- {{ .namespace + }}: The namespace of the resource being processed\n\nExample + YAML:\n\n\tsecrets:\n\t include:\n\t - matchExpressions:\n\t + \ - key: badlabel\n\t operator: DoesNotExist\n\t matchLabels:\n\t + \ goodlabel: goodvalue\n\t resourceNames:\n\t - + \"{{ .name }}-secret\"\n\t - \"{{ .kind }}-{{ .name }}-tls\"\n\t + \ - \"specificname\"" properties: matchExpressions: description: matchExpressions is a list of label selector diff --git a/packages/system/grafana-operator/templates/dashboards-deployment.yaml b/packages/system/grafana-operator/templates/dashboards-deployment.yaml new file mode 100644 index 00000000..f9b1f99b --- /dev/null +++ b/packages/system/grafana-operator/templates/dashboards-deployment.yaml @@ -0,0 +1,41 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: grafana-dashboards + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: grafana-dashboards + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: dashboards +spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/name: grafana-dashboards + app.kubernetes.io/instance: {{ .Release.Name }} + template: + metadata: + labels: + app.kubernetes.io/name: grafana-dashboards + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: dashboards + spec: + containers: + - name: dashboards + image: {{ $.Files.Get "images/grafana-dashboards.tag" | trim }} + ports: + - containerPort: 8080 + name: http + protocol: TCP + livenessProbe: + httpGet: + path: / + port: 8080 + initialDelaySeconds: 10 + periodSeconds: 10 + readinessProbe: + httpGet: + path: / + port: 8080 + initialDelaySeconds: 5 + periodSeconds: 5 diff --git a/packages/system/grafana-operator/templates/dashboards-service.yaml b/packages/system/grafana-operator/templates/dashboards-service.yaml new file mode 100644 index 00000000..011072c3 --- /dev/null +++ b/packages/system/grafana-operator/templates/dashboards-service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: grafana-dashboards + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/name: grafana-dashboards + app.kubernetes.io/instance: {{ .Release.Name }} + app.kubernetes.io/component: dashboards +spec: + type: ClusterIP + ports: + - port: 80 + targetPort: 8080 + protocol: TCP + name: http + selector: + app.kubernetes.io/name: grafana-dashboards + app.kubernetes.io/instance: {{ .Release.Name }} diff --git a/packages/system/kubeovn/charts/kube-ovn/templates/controller-deploy.yaml b/packages/system/kubeovn/charts/kube-ovn/templates/controller-deploy.yaml index cd0728b3..219e4ca0 100644 --- a/packages/system/kubeovn/charts/kube-ovn/templates/controller-deploy.yaml +++ b/packages/system/kubeovn/charts/kube-ovn/templates/controller-deploy.yaml @@ -74,19 +74,18 @@ spec: image: {{ .Values.global.registry.address }}/{{ .Values.global.images.kubeovn.repository }}:{{ .Values.global.images.kubeovn.tag }} imagePullPolicy: {{ .Values.image.pullPolicy }} args: - {{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} - /kube-ovn/start-controller.sh - --default-ls={{ .Values.networking.DEFAULT_SUBNET }} - - --default-cidr={{ index $cozyConfig.data "ipv4-pod-cidr" }} - - --default-gateway={{ index $cozyConfig.data "ipv4-pod-gateway" }} + - --default-cidr={{ .Values.ipv4.POD_CIDR }} + - --default-gateway={{ .Values.ipv4.POD_GATEWAY }} - --default-gateway-check={{- .Values.func.CHECK_GATEWAY }} - --default-logical-gateway={{- .Values.func.LOGICAL_GATEWAY }} - --default-u2o-interconnection={{- .Values.func.U2O_INTERCONNECTION }} - --default-exclude-ips={{- .Values.networking.EXCLUDE_IPS }} - --cluster-router={{ .Values.networking.DEFAULT_VPC }} - --node-switch={{ .Values.networking.NODE_SUBNET }} - - --node-switch-cidr={{ index $cozyConfig.data "ipv4-join-cidr" }} - - --service-cluster-ip-range={{ index $cozyConfig.data "ipv4-svc-cidr" }} + - --node-switch-cidr={{ .Values.ipv4.JOIN_CIDR }} + - --service-cluster-ip-range={{ .Values.ipv4.SVC_CIDR }} {{- if .Values.global.logVerbosity }} - --v={{ .Values.global.logVerbosity }} {{- end }} diff --git a/packages/system/kubeovn/charts/kube-ovn/templates/ovncni-ds.yaml b/packages/system/kubeovn/charts/kube-ovn/templates/ovncni-ds.yaml index c68e041d..d53c06c2 100644 --- a/packages/system/kubeovn/charts/kube-ovn/templates/ovncni-ds.yaml +++ b/packages/system/kubeovn/charts/kube-ovn/templates/ovncni-ds.yaml @@ -94,12 +94,11 @@ spec: - bash - /kube-ovn/start-cniserver.sh args: - {{- $cozyConfig := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} - --enable-mirror={{- .Values.debug.ENABLE_MIRROR }} - --mirror-iface={{- .Values.debug.MIRROR_IFACE }} - --node-switch={{ .Values.networking.NODE_SUBNET }} - --encap-checksum=true - - --service-cluster-ip-range={{ index $cozyConfig.data "ipv4-svc-cidr" }} + - --service-cluster-ip-range={{ .Values.ipv4.SVC_CIDR }} {{- if .Values.global.logVerbosity }} - --v={{ .Values.global.logVerbosity }} {{- end }} diff --git a/packages/system/linstor/images/piraeus-server/patches/skip-adjust-when-device-inaccessible.diff b/packages/system/linstor/images/piraeus-server/patches/skip-adjust-when-device-inaccessible.diff deleted file mode 100644 index 65c8be7c..00000000 --- a/packages/system/linstor/images/piraeus-server/patches/skip-adjust-when-device-inaccessible.diff +++ /dev/null @@ -1,260 +0,0 @@ -From 6a556821b9a0996d34389a27b941694ce810a44c Mon Sep 17 00:00:00 2001 -From: Andrei Kvapil -Date: Mon, 12 Jan 2026 14:26:57 +0100 -Subject: [PATCH 1/3] Fix: Skip DRBD adjust/res file regeneration when child - layer device is inaccessible - -When deleting encrypted (LUKS) resources, the LUKS layer may close its device -before the DRBD layer attempts to adjust the resource or regenerate the res -file. This causes 'Failed to adjust DRBD resource' errors. - -This fix adds checks before regenerateResFile() and drbdUtils.adjust() -to verify that child layer devices are accessible. If a child device doesn't -exist or is not accessible (e.g., LUKS device is closed during resource -deletion), these operations are skipped and the adjustRequired flag is cleared, -allowing resource deletion to proceed successfully. - -Co-Authored-By: Claude -Signed-off-by: Andrei Kvapil ---- - .../linbit/linstor/layer/drbd/DrbdLayer.java | 72 ++++++++++++++++--- - 1 file changed, 61 insertions(+), 11 deletions(-) - -diff --git a/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java b/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java -index 01967a31f..871d830d1 100644 ---- a/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java -+++ b/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java -@@ -592,7 +592,29 @@ public class DrbdLayer implements DeviceLayer - // The .res file might not have been generated in the prepare method since it was - // missing information from the child-layers. Now that we have processed them, we - // need to make sure the .res file exists in all circumstances. -- regenerateResFile(drbdRscData); -+ // However, if the underlying devices are not accessible (e.g., LUKS device is closed -+ // during resource deletion), we skip regenerating the res file to avoid errors -+ boolean canRegenerateResFile = true; -+ if (!skipDisk && !drbdRscData.getAbsResource().isDrbdDiskless(workerCtx)) -+ { -+ AbsRscLayerObject dataChild = drbdRscData.getChildBySuffix(RscLayerSuffixes.SUFFIX_DATA); -+ if (dataChild != null) -+ { -+ for (DrbdVlmData drbdVlmData : drbdRscData.getVlmLayerObjects().values()) -+ { -+ VlmProviderObject childVlm = dataChild.getVlmProviderObject(drbdVlmData.getVlmNr()); -+ if (childVlm == null || !childVlm.exists() || childVlm.getDevicePath() == null) -+ { -+ canRegenerateResFile = false; -+ break; -+ } -+ } -+ } -+ } -+ if (canRegenerateResFile) -+ { -+ regenerateResFile(drbdRscData); -+ } - - // createMetaData needs rendered resFile - for (DrbdVlmData drbdVlmData : createMetaData) -@@ -766,19 +788,47 @@ public class DrbdLayer implements DeviceLayer - - if (drbdRscData.isAdjustRequired()) - { -- try -+ // Check if underlying devices are accessible before adjusting -+ // This is important for encrypted resources (LUKS) where the device -+ // might be closed during deletion -+ boolean canAdjust = true; -+ if (!skipDisk && !drbdRscData.getAbsResource().isDrbdDiskless(workerCtx)) - { -- drbdUtils.adjust( -- drbdRscData, -- false, -- skipDisk, -- false -- ); -+ AbsRscLayerObject dataChild = drbdRscData.getChildBySuffix(RscLayerSuffixes.SUFFIX_DATA); -+ if (dataChild != null) -+ { -+ for (DrbdVlmData drbdVlmData : drbdRscData.getVlmLayerObjects().values()) -+ { -+ VlmProviderObject childVlm = dataChild.getVlmProviderObject(drbdVlmData.getVlmNr()); -+ if (childVlm == null || !childVlm.exists() || childVlm.getDevicePath() == null) -+ { -+ canAdjust = false; -+ break; -+ } -+ } -+ } - } -- catch (ExtCmdFailedException extCmdExc) -+ -+ if (canAdjust) -+ { -+ try -+ { -+ drbdUtils.adjust( -+ drbdRscData, -+ false, -+ skipDisk, -+ false -+ ); -+ } -+ catch (ExtCmdFailedException extCmdExc) -+ { -+ restoreBackupResFile(drbdRscData); -+ throw extCmdExc; -+ } -+ } -+ else - { -- restoreBackupResFile(drbdRscData); -- throw extCmdExc; -+ drbdRscData.setAdjustRequired(false); - } - } - --- -2.39.5 (Apple Git-154) - - -From afe51ea674c4a350c27d1f2cacfecf6fe42b8a7a Mon Sep 17 00:00:00 2001 -From: Andrei Kvapil -Date: Mon, 12 Jan 2026 14:27:52 +0100 -Subject: [PATCH 2/3] fix(satellite): skip lsblk when device path doesn't - physically exist - -Add physical device path existence check before calling lsblk in updateDiscGran(). -This prevents race condition when drbdadm adjust temporarily brings devices down/up -and the kernel hasn't created the device node yet. - -Issue: After satellite restart with patched code, some DRBD resources ended up in -Unknown state because: -1. drbdadm adjust successfully completes (brings devices up) -2. updateDiscGran() immediately tries to check discard granularity -3. /dev/drbd* device node doesn't exist yet (kernel hasn't created it) -4. lsblk fails with exit code 32 "not a block device" -5. StorageException interrupts DeviceManager cycle -6. DRBD device remains in incomplete state - -Solution: Check Files.exists(devicePath) before calling lsblk. If device doesn't -exist yet, skip the check - it will be retried in the next DeviceManager cycle -when the device node is available. - -Co-Authored-By: Claude -Signed-off-by: Andrei Kvapil ---- - .../linbit/linstor/core/devmgr/DeviceHandlerImpl.java | 9 +++++++-- - 1 file changed, 7 insertions(+), 2 deletions(-) - -diff --git a/satellite/src/main/java/com/linbit/linstor/core/devmgr/DeviceHandlerImpl.java b/satellite/src/main/java/com/linbit/linstor/core/devmgr/DeviceHandlerImpl.java -index 49138a8fd..1c13cfc9d 100644 ---- a/satellite/src/main/java/com/linbit/linstor/core/devmgr/DeviceHandlerImpl.java -+++ b/satellite/src/main/java/com/linbit/linstor/core/devmgr/DeviceHandlerImpl.java -@@ -68,6 +68,8 @@ import javax.inject.Inject; - import javax.inject.Provider; - import javax.inject.Singleton; - -+import java.nio.file.Files; -+import java.nio.file.Paths; - import java.util.ArrayList; - import java.util.Collection; - import java.util.Collections; -@@ -1645,8 +1647,11 @@ public class DeviceHandlerImpl implements DeviceHandler - - private void updateDiscGran(VlmProviderObject vlmData) throws DatabaseException, StorageException - { -- String devicePath = vlmData.getDevicePath(); -- if (devicePath != null && vlmData.exists()) -+ @Nullable String devicePath = vlmData.getDevicePath(); -+ // Check if device path physically exists before calling lsblk -+ // This is important for DRBD devices which might be temporarily unavailable during adjust -+ // (drbdadm adjust brings devices down/up, and kernel might not have created the device node yet) -+ if (devicePath != null && vlmData.exists() && Files.exists(Paths.get(devicePath))) - { - if (vlmData.getDiscGran() == VlmProviderObject.UNINITIALIZED_SIZE) - { --- -2.39.5 (Apple Git-154) - - -From de1f22e7c008c5479f85a3b1ebdf8461944210f4 Mon Sep 17 00:00:00 2001 -From: Andrei Kvapil -Date: Mon, 12 Jan 2026 14:28:23 +0100 -Subject: [PATCH 3/3] fix(drbd): only check child devices when disk access is - actually needed -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The previous implementation blocked `drbdadm adjust` whenever child -device paths were unavailable, even for operations that don't require -disk access (like network reconnect from StandAlone to Connected). - -After node reboot, DRBD resources often remain in StandAlone state -because: -1. updateResourceToCurrentDrbdState() correctly detects StandAlone - and sets adjustRequired=true -2. However, canAdjust check fails because child volumes may have - devicePath=null (due to INACTIVE flag, cloning state, or - initialization race) -3. adjust is skipped → adjustRequired=false → resources stay StandAlone - -Root cause: The canAdjust check was added to protect LUKS deletion -scenarios but was applied to ALL cases, including network reconnect -which doesn't need disk access. - -Fix: Check child device accessibility only when disk access is actually -required (volume creation, resize, metadata operations). Network -reconnect operations (StandAlone → Connected) now proceed without -checking child devices. - -This ensures automatic DRBD reconnection after reboot while preserving -protection for LUKS deletion scenarios. - -Co-Authored-By: Claude -Signed-off-by: Andrei Kvapil ---- - .../linbit/linstor/layer/drbd/DrbdLayer.java | 27 ++++++++++++++++++- - 1 file changed, 26 insertions(+), 1 deletion(-) - -diff --git a/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java b/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java -index 871d830d1..78b8195a4 100644 ---- a/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java -+++ b/satellite/src/main/java/com/linbit/linstor/layer/drbd/DrbdLayer.java -@@ -792,7 +792,32 @@ public class DrbdLayer implements DeviceLayer - // This is important for encrypted resources (LUKS) where the device - // might be closed during deletion - boolean canAdjust = true; -- if (!skipDisk && !drbdRscData.getAbsResource().isDrbdDiskless(workerCtx)) -+ -+ // IMPORTANT: Check child volumes only when disk access is actually needed. -+ // For network reconnect (StandAlone -> Connected), disk access is not required. -+ boolean needsDiskAccess = false; -+ -+ // Check if there are pending operations that require disk access -+ for (DrbdVlmData drbdVlmData : drbdRscData.getVlmLayerObjects().values()) -+ { -+ Volume vlm = (Volume) drbdVlmData.getVolume(); -+ StateFlags vlmFlags = vlm.getFlags(); -+ -+ // Disk access is needed if: -+ // - creating a new volume -+ // - resizing -+ // - checking/creating metadata -+ if (!drbdVlmData.exists() || -+ drbdVlmData.checkMetaData() || -+ vlmFlags.isSomeSet(workerCtx, Volume.Flags.RESIZE, Volume.Flags.DRBD_RESIZE)) -+ { -+ needsDiskAccess = true; -+ break; -+ } -+ } -+ -+ // Check child volumes only if disk access is actually needed -+ if (needsDiskAccess && !skipDisk && !drbdRscData.getAbsResource().isDrbdDiskless(workerCtx)) - { - AbsRscLayerObject dataChild = drbdRscData.getChildBySuffix(RscLayerSuffixes.SUFFIX_DATA); - if (dataChild != null) --- -2.39.5 (Apple Git-154) - diff --git a/packages/system/vertical-pod-autoscaler/templates/vpa-for-vpa.yaml b/packages/system/vertical-pod-autoscaler/templates/vpa-for-vpa.yaml index 5fb35fca..00fcadd7 100644 --- a/packages/system/vertical-pod-autoscaler/templates/vpa-for-vpa.yaml +++ b/packages/system/vertical-pod-autoscaler/templates/vpa-for-vpa.yaml @@ -16,15 +16,10 @@ metadata: name: vpa-for-vpa namespace: cozy-vpa-for-vpa spec: - chart: - spec: - chart: cozy-vertical-pod-autoscaler - reconcileStrategy: Revision - sourceRef: - kind: HelmRepository - name: cozystack-system - namespace: cozy-system - version: '>= 0.0.0-0' + chartRef: + kind: ExternalArtifact + name: cozystack-system-default-vertical-pod-autoscaler + namespace: cozy-system dependsOn: - name: monitoring-agents namespace: cozy-monitoring From 3618abed627f3a771ad611ae4cdf3e5e7d73c4c1 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Thu, 15 Jan 2026 16:06:56 +0100 Subject: [PATCH 023/125] refactor: move scripts to hack directory Move common-envs.mk and package.mk from scripts/ to hack/ directory. Update all Makefile includes to use new paths. Remove unused issue-flux-certificates.sh script. Co-Authored-By: Claude Signed-off-by: Andrei Kvapil --- Makefile | 2 + {scripts => hack}/common-envs.mk | 0 {scripts => hack}/package.mk | 0 packages/apps/Makefile | 2 +- packages/apps/bucket/Makefile | 2 +- packages/apps/clickhouse/Makefile | 4 +- packages/apps/ferretdb/Makefile | 2 +- packages/apps/foundationdb/Makefile | 2 +- packages/apps/http-cache/Makefile | 4 +- packages/apps/kafka/Makefile | 2 +- packages/apps/kubernetes/Makefile | 4 +- packages/apps/mysql/Makefile | 4 +- packages/apps/nats/Makefile | 2 +- packages/apps/postgres/Makefile | 2 +- packages/apps/rabbitmq/Makefile | 2 +- packages/apps/redis/Makefile | 2 +- packages/apps/tcp-balancer/Makefile | 2 +- packages/apps/tenant/Makefile | 2 +- packages/apps/virtual-machine/Makefile | 2 +- packages/apps/vm-disk/Makefile | 2 +- packages/apps/vm-instance/Makefile | 2 +- packages/apps/vpc/Makefile | 2 +- packages/apps/vpn/Makefile | 2 +- packages/core/flux-aio/Makefile | 2 +- packages/core/installer/Makefile | 2 +- packages/core/platform/Makefile | 2 +- packages/core/talos/Makefile | 2 +- packages/core/testing/Makefile | 2 +- packages/extra/Makefile | 2 +- packages/extra/bootbox/Makefile | 2 +- packages/extra/etcd/Makefile | 2 +- packages/extra/info/Makefile | 2 +- packages/extra/ingress/Makefile | 2 +- packages/extra/monitoring/Makefile | 4 +- packages/extra/seaweedfs/Makefile | 2 +- packages/library/Makefile | 2 +- packages/library/cozy-lib/Makefile | 4 +- packages/system/Makefile | 2 +- packages/system/backup-controller/Makefile | 4 +- .../system/backupstrategy-controller/Makefile | 4 +- packages/system/bootbox-rd/Makefile | 2 +- packages/system/bootbox/Makefile | 2 +- packages/system/bucket-rd/Makefile | 2 +- packages/system/bucket/Makefile | 4 +- packages/system/capi-operator/Makefile | 2 +- .../system/capi-providers-bootstrap/Makefile | 2 +- packages/system/capi-providers-core/Makefile | 2 +- .../system/capi-providers-cpprovider/Makefile | 2 +- .../capi-providers-infraprovider/Makefile | 2 +- packages/system/cert-manager-crds/Makefile | 2 +- packages/system/cert-manager-issuers/Makefile | 2 +- packages/system/cert-manager/Makefile | 2 +- packages/system/cilium-networkpolicy/Makefile | 4 +- packages/system/cilium/Makefile | 4 +- packages/system/clickhouse-operator/Makefile | 2 +- packages/system/clickhouse-rd/Makefile | 2 +- packages/system/coredns/Makefile | 2 +- packages/system/cozy-proxy/Makefile | 4 +- packages/system/cozystack-api/Makefile | 4 +- packages/system/cozystack-basics/Makefile | 2 +- packages/system/cozystack-controller/Makefile | 4 +- .../Makefile | 2 +- packages/system/dashboard/Makefile | 4 +- packages/system/etcd-operator/Makefile | 2 +- packages/system/etcd-rd/Makefile | 2 +- packages/system/external-dns/Makefile | 2 +- .../system/external-secrets-operator/Makefile | 2 +- packages/system/ferretdb-rd/Makefile | 2 +- packages/system/fluxcd-operator/Makefile | 2 +- packages/system/fluxcd/Makefile | 2 +- .../system/foundationdb-operator/Makefile | 2 +- packages/system/foundationdb-rd/Makefile | 2 +- packages/system/gateway-api-crds/Makefile | 2 +- packages/system/goldpinger/Makefile | 2 +- packages/system/gpu-operator/Makefile | 4 +- packages/system/grafana-operator/Makefile | 4 +- packages/system/hetzner-robotlb/Makefile | 2 +- packages/system/http-cache-rd/Makefile | 2 +- packages/system/info-rd/Makefile | 2 +- packages/system/ingress-nginx/Makefile | 2 +- packages/system/ingress-rd/Makefile | 2 +- packages/system/kafka-operator/Makefile | 2 +- packages/system/kafka-rd/Makefile | 2 +- packages/system/kamaji/Makefile | 4 +- packages/system/keycloak-configure/Makefile | 4 +- packages/system/keycloak-operator/Makefile | 4 +- packages/system/keycloak/Makefile | 4 +- packages/system/kubeovn-plunger/Makefile | 4 +- packages/system/kubeovn-webhook/Makefile | 4 +- packages/system/kubeovn/Makefile | 4 +- packages/system/kubernetes-rd/Makefile | 2 +- .../system/kubevirt-cdi-operator/Makefile | 2 +- packages/system/kubevirt-cdi/Makefile | 2 +- .../system/kubevirt-instancetypes/Makefile | 2 +- packages/system/kubevirt-operator/Makefile | 2 +- packages/system/kubevirt/Makefile | 2 +- .../lineage-controller-webhook/Makefile | 4 +- packages/system/linstor-scheduler/Makefile | 2 +- packages/system/linstor/Makefile | 4 +- packages/system/mariadb-operator/Makefile | 2 +- packages/system/metallb/Makefile | 4 +- packages/system/metrics-server/Makefile | 2 +- packages/system/monitoring-agents/Makefile | 2 +- packages/system/monitoring-rd/Makefile | 2 +- packages/system/multus/Makefile | 4 +- packages/system/mysql-rd/Makefile | 2 +- packages/system/nats-rd/Makefile | 2 +- packages/system/nats/Makefile | 2 +- packages/system/nfs-driver/Makefile | 4 +- .../system/objectstorage-controller/Makefile | 4 +- packages/system/piraeus-operator/Makefile | 2 +- packages/system/postgres-operator/Makefile | 2 +- packages/system/postgres-rd/Makefile | 2 +- .../system/prometheus-operator-crds/Makefile | 2 +- packages/system/rabbitmq-operator/Makefile | 2 +- packages/system/rabbitmq-rd/Makefile | 2 +- packages/system/redis-operator/Makefile | 4 +- packages/system/redis-rd/Makefile | 2 +- packages/system/reloader/Makefile | 2 +- packages/system/seaweedfs-rd/Makefile | 2 +- packages/system/seaweedfs/Makefile | 4 +- packages/system/snapshot-controller/Makefile | 2 +- packages/system/tcp-balancer-rd/Makefile | 2 +- packages/system/telepresence/Makefile | 2 +- packages/system/tenant-rd/Makefile | 2 +- packages/system/velero/Makefile | 2 +- .../vertical-pod-autoscaler-crds/Makefile | 2 +- .../system/vertical-pod-autoscaler/Makefile | 2 +- .../system/victoria-metrics-operator/Makefile | 2 +- packages/system/virtual-machine-rd/Makefile | 2 +- .../system/virtualprivatecloud-rd/Makefile | 2 +- packages/system/vm-disk-rd/Makefile | 2 +- packages/system/vm-instance-rd/Makefile | 2 +- packages/system/vpn-rd/Makefile | 2 +- packages/system/vsnap-crd/Makefile | 2 +- scripts/issue-flux-certificates.sh | 63 ------------------- 136 files changed, 166 insertions(+), 227 deletions(-) rename {scripts => hack}/common-envs.mk (100%) rename {scripts => hack}/package.mk (100%) delete mode 100755 scripts/issue-flux-certificates.sh diff --git a/Makefile b/Makefile index 971775cb..c037e023 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,7 @@ .PHONY: manifests assets unit-tests helm-unit-tests +include hack/common-envs.mk + build-deps: @command -V find docker skopeo jq gh helm > /dev/null @yq --version | grep -q "mikefarah" || (echo "mikefarah/yq is required" && exit 1) diff --git a/scripts/common-envs.mk b/hack/common-envs.mk similarity index 100% rename from scripts/common-envs.mk rename to hack/common-envs.mk diff --git a/scripts/package.mk b/hack/package.mk similarity index 100% rename from scripts/package.mk rename to hack/package.mk diff --git a/packages/apps/Makefile b/packages/apps/Makefile index b3917f20..50502f2b 100644 --- a/packages/apps/Makefile +++ b/packages/apps/Makefile @@ -1,7 +1,7 @@ OUT=../../_out/repos/apps CHARTS := $(shell find . -maxdepth 2 -name Chart.yaml | awk -F/ '{print $$2}') -include ../../scripts/common-envs.mk +include ../../hack/common-envs.mk repo: rm -rf "$(OUT)" diff --git a/packages/apps/bucket/Makefile b/packages/apps/bucket/Makefile index d448d0c0..27cd199b 100644 --- a/packages/apps/bucket/Makefile +++ b/packages/apps/bucket/Makefile @@ -1,4 +1,4 @@ -include ../../../scripts/package.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/apps/clickhouse/Makefile b/packages/apps/clickhouse/Makefile index 44ac851f..2c77add9 100644 --- a/packages/apps/clickhouse/Makefile +++ b/packages/apps/clickhouse/Makefile @@ -1,7 +1,7 @@ CLICKHOUSE_BACKUP_TAG = $(shell awk '$$0 ~ /^version:/ {print $$2}' Chart.yaml) -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/apps/ferretdb/Makefile b/packages/apps/ferretdb/Makefile index bd52fb05..40b7423f 100644 --- a/packages/apps/ferretdb/Makefile +++ b/packages/apps/ferretdb/Makefile @@ -1,4 +1,4 @@ -include ../../../scripts/package.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/apps/foundationdb/Makefile b/packages/apps/foundationdb/Makefile index b885e4b1..76c84980 100644 --- a/packages/apps/foundationdb/Makefile +++ b/packages/apps/foundationdb/Makefile @@ -1,4 +1,4 @@ -include ../../../scripts/package.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md \ No newline at end of file diff --git a/packages/apps/http-cache/Makefile b/packages/apps/http-cache/Makefile index 3237f634..2cc11f87 100644 --- a/packages/apps/http-cache/Makefile +++ b/packages/apps/http-cache/Makefile @@ -1,7 +1,7 @@ NGINX_CACHE_TAG = $(shell awk '$$1 == "version:" {print $$2}' Chart.yaml) -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk image: image-nginx diff --git a/packages/apps/kafka/Makefile b/packages/apps/kafka/Makefile index 0d71076e..2cffdfa7 100644 --- a/packages/apps/kafka/Makefile +++ b/packages/apps/kafka/Makefile @@ -1,4 +1,4 @@ -include ../../../scripts/package.mk +include ../../../hack/package.mk PRESET_ENUM := ["nano","micro","small","medium","large","xlarge","2xlarge"] generate: diff --git a/packages/apps/kubernetes/Makefile b/packages/apps/kubernetes/Makefile index ae6dd757..e6ed7c6f 100644 --- a/packages/apps/kubernetes/Makefile +++ b/packages/apps/kubernetes/Makefile @@ -1,8 +1,8 @@ KUBERNETES_VERSION = v1.33 KUBERNETES_PKG_TAG = $(shell awk '$$1 == "version:" {print $$2}' Chart.yaml) -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/apps/mysql/Makefile b/packages/apps/mysql/Makefile index f4ea3f78..e8f02703 100644 --- a/packages/apps/mysql/Makefile +++ b/packages/apps/mysql/Makefile @@ -1,7 +1,7 @@ MARIADB_BACKUP_TAG = $(shell awk '$$1 == "version:" {print $$2}' Chart.yaml) -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/apps/nats/Makefile b/packages/apps/nats/Makefile index 8b1dce9d..d1cfda8e 100644 --- a/packages/apps/nats/Makefile +++ b/packages/apps/nats/Makefile @@ -1,4 +1,4 @@ -include ../../../scripts/package.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/apps/postgres/Makefile b/packages/apps/postgres/Makefile index a2ba188f..aa9b0ccc 100644 --- a/packages/apps/postgres/Makefile +++ b/packages/apps/postgres/Makefile @@ -1,4 +1,4 @@ -include ../../../scripts/package.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/apps/rabbitmq/Makefile b/packages/apps/rabbitmq/Makefile index 8b1dce9d..d1cfda8e 100644 --- a/packages/apps/rabbitmq/Makefile +++ b/packages/apps/rabbitmq/Makefile @@ -1,4 +1,4 @@ -include ../../../scripts/package.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/apps/redis/Makefile b/packages/apps/redis/Makefile index a2ba188f..aa9b0ccc 100644 --- a/packages/apps/redis/Makefile +++ b/packages/apps/redis/Makefile @@ -1,4 +1,4 @@ -include ../../../scripts/package.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/apps/tcp-balancer/Makefile b/packages/apps/tcp-balancer/Makefile index 8b1dce9d..d1cfda8e 100644 --- a/packages/apps/tcp-balancer/Makefile +++ b/packages/apps/tcp-balancer/Makefile @@ -1,4 +1,4 @@ -include ../../../scripts/package.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/apps/tenant/Makefile b/packages/apps/tenant/Makefile index 8b1dce9d..d1cfda8e 100644 --- a/packages/apps/tenant/Makefile +++ b/packages/apps/tenant/Makefile @@ -1,4 +1,4 @@ -include ../../../scripts/package.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/apps/virtual-machine/Makefile b/packages/apps/virtual-machine/Makefile index b70ac622..7c482228 100644 --- a/packages/apps/virtual-machine/Makefile +++ b/packages/apps/virtual-machine/Makefile @@ -1,4 +1,4 @@ -include ../../../scripts/package.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/apps/vm-disk/Makefile b/packages/apps/vm-disk/Makefile index 8b1dce9d..d1cfda8e 100644 --- a/packages/apps/vm-disk/Makefile +++ b/packages/apps/vm-disk/Makefile @@ -1,4 +1,4 @@ -include ../../../scripts/package.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/apps/vm-instance/Makefile b/packages/apps/vm-instance/Makefile index 105ca7db..3b6d471d 100644 --- a/packages/apps/vm-instance/Makefile +++ b/packages/apps/vm-instance/Makefile @@ -1,4 +1,4 @@ -include ../../../scripts/package.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/apps/vpc/Makefile b/packages/apps/vpc/Makefile index 8807454f..ea4f5d1b 100644 --- a/packages/apps/vpc/Makefile +++ b/packages/apps/vpc/Makefile @@ -1,4 +1,4 @@ -include ../../../scripts/package.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json diff --git a/packages/apps/vpn/Makefile b/packages/apps/vpn/Makefile index 8b1dce9d..d1cfda8e 100644 --- a/packages/apps/vpn/Makefile +++ b/packages/apps/vpn/Makefile @@ -1,4 +1,4 @@ -include ../../../scripts/package.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/core/flux-aio/Makefile b/packages/core/flux-aio/Makefile index 0121d6d4..c5eb27f9 100644 --- a/packages/core/flux-aio/Makefile +++ b/packages/core/flux-aio/Makefile @@ -1,7 +1,7 @@ NAME=flux-aio NAMESPACE=cozy-$(NAME) -include ../../../scripts/common-envs.mk +include ../../../hack/common-envs.mk show: cozyhr show -n $(NAMESPACE) $(NAME) --plain diff --git a/packages/core/installer/Makefile b/packages/core/installer/Makefile index f4b527bf..84f3cbf0 100644 --- a/packages/core/installer/Makefile +++ b/packages/core/installer/Makefile @@ -1,7 +1,7 @@ NAME=installer NAMESPACE=cozy-system -include ../../../scripts/common-envs.mk +include ../../../hack/common-envs.mk pre-checks: ../../../hack/pre-checks.sh diff --git a/packages/core/platform/Makefile b/packages/core/platform/Makefile index d5d63e6e..7a53efa7 100644 --- a/packages/core/platform/Makefile +++ b/packages/core/platform/Makefile @@ -1,7 +1,7 @@ NAME=platform NAMESPACE=cozy-system -include ../../../scripts/common-envs.mk +include ../../../hack/common-envs.mk show: cozyhr show --namespace $(NAMESPACE) $(NAME) --plain diff --git a/packages/core/talos/Makefile b/packages/core/talos/Makefile index 24d9c9f2..4e8a8dfb 100644 --- a/packages/core/talos/Makefile +++ b/packages/core/talos/Makefile @@ -3,7 +3,7 @@ NAMESPACE=cozy-system TALOS_VERSION=$(shell awk '/^version:/ {print $$2}' images/talos/profiles/installer.yaml) -include ../../../scripts/common-envs.mk +include ../../../hack/common-envs.mk update: hack/gen-profiles.sh diff --git a/packages/core/testing/Makefile b/packages/core/testing/Makefile index f345d155..46705011 100644 --- a/packages/core/testing/Makefile +++ b/packages/core/testing/Makefile @@ -6,7 +6,7 @@ SANDBOX_NAME := cozy-e2e-sandbox-$(shell echo "$$(hostname):$$(pwd)" | sha256sum ROOT_DIR = $(dir $(abspath $(firstword $(MAKEFILE_LIST))/../../..)) -include ../../../scripts/common-envs.mk +include ../../../hack/common-envs.mk help: ## Show this help. diff --git a/packages/extra/Makefile b/packages/extra/Makefile index 5872855b..dd3293a0 100644 --- a/packages/extra/Makefile +++ b/packages/extra/Makefile @@ -1,7 +1,7 @@ OUT=../../_out/repos/extra CHARTS := $(shell find . -maxdepth 2 -name Chart.yaml | awk -F/ '{print $$2}') -include ../../scripts/common-envs.mk +include ../../hack/common-envs.mk repo: rm -rf "$(OUT)" diff --git a/packages/extra/bootbox/Makefile b/packages/extra/bootbox/Makefile index d9a1e261..57fa0a77 100644 --- a/packages/extra/bootbox/Makefile +++ b/packages/extra/bootbox/Makefile @@ -1,7 +1,7 @@ NAME=bootbox NAMESPACE=tenant-root -include ../../../scripts/package.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/extra/etcd/Makefile b/packages/extra/etcd/Makefile index b309346c..f37d6e1e 100644 --- a/packages/extra/etcd/Makefile +++ b/packages/extra/etcd/Makefile @@ -1,6 +1,6 @@ NAME=etcd -include ../../../scripts/package.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/extra/info/Makefile b/packages/extra/info/Makefile index e09b33a0..02e04d53 100644 --- a/packages/extra/info/Makefile +++ b/packages/extra/info/Makefile @@ -1,6 +1,6 @@ NAME=etcd -include ../../../scripts/package.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/extra/ingress/Makefile b/packages/extra/ingress/Makefile index 8134cb22..958ce484 100644 --- a/packages/extra/ingress/Makefile +++ b/packages/extra/ingress/Makefile @@ -1,6 +1,6 @@ NAME=ingress -include ../../../scripts/package.mk +include ../../../hack/package.mk update: get-cloudflare-ips diff --git a/packages/extra/monitoring/Makefile b/packages/extra/monitoring/Makefile index b21607ab..1ae213b6 100644 --- a/packages/extra/monitoring/Makefile +++ b/packages/extra/monitoring/Makefile @@ -2,8 +2,8 @@ GRAFANA_TAG = $(shell awk '$$1 == "version:" {print $$2}' Chart.yaml) NAME=monitoring -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/extra/seaweedfs/Makefile b/packages/extra/seaweedfs/Makefile index be84180b..eaa1b906 100644 --- a/packages/extra/seaweedfs/Makefile +++ b/packages/extra/seaweedfs/Makefile @@ -1,6 +1,6 @@ NAME=seaweedfs -include ../../../scripts/package.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/library/Makefile b/packages/library/Makefile index da811e7e..620dcf10 100644 --- a/packages/library/Makefile +++ b/packages/library/Makefile @@ -1,7 +1,7 @@ OUT=../../_out/repos/library CHARTS := $(shell find . -maxdepth 2 -name Chart.yaml | awk -F/ '{print $$2}') -include ../../scripts/common-envs.mk +include ../../hack/common-envs.mk repo: rm -rf "$(OUT)" diff --git a/packages/library/cozy-lib/Makefile b/packages/library/cozy-lib/Makefile index 362bf32c..925f90c1 100644 --- a/packages/library/cozy-lib/Makefile +++ b/packages/library/cozy-lib/Makefile @@ -1,5 +1,5 @@ -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md diff --git a/packages/system/Makefile b/packages/system/Makefile index e068be8a..9173d353 100644 --- a/packages/system/Makefile +++ b/packages/system/Makefile @@ -2,7 +2,7 @@ OUT=../../_out/repos/system CHARTS := $(shell grep -F 'version: 0.0.0' */Chart.yaml | cut -f1 -d/) VERSIONED_CHARTS := $(shell grep '^version:' */Chart.yaml | grep -Fv '0.0.0' | cut -f1 -d/) -include ../../scripts/common-envs.mk +include ../../hack/common-envs.mk repo: rm -rf "$(OUT)" diff --git a/packages/system/backup-controller/Makefile b/packages/system/backup-controller/Makefile index 56d58f20..472a99ba 100644 --- a/packages/system/backup-controller/Makefile +++ b/packages/system/backup-controller/Makefile @@ -1,8 +1,8 @@ NAME=backup-controller NAMESPACE=cozy-backup-controller -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk image: image-backup-controller diff --git a/packages/system/backupstrategy-controller/Makefile b/packages/system/backupstrategy-controller/Makefile index a5556838..e6f23ca7 100644 --- a/packages/system/backupstrategy-controller/Makefile +++ b/packages/system/backupstrategy-controller/Makefile @@ -1,8 +1,8 @@ NAME=backupstrategy-controller NAMESPACE=cozy-backup-controller -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk image: image-backupstrategy-controller diff --git a/packages/system/bootbox-rd/Makefile b/packages/system/bootbox-rd/Makefile index b570a84f..9d03ab2b 100644 --- a/packages/system/bootbox-rd/Makefile +++ b/packages/system/bootbox-rd/Makefile @@ -1,4 +1,4 @@ export NAME=bootbox-rd export NAMESPACE=cozy-system -include ../../../scripts/package.mk +include ../../../hack/package.mk diff --git a/packages/system/bootbox/Makefile b/packages/system/bootbox/Makefile index ce4e1af0..2e666462 100644 --- a/packages/system/bootbox/Makefile +++ b/packages/system/bootbox/Makefile @@ -1,7 +1,7 @@ export NAME=bootbox export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/bucket-rd/Makefile b/packages/system/bucket-rd/Makefile index 53e89367..259617e8 100644 --- a/packages/system/bucket-rd/Makefile +++ b/packages/system/bucket-rd/Makefile @@ -1,4 +1,4 @@ export NAME=bucket-rd export NAMESPACE=cozy-system -include ../../../scripts/package.mk +include ../../../hack/package.mk diff --git a/packages/system/bucket/Makefile b/packages/system/bucket/Makefile index d555b71c..87944b28 100644 --- a/packages/system/bucket/Makefile +++ b/packages/system/bucket/Makefile @@ -2,8 +2,8 @@ S3MANAGER_TAG=v0.5.0 export NAME=s3manager-system -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk update: @echo Nothing to update diff --git a/packages/system/capi-operator/Makefile b/packages/system/capi-operator/Makefile index dc421cee..9349f549 100644 --- a/packages/system/capi-operator/Makefile +++ b/packages/system/capi-operator/Makefile @@ -5,7 +5,7 @@ export REPO_URL=https://kubernetes-sigs.github.io/cluster-api-operator export CHART_NAME=cluster-api-operator export CHART_VERSION=^0.19 -include ../../../scripts/package.mk +include ../../../hack/package.mk update: clean capi-operator-update rm -rf charts/cluster-api-operator/charts/ diff --git a/packages/system/capi-providers-bootstrap/Makefile b/packages/system/capi-providers-bootstrap/Makefile index f588e439..54d8ca8b 100644 --- a/packages/system/capi-providers-bootstrap/Makefile +++ b/packages/system/capi-providers-bootstrap/Makefile @@ -1,4 +1,4 @@ export NAME=capi-providers-bootstrap export NAMESPACE=cozy-cluster-api -include ../../../scripts/package.mk +include ../../../hack/package.mk diff --git a/packages/system/capi-providers-core/Makefile b/packages/system/capi-providers-core/Makefile index a9bae779..f5f038c1 100644 --- a/packages/system/capi-providers-core/Makefile +++ b/packages/system/capi-providers-core/Makefile @@ -1,4 +1,4 @@ export NAME=capi-providers-core export NAMESPACE=cozy-cluster-api -include ../../../scripts/package.mk +include ../../../hack/package.mk diff --git a/packages/system/capi-providers-cpprovider/Makefile b/packages/system/capi-providers-cpprovider/Makefile index 06f0dd6d..caec8d96 100644 --- a/packages/system/capi-providers-cpprovider/Makefile +++ b/packages/system/capi-providers-cpprovider/Makefile @@ -1,4 +1,4 @@ export NAME=capi-providers-cpprovider export NAMESPACE=cozy-cluster-api -include ../../../scripts/package.mk +include ../../../hack/package.mk diff --git a/packages/system/capi-providers-infraprovider/Makefile b/packages/system/capi-providers-infraprovider/Makefile index 8e4423f3..042a4f0e 100644 --- a/packages/system/capi-providers-infraprovider/Makefile +++ b/packages/system/capi-providers-infraprovider/Makefile @@ -1,4 +1,4 @@ export NAME=capi-providers-infraprovider export NAMESPACE=cozy-cluster-api -include ../../../scripts/package.mk +include ../../../hack/package.mk diff --git a/packages/system/cert-manager-crds/Makefile b/packages/system/cert-manager-crds/Makefile index 0d665914..48e5644c 100644 --- a/packages/system/cert-manager-crds/Makefile +++ b/packages/system/cert-manager-crds/Makefile @@ -1,4 +1,4 @@ -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/cert-manager-issuers/Makefile b/packages/system/cert-manager-issuers/Makefile index a7a6ce10..8808dbfd 100644 --- a/packages/system/cert-manager-issuers/Makefile +++ b/packages/system/cert-manager-issuers/Makefile @@ -1,4 +1,4 @@ export NAME=cert-manager-issuers export NAMESPACE=cozy-cert-manager -include ../../../scripts/package.mk +include ../../../hack/package.mk diff --git a/packages/system/cert-manager/Makefile b/packages/system/cert-manager/Makefile index d48ad5be..2d256086 100644 --- a/packages/system/cert-manager/Makefile +++ b/packages/system/cert-manager/Makefile @@ -1,7 +1,7 @@ export NAME=cert-manager export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/cilium-networkpolicy/Makefile b/packages/system/cilium-networkpolicy/Makefile index c81b87e9..a24a54b5 100644 --- a/packages/system/cilium-networkpolicy/Makefile +++ b/packages/system/cilium-networkpolicy/Makefile @@ -1,5 +1,5 @@ export NAME=cilium-networkpolicy export NAMESPACE=cozy-$(NAME) -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk diff --git a/packages/system/cilium/Makefile b/packages/system/cilium/Makefile index 4d1c6ae7..267c75cc 100644 --- a/packages/system/cilium/Makefile +++ b/packages/system/cilium/Makefile @@ -3,8 +3,8 @@ CILIUM_TAG=$(shell awk '$$1 == "version:" {print $$2}' charts/cilium/Chart.yaml) export NAME=cilium export NAMESPACE=cozy-$(NAME) -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/clickhouse-operator/Makefile b/packages/system/clickhouse-operator/Makefile index e821b664..289631d6 100644 --- a/packages/system/clickhouse-operator/Makefile +++ b/packages/system/clickhouse-operator/Makefile @@ -1,7 +1,7 @@ export NAME=clickhouse-operator export NAMESPACE=cozy-clickhouse-operator -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/clickhouse-rd/Makefile b/packages/system/clickhouse-rd/Makefile index 4ac0738a..792ef31b 100644 --- a/packages/system/clickhouse-rd/Makefile +++ b/packages/system/clickhouse-rd/Makefile @@ -1,4 +1,4 @@ export NAME=clickhouse-rd export NAMESPACE=cozy-system -include ../../../scripts/package.mk +include ../../../hack/package.mk diff --git a/packages/system/coredns/Makefile b/packages/system/coredns/Makefile index a610a862..4aca5b38 100644 --- a/packages/system/coredns/Makefile +++ b/packages/system/coredns/Makefile @@ -1,7 +1,7 @@ export NAME=coredns export NAMESPACE=kube-system -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/cozy-proxy/Makefile b/packages/system/cozy-proxy/Makefile index 8f4f6192..c1da95c7 100644 --- a/packages/system/cozy-proxy/Makefile +++ b/packages/system/cozy-proxy/Makefile @@ -1,8 +1,8 @@ NAME=cozy-proxy NAMESPACE=cozy-system -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/cozystack-api/Makefile b/packages/system/cozystack-api/Makefile index a3e0d931..c4e6f459 100644 --- a/packages/system/cozystack-api/Makefile +++ b/packages/system/cozystack-api/Makefile @@ -1,8 +1,8 @@ NAME=cozystack-api NAMESPACE=cozy-system -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk run-local: openssl req -nodes -new -x509 -keyout /tmp/ca.key -out /tmp/ca.crt -subj "/CN=kube-ca" diff --git a/packages/system/cozystack-basics/Makefile b/packages/system/cozystack-basics/Makefile index fe85b39b..2d5f6c59 100644 --- a/packages/system/cozystack-basics/Makefile +++ b/packages/system/cozystack-basics/Makefile @@ -1,4 +1,4 @@ export NAME=tenant-root export NAMESPACE=cozy-system -include ../../../scripts/package.mk +include ../../../hack/package.mk diff --git a/packages/system/cozystack-controller/Makefile b/packages/system/cozystack-controller/Makefile index 9bbfb5a6..cb0e5578 100644 --- a/packages/system/cozystack-controller/Makefile +++ b/packages/system/cozystack-controller/Makefile @@ -1,8 +1,8 @@ NAME=cozystack-controller NAMESPACE=cozy-system -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk image: image-cozystack-controller update-version diff --git a/packages/system/cozystack-resource-definition-crd/Makefile b/packages/system/cozystack-resource-definition-crd/Makefile index ddb24aef..caac4557 100644 --- a/packages/system/cozystack-resource-definition-crd/Makefile +++ b/packages/system/cozystack-resource-definition-crd/Makefile @@ -1,4 +1,4 @@ export NAME=cozystack-resource-definition-crd export NAMESPACE=cozy-system -include ../../../scripts/package.mk +include ../../../hack/package.mk diff --git a/packages/system/dashboard/Makefile b/packages/system/dashboard/Makefile index f89cba0d..a9f680a3 100644 --- a/packages/system/dashboard/Makefile +++ b/packages/system/dashboard/Makefile @@ -1,8 +1,8 @@ export NAME=dashboard export NAMESPACE=cozy-$(NAME) -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk update: update-crd update-dockerfiles image: image-openapi-ui image-openapi-ui-k8s-bff image-token-proxy update-tenant-text diff --git a/packages/system/etcd-operator/Makefile b/packages/system/etcd-operator/Makefile index a2154952..54a1edad 100644 --- a/packages/system/etcd-operator/Makefile +++ b/packages/system/etcd-operator/Makefile @@ -1,7 +1,7 @@ export NAME=etcd-operator export NAMESPACE=cozy-${NAME} -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/etcd-rd/Makefile b/packages/system/etcd-rd/Makefile index ad91bad9..df993ae6 100644 --- a/packages/system/etcd-rd/Makefile +++ b/packages/system/etcd-rd/Makefile @@ -1,4 +1,4 @@ export NAME=etcd-rd export NAMESPACE=cozy-system -include ../../../scripts/package.mk +include ../../../hack/package.mk diff --git a/packages/system/external-dns/Makefile b/packages/system/external-dns/Makefile index 1ddfa773..5b0197e1 100644 --- a/packages/system/external-dns/Makefile +++ b/packages/system/external-dns/Makefile @@ -1,7 +1,7 @@ export NAME=external-dns export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/external-secrets-operator/Makefile b/packages/system/external-secrets-operator/Makefile index f4d9215d..0e1cd83a 100644 --- a/packages/system/external-secrets-operator/Makefile +++ b/packages/system/external-secrets-operator/Makefile @@ -1,7 +1,7 @@ export NAME=external-secrets-operator export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/ferretdb-rd/Makefile b/packages/system/ferretdb-rd/Makefile index f6814169..1cc7ac30 100644 --- a/packages/system/ferretdb-rd/Makefile +++ b/packages/system/ferretdb-rd/Makefile @@ -1,4 +1,4 @@ export NAME=ferretdb-rd export NAMESPACE=cozy-system -include ../../../scripts/package.mk +include ../../../hack/package.mk diff --git a/packages/system/fluxcd-operator/Makefile b/packages/system/fluxcd-operator/Makefile index a603b85f..d9778f53 100644 --- a/packages/system/fluxcd-operator/Makefile +++ b/packages/system/fluxcd-operator/Makefile @@ -1,7 +1,7 @@ NAME=fluxcd-operator NAMESPACE=cozy-fluxcd -include ../../../scripts/package.mk +include ../../../hack/package.mk apply-locally: cozyhr apply --plain -n $(NAMESPACE) $(NAME) diff --git a/packages/system/fluxcd/Makefile b/packages/system/fluxcd/Makefile index 2c88b52d..9aa32fe9 100644 --- a/packages/system/fluxcd/Makefile +++ b/packages/system/fluxcd/Makefile @@ -1,7 +1,7 @@ NAME=fluxcd NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk apply-locally: cozyhr apply --plain -n $(NAMESPACE) $(NAME) diff --git a/packages/system/foundationdb-operator/Makefile b/packages/system/foundationdb-operator/Makefile index f735e7c5..16ca953f 100644 --- a/packages/system/foundationdb-operator/Makefile +++ b/packages/system/foundationdb-operator/Makefile @@ -1,7 +1,7 @@ export NAME=foundationdb-operator export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/foundationdb-rd/Makefile b/packages/system/foundationdb-rd/Makefile index b6e6d41a..2b3b51c6 100644 --- a/packages/system/foundationdb-rd/Makefile +++ b/packages/system/foundationdb-rd/Makefile @@ -1,4 +1,4 @@ export NAME=foundationdb-rd export NAMESPACE=cozy-system -include ../../../scripts/package.mk +include ../../../hack/package.mk diff --git a/packages/system/gateway-api-crds/Makefile b/packages/system/gateway-api-crds/Makefile index c4311662..ad51d591 100644 --- a/packages/system/gateway-api-crds/Makefile +++ b/packages/system/gateway-api-crds/Makefile @@ -1,7 +1,7 @@ export NAME=gateway-api-crds export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf templates diff --git a/packages/system/goldpinger/Makefile b/packages/system/goldpinger/Makefile index 3ddd79ba..0aeef2ad 100644 --- a/packages/system/goldpinger/Makefile +++ b/packages/system/goldpinger/Makefile @@ -1,4 +1,4 @@ -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/gpu-operator/Makefile b/packages/system/gpu-operator/Makefile index 286451f3..49b26e26 100644 --- a/packages/system/gpu-operator/Makefile +++ b/packages/system/gpu-operator/Makefile @@ -1,8 +1,8 @@ export NAME=gpu-operator export NAMESPACE=cozy-$(NAME) -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/grafana-operator/Makefile b/packages/system/grafana-operator/Makefile index a752d25b..34d36142 100644 --- a/packages/system/grafana-operator/Makefile +++ b/packages/system/grafana-operator/Makefile @@ -1,8 +1,8 @@ export NAME=grafana-operator export NAMESPACE=cozy-grafana-operator -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/hetzner-robotlb/Makefile b/packages/system/hetzner-robotlb/Makefile index 9cd69463..c5ea0e4d 100644 --- a/packages/system/hetzner-robotlb/Makefile +++ b/packages/system/hetzner-robotlb/Makefile @@ -1,7 +1,7 @@ export NAME=hetzner-robotlb export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/http-cache-rd/Makefile b/packages/system/http-cache-rd/Makefile index cf367cf4..1d4b5be4 100644 --- a/packages/system/http-cache-rd/Makefile +++ b/packages/system/http-cache-rd/Makefile @@ -1,4 +1,4 @@ export NAME=http-cache-rd export NAMESPACE=cozy-system -include ../../../scripts/package.mk +include ../../../hack/package.mk diff --git a/packages/system/info-rd/Makefile b/packages/system/info-rd/Makefile index 7cfed08d..15cf2513 100644 --- a/packages/system/info-rd/Makefile +++ b/packages/system/info-rd/Makefile @@ -1,4 +1,4 @@ export NAME=info-rd export NAMESPACE=cozy-system -include ../../../scripts/package.mk +include ../../../hack/package.mk diff --git a/packages/system/ingress-nginx/Makefile b/packages/system/ingress-nginx/Makefile index 9ad10ae1..573c253b 100644 --- a/packages/system/ingress-nginx/Makefile +++ b/packages/system/ingress-nginx/Makefile @@ -1,6 +1,6 @@ NAME=ingress-nginx-system -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/ingress-rd/Makefile b/packages/system/ingress-rd/Makefile index 82976cc6..be48593e 100644 --- a/packages/system/ingress-rd/Makefile +++ b/packages/system/ingress-rd/Makefile @@ -1,4 +1,4 @@ export NAME=ingress-rd export NAMESPACE=cozy-system -include ../../../scripts/package.mk +include ../../../hack/package.mk diff --git a/packages/system/kafka-operator/Makefile b/packages/system/kafka-operator/Makefile index 32fa2207..0e4811e5 100644 --- a/packages/system/kafka-operator/Makefile +++ b/packages/system/kafka-operator/Makefile @@ -1,7 +1,7 @@ export NAME=kafka-operator export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/kafka-rd/Makefile b/packages/system/kafka-rd/Makefile index 3c333dcf..f7d2b87d 100644 --- a/packages/system/kafka-rd/Makefile +++ b/packages/system/kafka-rd/Makefile @@ -1,4 +1,4 @@ export NAME=kafka-rd export NAMESPACE=cozy-system -include ../../../scripts/package.mk +include ../../../hack/package.mk diff --git a/packages/system/kamaji/Makefile b/packages/system/kamaji/Makefile index c24e2d4e..f82c4c7a 100644 --- a/packages/system/kamaji/Makefile +++ b/packages/system/kamaji/Makefile @@ -1,8 +1,8 @@ export NAME=kamaji export NAMESPACE=cozy-$(NAME) -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/keycloak-configure/Makefile b/packages/system/keycloak-configure/Makefile index b9fd5c10..4cd5bd2e 100644 --- a/packages/system/keycloak-configure/Makefile +++ b/packages/system/keycloak-configure/Makefile @@ -1,5 +1,5 @@ export NAME=keycloak-configure export NAMESPACE=cozy-keycloak -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk diff --git a/packages/system/keycloak-operator/Makefile b/packages/system/keycloak-operator/Makefile index a4f50b67..9c8d63c1 100644 --- a/packages/system/keycloak-operator/Makefile +++ b/packages/system/keycloak-operator/Makefile @@ -1,8 +1,8 @@ export NAME=keycloak-operator export NAMESPACE=cozy-keycloak -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/keycloak/Makefile b/packages/system/keycloak/Makefile index 62f4e98d..09afc864 100644 --- a/packages/system/keycloak/Makefile +++ b/packages/system/keycloak/Makefile @@ -1,5 +1,5 @@ export NAME=keycloak export NAMESPACE=cozy-keycloak -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk diff --git a/packages/system/kubeovn-plunger/Makefile b/packages/system/kubeovn-plunger/Makefile index a80c7e06..f56d935b 100644 --- a/packages/system/kubeovn-plunger/Makefile +++ b/packages/system/kubeovn-plunger/Makefile @@ -1,8 +1,8 @@ export NAME=kubeovn-plunger export NAMESPACE=cozy-kubeovn -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk image: docker buildx build -f images/kubeovn-plunger/Dockerfile ../../../ \ diff --git a/packages/system/kubeovn-webhook/Makefile b/packages/system/kubeovn-webhook/Makefile index 9134d55f..9b2e2744 100644 --- a/packages/system/kubeovn-webhook/Makefile +++ b/packages/system/kubeovn-webhook/Makefile @@ -1,8 +1,8 @@ export NAME=kubeovn-webhook export NAMESPACE=cozy-kubeovn -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk image: docker buildx build images/kubeovn-webhook \ diff --git a/packages/system/kubeovn/Makefile b/packages/system/kubeovn/Makefile index 45c5ab25..7e6d0d34 100644 --- a/packages/system/kubeovn/Makefile +++ b/packages/system/kubeovn/Makefile @@ -3,8 +3,8 @@ KUBEOVN_TAG=v0.40.0 export NAME=kubeovn export NAMESPACE=cozy-$(NAME) -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk update: rm -rf charts values.yaml Chart.yaml diff --git a/packages/system/kubernetes-rd/Makefile b/packages/system/kubernetes-rd/Makefile index 45969603..2d38b5d3 100644 --- a/packages/system/kubernetes-rd/Makefile +++ b/packages/system/kubernetes-rd/Makefile @@ -1,4 +1,4 @@ export NAME=kubernetes-rd export NAMESPACE=cozy-system -include ../../../scripts/package.mk +include ../../../hack/package.mk diff --git a/packages/system/kubevirt-cdi-operator/Makefile b/packages/system/kubevirt-cdi-operator/Makefile index 7022599f..7232216f 100644 --- a/packages/system/kubevirt-cdi-operator/Makefile +++ b/packages/system/kubevirt-cdi-operator/Makefile @@ -1,7 +1,7 @@ export NAME=kubevirt-cdi-operator export NAMESPACE=cozy-kubevirt-cdi -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf templates diff --git a/packages/system/kubevirt-cdi/Makefile b/packages/system/kubevirt-cdi/Makefile index 0b3791a1..709e4880 100644 --- a/packages/system/kubevirt-cdi/Makefile +++ b/packages/system/kubevirt-cdi/Makefile @@ -1,7 +1,7 @@ export NAME=kubevirt-cdi export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf templates diff --git a/packages/system/kubevirt-instancetypes/Makefile b/packages/system/kubevirt-instancetypes/Makefile index d0498f10..b9f84b83 100644 --- a/packages/system/kubevirt-instancetypes/Makefile +++ b/packages/system/kubevirt-instancetypes/Makefile @@ -1,7 +1,7 @@ export NAME=kubevirt-instancetypes export NAMESPACE=cozy-kubevirt -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf templates diff --git a/packages/system/kubevirt-operator/Makefile b/packages/system/kubevirt-operator/Makefile index 42bf80a7..69b6a692 100644 --- a/packages/system/kubevirt-operator/Makefile +++ b/packages/system/kubevirt-operator/Makefile @@ -1,7 +1,7 @@ export NAME=kubevirt-operator export NAMESPACE=cozy-kubevirt -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf templates diff --git a/packages/system/kubevirt/Makefile b/packages/system/kubevirt/Makefile index 2c2e57ab..246001d0 100644 --- a/packages/system/kubevirt/Makefile +++ b/packages/system/kubevirt/Makefile @@ -1,7 +1,7 @@ export NAME=kubevirt export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf templates diff --git a/packages/system/lineage-controller-webhook/Makefile b/packages/system/lineage-controller-webhook/Makefile index 04d81a12..d5bada31 100644 --- a/packages/system/lineage-controller-webhook/Makefile +++ b/packages/system/lineage-controller-webhook/Makefile @@ -1,8 +1,8 @@ NAME=lineage-controller-webhook NAMESPACE=cozy-system -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk image: image-lineage-controller-webhook diff --git a/packages/system/linstor-scheduler/Makefile b/packages/system/linstor-scheduler/Makefile index 90a31bc8..79b50c28 100644 --- a/packages/system/linstor-scheduler/Makefile +++ b/packages/system/linstor-scheduler/Makefile @@ -1,7 +1,7 @@ export NAME=linstor-scheduler export NAMESPACE=cozy-linstor -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/linstor/Makefile b/packages/system/linstor/Makefile index b5d7c227..b211baec 100644 --- a/packages/system/linstor/Makefile +++ b/packages/system/linstor/Makefile @@ -1,8 +1,8 @@ export NAME=linstor export NAMESPACE=cozy-$(NAME) -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk LINSTOR_VERSION ?= 1.32.3 LINSTOR_CSI_VERSION ?= v1.10.5 diff --git a/packages/system/mariadb-operator/Makefile b/packages/system/mariadb-operator/Makefile index ecbd51d9..905653ca 100644 --- a/packages/system/mariadb-operator/Makefile +++ b/packages/system/mariadb-operator/Makefile @@ -1,7 +1,7 @@ export NAME=mariadb-operator export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/metallb/Makefile b/packages/system/metallb/Makefile index 5323f414..5262e558 100644 --- a/packages/system/metallb/Makefile +++ b/packages/system/metallb/Makefile @@ -1,8 +1,8 @@ export NAME=metallb export NAMESPACE=cozy-$(NAME) -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/metrics-server/Makefile b/packages/system/metrics-server/Makefile index fb89db64..87477ea0 100644 --- a/packages/system/metrics-server/Makefile +++ b/packages/system/metrics-server/Makefile @@ -1,7 +1,7 @@ export NAME=metrics-server export NAMESPACE=cozy-monitoring -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/monitoring-agents/Makefile b/packages/system/monitoring-agents/Makefile index f324bfef..339b15a2 100644 --- a/packages/system/monitoring-agents/Makefile +++ b/packages/system/monitoring-agents/Makefile @@ -1,7 +1,7 @@ export NAME=monitoring-agents export NAMESPACE=cozy-monitoring -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/monitoring-rd/Makefile b/packages/system/monitoring-rd/Makefile index ecce61e3..5b0bf43b 100644 --- a/packages/system/monitoring-rd/Makefile +++ b/packages/system/monitoring-rd/Makefile @@ -1,4 +1,4 @@ export NAME=monitoring-rd export NAMESPACE=cozy-system -include ../../../scripts/package.mk +include ../../../hack/package.mk diff --git a/packages/system/multus/Makefile b/packages/system/multus/Makefile index b7ae5bfc..9b34606d 100644 --- a/packages/system/multus/Makefile +++ b/packages/system/multus/Makefile @@ -1,8 +1,8 @@ export NAME=multus export NAMESPACE=cozy-$(NAME) -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk update: rm -rf templates diff --git a/packages/system/mysql-rd/Makefile b/packages/system/mysql-rd/Makefile index a2d28923..2a8af787 100644 --- a/packages/system/mysql-rd/Makefile +++ b/packages/system/mysql-rd/Makefile @@ -1,4 +1,4 @@ export NAME=mysql-rd export NAMESPACE=cozy-system -include ../../../scripts/package.mk +include ../../../hack/package.mk diff --git a/packages/system/nats-rd/Makefile b/packages/system/nats-rd/Makefile index 2b5b232e..4a72284c 100644 --- a/packages/system/nats-rd/Makefile +++ b/packages/system/nats-rd/Makefile @@ -1,4 +1,4 @@ export NAME=nats-rd export NAMESPACE=cozy-system -include ../../../scripts/package.mk +include ../../../hack/package.mk diff --git a/packages/system/nats/Makefile b/packages/system/nats/Makefile index 25657a89..88883ab0 100644 --- a/packages/system/nats/Makefile +++ b/packages/system/nats/Makefile @@ -1,4 +1,4 @@ -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/nfs-driver/Makefile b/packages/system/nfs-driver/Makefile index e3af6c03..2f14aa28 100644 --- a/packages/system/nfs-driver/Makefile +++ b/packages/system/nfs-driver/Makefile @@ -1,8 +1,8 @@ export NAME=nfs-driver export NAMESPACE=cozy-$(NAME) -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/objectstorage-controller/Makefile b/packages/system/objectstorage-controller/Makefile index 54c195a0..dacc3353 100644 --- a/packages/system/objectstorage-controller/Makefile +++ b/packages/system/objectstorage-controller/Makefile @@ -1,8 +1,8 @@ export NAME=objectstorage-controller export NAMESPACE=cozy-$(NAME) -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk update: rm -rf templates diff --git a/packages/system/piraeus-operator/Makefile b/packages/system/piraeus-operator/Makefile index a10c7369..b46680f2 100644 --- a/packages/system/piraeus-operator/Makefile +++ b/packages/system/piraeus-operator/Makefile @@ -1,7 +1,7 @@ export NAME=piraeus-operator export NAMESPACE=cozy-linstor -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/postgres-operator/Makefile b/packages/system/postgres-operator/Makefile index f1a0bba0..f58f235f 100644 --- a/packages/system/postgres-operator/Makefile +++ b/packages/system/postgres-operator/Makefile @@ -1,7 +1,7 @@ export NAME=postgres-operator export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/postgres-rd/Makefile b/packages/system/postgres-rd/Makefile index 0c8b1cb0..7da8103a 100644 --- a/packages/system/postgres-rd/Makefile +++ b/packages/system/postgres-rd/Makefile @@ -1,4 +1,4 @@ export NAME=postgres-rd export NAMESPACE=cozy-system -include ../../../scripts/package.mk +include ../../../hack/package.mk diff --git a/packages/system/prometheus-operator-crds/Makefile b/packages/system/prometheus-operator-crds/Makefile index 173da9c1..ed55f286 100644 --- a/packages/system/prometheus-operator-crds/Makefile +++ b/packages/system/prometheus-operator-crds/Makefile @@ -1,7 +1,7 @@ export NAME=prometheus-operator-crds export NAMESPACE=cozy-victoria-metrics-operator -include ../../../scripts/package.mk +include ../../../hack/package.mk update: helm repo add prometheus-community https://prometheus-community.github.io/helm-charts diff --git a/packages/system/rabbitmq-operator/Makefile b/packages/system/rabbitmq-operator/Makefile index 12eda697..a75e37cd 100644 --- a/packages/system/rabbitmq-operator/Makefile +++ b/packages/system/rabbitmq-operator/Makefile @@ -1,7 +1,7 @@ export NAME=rabbitmq-operator export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf templates/cluster-operator.yml diff --git a/packages/system/rabbitmq-rd/Makefile b/packages/system/rabbitmq-rd/Makefile index 9c5be0d5..7db599d7 100644 --- a/packages/system/rabbitmq-rd/Makefile +++ b/packages/system/rabbitmq-rd/Makefile @@ -1,4 +1,4 @@ export NAME=rabbitmq-rd export NAMESPACE=cozy-system -include ../../../scripts/package.mk +include ../../../hack/package.mk diff --git a/packages/system/redis-operator/Makefile b/packages/system/redis-operator/Makefile index 650cbc72..9e21b1b4 100644 --- a/packages/system/redis-operator/Makefile +++ b/packages/system/redis-operator/Makefile @@ -2,8 +2,8 @@ REDIS_OPERATOR_TAG=$(shell grep -F 'ARG VERSION=' images/redis-operator/Dockerfi export NAME=redis-operator export NAMESPACE=cozy-$(NAME) -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/redis-rd/Makefile b/packages/system/redis-rd/Makefile index bed18877..e6aca9de 100644 --- a/packages/system/redis-rd/Makefile +++ b/packages/system/redis-rd/Makefile @@ -1,4 +1,4 @@ export NAME=redis-rd export NAMESPACE=cozy-system -include ../../../scripts/package.mk +include ../../../hack/package.mk diff --git a/packages/system/reloader/Makefile b/packages/system/reloader/Makefile index 378dc23c..6dc1f004 100644 --- a/packages/system/reloader/Makefile +++ b/packages/system/reloader/Makefile @@ -1,7 +1,7 @@ export NAME=reloader export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/seaweedfs-rd/Makefile b/packages/system/seaweedfs-rd/Makefile index 5be03dbb..b4c5a2da 100644 --- a/packages/system/seaweedfs-rd/Makefile +++ b/packages/system/seaweedfs-rd/Makefile @@ -1,4 +1,4 @@ export NAME=seaweedfs-rd export NAMESPACE=cozy-system -include ../../../scripts/package.mk +include ../../../hack/package.mk diff --git a/packages/system/seaweedfs/Makefile b/packages/system/seaweedfs/Makefile index d1f21a80..c3a2f777 100644 --- a/packages/system/seaweedfs/Makefile +++ b/packages/system/seaweedfs/Makefile @@ -1,7 +1,7 @@ export NAME=seaweedfs-system -include ../../../scripts/common-envs.mk -include ../../../scripts/package.mk +include ../../../hack/common-envs.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/snapshot-controller/Makefile b/packages/system/snapshot-controller/Makefile index 65d37f1f..ca5d39e7 100644 --- a/packages/system/snapshot-controller/Makefile +++ b/packages/system/snapshot-controller/Makefile @@ -1,7 +1,7 @@ export NAME=snapshot-controller export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/tcp-balancer-rd/Makefile b/packages/system/tcp-balancer-rd/Makefile index 39a0495f..c85634a6 100644 --- a/packages/system/tcp-balancer-rd/Makefile +++ b/packages/system/tcp-balancer-rd/Makefile @@ -1,4 +1,4 @@ export NAME=tcp-balancer-rd export NAMESPACE=cozy-system -include ../../../scripts/package.mk +include ../../../hack/package.mk diff --git a/packages/system/telepresence/Makefile b/packages/system/telepresence/Makefile index 1aec8917..d7f83e05 100644 --- a/packages/system/telepresence/Makefile +++ b/packages/system/telepresence/Makefile @@ -1,7 +1,7 @@ export NAME=traffic-manager export NAMESPACE=cozy-telepresence -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/tenant-rd/Makefile b/packages/system/tenant-rd/Makefile index 37852fe6..11db2069 100644 --- a/packages/system/tenant-rd/Makefile +++ b/packages/system/tenant-rd/Makefile @@ -1,4 +1,4 @@ export NAME=tenant-rd export NAMESPACE=cozy-system -include ../../../scripts/package.mk +include ../../../hack/package.mk diff --git a/packages/system/velero/Makefile b/packages/system/velero/Makefile index ca4ebd5e..44eba951 100644 --- a/packages/system/velero/Makefile +++ b/packages/system/velero/Makefile @@ -1,7 +1,7 @@ export NAME=velero export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/vertical-pod-autoscaler-crds/Makefile b/packages/system/vertical-pod-autoscaler-crds/Makefile index 9290640e..52b84278 100644 --- a/packages/system/vertical-pod-autoscaler-crds/Makefile +++ b/packages/system/vertical-pod-autoscaler-crds/Makefile @@ -1,7 +1,7 @@ export NAME=vertical-pod-autoscaler export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: curl -o ./templates/vpa-v1-crd-gen.yaml https://raw.githubusercontent.com/kubernetes/autoscaler/refs/heads/master/vertical-pod-autoscaler/deploy/vpa-v1-crd-gen.yaml diff --git a/packages/system/vertical-pod-autoscaler/Makefile b/packages/system/vertical-pod-autoscaler/Makefile index 389f9c6e..1e5372e1 100644 --- a/packages/system/vertical-pod-autoscaler/Makefile +++ b/packages/system/vertical-pod-autoscaler/Makefile @@ -1,7 +1,7 @@ export NAME=vertical-pod-autoscaler export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/victoria-metrics-operator/Makefile b/packages/system/victoria-metrics-operator/Makefile index a78b7b76..981a1dbc 100644 --- a/packages/system/victoria-metrics-operator/Makefile +++ b/packages/system/victoria-metrics-operator/Makefile @@ -1,7 +1,7 @@ export NAME=victoria-metrics-operator export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf charts diff --git a/packages/system/virtual-machine-rd/Makefile b/packages/system/virtual-machine-rd/Makefile index 4d59946b..79142c9b 100644 --- a/packages/system/virtual-machine-rd/Makefile +++ b/packages/system/virtual-machine-rd/Makefile @@ -1,4 +1,4 @@ export NAME=virtual-machine-rd export NAMESPACE=cozy-system -include ../../../scripts/package.mk +include ../../../hack/package.mk diff --git a/packages/system/virtualprivatecloud-rd/Makefile b/packages/system/virtualprivatecloud-rd/Makefile index 9d9b6c50..013e96b1 100644 --- a/packages/system/virtualprivatecloud-rd/Makefile +++ b/packages/system/virtualprivatecloud-rd/Makefile @@ -1,4 +1,4 @@ export NAME=virtualprivatecloud-rd export NAMESPACE=cozy-system -include ../../../scripts/package.mk +include ../../../hack/package.mk diff --git a/packages/system/vm-disk-rd/Makefile b/packages/system/vm-disk-rd/Makefile index e6de276d..5b73fd4c 100644 --- a/packages/system/vm-disk-rd/Makefile +++ b/packages/system/vm-disk-rd/Makefile @@ -1,4 +1,4 @@ export NAME=vm-disk-rd export NAMESPACE=cozy-system -include ../../../scripts/package.mk +include ../../../hack/package.mk diff --git a/packages/system/vm-instance-rd/Makefile b/packages/system/vm-instance-rd/Makefile index badc4951..621d74bd 100644 --- a/packages/system/vm-instance-rd/Makefile +++ b/packages/system/vm-instance-rd/Makefile @@ -1,4 +1,4 @@ export NAME=vm-instance-rd export NAMESPACE=cozy-system -include ../../../scripts/package.mk +include ../../../hack/package.mk diff --git a/packages/system/vpn-rd/Makefile b/packages/system/vpn-rd/Makefile index c6ee47bb..d37f46fc 100644 --- a/packages/system/vpn-rd/Makefile +++ b/packages/system/vpn-rd/Makefile @@ -1,4 +1,4 @@ export NAME=vpn-rd export NAMESPACE=cozy-system -include ../../../scripts/package.mk +include ../../../hack/package.mk diff --git a/packages/system/vsnap-crd/Makefile b/packages/system/vsnap-crd/Makefile index 57e5a290..005eefbd 100644 --- a/packages/system/vsnap-crd/Makefile +++ b/packages/system/vsnap-crd/Makefile @@ -1,7 +1,7 @@ export NAME=vsnap-crd export NAMESPACE=cozy-$(NAME) -include ../../../scripts/package.mk +include ../../../hack/package.mk update: rm -rf templates diff --git a/scripts/issue-flux-certificates.sh b/scripts/issue-flux-certificates.sh deleted file mode 100755 index 825447e2..00000000 --- a/scripts/issue-flux-certificates.sh +++ /dev/null @@ -1,63 +0,0 @@ -#!/bin/sh -set -e - -if kubectl get secret -n cozy-system cozystack-assets-tls >/dev/null 2>&1 && kubectl get secret -n cozy-public cozystack-assets-tls >/dev/null 2>&1; then - echo "Secret cozystack-assets-tls already exists in both cozy-system and cozy-public namespaces. Exiting." - exit 0 -fi - -USER_CN="cozystack-assets-reader" -CSR_NAME="csr-${USER_CN}-$(date +%s)" - -# make temp directory and cleanup handler -TMPDIR=$(mktemp -d) -trap 'rm -rf "$TMPDIR"' EXIT - -# move into tmpdir -cd "$TMPDIR" - -openssl genrsa -out tls.key 2048 -openssl req -new -key tls.key -subj "/CN=${USER_CN}" -out tls.csr - -CSR_B64=$(base64 < tls.csr | tr -d '\n') - -cat < tls.crt - -kubectl get -n kube-public configmap kube-root-ca.crt \ - -o jsonpath='{.data.ca\.crt}' > ca.crt - -kubectl create secret generic "cozystack-assets-tls" \ - --namespace='cozy-system' \ - --type='kubernetes.io/tls' \ - --from-file=tls.crt \ - --from-file=tls.key \ - --from-file=ca.crt \ - --dry-run=client -o yaml | kubectl apply -f - - -kubectl create secret generic "cozystack-assets-tls" \ - --namespace='cozy-public' \ - --type='kubernetes.io/tls' \ - --from-file=tls.crt \ - --from-file=tls.key \ - --from-file=ca.crt \ - --dry-run=client -o yaml | kubectl apply -f - From 6dbfcbb93e2abecb0a53bac7cbd000dca85e8575 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Thu, 15 Jan 2026 15:50:30 +0100 Subject: [PATCH 024/125] feat(cozypkg): add cross-platform build targets with version injection - Add pattern rule for building cozypkg binaries per platform (assets-cozypkg--) with checksums generation - Add Version variable to cozypkg CLI injected via ldflags - Split manifests into separate cozystack-crds.yaml and cozystack-operator.yaml files - Update CI workflow to handle CRDs and operator as separate artifacts - Update e2e tests and upload script for new manifest structure - Suppress git describe stderr when no tags exist Co-Authored-By: Claude Signed-off-by: Andrei Kvapil --- .github/workflows/pull-requests.yaml | 47 +++++++++++++++------ Makefile | 25 ++++++++++- cmd/cozypkg/cmd/root.go | 5 ++- hack/e2e-install-cozystack.bats | 11 +++-- hack/upload-assets.sh | 5 ++- packages/core/installer/templates/crds.yaml | 1 - packages/core/testing/Makefile | 3 +- 7 files changed, 74 insertions(+), 23 deletions(-) diff --git a/.github/workflows/pull-requests.yaml b/.github/workflows/pull-requests.yaml index 2de01160..53787ad3 100644 --- a/.github/workflows/pull-requests.yaml +++ b/.github/workflows/pull-requests.yaml @@ -71,11 +71,17 @@ jobs: name: pr-patch path: _out/assets/pr.patch - - name: Upload installer + - name: Upload CRDs uses: actions/upload-artifact@v4 with: - name: cozystack-installer - path: _out/assets/cozystack-installer.yaml + name: cozystack-crds + path: _out/assets/cozystack-crds.yaml + + - name: Upload operator + uses: actions/upload-artifact@v4 + with: + name: cozystack-operator + path: _out/assets/cozystack-operator.yaml - name: Upload Talos image uses: actions/upload-artifact@v4 @@ -88,8 +94,9 @@ jobs: runs-on: ubuntu-latest if: contains(github.event.pull_request.labels.*.name, 'release') outputs: - installer_id: ${{ steps.fetch_assets.outputs.installer_id }} - disk_id: ${{ steps.fetch_assets.outputs.disk_id }} + crds_id: ${{ steps.fetch_assets.outputs.crds_id }} + operator_id: ${{ steps.fetch_assets.outputs.operator_id }} + disk_id: ${{ steps.fetch_assets.outputs.disk_id }} steps: - name: Checkout code @@ -132,14 +139,16 @@ jobs: return; } const find = (n) => draft.assets.find(a => a.name === n)?.id; - const installerId = find('cozystack-installer.yaml'); - const diskId = find('nocloud-amd64.raw.xz'); - if (!installerId || !diskId) { + const crdsId = find('cozystack-crds.yaml'); + const operatorId = find('cozystack-operator.yaml'); + const diskId = find('nocloud-amd64.raw.xz'); + if (!crdsId || !operatorId || !diskId) { core.setFailed('Required assets missing in draft release'); return; } - core.setOutput('installer_id', installerId); - core.setOutput('disk_id', diskId); + core.setOutput('crds_id', crdsId); + core.setOutput('operator_id', operatorId); + core.setOutput('disk_id', diskId); prepare_env: @@ -224,11 +233,18 @@ jobs: run: mkdir -p _out/assets # ▸ Regular PR path – download artefacts produced by the *build* job - - name: "Download installer (regular PR)" + - name: "Download CRDs (regular PR)" if: "!contains(github.event.pull_request.labels.*.name, 'release')" uses: actions/download-artifact@v4 with: - name: cozystack-installer + name: cozystack-crds + path: _out/assets + + - name: "Download operator (regular PR)" + if: "!contains(github.event.pull_request.labels.*.name, 'release')" + uses: actions/download-artifact@v4 + with: + name: cozystack-operator path: _out/assets # ▸ Release PR path – fetch artefacts from the corresponding draft release @@ -237,8 +253,11 @@ jobs: run: | mkdir -p _out/assets curl -sSL -H "Authorization: token ${GH_PAT}" -H "Accept: application/octet-stream" \ - -o _out/assets/cozystack-installer.yaml \ - "https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/assets/${{ needs.resolve_assets.outputs.installer_id }}" + -o _out/assets/cozystack-crds.yaml \ + "https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/assets/${{ needs.resolve_assets.outputs.crds_id }}" + curl -sSL -H "Authorization: token ${GH_PAT}" -H "Accept: application/octet-stream" \ + -o _out/assets/cozystack-operator.yaml \ + "https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/assets/${{ needs.resolve_assets.outputs.operator_id }}" env: GH_PAT: ${{ secrets.GH_PAT }} diff --git a/Makefile b/Makefile index c037e023..d6d536f4 100644 --- a/Makefile +++ b/Makefile @@ -37,11 +37,32 @@ build: build-deps manifests: mkdir -p _out/assets - (cd packages/core/installer/; helm template --namespace cozy-installer installer .) > _out/assets/cozystack-installer.yaml + helm template installer packages/core/installer -n cozy-system \ + -s templates/crds.yaml \ + > _out/assets/cozystack-crds.yaml + helm template installer packages/core/installer -n cozy-system \ + -s templates/cozystack-operator.yaml \ + -s templates/packagesource.yaml \ + > _out/assets/cozystack-operator.yaml -assets: +cozypkg: + go build -ldflags "-X github.com/cozystack/cozystack/cmd/cozypkg/cmd.Version=v$(COZYSTACK_VERSION)" -o _out/bin/cozypkg ./cmd/cozypkg + +assets: assets-talos assets-cozypkg + +assets-talos: make -C packages/core/talos assets +assets-cozypkg: assets-cozypkg-linux-amd64 assets-cozypkg-linux-arm64 assets-cozypkg-darwin-amd64 assets-cozypkg-darwin-arm64 assets-cozypkg-windows-amd64 assets-cozypkg-windows-arm64 + (cd _out/assets/ && sha256sum cozypkg-*.tar.gz) > _out/assets/cozypkg-checksums.txt + +assets-cozypkg-%: + $(eval EXT := $(if $(filter windows,$(firstword $(subst -, ,$*))),.exe,)) + mkdir -p _out/assets + GOOS=$(firstword $(subst -, ,$*)) GOARCH=$(lastword $(subst -, ,$*)) go build -ldflags "-X github.com/cozystack/cozystack/cmd/cozypkg/cmd.Version=v$(COZYSTACK_VERSION)" -o _out/bin/cozypkg-$*/cozypkg$(EXT) ./cmd/cozypkg + cp LICENSE _out/bin/cozypkg-$*/LICENSE + tar -C _out/bin/cozypkg-$* -czf _out/assets/cozypkg-$*.tar.gz LICENSE cozypkg$(EXT) + test: make -C packages/core/testing apply make -C packages/core/testing test diff --git a/cmd/cozypkg/cmd/root.go b/cmd/cozypkg/cmd/root.go index 62bd54aa..69b0ea6d 100644 --- a/cmd/cozypkg/cmd/root.go +++ b/cmd/cozypkg/cmd/root.go @@ -23,6 +23,9 @@ import ( "github.com/spf13/cobra" ) +// Version is set at build time via -ldflags. +var Version = "dev" + // rootCmd represents the base command when called without any subcommands. var rootCmd = &cobra.Command{ Use: "cozypkg", @@ -44,6 +47,6 @@ func Execute() error { } func init() { - // Commands are registered in their respective init() functions + rootCmd.Version = Version } diff --git a/hack/e2e-install-cozystack.bats b/hack/e2e-install-cozystack.bats index 124bd8bf..4a549c4b 100644 --- a/hack/e2e-install-cozystack.bats +++ b/hack/e2e-install-cozystack.bats @@ -1,8 +1,12 @@ #!/usr/bin/env bats @test "Required installer assets exist" { - if [ ! -f _out/assets/cozystack-installer.yaml ]; then - echo "Missing: _out/assets/cozystack-installer.yaml" >&2 + if [ ! -f _out/assets/cozystack-crds.yaml ]; then + echo "Missing: _out/assets/cozystack-crds.yaml" >&2 + exit 1 + fi + if [ ! -f _out/assets/cozystack-operator.yaml ]; then + echo "Missing: _out/assets/cozystack-operator.yaml" >&2 exit 1 fi } @@ -12,7 +16,8 @@ kubectl create namespace cozy-system --dry-run=client -o yaml | kubectl apply -f - # Apply installer manifests (CRDs + operator) - kubectl apply -f _out/assets/cozystack-installer.yaml + kubectl apply -f _out/assets/cozystack-crds.yaml + kubectl apply -f _out/assets/cozystack-operator.yaml # Wait for the operator deployment to become available kubectl wait deployment/cozystack-operator -n cozy-system --timeout=1m --for=condition=Available diff --git a/hack/upload-assets.sh b/hack/upload-assets.sh index 9788c04f..0f1e9aeb 100755 --- a/hack/upload-assets.sh +++ b/hack/upload-assets.sh @@ -3,9 +3,12 @@ set -xe version=${VERSION:-$(git describe --tags)} -gh release upload --clobber $version _out/assets/cozystack-installer.yaml +gh release upload --clobber $version _out/assets/cozystack-crds.yaml +gh release upload --clobber $version _out/assets/cozystack-operator.yaml gh release upload --clobber $version _out/assets/metal-amd64.iso gh release upload --clobber $version _out/assets/metal-amd64.raw.xz gh release upload --clobber $version _out/assets/nocloud-amd64.raw.xz gh release upload --clobber $version _out/assets/kernel-amd64 gh release upload --clobber $version _out/assets/initramfs-metal-amd64.xz +gh release upload --clobber $version _out/assets/cozypkg-*.tar.gz +gh release upload --clobber $version _out/assets/cozypkg-checksums.txt diff --git a/packages/core/installer/templates/crds.yaml b/packages/core/installer/templates/crds.yaml index 32b438c1..7c7ea584 100644 --- a/packages/core/installer/templates/crds.yaml +++ b/packages/core/installer/templates/crds.yaml @@ -1,4 +1,3 @@ {{- range $path, $_ := .Files.Glob "definitions/*.yaml" }} ---- {{ $.Files.Get $path }} {{- end }} diff --git a/packages/core/testing/Makefile b/packages/core/testing/Makefile index 46705011..baccbfce 100644 --- a/packages/core/testing/Makefile +++ b/packages/core/testing/Makefile @@ -31,7 +31,8 @@ copy-nocloud-image: docker cp ../../../_out/assets/nocloud-amd64.raw.xz "${SANDBOX_NAME}":/workspace/_out/assets/nocloud-amd64.raw.xz copy-installer-manifest: - docker cp ../../../_out/assets/cozystack-installer.yaml "${SANDBOX_NAME}":/workspace/_out/assets/cozystack-installer.yaml + docker cp ../../../_out/assets/cozystack-crds.yaml "${SANDBOX_NAME}":/workspace/_out/assets/cozystack-crds.yaml + docker cp ../../../_out/assets/cozystack-operator.yaml "${SANDBOX_NAME}":/workspace/_out/assets/cozystack-operator.yaml prepare-cluster: copy-nocloud-image docker exec "${SANDBOX_NAME}" sh -c 'cd /workspace && hack/cozytest.sh hack/e2e-prepare-cluster.bats' From 57c8cc26d421d5c056f98f57a7dee3138f0241b2 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Thu, 15 Jan 2026 17:12:39 +0100 Subject: [PATCH 025/125] refactor(api): rename CozystackResourceDefinition to ApplicationDefinition Rename the CRD and all related types for better clarity: - CozystackResourceDefinition -> ApplicationDefinition - CozystackResourceDefinitionList -> ApplicationDefinitionList - CozystackResourceDefinitionSpec -> ApplicationDefinitionSpec - All related nested types updated accordingly Updated components: - API types and generated deepcopy code - Controllers and reconcilers - Dashboard, lineagecontrollerwebhook, crdmem packages - CRD YAML definition and Helm chart - All 25 cozyrds YAML manifests - Migration scripts and documentation Added migration 23 to remove old cozystack-resource-definition-crd HelmRelease. Co-Authored-By: Claude Signed-off-by: Andrei Kvapil --- ...pes.go => applicationdefinitions_types.go} | 46 +- api/v1alpha1/zz_generated.deepcopy.go | 438 +++++++++--------- cmd/cozystack-controller/main.go | 8 +- hack/update-codegen.sh | 6 +- hack/update-crd.sh | 4 +- ...go => applicationdefinition_controller.go} | 36 +- ...> applicationdefinition_helmreconciler.go} | 68 +-- internal/controller/dashboard/breadcrumb.go | 2 +- .../controller/dashboard/customcolumns.go | 2 +- .../dashboard/customformsoverride.go | 2 +- .../dashboard/customformsprefill.go | 2 +- internal/controller/dashboard/factory.go | 4 +- internal/controller/dashboard/helpers.go | 4 +- internal/controller/dashboard/manager.go | 18 +- .../controller/dashboard/marketplacepanel.go | 2 +- internal/controller/dashboard/sidebar.go | 10 +- .../controller/dashboard/tableurimapping.go | 2 +- internal/lineagecontrollerwebhook/config.go | 4 +- .../lineagecontrollerwebhook/controller.go | 10 +- internal/lineagecontrollerwebhook/matcher.go | 6 +- internal/lineagecontrollerwebhook/webhook.go | 4 +- internal/shared/crdmem/memory.go | 16 +- .../platform/images/migrations/migrations/23 | 11 + .../platform/sources/cozystack-engine.yaml | 28 +- packages/core/platform/values.yaml | 2 +- .../Chart.yaml | 2 +- .../Makefile | 2 +- .../cozystack.io_applicationdefinitions.yaml} | 24 +- .../templates/crd.yaml | 2 + .../values.yaml | 0 .../system/bootbox-rd/cozyrds/bootbox.yaml | 2 +- packages/system/bucket-rd/cozyrds/bucket.yaml | 2 +- .../clickhouse-rd/cozyrds/clickhouse.yaml | 2 +- .../templates/crd.yaml | 2 - packages/system/etcd-rd/cozyrds/etcd.yaml | 2 +- .../system/ferretdb-rd/cozyrds/ferretdb.yaml | 2 +- .../foundationdb-rd/cozyrds/foundationdb.yaml | 2 +- .../http-cache-rd/cozyrds/http-cache.yaml | 2 +- packages/system/info-rd/cozyrds/info.yaml | 2 +- .../system/ingress-rd/cozyrds/ingress.yaml | 2 +- packages/system/kafka-rd/cozyrds/kafka.yaml | 2 +- .../kubernetes-rd/cozyrds/kubernetes.yaml | 2 +- .../monitoring-rd/cozyrds/monitoring.yaml | 2 +- packages/system/mysql-rd/cozyrds/mysql.yaml | 2 +- packages/system/nats-rd/cozyrds/nats.yaml | 2 +- .../system/postgres-rd/cozyrds/postgres.yaml | 2 +- .../system/rabbitmq-rd/cozyrds/rabbitmq.yaml | 2 +- packages/system/redis-rd/cozyrds/redis.yaml | 2 +- .../seaweedfs-rd/cozyrds/seaweedfs.yaml | 2 +- .../tcp-balancer-rd/cozyrds/tcp-balancer.yaml | 2 +- packages/system/tenant-rd/cozyrds/tenant.yaml | 2 +- .../cozyrds/virtual-machine.yaml | 2 +- .../cozyrds/virtualprivatecloud.yaml | 2 +- .../system/vm-disk-rd/cozyrds/vm-disk.yaml | 2 +- .../vm-instance-rd/cozyrds/vm-instance.yaml | 2 +- packages/system/vpn-rd/cozyrds/vpn.yaml | 2 +- pkg/cmd/server/start.go | 6 +- 57 files changed, 417 insertions(+), 406 deletions(-) rename api/v1alpha1/{cozystackresourcedefinitions_types.go => applicationdefinitions_types.go} (76%) rename internal/controller/{cozystackresource_controller.go => applicationdefinition_controller.go} (74%) rename internal/controller/{cozystackresourcedefinition_helmreconciler.go => applicationdefinition_helmreconciler.go} (62%) create mode 100755 packages/core/platform/images/migrations/migrations/23 rename packages/system/{cozystack-resource-definition-crd => application-definition-crd}/Chart.yaml (74%) rename packages/system/{cozystack-resource-definition-crd => application-definition-crd}/Makefile (57%) rename packages/system/{cozystack-resource-definition-crd/definition/cozystack.io_cozystackresourcedefinitions.yaml => application-definition-crd/definition/cozystack.io_applicationdefinitions.yaml} (97%) create mode 100644 packages/system/application-definition-crd/templates/crd.yaml rename packages/system/{cozystack-resource-definition-crd => application-definition-crd}/values.yaml (100%) delete mode 100644 packages/system/cozystack-resource-definition-crd/templates/crd.yaml diff --git a/api/v1alpha1/cozystackresourcedefinitions_types.go b/api/v1alpha1/applicationdefinitions_types.go similarity index 76% rename from api/v1alpha1/cozystackresourcedefinitions_types.go rename to api/v1alpha1/applicationdefinitions_types.go index 04b24294..4a4cd226 100644 --- a/api/v1alpha1/cozystackresourcedefinitions_types.go +++ b/api/v1alpha1/applicationdefinitions_types.go @@ -24,45 +24,45 @@ import ( // +kubebuilder:object:root=true // +kubebuilder:resource:scope=Cluster -// CozystackResourceDefinition is the Schema for the cozystackresourcedefinitions API -type CozystackResourceDefinition struct { +// ApplicationDefinition is the Schema for the applicationdefinitions API +type ApplicationDefinition struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - Spec CozystackResourceDefinitionSpec `json:"spec,omitempty"` + Spec ApplicationDefinitionSpec `json:"spec,omitempty"` } // +kubebuilder:object:root=true -// CozystackResourceDefinitionList contains a list of CozystackResourceDefinitions -type CozystackResourceDefinitionList struct { +// ApplicationDefinitionList contains a list of ApplicationDefinitions +type ApplicationDefinitionList struct { metav1.TypeMeta `json:",inline"` metav1.ListMeta `json:"metadata,omitempty"` - Items []CozystackResourceDefinition `json:"items"` + Items []ApplicationDefinition `json:"items"` } func init() { - SchemeBuilder.Register(&CozystackResourceDefinition{}, &CozystackResourceDefinitionList{}) + SchemeBuilder.Register(&ApplicationDefinition{}, &ApplicationDefinitionList{}) } -type CozystackResourceDefinitionSpec struct { +type ApplicationDefinitionSpec struct { // Application configuration - Application CozystackResourceDefinitionApplication `json:"application"` + Application ApplicationDefinitionApplication `json:"application"` // Release configuration - Release CozystackResourceDefinitionRelease `json:"release"` + Release ApplicationDefinitionRelease `json:"release"` // Secret selectors - Secrets CozystackResourceDefinitionResources `json:"secrets,omitempty"` + Secrets ApplicationDefinitionResources `json:"secrets,omitempty"` // Service selectors - Services CozystackResourceDefinitionResources `json:"services,omitempty"` + Services ApplicationDefinitionResources `json:"services,omitempty"` // Ingress selectors - Ingresses CozystackResourceDefinitionResources `json:"ingresses,omitempty"` + Ingresses ApplicationDefinitionResources `json:"ingresses,omitempty"` // Dashboard configuration for this resource - Dashboard *CozystackResourceDefinitionDashboard `json:"dashboard,omitempty"` + Dashboard *ApplicationDefinitionDashboard `json:"dashboard,omitempty"` } -type CozystackResourceDefinitionApplication struct { +type ApplicationDefinitionApplication struct { // Kind of the application, used for UI and API Kind string `json:"kind"` // OpenAPI schema for the application, used for API validation @@ -73,7 +73,7 @@ type CozystackResourceDefinitionApplication struct { Singular string `json:"singular"` } -type CozystackResourceDefinitionRelease struct { +type ApplicationDefinitionRelease struct { // Reference to the chart source ChartRef *helmv2.CrossNamespaceSourceReference `json:"chartRef"` // Labels for the release @@ -82,7 +82,7 @@ type CozystackResourceDefinitionRelease struct { Prefix string `json:"prefix"` } -// CozystackResourceDefinitionResourceSelector extends metav1.LabelSelector with resourceNames support. +// ApplicationDefinitionResourceSelector extends metav1.LabelSelector with resourceNames support. // A resource matches this selector only if it satisfies ALL criteria: // - Label selector conditions (matchExpressions and matchLabels) // - AND has a name that matches one of the names in resourceNames (if specified) @@ -105,7 +105,7 @@ type CozystackResourceDefinitionRelease struct { // - "{{ .name }}-secret" // - "{{ .kind }}-{{ .name }}-tls" // - "specificname" -type CozystackResourceDefinitionResourceSelector struct { +type ApplicationDefinitionResourceSelector struct { metav1.LabelSelector `json:",inline"` // ResourceNames is a list of resource names to match // If specified, the resource must have one of these exact names to match the selector @@ -113,16 +113,16 @@ type CozystackResourceDefinitionResourceSelector struct { ResourceNames []string `json:"resourceNames,omitempty"` } -type CozystackResourceDefinitionResources struct { +type ApplicationDefinitionResources struct { // Exclude contains an array of resource selectors that target resources. // If a resource matches the selector in any of the elements in the array, it is // hidden from the user, regardless of the matches in the include array. - Exclude []*CozystackResourceDefinitionResourceSelector `json:"exclude,omitempty"` + Exclude []*ApplicationDefinitionResourceSelector `json:"exclude,omitempty"` // Include contains an array of resource selectors that target resources. // If a resource matches the selector in any of the elements in the array, and // matches none of the selectors in the exclude array that resource is marked // as a tenant resource and is visible to users. - Include []*CozystackResourceDefinitionResourceSelector `json:"include,omitempty"` + Include []*ApplicationDefinitionResourceSelector `json:"include,omitempty"` } // ---- Dashboard types ---- @@ -139,8 +139,8 @@ const ( DashboardTabYAML DashboardTab = "yaml" ) -// CozystackResourceDefinitionDashboard describes how this resource appears in the UI. -type CozystackResourceDefinitionDashboard struct { +// ApplicationDefinitionDashboard describes how this resource appears in the UI. +type ApplicationDefinitionDashboard struct { // Human-readable name shown in the UI (e.g., "Bucket") Singular string `json:"singular"` // Plural human-readable name (e.g., "Buckets") diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index f060990f..c1adf95c 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -28,6 +28,225 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ApplicationDefinition) DeepCopyInto(out *ApplicationDefinition) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationDefinition. +func (in *ApplicationDefinition) DeepCopy() *ApplicationDefinition { + if in == nil { + return nil + } + out := new(ApplicationDefinition) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ApplicationDefinition) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ApplicationDefinitionApplication) DeepCopyInto(out *ApplicationDefinitionApplication) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationDefinitionApplication. +func (in *ApplicationDefinitionApplication) DeepCopy() *ApplicationDefinitionApplication { + if in == nil { + return nil + } + out := new(ApplicationDefinitionApplication) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ApplicationDefinitionDashboard) DeepCopyInto(out *ApplicationDefinitionDashboard) { + *out = *in + if in.Tags != nil { + in, out := &in.Tags, &out.Tags + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Tabs != nil { + in, out := &in.Tabs, &out.Tabs + *out = make([]DashboardTab, len(*in)) + copy(*out, *in) + } + if in.KeysOrder != nil { + in, out := &in.KeysOrder, &out.KeysOrder + *out = make([][]string, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = make([]string, len(*in)) + copy(*out, *in) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationDefinitionDashboard. +func (in *ApplicationDefinitionDashboard) DeepCopy() *ApplicationDefinitionDashboard { + if in == nil { + return nil + } + out := new(ApplicationDefinitionDashboard) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ApplicationDefinitionList) DeepCopyInto(out *ApplicationDefinitionList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ApplicationDefinition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationDefinitionList. +func (in *ApplicationDefinitionList) DeepCopy() *ApplicationDefinitionList { + if in == nil { + return nil + } + out := new(ApplicationDefinitionList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ApplicationDefinitionList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ApplicationDefinitionRelease) DeepCopyInto(out *ApplicationDefinitionRelease) { + *out = *in + if in.ChartRef != nil { + in, out := &in.ChartRef, &out.ChartRef + *out = new(v2.CrossNamespaceSourceReference) + **out = **in + } + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *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 ApplicationDefinitionRelease. +func (in *ApplicationDefinitionRelease) DeepCopy() *ApplicationDefinitionRelease { + if in == nil { + return nil + } + out := new(ApplicationDefinitionRelease) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ApplicationDefinitionResourceSelector) DeepCopyInto(out *ApplicationDefinitionResourceSelector) { + *out = *in + in.LabelSelector.DeepCopyInto(&out.LabelSelector) + if in.ResourceNames != nil { + in, out := &in.ResourceNames, &out.ResourceNames + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationDefinitionResourceSelector. +func (in *ApplicationDefinitionResourceSelector) DeepCopy() *ApplicationDefinitionResourceSelector { + if in == nil { + return nil + } + out := new(ApplicationDefinitionResourceSelector) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ApplicationDefinitionResources) DeepCopyInto(out *ApplicationDefinitionResources) { + *out = *in + if in.Exclude != nil { + in, out := &in.Exclude, &out.Exclude + *out = make([]*ApplicationDefinitionResourceSelector, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(ApplicationDefinitionResourceSelector) + (*in).DeepCopyInto(*out) + } + } + } + if in.Include != nil { + in, out := &in.Include, &out.Include + *out = make([]*ApplicationDefinitionResourceSelector, len(*in)) + for i := range *in { + if (*in)[i] != nil { + in, out := &(*in)[i], &(*out)[i] + *out = new(ApplicationDefinitionResourceSelector) + (*in).DeepCopyInto(*out) + } + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationDefinitionResources. +func (in *ApplicationDefinitionResources) DeepCopy() *ApplicationDefinitionResources { + if in == nil { + return nil + } + out := new(ApplicationDefinitionResources) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ApplicationDefinitionSpec) DeepCopyInto(out *ApplicationDefinitionSpec) { + *out = *in + out.Application = in.Application + in.Release.DeepCopyInto(&out.Release) + in.Secrets.DeepCopyInto(&out.Secrets) + in.Services.DeepCopyInto(&out.Services) + in.Ingresses.DeepCopyInto(&out.Ingresses) + if in.Dashboard != nil { + in, out := &in.Dashboard, &out.Dashboard + *out = new(ApplicationDefinitionDashboard) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationDefinitionSpec. +func (in *ApplicationDefinitionSpec) DeepCopy() *ApplicationDefinitionSpec { + if in == nil { + return nil + } + out := new(ApplicationDefinitionSpec) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Component) DeepCopyInto(out *Component) { *out = *in @@ -78,225 +297,6 @@ func (in *ComponentInstall) DeepCopy() *ComponentInstall { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CozystackResourceDefinition) DeepCopyInto(out *CozystackResourceDefinition) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinition. -func (in *CozystackResourceDefinition) DeepCopy() *CozystackResourceDefinition { - if in == nil { - return nil - } - out := new(CozystackResourceDefinition) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *CozystackResourceDefinition) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CozystackResourceDefinitionApplication) DeepCopyInto(out *CozystackResourceDefinitionApplication) { - *out = *in -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionApplication. -func (in *CozystackResourceDefinitionApplication) DeepCopy() *CozystackResourceDefinitionApplication { - if in == nil { - return nil - } - out := new(CozystackResourceDefinitionApplication) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CozystackResourceDefinitionDashboard) DeepCopyInto(out *CozystackResourceDefinitionDashboard) { - *out = *in - if in.Tags != nil { - in, out := &in.Tags, &out.Tags - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.Tabs != nil { - in, out := &in.Tabs, &out.Tabs - *out = make([]DashboardTab, len(*in)) - copy(*out, *in) - } - if in.KeysOrder != nil { - in, out := &in.KeysOrder, &out.KeysOrder - *out = make([][]string, len(*in)) - for i := range *in { - if (*in)[i] != nil { - in, out := &(*in)[i], &(*out)[i] - *out = make([]string, len(*in)) - copy(*out, *in) - } - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionDashboard. -func (in *CozystackResourceDefinitionDashboard) DeepCopy() *CozystackResourceDefinitionDashboard { - if in == nil { - return nil - } - out := new(CozystackResourceDefinitionDashboard) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CozystackResourceDefinitionList) DeepCopyInto(out *CozystackResourceDefinitionList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]CozystackResourceDefinition, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionList. -func (in *CozystackResourceDefinitionList) DeepCopy() *CozystackResourceDefinitionList { - if in == nil { - return nil - } - out := new(CozystackResourceDefinitionList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *CozystackResourceDefinitionList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CozystackResourceDefinitionRelease) DeepCopyInto(out *CozystackResourceDefinitionRelease) { - *out = *in - if in.ChartRef != nil { - in, out := &in.ChartRef, &out.ChartRef - *out = new(v2.CrossNamespaceSourceReference) - **out = **in - } - if in.Labels != nil { - in, out := &in.Labels, &out.Labels - *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 CozystackResourceDefinitionRelease. -func (in *CozystackResourceDefinitionRelease) DeepCopy() *CozystackResourceDefinitionRelease { - if in == nil { - return nil - } - out := new(CozystackResourceDefinitionRelease) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CozystackResourceDefinitionResourceSelector) DeepCopyInto(out *CozystackResourceDefinitionResourceSelector) { - *out = *in - in.LabelSelector.DeepCopyInto(&out.LabelSelector) - if in.ResourceNames != nil { - in, out := &in.ResourceNames, &out.ResourceNames - *out = make([]string, len(*in)) - copy(*out, *in) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionResourceSelector. -func (in *CozystackResourceDefinitionResourceSelector) DeepCopy() *CozystackResourceDefinitionResourceSelector { - if in == nil { - return nil - } - out := new(CozystackResourceDefinitionResourceSelector) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CozystackResourceDefinitionResources) DeepCopyInto(out *CozystackResourceDefinitionResources) { - *out = *in - if in.Exclude != nil { - in, out := &in.Exclude, &out.Exclude - *out = make([]*CozystackResourceDefinitionResourceSelector, len(*in)) - for i := range *in { - if (*in)[i] != nil { - in, out := &(*in)[i], &(*out)[i] - *out = new(CozystackResourceDefinitionResourceSelector) - (*in).DeepCopyInto(*out) - } - } - } - if in.Include != nil { - in, out := &in.Include, &out.Include - *out = make([]*CozystackResourceDefinitionResourceSelector, len(*in)) - for i := range *in { - if (*in)[i] != nil { - in, out := &(*in)[i], &(*out)[i] - *out = new(CozystackResourceDefinitionResourceSelector) - (*in).DeepCopyInto(*out) - } - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionResources. -func (in *CozystackResourceDefinitionResources) DeepCopy() *CozystackResourceDefinitionResources { - if in == nil { - return nil - } - out := new(CozystackResourceDefinitionResources) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CozystackResourceDefinitionSpec) DeepCopyInto(out *CozystackResourceDefinitionSpec) { - *out = *in - out.Application = in.Application - in.Release.DeepCopyInto(&out.Release) - in.Secrets.DeepCopyInto(&out.Secrets) - in.Services.DeepCopyInto(&out.Services) - in.Ingresses.DeepCopyInto(&out.Ingresses) - if in.Dashboard != nil { - in, out := &in.Dashboard, &out.Dashboard - *out = new(CozystackResourceDefinitionDashboard) - (*in).DeepCopyInto(*out) - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CozystackResourceDefinitionSpec. -func (in *CozystackResourceDefinitionSpec) DeepCopy() *CozystackResourceDefinitionSpec { - if in == nil { - return nil - } - out := new(CozystackResourceDefinitionSpec) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DependencyStatus) DeepCopyInto(out *DependencyStatus) { *out = *in diff --git a/cmd/cozystack-controller/main.go b/cmd/cozystack-controller/main.go index 0e7199d3..a3a17d08 100644 --- a/cmd/cozystack-controller/main.go +++ b/cmd/cozystack-controller/main.go @@ -204,20 +204,20 @@ func main() { if reconcileDeployment { cozyAPIKind = "Deployment" } - if err = (&controller.CozystackResourceDefinitionReconciler{ + if err = (&controller.ApplicationDefinitionReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), CozystackAPIKind: cozyAPIKind, }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "CozystackResourceDefinitionReconciler") + setupLog.Error(err, "unable to create controller", "controller", "ApplicationDefinitionReconciler") os.Exit(1) } - if err = (&controller.CozystackResourceDefinitionHelmReconciler{ + if err = (&controller.ApplicationDefinitionHelmReconciler{ Client: mgr.GetClient(), Scheme: mgr.GetScheme(), }).SetupWithManager(mgr); err != nil { - setupLog.Error(err, "unable to create controller", "controller", "CozystackResourceDefinitionHelmReconciler") + setupLog.Error(err, "unable to create controller", "controller", "ApplicationDefinitionHelmReconciler") os.Exit(1) } diff --git a/hack/update-codegen.sh b/hack/update-codegen.sh index 3b2af7d9..188325e8 100755 --- a/hack/update-codegen.sh +++ b/hack/update-codegen.sh @@ -26,7 +26,7 @@ CONTROLLER_GEN="go run sigs.k8s.io/controller-tools/cmd/controller-gen@v0.16.4" TMPDIR=$(mktemp -d) OPERATOR_CRDDIR=packages/core/installer/definitions COZY_CONTROLLER_CRDDIR=packages/system/cozystack-controller/definitions -COZY_RD_CRDDIR=packages/system/cozystack-resource-definition-crd/definition +COZY_RD_CRDDIR=packages/system/application-definition-crd/definition BACKUPS_CORE_CRDDIR=packages/system/backup-controller/definitions BACKUPSTRATEGY_CRDDIR=packages/system/backupstrategy-controller/definitions @@ -66,8 +66,8 @@ $CONTROLLER_GEN rbac:roleName=manager-role crd paths="./api/..." output:crd:arti mv ${TMPDIR}/cozystack.io_packages.yaml ${OPERATOR_CRDDIR}/cozystack.io_packages.yaml mv ${TMPDIR}/cozystack.io_packagesources.yaml ${OPERATOR_CRDDIR}/cozystack.io_packagesources.yaml -mv ${TMPDIR}/cozystack.io_cozystackresourcedefinitions.yaml \ - ${COZY_RD_CRDDIR}/cozystack.io_cozystackresourcedefinitions.yaml +mv ${TMPDIR}/cozystack.io_applicationdefinitions.yaml \ + ${COZY_RD_CRDDIR}/cozystack.io_applicationdefinitions.yaml mv ${TMPDIR}/backups.cozystack.io*.yaml ${BACKUPS_CORE_CRDDIR}/ mv ${TMPDIR}/strategy.backups.cozystack.io*.yaml ${BACKUPSTRATEGY_CRDDIR}/ diff --git a/hack/update-crd.sh b/hack/update-crd.sh index ce9d5d10..4bd2095a 100755 --- a/hack/update-crd.sh +++ b/hack/update-crd.sh @@ -79,7 +79,7 @@ OUT="${OUT:-$CRD_DIR/$NAME.yaml}" if [[ ! -f "$OUT" ]]; then cat >"$OUT" < 0 { + // Check and update labels from ApplicationDefinition + if len(appDef.Spec.Release.Labels) > 0 { if hrCopy.Labels == nil { hrCopy.Labels = make(map[string]string) } - for key, value := range crd.Spec.Release.Labels { + for key, value := range appDef.Spec.Release.Labels { if hrCopy.Labels[key] != value { logger.V(4).Info("Updating HelmRelease label", "name", hr.Name, "namespace", hr.Namespace, "label", key, "value", value) hrCopy.Labels[key] = value diff --git a/internal/controller/dashboard/breadcrumb.go b/internal/controller/dashboard/breadcrumb.go index 5122f605..aadcacac 100644 --- a/internal/controller/dashboard/breadcrumb.go +++ b/internal/controller/dashboard/breadcrumb.go @@ -14,7 +14,7 @@ import ( ) // ensureBreadcrumb creates or updates a Breadcrumb resource for the given CRD -func (m *Manager) ensureBreadcrumb(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) error { +func (m *Manager) ensureBreadcrumb(ctx context.Context, crd *cozyv1alpha1.ApplicationDefinition) error { group, version, kind := pickGVK(crd) lowerKind := strings.ToLower(kind) diff --git a/internal/controller/dashboard/customcolumns.go b/internal/controller/dashboard/customcolumns.go index 6c23d68b..be1318f8 100644 --- a/internal/controller/dashboard/customcolumns.go +++ b/internal/controller/dashboard/customcolumns.go @@ -21,7 +21,7 @@ import ( // // metadata.name: stock-namespace-.. // spec.id: stock-namespace-/// -func (m *Manager) ensureCustomColumnsOverride(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) (controllerutil.OperationResult, error) { +func (m *Manager) ensureCustomColumnsOverride(ctx context.Context, crd *cozyv1alpha1.ApplicationDefinition) (controllerutil.OperationResult, error) { g, v, kind := pickGVK(crd) plural := pickPlural(kind, crd) // Details page segment uses lowercase kind, mirroring your example diff --git a/internal/controller/dashboard/customformsoverride.go b/internal/controller/dashboard/customformsoverride.go index 2b0daa08..60bc82fc 100644 --- a/internal/controller/dashboard/customformsoverride.go +++ b/internal/controller/dashboard/customformsoverride.go @@ -15,7 +15,7 @@ import ( ) // ensureCustomFormsOverride creates or updates a CustomFormsOverride resource for the given CRD -func (m *Manager) ensureCustomFormsOverride(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) error { +func (m *Manager) ensureCustomFormsOverride(ctx context.Context, crd *cozyv1alpha1.ApplicationDefinition) error { g, v, kind := pickGVK(crd) plural := pickPlural(kind, crd) diff --git a/internal/controller/dashboard/customformsprefill.go b/internal/controller/dashboard/customformsprefill.go index d2761873..35d22ff1 100644 --- a/internal/controller/dashboard/customformsprefill.go +++ b/internal/controller/dashboard/customformsprefill.go @@ -16,7 +16,7 @@ import ( ) // ensureCustomFormsPrefill creates or updates a CustomFormsPrefill resource for the given CRD -func (m *Manager) ensureCustomFormsPrefill(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) (reconcile.Result, error) { +func (m *Manager) ensureCustomFormsPrefill(ctx context.Context, crd *cozyv1alpha1.ApplicationDefinition) (reconcile.Result, error) { logger := log.FromContext(ctx) app := crd.Spec.Application diff --git a/internal/controller/dashboard/factory.go b/internal/controller/dashboard/factory.go index 53d771d0..84578dd0 100644 --- a/internal/controller/dashboard/factory.go +++ b/internal/controller/dashboard/factory.go @@ -15,7 +15,7 @@ import ( ) // ensureFactory creates or updates a Factory resource for the given CRD -func (m *Manager) ensureFactory(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) error { +func (m *Manager) ensureFactory(ctx context.Context, crd *cozyv1alpha1.ApplicationDefinition) error { g, v, kind := pickGVK(crd) plural := pickPlural(kind, crd) @@ -557,7 +557,7 @@ type factoryFlags struct { // factoryFeatureFlags tries several conventional locations so you can evolve the API // without breaking the controller. Defaults are false (hidden). -func factoryFeatureFlags(crd *cozyv1alpha1.CozystackResourceDefinition) factoryFlags { +func factoryFeatureFlags(crd *cozyv1alpha1.ApplicationDefinition) factoryFlags { var f factoryFlags f.Workloads = true diff --git a/internal/controller/dashboard/helpers.go b/internal/controller/dashboard/helpers.go index a0023a05..2a35bbff 100644 --- a/internal/controller/dashboard/helpers.go +++ b/internal/controller/dashboard/helpers.go @@ -23,7 +23,7 @@ type fieldInfo struct { // pickGVK tries to read group/version/kind from the CRD. We prefer the "application" section, // falling back to other likely fields if your schema differs. -func pickGVK(crd *cozyv1alpha1.CozystackResourceDefinition) (group, version, kind string) { +func pickGVK(crd *cozyv1alpha1.ApplicationDefinition) (group, version, kind string) { // Best guess based on your examples: if crd.Spec.Application.Kind != "" { kind = crd.Spec.Application.Kind @@ -41,7 +41,7 @@ func pickGVK(crd *cozyv1alpha1.CozystackResourceDefinition) (group, version, kin } // pickPlural prefers a field on the CRD if you have it; otherwise do a simple lowercase + "s". -func pickPlural(kind string, crd *cozyv1alpha1.CozystackResourceDefinition) string { +func pickPlural(kind string, crd *cozyv1alpha1.ApplicationDefinition) string { // If you have crd.Spec.Application.Plural, prefer it. Example: if crd.Spec.Application.Plural != "" { return crd.Spec.Application.Plural diff --git a/internal/controller/dashboard/manager.go b/internal/controller/dashboard/manager.go index 12e50fbc..23897586 100644 --- a/internal/controller/dashboard/manager.go +++ b/internal/controller/dashboard/manager.go @@ -41,7 +41,7 @@ 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. +// It’s easy to extend: add new ensure* methods and wire them into EnsureForAppDef. type Manager struct { client.Client Scheme *runtime.Scheme @@ -56,7 +56,7 @@ func NewManager(c client.Client, scheme *runtime.Scheme) *Manager { func (m *Manager) SetupWithManager(mgr ctrl.Manager) error { if err := ctrl.NewControllerManagedBy(mgr). Named("dashboard-reconciler"). - For(&cozyv1alpha1.CozystackResourceDefinition{}). + For(&cozyv1alpha1.ApplicationDefinition{}). Complete(m); err != nil { return err } @@ -72,7 +72,7 @@ func (m *Manager) SetupWithManager(mgr ctrl.Manager) error { func (m *Manager) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { l := log.FromContext(ctx) - crd := &cozyv1alpha1.CozystackResourceDefinition{} + crd := &cozyv1alpha1.ApplicationDefinition{} err := m.Get(ctx, types.NamespacedName{Name: req.Name}, crd) if err != nil { @@ -85,10 +85,10 @@ func (m *Manager) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, return ctrl.Result{}, err } - return m.EnsureForCRD(ctx, crd) + return m.EnsureForAppDef(ctx, crd) } -// EnsureForCRD is the single entry-point used by the controller. +// EnsureForAppDef is the single entry-point used by the controller. // Add more ensure* calls here as you implement support for other resources: // // - ensureBreadcrumb (implemented) @@ -99,7 +99,7 @@ func (m *Manager) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, // - ensureMarketplacePanel (implemented) // - ensureSidebar (implemented) // - ensureTableUriMapping (implemented) -func (m *Manager) EnsureForCRD(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) (reconcile.Result, error) { +func (m *Manager) EnsureForAppDef(ctx context.Context, crd *cozyv1alpha1.ApplicationDefinition) (reconcile.Result, error) { // Early return if crd.Spec.Dashboard is nil to prevent oscillation if crd.Spec.Dashboard == nil { return reconcile.Result{}, nil @@ -148,7 +148,7 @@ func (m *Manager) InitializeStaticResources(ctx context.Context) error { } // addDashboardLabels adds standard dashboard management labels to a resource -func (m *Manager) addDashboardLabels(obj client.Object, crd *cozyv1alpha1.CozystackResourceDefinition, resourceType string) { +func (m *Manager) addDashboardLabels(obj client.Object, crd *cozyv1alpha1.ApplicationDefinition, resourceType string) { labels := obj.GetLabels() if labels == nil { labels = make(map[string]string) @@ -197,7 +197,7 @@ 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 { - var crdList cozyv1alpha1.CozystackResourceDefinitionList + var crdList cozyv1alpha1.ApplicationDefinitionList if err := m.List(ctx, &crdList, &client.ListOptions{}); err != nil { return err } @@ -228,7 +228,7 @@ func (m *Manager) CleanupOrphanedResources(ctx context.Context) error { } // buildExpectedResourceSet creates a map of expected resource names by type -func (m *Manager) buildExpectedResourceSet(crds []cozyv1alpha1.CozystackResourceDefinition) map[string]map[string]bool { +func (m *Manager) buildExpectedResourceSet(crds []cozyv1alpha1.ApplicationDefinition) map[string]map[string]bool { expected := make(map[string]map[string]bool) // Initialize maps for each resource type diff --git a/internal/controller/dashboard/marketplacepanel.go b/internal/controller/dashboard/marketplacepanel.go index 82a8f336..6bfce752 100644 --- a/internal/controller/dashboard/marketplacepanel.go +++ b/internal/controller/dashboard/marketplacepanel.go @@ -16,7 +16,7 @@ import ( ) // ensureMarketplacePanel creates or updates a MarketplacePanel resource for the given CRD -func (m *Manager) ensureMarketplacePanel(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) (reconcile.Result, error) { +func (m *Manager) ensureMarketplacePanel(ctx context.Context, crd *cozyv1alpha1.ApplicationDefinition) (reconcile.Result, error) { logger := log.FromContext(ctx) mp := &dashv1alpha1.MarketplacePanel{} diff --git a/internal/controller/dashboard/sidebar.go b/internal/controller/dashboard/sidebar.go index c6f9d3ba..5bb378c2 100644 --- a/internal/controller/dashboard/sidebar.go +++ b/internal/controller/dashboard/sidebar.go @@ -28,12 +28,12 @@ import ( // - Categories are ordered strictly as: // Marketplace, IaaS, PaaS, NaaS, , Resources, Backups, Administration // - Items within each category: sort by Weight (desc), then Label (A→Z). -func (m *Manager) ensureSidebar(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) error { +func (m *Manager) ensureSidebar(ctx context.Context, crd *cozyv1alpha1.ApplicationDefinition) error { // Build the full menu once. // 1) Fetch all CRDs - var all []cozyv1alpha1.CozystackResourceDefinition - var crdList cozyv1alpha1.CozystackResourceDefinitionList + var all []cozyv1alpha1.ApplicationDefinition + var crdList cozyv1alpha1.ApplicationDefinitionList if err := m.List(ctx, &crdList, &client.ListOptions{}); err != nil { return err } @@ -263,7 +263,7 @@ func (m *Manager) ensureSidebar(ctx context.Context, crd *cozyv1alpha1.Cozystack // upsertMultipleSidebars creates/updates several Sidebar resources with the same menu spec. func (m *Manager) upsertMultipleSidebars( ctx context.Context, - crd *cozyv1alpha1.CozystackResourceDefinition, + crd *cozyv1alpha1.ApplicationDefinition, ids []string, keysAndTags map[string]any, menuItems []any, @@ -370,7 +370,7 @@ func orderCategoryLabels[T any](cats map[string][]T) []string { } // safeCategory returns spec.dashboard.category or "Resources" if not set. -func safeCategory(def *cozyv1alpha1.CozystackResourceDefinition) string { +func safeCategory(def *cozyv1alpha1.ApplicationDefinition) string { if def == nil || def.Spec.Dashboard == nil { return "Resources" } diff --git a/internal/controller/dashboard/tableurimapping.go b/internal/controller/dashboard/tableurimapping.go index 6e8a395d..e9a4849c 100644 --- a/internal/controller/dashboard/tableurimapping.go +++ b/internal/controller/dashboard/tableurimapping.go @@ -7,7 +7,7 @@ import ( ) // ensureTableUriMapping creates or updates a TableUriMapping resource for the given CRD -func (m *Manager) ensureTableUriMapping(ctx context.Context, crd *cozyv1alpha1.CozystackResourceDefinition) error { +func (m *Manager) ensureTableUriMapping(ctx context.Context, crd *cozyv1alpha1.ApplicationDefinition) error { // Links are fully managed by the CustomColumnsOverride. return nil } diff --git a/internal/lineagecontrollerwebhook/config.go b/internal/lineagecontrollerwebhook/config.go index 7204ab66..ce4b6898 100644 --- a/internal/lineagecontrollerwebhook/config.go +++ b/internal/lineagecontrollerwebhook/config.go @@ -14,14 +14,14 @@ type appRef struct { } type runtimeConfig struct { - appCRDMap map[appRef]*cozyv1alpha1.CozystackResourceDefinition + appCRDMap map[appRef]*cozyv1alpha1.ApplicationDefinition } func (l *LineageControllerWebhook) initConfig() { l.initOnce.Do(func() { if l.config.Load() == nil { l.config.Store(&runtimeConfig{ - appCRDMap: make(map[appRef]*cozyv1alpha1.CozystackResourceDefinition), + appCRDMap: make(map[appRef]*cozyv1alpha1.ApplicationDefinition), }) } }) diff --git a/internal/lineagecontrollerwebhook/controller.go b/internal/lineagecontrollerwebhook/controller.go index 092d16e7..42bdb396 100644 --- a/internal/lineagecontrollerwebhook/controller.go +++ b/internal/lineagecontrollerwebhook/controller.go @@ -8,23 +8,23 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" ) -// +kubebuilder:rbac:groups=cozystack.io,resources=cozystackresourcedefinitions,verbs=list;watch;get +// +kubebuilder:rbac:groups=cozystack.io,resources=applicationdefinitions,verbs=list;watch;get func (c *LineageControllerWebhook) SetupWithManagerAsController(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). - For(&cozyv1alpha1.CozystackResourceDefinition{}). + For(&cozyv1alpha1.ApplicationDefinition{}). Complete(c) } func (c *LineageControllerWebhook) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { l := log.FromContext(ctx) - crds := &cozyv1alpha1.CozystackResourceDefinitionList{} + crds := &cozyv1alpha1.ApplicationDefinitionList{} if err := c.List(ctx, crds); err != nil { - l.Error(err, "failed reading CozystackResourceDefinitions") + l.Error(err, "failed reading ApplicationDefinitions") return ctrl.Result{}, err } cfg := &runtimeConfig{ - appCRDMap: make(map[appRef]*cozyv1alpha1.CozystackResourceDefinition), + appCRDMap: make(map[appRef]*cozyv1alpha1.ApplicationDefinition), } for _, crd := range crds.Items { appRef := appRef{ diff --git a/internal/lineagecontrollerwebhook/matcher.go b/internal/lineagecontrollerwebhook/matcher.go index 7c756a49..6e7bd318 100644 --- a/internal/lineagecontrollerwebhook/matcher.go +++ b/internal/lineagecontrollerwebhook/matcher.go @@ -42,7 +42,7 @@ func matchName(ctx context.Context, name string, templateContext map[string]stri return false } -func matchResourceToSelector(ctx context.Context, name string, templateContext, l map[string]string, s *cozyv1alpha1.CozystackResourceDefinitionResourceSelector) bool { +func matchResourceToSelector(ctx context.Context, name string, templateContext, l map[string]string, s *cozyv1alpha1.ApplicationDefinitionResourceSelector) bool { sel, err := metav1.LabelSelectorAsSelector(&s.LabelSelector) if err != nil { log.FromContext(ctx).Error(err, "failed to convert label selector to selector") @@ -53,7 +53,7 @@ func matchResourceToSelector(ctx context.Context, name string, templateContext, return labelMatches && nameMatches } -func matchResourceToSelectorArray(ctx context.Context, name string, templateContext, l map[string]string, ss []*cozyv1alpha1.CozystackResourceDefinitionResourceSelector) bool { +func matchResourceToSelectorArray(ctx context.Context, name string, templateContext, l map[string]string, ss []*cozyv1alpha1.ApplicationDefinitionResourceSelector) bool { for _, s := range ss { if matchResourceToSelector(ctx, name, templateContext, l, s) { return true @@ -62,7 +62,7 @@ func matchResourceToSelectorArray(ctx context.Context, name string, templateCont return false } -func matchResourceToExcludeInclude(ctx context.Context, name string, templateContext, l map[string]string, resources *cozyv1alpha1.CozystackResourceDefinitionResources) bool { +func matchResourceToExcludeInclude(ctx context.Context, name string, templateContext, l map[string]string, resources *cozyv1alpha1.ApplicationDefinitionResources) bool { if resources == nil { return false } diff --git a/internal/lineagecontrollerwebhook/webhook.go b/internal/lineagecontrollerwebhook/webhook.go index 0841c891..299cbaee 100644 --- a/internal/lineagecontrollerwebhook/webhook.go +++ b/internal/lineagecontrollerwebhook/webhook.go @@ -33,8 +33,8 @@ const ( ManagerNameKey = "apps.cozystack.io/application.name" ) -// getResourceSelectors returns the appropriate CozystackResourceDefinitionResources for a given GroupKind -func (h *LineageControllerWebhook) getResourceSelectors(gk schema.GroupKind, crd *cozyv1alpha1.CozystackResourceDefinition) *cozyv1alpha1.CozystackResourceDefinitionResources { +// getResourceSelectors returns the appropriate ApplicationDefinitionResources for a given GroupKind +func (h *LineageControllerWebhook) getResourceSelectors(gk schema.GroupKind, crd *cozyv1alpha1.ApplicationDefinition) *cozyv1alpha1.ApplicationDefinitionResources { switch { case gk.Group == "" && gk.Kind == "Secret": return &crd.Spec.Secrets diff --git a/internal/shared/crdmem/memory.go b/internal/shared/crdmem/memory.go index fbfbeeea..f0446627 100644 --- a/internal/shared/crdmem/memory.go +++ b/internal/shared/crdmem/memory.go @@ -11,13 +11,13 @@ import ( type Memory struct { mu sync.RWMutex - data map[string]cozyv1alpha1.CozystackResourceDefinition + data map[string]cozyv1alpha1.ApplicationDefinition primed bool primeOnce sync.Once } func New() *Memory { - return &Memory{data: make(map[string]cozyv1alpha1.CozystackResourceDefinition)} + return &Memory{data: make(map[string]cozyv1alpha1.ApplicationDefinition)} } var ( @@ -30,7 +30,7 @@ func Global() *Memory { return global } -func (m *Memory) Upsert(obj *cozyv1alpha1.CozystackResourceDefinition) { +func (m *Memory) Upsert(obj *cozyv1alpha1.ApplicationDefinition) { if obj == nil { return } @@ -45,10 +45,10 @@ func (m *Memory) Delete(name string) { m.mu.Unlock() } -func (m *Memory) Snapshot() []cozyv1alpha1.CozystackResourceDefinition { +func (m *Memory) Snapshot() []cozyv1alpha1.ApplicationDefinition { m.mu.RLock() defer m.mu.RUnlock() - out := make([]cozyv1alpha1.CozystackResourceDefinition, 0, len(m.data)) + out := make([]cozyv1alpha1.ApplicationDefinition, 0, len(m.data)) for _, v := range m.data { out = append(out, v) } @@ -72,7 +72,7 @@ func (m *Memory) EnsurePrimingWithManager(mgr ctrl.Manager) error { if ok := mgr.GetCache().WaitForCacheSync(ctx); !ok { return nil } - var list cozyv1alpha1.CozystackResourceDefinitionList + var list cozyv1alpha1.ApplicationDefinitionList if err := mgr.GetClient().List(ctx, &list); err == nil { for i := range list.Items { m.Upsert(&list.Items[i]) @@ -87,11 +87,11 @@ func (m *Memory) EnsurePrimingWithManager(mgr ctrl.Manager) error { return errOut } -func (m *Memory) ListFromCacheOrAPI(ctx context.Context, c client.Client) ([]cozyv1alpha1.CozystackResourceDefinition, error) { +func (m *Memory) ListFromCacheOrAPI(ctx context.Context, c client.Client) ([]cozyv1alpha1.ApplicationDefinition, error) { if m.IsPrimed() { return m.Snapshot(), nil } - var list cozyv1alpha1.CozystackResourceDefinitionList + var list cozyv1alpha1.ApplicationDefinitionList if err := c.List(ctx, &list); err != nil { return nil, err } diff --git a/packages/core/platform/images/migrations/migrations/23 b/packages/core/platform/images/migrations/migrations/23 new file mode 100755 index 00000000..4e8c8e18 --- /dev/null +++ b/packages/core/platform/images/migrations/migrations/23 @@ -0,0 +1,11 @@ +#!/bin/sh +# Migration 23 --> 24 + +set -euo pipefail + +# Remove old cozystack-resource-definition-crd HelmRelease (renamed to application-definition-crd) +kubectl delete hr -n cozy-system cozystack-resource-definition-crd --ignore-not-found + +# Stamp version +kubectl create configmap -n cozy-system cozystack-version \ + --from-literal=version=24 --dry-run=client -o yaml | kubectl apply -f- diff --git a/packages/core/platform/sources/cozystack-engine.yaml b/packages/core/platform/sources/cozystack-engine.yaml index 7d1b03c8..596c81c4 100644 --- a/packages/core/platform/sources/cozystack-engine.yaml +++ b/packages/core/platform/sources/cozystack-engine.yaml @@ -17,18 +17,18 @@ spec: - name: cozy-lib path: library/cozy-lib components: - - name: cozystack-resource-definition-crd - path: system/cozystack-resource-definition-crd + - name: application-definition-crd + path: system/application-definition-crd install: namespace: cozy-system - releaseName: cozystack-resource-definition-crd + releaseName: application-definition-crd - name: cozystack-controller path: system/cozystack-controller install: namespace: cozy-system releaseName: cozystack-controller dependsOn: - - cozystack-resource-definition-crd + - application-definition-crd - name: cozystack-api path: system/cozystack-api install: @@ -36,7 +36,7 @@ spec: releaseName: cozystack-api dependsOn: - cozystack-controller - - cozystack-resource-definition-crd + - application-definition-crd - name: lineage-controller-webhook path: system/lineage-controller-webhook install: @@ -44,7 +44,7 @@ spec: releaseName: lineage-controller-webhook dependsOn: - cozystack-controller - - cozystack-resource-definition-crd + - application-definition-crd - name: dashboard path: system/dashboard install: @@ -53,7 +53,7 @@ spec: dependsOn: - cozystack-api - cozystack-controller - - cozystack-resource-definition-crd + - application-definition-crd - name: oidc dependsOn: - cozystack.networking @@ -63,18 +63,18 @@ spec: - name: cozy-lib path: library/cozy-lib components: - - name: cozystack-resource-definition-crd - path: system/cozystack-resource-definition-crd + - name: application-definition-crd + path: system/application-definition-crd install: namespace: cozy-system - releaseName: cozystack-resource-definition-crd + releaseName: application-definition-crd - name: cozystack-controller path: system/cozystack-controller install: namespace: cozy-system releaseName: cozystack-controller dependsOn: - - cozystack-resource-definition-crd + - application-definition-crd - name: cozystack-api path: system/cozystack-api install: @@ -82,7 +82,7 @@ spec: releaseName: cozystack-api dependsOn: - cozystack-controller - - cozystack-resource-definition-crd + - application-definition-crd - name: lineage-controller-webhook path: system/lineage-controller-webhook install: @@ -90,7 +90,7 @@ spec: releaseName: lineage-controller-webhook dependsOn: - cozystack-controller - - cozystack-resource-definition-crd + - application-definition-crd - name: dashboard path: system/dashboard install: @@ -100,7 +100,7 @@ spec: - cozystack-api - cozystack-controller - keycloak-configure - - cozystack-resource-definition-crd + - application-definition-crd - name: keycloak-configure path: system/keycloak-configure install: diff --git a/packages/core/platform/values.yaml b/packages/core/platform/values.yaml index 70b8ff86..a358ef22 100644 --- a/packages/core/platform/values.yaml +++ b/packages/core/platform/values.yaml @@ -6,7 +6,7 @@ sourceRef: migrations: enabled: false image: ghcr.io/cozystack/cozystack/platform-migrations:latest - targetVersion: 23 + targetVersion: 24 # Bundle deployment configuration bundles: system: diff --git a/packages/system/cozystack-resource-definition-crd/Chart.yaml b/packages/system/application-definition-crd/Chart.yaml similarity index 74% rename from packages/system/cozystack-resource-definition-crd/Chart.yaml rename to packages/system/application-definition-crd/Chart.yaml index 9924c7fa..f272bf43 100644 --- a/packages/system/cozystack-resource-definition-crd/Chart.yaml +++ b/packages/system/application-definition-crd/Chart.yaml @@ -1,3 +1,3 @@ apiVersion: v2 -name: cozystack-resource-definition-crd +name: application-definition-crd version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process diff --git a/packages/system/cozystack-resource-definition-crd/Makefile b/packages/system/application-definition-crd/Makefile similarity index 57% rename from packages/system/cozystack-resource-definition-crd/Makefile rename to packages/system/application-definition-crd/Makefile index caac4557..adb378af 100644 --- a/packages/system/cozystack-resource-definition-crd/Makefile +++ b/packages/system/application-definition-crd/Makefile @@ -1,4 +1,4 @@ -export NAME=cozystack-resource-definition-crd +export NAME=application-definition-crd export NAMESPACE=cozy-system include ../../../hack/package.mk diff --git a/packages/system/cozystack-resource-definition-crd/definition/cozystack.io_cozystackresourcedefinitions.yaml b/packages/system/application-definition-crd/definition/cozystack.io_applicationdefinitions.yaml similarity index 97% rename from packages/system/cozystack-resource-definition-crd/definition/cozystack.io_cozystackresourcedefinitions.yaml rename to packages/system/application-definition-crd/definition/cozystack.io_applicationdefinitions.yaml index 6c6c15f5..44d33865 100644 --- a/packages/system/cozystack-resource-definition-crd/definition/cozystack.io_cozystackresourcedefinitions.yaml +++ b/packages/system/application-definition-crd/definition/cozystack.io_applicationdefinitions.yaml @@ -4,20 +4,20 @@ kind: CustomResourceDefinition metadata: annotations: controller-gen.kubebuilder.io/version: v0.16.4 - name: cozystackresourcedefinitions.cozystack.io + name: applicationdefinitions.cozystack.io spec: group: cozystack.io names: - kind: CozystackResourceDefinition - listKind: CozystackResourceDefinitionList - plural: cozystackresourcedefinitions - singular: cozystackresourcedefinition + kind: ApplicationDefinition + listKind: ApplicationDefinitionList + plural: applicationdefinitions + singular: applicationdefinition scope: Cluster versions: - name: v1alpha1 schema: openAPIV3Schema: - description: CozystackResourceDefinition is the Schema for the cozystackresourcedefinitions + description: ApplicationDefinition is the Schema for the applicationdefinitions API properties: apiVersion: @@ -135,7 +135,7 @@ spec: If a resource matches the selector in any of the elements in the array, it is hidden from the user, regardless of the matches in the include array. items: - description: "CozystackResourceDefinitionResourceSelector extends + description: "ApplicationDefinitionResourceSelector extends metav1.LabelSelector with resourceNames support.\nA resource matches this selector only if it satisfies ALL criteria:\n- Label selector conditions (matchExpressions and matchLabels)\n- @@ -210,7 +210,7 @@ spec: matches none of the selectors in the exclude array that resource is marked as a tenant resource and is visible to users. items: - description: "CozystackResourceDefinitionResourceSelector extends + description: "ApplicationDefinitionResourceSelector extends metav1.LabelSelector with resourceNames support.\nA resource matches this selector only if it satisfies ALL criteria:\n- Label selector conditions (matchExpressions and matchLabels)\n- @@ -332,7 +332,7 @@ spec: If a resource matches the selector in any of the elements in the array, it is hidden from the user, regardless of the matches in the include array. items: - description: "CozystackResourceDefinitionResourceSelector extends + description: "ApplicationDefinitionResourceSelector extends metav1.LabelSelector with resourceNames support.\nA resource matches this selector only if it satisfies ALL criteria:\n- Label selector conditions (matchExpressions and matchLabels)\n- @@ -407,7 +407,7 @@ spec: matches none of the selectors in the exclude array that resource is marked as a tenant resource and is visible to users. items: - description: "CozystackResourceDefinitionResourceSelector extends + description: "ApplicationDefinitionResourceSelector extends metav1.LabelSelector with resourceNames support.\nA resource matches this selector only if it satisfies ALL criteria:\n- Label selector conditions (matchExpressions and matchLabels)\n- @@ -485,7 +485,7 @@ spec: If a resource matches the selector in any of the elements in the array, it is hidden from the user, regardless of the matches in the include array. items: - description: "CozystackResourceDefinitionResourceSelector extends + description: "ApplicationDefinitionResourceSelector extends metav1.LabelSelector with resourceNames support.\nA resource matches this selector only if it satisfies ALL criteria:\n- Label selector conditions (matchExpressions and matchLabels)\n- @@ -560,7 +560,7 @@ spec: matches none of the selectors in the exclude array that resource is marked as a tenant resource and is visible to users. items: - description: "CozystackResourceDefinitionResourceSelector extends + description: "ApplicationDefinitionResourceSelector extends metav1.LabelSelector with resourceNames support.\nA resource matches this selector only if it satisfies ALL criteria:\n- Label selector conditions (matchExpressions and matchLabels)\n- diff --git a/packages/system/application-definition-crd/templates/crd.yaml b/packages/system/application-definition-crd/templates/crd.yaml new file mode 100644 index 00000000..3c94f229 --- /dev/null +++ b/packages/system/application-definition-crd/templates/crd.yaml @@ -0,0 +1,2 @@ +--- +{{ .Files.Get "definition/cozystack.io_applicationdefinitions.yaml" }} diff --git a/packages/system/cozystack-resource-definition-crd/values.yaml b/packages/system/application-definition-crd/values.yaml similarity index 100% rename from packages/system/cozystack-resource-definition-crd/values.yaml rename to packages/system/application-definition-crd/values.yaml diff --git a/packages/system/bootbox-rd/cozyrds/bootbox.yaml b/packages/system/bootbox-rd/cozyrds/bootbox.yaml index 7ed1f52f..5a1871bf 100644 --- a/packages/system/bootbox-rd/cozyrds/bootbox.yaml +++ b/packages/system/bootbox-rd/cozyrds/bootbox.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: bootbox spec: diff --git a/packages/system/bucket-rd/cozyrds/bucket.yaml b/packages/system/bucket-rd/cozyrds/bucket.yaml index 8596bf5a..b0e06500 100644 --- a/packages/system/bucket-rd/cozyrds/bucket.yaml +++ b/packages/system/bucket-rd/cozyrds/bucket.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: bucket spec: diff --git a/packages/system/clickhouse-rd/cozyrds/clickhouse.yaml b/packages/system/clickhouse-rd/cozyrds/clickhouse.yaml index de35d626..8f116af6 100644 --- a/packages/system/clickhouse-rd/cozyrds/clickhouse.yaml +++ b/packages/system/clickhouse-rd/cozyrds/clickhouse.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: clickhouse spec: diff --git a/packages/system/cozystack-resource-definition-crd/templates/crd.yaml b/packages/system/cozystack-resource-definition-crd/templates/crd.yaml deleted file mode 100644 index 40a93ad3..00000000 --- a/packages/system/cozystack-resource-definition-crd/templates/crd.yaml +++ /dev/null @@ -1,2 +0,0 @@ ---- -{{ .Files.Get "definition/cozystack.io_cozystackresourcedefinitions.yaml" }} diff --git a/packages/system/etcd-rd/cozyrds/etcd.yaml b/packages/system/etcd-rd/cozyrds/etcd.yaml index e4483602..eecb4450 100644 --- a/packages/system/etcd-rd/cozyrds/etcd.yaml +++ b/packages/system/etcd-rd/cozyrds/etcd.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: etcd spec: diff --git a/packages/system/ferretdb-rd/cozyrds/ferretdb.yaml b/packages/system/ferretdb-rd/cozyrds/ferretdb.yaml index a3236914..427cb0c5 100644 --- a/packages/system/ferretdb-rd/cozyrds/ferretdb.yaml +++ b/packages/system/ferretdb-rd/cozyrds/ferretdb.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: ferretdb spec: diff --git a/packages/system/foundationdb-rd/cozyrds/foundationdb.yaml b/packages/system/foundationdb-rd/cozyrds/foundationdb.yaml index 5cc8ccd6..dcd2f9d9 100644 --- a/packages/system/foundationdb-rd/cozyrds/foundationdb.yaml +++ b/packages/system/foundationdb-rd/cozyrds/foundationdb.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: foundationdb spec: diff --git a/packages/system/http-cache-rd/cozyrds/http-cache.yaml b/packages/system/http-cache-rd/cozyrds/http-cache.yaml index 991e933f..f379b1c7 100644 --- a/packages/system/http-cache-rd/cozyrds/http-cache.yaml +++ b/packages/system/http-cache-rd/cozyrds/http-cache.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: http-cache spec: diff --git a/packages/system/info-rd/cozyrds/info.yaml b/packages/system/info-rd/cozyrds/info.yaml index 68ed9b32..ee76049a 100644 --- a/packages/system/info-rd/cozyrds/info.yaml +++ b/packages/system/info-rd/cozyrds/info.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: info spec: diff --git a/packages/system/ingress-rd/cozyrds/ingress.yaml b/packages/system/ingress-rd/cozyrds/ingress.yaml index b547c28c..0812a12d 100644 --- a/packages/system/ingress-rd/cozyrds/ingress.yaml +++ b/packages/system/ingress-rd/cozyrds/ingress.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: ingress spec: diff --git a/packages/system/kafka-rd/cozyrds/kafka.yaml b/packages/system/kafka-rd/cozyrds/kafka.yaml index 5e0a7da1..121e46a7 100644 --- a/packages/system/kafka-rd/cozyrds/kafka.yaml +++ b/packages/system/kafka-rd/cozyrds/kafka.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: kafka spec: diff --git a/packages/system/kubernetes-rd/cozyrds/kubernetes.yaml b/packages/system/kubernetes-rd/cozyrds/kubernetes.yaml index c5516b91..93dfb33b 100644 --- a/packages/system/kubernetes-rd/cozyrds/kubernetes.yaml +++ b/packages/system/kubernetes-rd/cozyrds/kubernetes.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: kubernetes spec: diff --git a/packages/system/monitoring-rd/cozyrds/monitoring.yaml b/packages/system/monitoring-rd/cozyrds/monitoring.yaml index e8a9a02b..f039dca2 100644 --- a/packages/system/monitoring-rd/cozyrds/monitoring.yaml +++ b/packages/system/monitoring-rd/cozyrds/monitoring.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: monitoring spec: diff --git a/packages/system/mysql-rd/cozyrds/mysql.yaml b/packages/system/mysql-rd/cozyrds/mysql.yaml index bb9a170a..4e94111c 100644 --- a/packages/system/mysql-rd/cozyrds/mysql.yaml +++ b/packages/system/mysql-rd/cozyrds/mysql.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: mysql spec: diff --git a/packages/system/nats-rd/cozyrds/nats.yaml b/packages/system/nats-rd/cozyrds/nats.yaml index c9b44601..2e825774 100644 --- a/packages/system/nats-rd/cozyrds/nats.yaml +++ b/packages/system/nats-rd/cozyrds/nats.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: nats spec: diff --git a/packages/system/postgres-rd/cozyrds/postgres.yaml b/packages/system/postgres-rd/cozyrds/postgres.yaml index 05d18b51..2582d578 100644 --- a/packages/system/postgres-rd/cozyrds/postgres.yaml +++ b/packages/system/postgres-rd/cozyrds/postgres.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: postgres spec: diff --git a/packages/system/rabbitmq-rd/cozyrds/rabbitmq.yaml b/packages/system/rabbitmq-rd/cozyrds/rabbitmq.yaml index 39c6962d..46f6d47e 100644 --- a/packages/system/rabbitmq-rd/cozyrds/rabbitmq.yaml +++ b/packages/system/rabbitmq-rd/cozyrds/rabbitmq.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: rabbitmq spec: diff --git a/packages/system/redis-rd/cozyrds/redis.yaml b/packages/system/redis-rd/cozyrds/redis.yaml index 176743ff..174b16bb 100644 --- a/packages/system/redis-rd/cozyrds/redis.yaml +++ b/packages/system/redis-rd/cozyrds/redis.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: redis spec: diff --git a/packages/system/seaweedfs-rd/cozyrds/seaweedfs.yaml b/packages/system/seaweedfs-rd/cozyrds/seaweedfs.yaml index b83c7b23..176ce49b 100644 --- a/packages/system/seaweedfs-rd/cozyrds/seaweedfs.yaml +++ b/packages/system/seaweedfs-rd/cozyrds/seaweedfs.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: seaweedfs spec: diff --git a/packages/system/tcp-balancer-rd/cozyrds/tcp-balancer.yaml b/packages/system/tcp-balancer-rd/cozyrds/tcp-balancer.yaml index b7ce2e7a..17310d6d 100644 --- a/packages/system/tcp-balancer-rd/cozyrds/tcp-balancer.yaml +++ b/packages/system/tcp-balancer-rd/cozyrds/tcp-balancer.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: tcp-balancer spec: diff --git a/packages/system/tenant-rd/cozyrds/tenant.yaml b/packages/system/tenant-rd/cozyrds/tenant.yaml index 08ed1336..bbdcedfe 100644 --- a/packages/system/tenant-rd/cozyrds/tenant.yaml +++ b/packages/system/tenant-rd/cozyrds/tenant.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: tenant spec: diff --git a/packages/system/virtual-machine-rd/cozyrds/virtual-machine.yaml b/packages/system/virtual-machine-rd/cozyrds/virtual-machine.yaml index 30b2abf2..c89f2c7a 100644 --- a/packages/system/virtual-machine-rd/cozyrds/virtual-machine.yaml +++ b/packages/system/virtual-machine-rd/cozyrds/virtual-machine.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: virtual-machine spec: diff --git a/packages/system/virtualprivatecloud-rd/cozyrds/virtualprivatecloud.yaml b/packages/system/virtualprivatecloud-rd/cozyrds/virtualprivatecloud.yaml index a39bf7a2..1ab67253 100644 --- a/packages/system/virtualprivatecloud-rd/cozyrds/virtualprivatecloud.yaml +++ b/packages/system/virtualprivatecloud-rd/cozyrds/virtualprivatecloud.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: virtualprivatecloud spec: diff --git a/packages/system/vm-disk-rd/cozyrds/vm-disk.yaml b/packages/system/vm-disk-rd/cozyrds/vm-disk.yaml index 5760b8fc..d8c1f689 100644 --- a/packages/system/vm-disk-rd/cozyrds/vm-disk.yaml +++ b/packages/system/vm-disk-rd/cozyrds/vm-disk.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: vm-disk spec: diff --git a/packages/system/vm-instance-rd/cozyrds/vm-instance.yaml b/packages/system/vm-instance-rd/cozyrds/vm-instance.yaml index aa89adbc..cd18a3b3 100644 --- a/packages/system/vm-instance-rd/cozyrds/vm-instance.yaml +++ b/packages/system/vm-instance-rd/cozyrds/vm-instance.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: vm-instance spec: diff --git a/packages/system/vpn-rd/cozyrds/vpn.yaml b/packages/system/vpn-rd/cozyrds/vpn.yaml index 4f889c1e..a3bf6422 100644 --- a/packages/system/vpn-rd/cozyrds/vpn.yaml +++ b/packages/system/vpn-rd/cozyrds/vpn.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: vpn spec: diff --git a/pkg/cmd/server/start.go b/pkg/cmd/server/start.go index 2826cfd5..6ff8d2d1 100644 --- a/pkg/cmd/server/start.go +++ b/pkg/cmd/server/start.go @@ -124,7 +124,7 @@ func (o *CozyServerOptions) Complete() error { return fmt.Errorf("client initialization failed: %w", err) } - crdList := &v1alpha1.CozystackResourceDefinitionList{} + crdList := &v1alpha1.ApplicationDefinitionList{} // Retry with exponential backoff for at least 30 minutes const maxRetryDuration = 30 * time.Minute @@ -142,11 +142,11 @@ func (o *CozyServerOptions) Complete() error { // Check if we've exceeded the maximum retry duration if time.Since(startTime) >= maxRetryDuration { - return fmt.Errorf("failed to list CozystackResourceDefinitions after %v: %w", maxRetryDuration, err) + return fmt.Errorf("failed to list ApplicationDefinitions after %v: %w", maxRetryDuration, err) } // Log the error and wait before retrying - fmt.Printf("Failed to list CozystackResourceDefinitions (retrying in %v): %v\n", delay, err) + fmt.Printf("Failed to list ApplicationDefinitions (retrying in %v): %v\n", delay, err) time.Sleep(delay) delay = time.Duration(float64(delay) * 1.5) From f004668622f434a940b510827930d976b8405708 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Fri, 16 Jan 2026 00:15:03 +0100 Subject: [PATCH 026/125] fix(talos): skip rebuilding assets if files already exist Add file existence checks to talos asset targets to prevent rebuilding kernel, initramfs, installer, iso, nocloud, and metal assets when they already exist from a previous build step. This fixes duplicate talos builds in the release pipeline where `make build` and `make assets` both triggered talos image generation. Co-Authored-By: Claude Signed-off-by: Andrei Kvapil --- packages/core/talos/Makefile | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/packages/core/talos/Makefile b/packages/core/talos/Makefile index 4e8a8dfb..e06793b9 100644 --- a/packages/core/talos/Makefile +++ b/packages/core/talos/Makefile @@ -30,9 +30,27 @@ image-matchbox: assets: talos-iso talos-nocloud talos-metal talos-kernel talos-initramfs -talos-initramfs talos-kernel talos-installer talos-iso talos-nocloud talos-metal: +talos-initramfs: + test -f ../../../_out/assets/initramfs-metal-amd64.xz || $(MAKE) build-talos-initramfs + +talos-kernel: + test -f ../../../_out/assets/kernel-amd64 || $(MAKE) build-talos-kernel + +talos-installer: + test -f ../../../_out/assets/installer-amd64.tar || $(MAKE) build-talos-installer + +talos-iso: + test -f ../../../_out/assets/metal-amd64.iso || $(MAKE) build-talos-iso + +talos-nocloud: + test -f ../../../_out/assets/nocloud-amd64.raw.xz || $(MAKE) build-talos-nocloud + +talos-metal: + test -f ../../../_out/assets/metal-amd64.raw.xz || $(MAKE) build-talos-metal + +build-talos-initramfs build-talos-kernel build-talos-installer build-talos-iso build-talos-nocloud build-talos-metal: mkdir -p ../../../_out/assets - cat images/talos/profiles/$(subst talos-,,$@).yaml | \ + cat images/talos/profiles/$(subst build-talos-,,$@).yaml | \ docker run --rm -i -v /dev:/dev --privileged "ghcr.io/siderolabs/imager:$(TALOS_VERSION)" --tar-to-stdout - | \ tar -C ../../../_out/assets -xzf- From 09290b5586ed417bc55e59ebe807a6fb10b7e886 Mon Sep 17 00:00:00 2001 From: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com> Date: Thu, 15 Jan 2026 23:32:41 +0000 Subject: [PATCH 027/125] Prepare release v1.0.0-alpha.1 Signed-off-by: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com> --- packages/apps/http-cache/images/nginx-cache.tag | 2 +- packages/apps/kubernetes/images/kubevirt-cloud-provider.tag | 2 +- packages/apps/kubernetes/images/kubevirt-csi-driver.tag | 2 +- packages/apps/kubernetes/images/ubuntu-container-disk.tag | 2 +- packages/apps/mysql/images/mariadb-backup.tag | 2 +- packages/core/installer/values.yaml | 6 +++--- packages/core/testing/values.yaml | 2 +- packages/extra/bootbox/images/matchbox.tag | 2 +- packages/extra/monitoring/images/grafana.tag | 2 +- packages/extra/seaweedfs/images/objectstorage-sidecar.tag | 2 +- packages/system/backup-controller/values.yaml | 2 +- packages/system/backupstrategy-controller/values.yaml | 2 +- packages/system/bucket/images/s3manager.tag | 2 +- packages/system/cilium/values.yaml | 4 ++-- packages/system/cozystack-api/values.yaml | 2 +- packages/system/cozystack-controller/values.yaml | 4 ++-- packages/system/dashboard/templates/configmap.yaml | 2 +- packages/system/dashboard/values.yaml | 6 +++--- .../system/grafana-operator/images/grafana-dashboards.tag | 2 +- packages/system/kamaji/values.yaml | 4 ++-- packages/system/kubeovn-plunger/values.yaml | 2 +- packages/system/kubeovn-webhook/values.yaml | 2 +- packages/system/kubevirt-csi-node/values.yaml | 2 +- packages/system/lineage-controller-webhook/values.yaml | 2 +- packages/system/linstor/values.yaml | 4 ++-- packages/system/metallb/values.yaml | 4 ++-- packages/system/objectstorage-controller/values.yaml | 2 +- packages/system/seaweedfs/values.yaml | 2 +- 28 files changed, 37 insertions(+), 37 deletions(-) diff --git a/packages/apps/http-cache/images/nginx-cache.tag b/packages/apps/http-cache/images/nginx-cache.tag index 185dcc66..3f0bbd33 100644 --- a/packages/apps/http-cache/images/nginx-cache.tag +++ b/packages/apps/http-cache/images/nginx-cache.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/nginx-cache:0.0.0@sha256:e0a07082bb6fc6aeaae2315f335386f1705a646c72f9e0af512aebbca5cb2b15 +ghcr.io/cozystack/cozystack/nginx-cache:0.0.0@sha256:31ebc09cfa11d8b438d2bbb32fa61b133aaf4b48b1a1282c9e59b5c127af61c1 diff --git a/packages/apps/kubernetes/images/kubevirt-cloud-provider.tag b/packages/apps/kubernetes/images/kubevirt-cloud-provider.tag index 8120dd48..ca661f3a 100644 --- a/packages/apps/kubernetes/images/kubevirt-cloud-provider.tag +++ b/packages/apps/kubernetes/images/kubevirt-cloud-provider.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/kubevirt-cloud-provider:0.0.0@sha256:5335c044313b69ee13b30ca4941687e509005e55f4ae25723861edbf2fbd6dd2 +ghcr.io/cozystack/cozystack/kubevirt-cloud-provider:0.0.0@sha256:dee69d15fa8616aa6a1e5a67fc76370e7698a7f58b25e30650eb39c9fb826de8 diff --git a/packages/apps/kubernetes/images/kubevirt-csi-driver.tag b/packages/apps/kubernetes/images/kubevirt-csi-driver.tag index 326da62f..ea6c26b9 100644 --- a/packages/apps/kubernetes/images/kubevirt-csi-driver.tag +++ b/packages/apps/kubernetes/images/kubevirt-csi-driver.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:0e5d8bbdc587a96e07bbf263f23d4d237dac8bd4528b34f44d31b042c6a0e495 +ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:15b94dca216b73336e7f39f4ea1b76b7656890d6be8a8cf0d9a786b4006781f9 diff --git a/packages/apps/kubernetes/images/ubuntu-container-disk.tag b/packages/apps/kubernetes/images/ubuntu-container-disk.tag index a35d1278..06de29fa 100644 --- a/packages/apps/kubernetes/images/ubuntu-container-disk.tag +++ b/packages/apps/kubernetes/images/ubuntu-container-disk.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.33@sha256:a09724a7f95283f9130b3da2a89d81c4c6051c6edf0392a81b6fc90f404b76b6 +ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.33@sha256:d25e567bc8b17b596e050f5ff410e36112c7966e33f4b372c752e7350bacc894 diff --git a/packages/apps/mysql/images/mariadb-backup.tag b/packages/apps/mysql/images/mariadb-backup.tag index af6247da..792c010b 100644 --- a/packages/apps/mysql/images/mariadb-backup.tag +++ b/packages/apps/mysql/images/mariadb-backup.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/mariadb-backup:0.0.0@sha256:1c0beb1b23a109b0e13727b4c73d2c74830e11cede92858ab20101b66f45a858 +ghcr.io/cozystack/cozystack/mariadb-backup:0.0.0@sha256:aca403030ff5d831415d72367866fdf291fab73ee2cfddbe4c93c2915a316ab1 diff --git a/packages/core/installer/values.yaml b/packages/core/installer/values.yaml index e38b4dfb..7639d999 100644 --- a/packages/core/installer/values.yaml +++ b/packages/core/installer/values.yaml @@ -1,5 +1,5 @@ cozystackOperator: - image: ghcr.io/cozystack/cozystack/cozystack-operator:latest@sha256:1e998a7cf2b9ed10b1320d9b3282e9362283fcda524f22fa0ad40579e7e464ff + image: ghcr.io/cozystack/cozystack/cozystack-operator:v1.0.0-alpha.1@sha256:d1beb9728b5be3ff6ee59eab071ac739cbb69d95f4bae173baa56323e97b0e5a platformSourceUrl: 'oci://ghcr.io/cozystack/cozystack/cozystack-packages' - platformSourceRef: 'digest=sha256:319e78e454cdc28e1d6edd701e4b09723bfb12a03c71b77271d136b09002ad7e' - cozystackVersion: latest + platformSourceRef: 'digest=sha256:ab03446b256c0faa9de0656854319f3b56e1252dab6f7b51bc6c95609d192556' + cozystackVersion: v1.0.0-alpha.1 diff --git a/packages/core/testing/values.yaml b/packages/core/testing/values.yaml index b00e9ea3..af105f30 100644 --- a/packages/core/testing/values.yaml +++ b/packages/core/testing/values.yaml @@ -1,2 +1,2 @@ e2e: - image: ghcr.io/cozystack/cozystack/e2e-sandbox:v0.40.2@sha256:78a556a059411cac89249c2e22596223128a1c74fbfcac4787fd74e4a66f66a6 + image: ghcr.io/cozystack/cozystack/e2e-sandbox:v1.0.0-alpha.1@sha256:78a556a059411cac89249c2e22596223128a1c74fbfcac4787fd74e4a66f66a6 diff --git a/packages/extra/bootbox/images/matchbox.tag b/packages/extra/bootbox/images/matchbox.tag index 522bb744..8ec9a817 100644 --- a/packages/extra/bootbox/images/matchbox.tag +++ b/packages/extra/bootbox/images/matchbox.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/matchbox:v0.40.2@sha256:f72d35fc691021d6b473360f2edaf0b1f176f737ca18f8cb9322d16c69705020 +ghcr.io/cozystack/cozystack/matchbox:v1.0.0-alpha.1@sha256:fa4b9a4d2e94648e5f17d9e46f8dfaddee423652676052b1976f5be998fa9cad diff --git a/packages/extra/monitoring/images/grafana.tag b/packages/extra/monitoring/images/grafana.tag index 9d53b4f1..06fa403d 100644 --- a/packages/extra/monitoring/images/grafana.tag +++ b/packages/extra/monitoring/images/grafana.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/grafana:0.0.0@sha256:c63978e1ed0304e8518b31ddee56c4e8115541b997d8efbe1c0a74da57140399 +ghcr.io/cozystack/cozystack/grafana:0.0.0@sha256:8ce0cd90c8f614cdabf5a41f8aa50b7dfbd02b31b9a0bd7897927e7f89968e07 diff --git a/packages/extra/seaweedfs/images/objectstorage-sidecar.tag b/packages/extra/seaweedfs/images/objectstorage-sidecar.tag index 90d0d249..cc735976 100644 --- a/packages/extra/seaweedfs/images/objectstorage-sidecar.tag +++ b/packages/extra/seaweedfs/images/objectstorage-sidecar.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/objectstorage-sidecar:v0.40.2@sha256:6735e6f050355355a13439ff815539a1b92eba09623e500e92dd7d4e600c59b4 +ghcr.io/cozystack/cozystack/objectstorage-sidecar:v1.0.0-alpha.1@sha256:d342ac197b97b81ec42712ecd9d762c6c7710a401736e9d09c64a5b8d59774cc diff --git a/packages/system/backup-controller/values.yaml b/packages/system/backup-controller/values.yaml index 557c201a..7d7d8740 100644 --- a/packages/system/backup-controller/values.yaml +++ b/packages/system/backup-controller/values.yaml @@ -1,5 +1,5 @@ backupController: - image: "" + image: "ghcr.io/cozystack/cozystack/backup-controller:v1.0.0-alpha.1@sha256:17502897a36a0723b2ca65e88b336e6d64d67d2ff578d6aa22165f2e0c718cc6" replicas: 2 debug: false metrics: diff --git a/packages/system/backupstrategy-controller/values.yaml b/packages/system/backupstrategy-controller/values.yaml index 6b04244a..752af01a 100644 --- a/packages/system/backupstrategy-controller/values.yaml +++ b/packages/system/backupstrategy-controller/values.yaml @@ -1,5 +1,5 @@ backupStrategyController: - image: "" + image: "ghcr.io/cozystack/cozystack/backupstrategy-controller:v1.0.0-alpha.1@sha256:d13aa9b1221484e6e70cb9d773b0d1cc0644eeaae2e78470cd5a89061dff9e05" replicas: 2 debug: false metrics: diff --git a/packages/system/bucket/images/s3manager.tag b/packages/system/bucket/images/s3manager.tag index b71ecf9e..88b26eac 100644 --- a/packages/system/bucket/images/s3manager.tag +++ b/packages/system/bucket/images/s3manager.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/s3manager:v0.5.0@sha256:07f016720c4692fc270a415a327a88773b90d2821922098496ad1269beac6c94 +ghcr.io/cozystack/cozystack/s3manager:v0.5.0@sha256:79463ccddbbd2a4048863944a858b2aced3f6e1c2d77f7ef4fa57e30624f6e35 diff --git a/packages/system/cilium/values.yaml b/packages/system/cilium/values.yaml index 95159b1f..1c759ac6 100644 --- a/packages/system/cilium/values.yaml +++ b/packages/system/cilium/values.yaml @@ -15,8 +15,8 @@ cilium: mode: "kubernetes" image: repository: ghcr.io/cozystack/cozystack/cilium - tag: 1.17.8 - digest: "sha256:81262986a41487bfa3d0465091d3a386def5bd1ab476350bd4af2fdee5846fe6" + tag: 1.18.5 + digest: "sha256:c14a0bcb1a1531c72725b3a4c40aefa2bcd5c129810bf58ea8e37d3fcff2a326" envoy: enabled: false rollOutCiliumPods: true diff --git a/packages/system/cozystack-api/values.yaml b/packages/system/cozystack-api/values.yaml index 04e86c7c..40b8ca78 100644 --- a/packages/system/cozystack-api/values.yaml +++ b/packages/system/cozystack-api/values.yaml @@ -1,5 +1,5 @@ cozystackAPI: - image: ghcr.io/cozystack/cozystack/cozystack-api:v0.40.2@sha256:408885b509d80ca30a85416f87d07112bc7f070374e71e80b64818fbb24ad1ee + image: ghcr.io/cozystack/cozystack/cozystack-api:v1.0.0-alpha.1@sha256:ad2d688c29b15d6cc77c526e9dd57bd4b2887a79ac1fb3fea636e69bc11d3b28 localK8sAPIEndpoint: enabled: true replicas: 2 diff --git a/packages/system/cozystack-controller/values.yaml b/packages/system/cozystack-controller/values.yaml index de8eedd5..7d0826a5 100644 --- a/packages/system/cozystack-controller/values.yaml +++ b/packages/system/cozystack-controller/values.yaml @@ -1,6 +1,6 @@ cozystackController: - image: ghcr.io/cozystack/cozystack/cozystack-controller:v0.40.2@sha256:1878af4389ee3ddc85f2079cc250bccc14b923fb74c665c9aa902a659a394ba6 + image: ghcr.io/cozystack/cozystack/cozystack-controller:v1.0.0-alpha.1@sha256:b9c3f3ca0a65a7ed43d690e805ec42e1a7b3091418b7082e60478a82a5c8ddc3 debug: false disableTelemetry: false - cozystackVersion: "v0.40.2" + cozystackVersion: "v1.0.0-alpha.1" cozystackAPIKind: "DaemonSet" diff --git a/packages/system/dashboard/templates/configmap.yaml b/packages/system/dashboard/templates/configmap.yaml index a929b0b1..dd739a5c 100644 --- a/packages/system/dashboard/templates/configmap.yaml +++ b/packages/system/dashboard/templates/configmap.yaml @@ -1,6 +1,6 @@ {{- $brandingConfig := .Values._cluster.branding | default dict }} -{{- $tenantText := "v0.40.2" }} +{{- $tenantText := "v1.0.0-alpha.1" }} {{- $footerText := "Cozystack" }} {{- $titleText := "Cozystack Dashboard" }} {{- $logoText := "" }} diff --git a/packages/system/dashboard/values.yaml b/packages/system/dashboard/values.yaml index 091aaa4d..ec896d8b 100644 --- a/packages/system/dashboard/values.yaml +++ b/packages/system/dashboard/values.yaml @@ -1,6 +1,6 @@ openapiUI: - image: ghcr.io/cozystack/cozystack/openapi-ui:v0.40.2@sha256:a8d3770107c0279add903e6b5acdc5ca8a15551fb07514e40772f8010c5386ef + image: ghcr.io/cozystack/cozystack/openapi-ui:v1.0.0-alpha.1@sha256:7a3cf1a20dac3bf5f7ecad6cf2ebd5be034814ec6f8ca9ea5b467596481ae3ec openapiUIK8sBff: - image: ghcr.io/cozystack/cozystack/openapi-ui-k8s-bff:v0.40.2@sha256:3071dfc130dbaf185b1a46ed42f0800824e77c2f4e64c5ed87055311f3c5f2b1 + image: ghcr.io/cozystack/cozystack/openapi-ui-k8s-bff:v1.0.0-alpha.1@sha256:6e16fb9bad7ec5f05fdfe5be83f21846b6cd0cedd4a692319b0106257683330b tokenProxy: - image: ghcr.io/cozystack/cozystack/token-proxy:v0.40.2@sha256:4fc8a11f8a1a81aa0774ae2b1ed2e05d36d0b3ef1e37979cc4994e65114d93ae + image: ghcr.io/cozystack/cozystack/token-proxy:v1.0.0-alpha.1@sha256:73887f80d96e7e3c16f1cebab521b05b4308bf4662ccc6724e6a8a9745ed8254 diff --git a/packages/system/grafana-operator/images/grafana-dashboards.tag b/packages/system/grafana-operator/images/grafana-dashboards.tag index bd0f3b14..41aeb19c 100644 --- a/packages/system/grafana-operator/images/grafana-dashboards.tag +++ b/packages/system/grafana-operator/images/grafana-dashboards.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/grafana-dashboards:latest +ghcr.io/cozystack/cozystack/grafana-dashboards:v1.0.0-alpha.1@sha256:e320863f0b04a71b0eccd5a4608aad34a882c79c66f3b4c2c4d1089f9ee3bb25 diff --git a/packages/system/kamaji/values.yaml b/packages/system/kamaji/values.yaml index 4aea9c22..24d5ed70 100644 --- a/packages/system/kamaji/values.yaml +++ b/packages/system/kamaji/values.yaml @@ -3,7 +3,7 @@ kamaji: deploy: false image: pullPolicy: IfNotPresent - tag: v0.40.2@sha256:cf9aae159deb586b1af5e5f6299247fe82f01646d58102bf5ca264c40f9edda2 + tag: v1.0.0-alpha.1@sha256:777f1df253dc97da7f42019857e7a893c34bd98da1cf36e38533adbdf601acf7 repository: ghcr.io/cozystack/cozystack/kamaji resources: limits: @@ -13,4 +13,4 @@ kamaji: cpu: 100m memory: 100Mi extraArgs: - - --migrate-image=ghcr.io/cozystack/cozystack/kamaji:v0.40.2@sha256:cf9aae159deb586b1af5e5f6299247fe82f01646d58102bf5ca264c40f9edda2 + - --migrate-image=ghcr.io/cozystack/cozystack/kamaji:v1.0.0-alpha.1@sha256:777f1df253dc97da7f42019857e7a893c34bd98da1cf36e38533adbdf601acf7 diff --git a/packages/system/kubeovn-plunger/values.yaml b/packages/system/kubeovn-plunger/values.yaml index 4229b116..c3917ffb 100644 --- a/packages/system/kubeovn-plunger/values.yaml +++ b/packages/system/kubeovn-plunger/values.yaml @@ -1,4 +1,4 @@ portSecurity: true routes: "" -image: ghcr.io/cozystack/cozystack/kubeovn-plunger:v0.40.2@sha256:fdcc081d3c28a023a6950deac4d6124a0290d1bb4bd13c8c723569ac40d6874e +image: ghcr.io/cozystack/cozystack/kubeovn-plunger:v1.0.0-alpha.1@sha256:dc5fc9d7a54b2608e088b24c8bef03693e450e8952cf0476266a1b68eeb7e455 ovnCentralName: ovn-central diff --git a/packages/system/kubeovn-webhook/values.yaml b/packages/system/kubeovn-webhook/values.yaml index 6ec8f26e..34a48ef1 100644 --- a/packages/system/kubeovn-webhook/values.yaml +++ b/packages/system/kubeovn-webhook/values.yaml @@ -1,3 +1,3 @@ portSecurity: true routes: "" -image: ghcr.io/cozystack/cozystack/kubeovn-webhook:v0.40.2@sha256:e6334c29d3aaf0dea766c88e3e05b53ad623d1bb497b3c836e6f76adade45b29 +image: ghcr.io/cozystack/cozystack/kubeovn-webhook:v1.0.0-alpha.1@sha256:e18f9fd679e38f65362a8d0042f25468272f6d081136ad47027168d8e7e07a4a diff --git a/packages/system/kubevirt-csi-node/values.yaml b/packages/system/kubevirt-csi-node/values.yaml index d3fac521..5644e6d8 100644 --- a/packages/system/kubevirt-csi-node/values.yaml +++ b/packages/system/kubevirt-csi-node/values.yaml @@ -1,3 +1,3 @@ storageClass: replicated csiDriver: - image: ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:0e5d8bbdc587a96e07bbf263f23d4d237dac8bd4528b34f44d31b042c6a0e495 + image: ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:15b94dca216b73336e7f39f4ea1b76b7656890d6be8a8cf0d9a786b4006781f9 diff --git a/packages/system/lineage-controller-webhook/values.yaml b/packages/system/lineage-controller-webhook/values.yaml index 554d9ef2..41d00955 100644 --- a/packages/system/lineage-controller-webhook/values.yaml +++ b/packages/system/lineage-controller-webhook/values.yaml @@ -1,5 +1,5 @@ lineageControllerWebhook: - image: ghcr.io/cozystack/cozystack/lineage-controller-webhook:v0.40.2@sha256:16a700334d4d9cebea2516874c04d0799a450052f3652a0c91668c37d1d0e227 + image: ghcr.io/cozystack/cozystack/lineage-controller-webhook:v1.0.0-alpha.1@sha256:887c264efecb31cb7ca38034ecf325cf461e2302d703422364d8b20e4f18c56b debug: false localK8sAPIEndpoint: enabled: true diff --git a/packages/system/linstor/values.yaml b/packages/system/linstor/values.yaml index da62e9da..684167cb 100644 --- a/packages/system/linstor/values.yaml +++ b/packages/system/linstor/values.yaml @@ -1,7 +1,7 @@ piraeusServer: image: repository: ghcr.io/cozystack/cozystack/piraeus-server - tag: 1.32.3@sha256:1138c8dc0a117360ef70e2e2ab97bc2696419b63f46358f7668c7e01a96c419b + tag: 1.32.3@sha256:0e78fa31a3fe4ec2af43d1e59a9fc0f6d765780e32d473e18e1c495714051802 linstor: autoDiskful: enabled: true @@ -10,4 +10,4 @@ linstor: linstorCSI: image: repository: ghcr.io/cozystack/cozystack/linstor-csi - tag: v1.10.5@sha256:276e35e644cc4f1a17bc3842ded84aef4d9a5d750b5bdf3fc7238e369e88ada8 + tag: v1.10.5@sha256:68465f120cfeec3d7ccbb389dd9bdbf7df1675da3ab9ba91c3feff21a799bc36 diff --git a/packages/system/metallb/values.yaml b/packages/system/metallb/values.yaml index d392f4c4..dd5f76e6 100644 --- a/packages/system/metallb/values.yaml +++ b/packages/system/metallb/values.yaml @@ -4,8 +4,8 @@ metallb: controller: image: repository: ghcr.io/cozystack/cozystack/metallb-controller - tag: v0.15.2@sha256:0e9080234fc8eedab78ad2831fb38df375c383e901a752d72b353c8d13b9605f + tag: v0.15.2@sha256:623ce74b5802bff6e29f29478ccab29ce4162a64148be006c69e16cc3207e289 speaker: image: repository: ghcr.io/cozystack/cozystack/metallb-speaker - tag: v0.15.2@sha256:e14d4c328c3ab91a6eadfeea90da96388503492d165e7e8582f291b1872e53b2 + tag: v0.15.2@sha256:f264058afd9228452a260ab9c9dd1859404745627a2a38c2ba4671e27f3b3bb2 diff --git a/packages/system/objectstorage-controller/values.yaml b/packages/system/objectstorage-controller/values.yaml index 041c3ad2..1d56ae5d 100644 --- a/packages/system/objectstorage-controller/values.yaml +++ b/packages/system/objectstorage-controller/values.yaml @@ -1,3 +1,3 @@ objectstorage: controller: - image: "ghcr.io/cozystack/cozystack/objectstorage-controller:v0.40.2@sha256:f44845c034643c3b9040b70dc417e633d8ab0cc56a304a88dace733f2b4f1d70" + image: "ghcr.io/cozystack/cozystack/objectstorage-controller:v1.0.0-alpha.1@sha256:76756860145d49abe1585c8ca600267a07fcdbb0b359bc9c127baf9efb8e006b" diff --git a/packages/system/seaweedfs/values.yaml b/packages/system/seaweedfs/values.yaml index fd8f7ff0..941755aa 100644 --- a/packages/system/seaweedfs/values.yaml +++ b/packages/system/seaweedfs/values.yaml @@ -177,7 +177,7 @@ seaweedfs: bucketClassName: "seaweedfs" region: "" sidecar: - image: "ghcr.io/cozystack/cozystack/objectstorage-sidecar:v0.40.2@sha256:6735e6f050355355a13439ff815539a1b92eba09623e500e92dd7d4e600c59b4" + image: "ghcr.io/cozystack/cozystack/objectstorage-sidecar:v1.0.0-alpha.1@sha256:d342ac197b97b81ec42712ecd9d762c6c7710a401736e9d09c64a5b8d59774cc" certificates: commonName: "SeaweedFS CA" ipAddresses: [] From cc70dabe8518878a0efe87ac13be847a9e57ef63 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Fri, 16 Jan 2026 15:39:01 +0100 Subject: [PATCH 028/125] [docs] Generate changelog for v1.0.0-alpha.1 Co-Authored-By: Claude Signed-off-by: Andrei Kvapil --- docs/changelogs/v1.0.0-alpha.1.md | 109 ++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 docs/changelogs/v1.0.0-alpha.1.md diff --git a/docs/changelogs/v1.0.0-alpha.1.md b/docs/changelogs/v1.0.0-alpha.1.md new file mode 100644 index 00000000..5ca60dd9 --- /dev/null +++ b/docs/changelogs/v1.0.0-alpha.1.md @@ -0,0 +1,109 @@ +# Cozystack v1.0.0-alpha.1 — "Package-Based Architecture" + +This alpha release introduces a fundamental architectural shift from HelmRelease bundles to Package-based deployment managed by the cozystack-operator. It includes significant API changes that rename the core CRD, Flux sharding for improved tenant workload distribution, enhanced monitoring capabilities, and various improvements to virtual machines, tenants, and the build workflow. + +> **⚠️ Alpha Release Warning**: This is a pre-release version intended for testing and early adoption. Breaking changes may occur before the stable v1.0.0 release. + +## Breaking Changes + +### API Rename: CozystackResourceDefinition → ApplicationDefinition + +The `CozystackResourceDefinition` CRD has been renamed to `ApplicationDefinition` for better clarity and consistency. This change affects: +- All Go types and controller files +- CRD Helm chart renamed from `cozystack-resource-definition-crd` to `application-definition-crd` +- All cozyrds YAML manifests updated to use `kind: ApplicationDefinition` + +A migration (v24) is included to handle the transition automatically. + +### Package-Based Deployment + +The platform now uses Package resources managed by cozystack-operator instead of HelmRelease bundles. Key changes: +- Restructured values.yaml with full configuration support (networking, publishing, authentication, scheduling, branding, resources) +- Added values-isp-full.yaml and values-isp-hosted.yaml for bundle variants +- Package resources replace old HelmRelease templates +- PackageSources moved from sources/ to templates/sources/ +- Migration script `hack/migrate-to-version-1.0.sh` provided for converting ConfigMaps to Package resources + +--- + +## Major Features and Improvements + +### Platform Architecture + +* **[platform] Migrate from HelmRelease bundles to Package-based deployment**: Replaced HelmRelease bundle system with Package resources managed by cozystack-operator. Includes restructured values.yaml with full configuration support and migration tooling ([**@kvaps**](https://github.com/kvaps) in #1816). +* **refactor(api): rename CozystackResourceDefinition to ApplicationDefinition**: Renamed CRD and all related types for better clarity and consistency. Updated all Go types, controllers, and 25+ YAML manifests ([**@kvaps**](https://github.com/kvaps) in #1864). +* **feat(flux): implement flux sharding for tenant HelmReleases**: Added Flux sharding support to distribute tenant HelmRelease reconciliation across multiple controllers, improving scalability in multi-tenant environments ([**@kvaps**](https://github.com/kvaps) in #1816). +* **refactor(installer): migrate installer to cozystack-operator**: Moved installer functionality to cozystack-operator for unified management ([**@kvaps**](https://github.com/kvaps) in #1816). +* **feat(api): add chartRef to ApplicationDefinition**: Added chartRef field to support ExternalArtifact references for flexible chart sourcing ([**@kvaps**](https://github.com/kvaps) in #1816). +* **feat(api): show only hash in version column for applications and modules**: Simplified version display in API responses for cleaner output ([**@kvaps**](https://github.com/kvaps) in #1816). + +### Virtual Machines + +* **[vm] Always expose VMs with a service**: Virtual machines are now always exposed with at least a ClusterIP service, ensuring they have in-cluster DNS names and can be accessed from other pods even without public IP addresses ([**@lllamnyp**](https://github.com/lllamnyp) in #1738, #1751). + +### Monitoring + +* **[monitoring] Add SLACK_SEVERITY_FILTER field and VMAgent for tenant monitoring**: Introduced the SLACK_SEVERITY_FILTER environment variable in the Alerta deployment to enable filtering of alert severities for Slack notifications based on the disabledSeverity configuration. Additionally, added a VMAgent resource template for scraping metrics within tenant namespaces, improving monitoring granularity and control ([**@IvanHunters**](https://github.com/IvanHunters) in #1712). + +### Tenants + +* **[tenant] Allow egress to parent ingress pods**: Updated tenant network policies to allow egress traffic to parent cluster ingress pods, enabling proper communication patterns between tenant namespaces and parent cluster ingress controllers ([**@lexfrei**](https://github.com/lexfrei) in #1765, #1776). +* **[tenant] Run cleanup job from system namespace**: Moved tenant cleanup job to run from system namespace, improving security and resource isolation for tenant cleanup operations ([**@lllamnyp**](https://github.com/lllamnyp) in #1774, #1777). + +### System + +* **[system] Add resource requests and limits to etcd-defrag**: Added resource requests and limits to etcd-defrag job to ensure proper resource allocation and prevent resource contention during etcd maintenance operations ([**@matthieu-robin**](https://github.com/matthieu-robin) in #1785, #1786). + +### Development and Build + +* **feat(cozypkg): add cross-platform build targets with version injection**: Added cross-platform build targets (linux/amd64, linux/arm64, darwin/amd64, darwin/arm64) for cozypkg/cozyhr tool with automatic version injection from git tags ([**@kvaps**](https://github.com/kvaps) in #1862). +* **refactor: move scripts to hack directory**: Reorganized scripts to standard hack/ location following Kubernetes project conventions ([**@kvaps**](https://github.com/kvaps) in #1863). + +## Fixes + +* **fix(talos): skip rebuilding assets if files already exist**: Improved Talos package build process to avoid redundant asset rebuilds when files are already present, reducing build time ([**@kvaps**](https://github.com/kvaps)). +* **[kubevirt-operator] Fix typo in VMNotRunningFor10Minutes alert**: Fixed typo in VM alert name, ensuring proper alert triggering and monitoring for virtual machines that are not running for extended periods ([**@lexfrei**](https://github.com/lexfrei) in #1770, #1775). + +## Documentation + +* **[website] docs(storage): simplify NFS driver setup instructions**: Simplified NFS driver setup documentation with clearer instructions ([**@kvaps**](https://github.com/kvaps) in [cozystack/website#399](https://github.com/cozystack/website/pull/399)). +* **[website] Add Hidora organization support details**: Added Hidora to the support page with organization details ([**@matthieu-robin**](https://github.com/matthieu-robin) in [cozystack/website#397](https://github.com/cozystack/website/pull/397)). +* **[website] Update LinkedIn link for Hidora organization**: Updated LinkedIn link for Hidora organization on the support page ([**@matthieu-robin**](https://github.com/matthieu-robin) in [cozystack/website#398](https://github.com/cozystack/website/pull/398)). + +--- + +## Migration Guide + +### From v0.39.x / v0.40.x to v1.0.0-alpha.1 + +1. **Backup your cluster** before upgrading +2. Run the migration script: `hack/migrate-to-version-1.0.sh` +3. The migration will: + - Convert ConfigMaps to Package resources + - Rename CozystackResourceDefinition to ApplicationDefinition + - Update HelmRelease references to use Package-based deployment + +### Known Issues + +- This is an alpha release; some features may be incomplete or change before stable release +- Migration script should be tested in a non-production environment first + +--- + +## Contributors + +We'd like to thank all contributors who made this release possible: + +* [**@IvanHunters**](https://github.com/IvanHunters) +* [**@kvaps**](https://github.com/kvaps) +* [**@lexfrei**](https://github.com/lexfrei) +* [**@lllamnyp**](https://github.com/lllamnyp) +* [**@matthieu-robin**](https://github.com/matthieu-robin) + +--- + +**Full Changelog**: [v0.39.0...v1.0.0-alpha.1](https://github.com/cozystack/cozystack/compare/v0.39.0...v1.0.0-alpha.1) + + From 8e26028a132a2faef758d3f57bcd117832d5ff20 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Fri, 16 Jan 2026 15:51:11 +0100 Subject: [PATCH 029/125] [docs] Update changelog for v1.0.0-alpha.1: add cozystack-operator and backup system Co-Authored-By: Claude Signed-off-by: Andrei Kvapil --- docs/changelogs/v1.0.0-alpha.1.md | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/docs/changelogs/v1.0.0-alpha.1.md b/docs/changelogs/v1.0.0-alpha.1.md index 5ca60dd9..4411b35f 100644 --- a/docs/changelogs/v1.0.0-alpha.1.md +++ b/docs/changelogs/v1.0.0-alpha.1.md @@ -1,6 +1,6 @@ # Cozystack v1.0.0-alpha.1 — "Package-Based Architecture" -This alpha release introduces a fundamental architectural shift from HelmRelease bundles to Package-based deployment managed by the cozystack-operator. It includes significant API changes that rename the core CRD, Flux sharding for improved tenant workload distribution, enhanced monitoring capabilities, and various improvements to virtual machines, tenants, and the build workflow. +This alpha release introduces a fundamental architectural shift from HelmRelease bundles to Package-based deployment managed by the new cozystack-operator. It includes a comprehensive backup system with Velero integration, significant API changes that rename the core CRD, Flux sharding for improved tenant workload distribution, enhanced monitoring capabilities, and various improvements to virtual machines, tenants, and the build workflow. > **⚠️ Alpha Release Warning**: This is a pre-release version intended for testing and early adoption. Breaking changes may occur before the stable v1.0.0 release. @@ -28,6 +28,29 @@ The platform now uses Package resources managed by cozystack-operator instead of ## Major Features and Improvements +### Cozystack Operator + +A new operator has been introduced to manage Package and PackageSource resources, providing declarative package management for the platform: + +* **[cozystack-operator] Introduce API objects: packages and packagesources**: Added new CRDs for declarative package management, defining the API for Package and PackageSource resources ([**@kvaps**](https://github.com/kvaps) in #1740). +* **[cozystack-operator] Introduce Cozystack-operator core logic**: Implemented core reconciliation logic for the operator, handling Package and PackageSource lifecycle management ([**@kvaps**](https://github.com/kvaps) in #1741). +* **[cozystack-operator] Add Package and PackageSource reconcilers**: Added controllers for Package and PackageSource resources with full reconciliation support ([**@kvaps**](https://github.com/kvaps) in #1755). +* **[cozystack-operator] Add deployment files**: Added Kubernetes deployment manifests for running cozystack-operator in the cluster ([**@kvaps**](https://github.com/kvaps) in #1761). +* **[platform] Add PackageSources for cozystack-operator**: Added PackageSource definitions for cozystack-operator integration ([**@kvaps**](https://github.com/kvaps) in #1760). +* **[cozypkg] Add tool for managing Package and PackageSources**: Added CLI tool for managing Package and PackageSource resources ([**@kvaps**](https://github.com/kvaps) in #1756). + +### Backup System + +Comprehensive backup functionality has been added with Velero integration for managing application backups: + +* **[backups] Implement core backup Plan controller**: Core controller for managing backup schedules and plans, providing the foundation for backup orchestration ([**@lllamnyp**](https://github.com/lllamnyp) in #1640). +* **[backups] Build and deploy backup controller**: Deployment infrastructure for the backup controller, including container image builds and Kubernetes manifests ([**@lllamnyp**](https://github.com/lllamnyp) in #1685). +* **[backups] Scaffold a backup strategy API group**: Added API group for backup strategies, enabling pluggable backup implementations ([**@lllamnyp**](https://github.com/lllamnyp) in #1687). +* **[backups] Add indices to core backup resources**: Added indices to backup resources for improved query performance ([**@lllamnyp**](https://github.com/lllamnyp) in #1719). +* **[backups] Stub the Job backup strategy controller**: Added stub implementation for Job-based backup strategy ([**@lllamnyp**](https://github.com/lllamnyp) in #1720). +* **[backups] Implement Velero strategy controller**: Integration with Velero for backup operations, enabling enterprise-grade backup capabilities ([**@androndo**](https://github.com/androndo) in #1762). +* **[backups,dashboard] User-facing UI**: Dashboard interface for managing backups and backup jobs, providing visibility into backup status and history ([**@lllamnyp**](https://github.com/lllamnyp) in #1737). + ### Platform Architecture * **[platform] Migrate from HelmRelease bundles to Package-based deployment**: Replaced HelmRelease bundle system with Package resources managed by cozystack-operator. Includes restructured values.yaml with full configuration support and migration tooling ([**@kvaps**](https://github.com/kvaps) in #1816). @@ -63,6 +86,7 @@ The platform now uses Package resources managed by cozystack-operator instead of * **fix(talos): skip rebuilding assets if files already exist**: Improved Talos package build process to avoid redundant asset rebuilds when files are already present, reducing build time ([**@kvaps**](https://github.com/kvaps)). * **[kubevirt-operator] Fix typo in VMNotRunningFor10Minutes alert**: Fixed typo in VM alert name, ensuring proper alert triggering and monitoring for virtual machines that are not running for extended periods ([**@lexfrei**](https://github.com/lexfrei) in #1770, #1775). +* **[backups] Fix malformed glob and split in template**: Fixed malformed glob pattern and split operation in backup template processing ([**@lllamnyp**](https://github.com/lllamnyp) in #1708). ## Documentation @@ -74,7 +98,7 @@ The platform now uses Package resources managed by cozystack-operator instead of ## Migration Guide -### From v0.39.x / v0.40.x to v1.0.0-alpha.1 +### From v0.38.x / v0.39.x to v1.0.0-alpha.1 1. **Backup your cluster** before upgrading 2. Run the migration script: `hack/migrate-to-version-1.0.sh` @@ -94,6 +118,7 @@ The platform now uses Package resources managed by cozystack-operator instead of We'd like to thank all contributors who made this release possible: +* [**@androndo**](https://github.com/androndo) * [**@IvanHunters**](https://github.com/IvanHunters) * [**@kvaps**](https://github.com/kvaps) * [**@lexfrei**](https://github.com/lexfrei) @@ -102,7 +127,7 @@ We'd like to thank all contributors who made this release possible: --- -**Full Changelog**: [v0.39.0...v1.0.0-alpha.1](https://github.com/cozystack/cozystack/compare/v0.39.0...v1.0.0-alpha.1) +**Full Changelog**: [v0.38.0...v1.0.0-alpha.1](https://github.com/cozystack/cozystack/compare/v0.38.0...v1.0.0-alpha.1) + +## Improvements + +* **[kubernetes] Increase default apiServer resourcesPreset to large**: Increased the default resource preset for kube-apiserver to `large` to ensure more reliable operation under higher workloads and prevent resource constraints ([**@kvaps**](https://github.com/kvaps) in #1875, #1882). + +* **[kubernetes] Increase kube-apiserver startup probe threshold**: Increased the startup probe threshold for kube-apiserver to allow more time for the API server to become ready, especially in scenarios with slow storage or high load ([**@kvaps**](https://github.com/kvaps) in #1876, #1883). + +* **[etcd] Increase probe thresholds for better recovery**: Increased etcd probe thresholds to provide more time for recovery operations, improving cluster resilience during network issues or temporary slowdowns ([**@kvaps**](https://github.com/kvaps) in #1874, #1878). + +## Fixes + +* **[dashboard] Fix view of loadbalancer IP in services window**: Fixed an issue where load balancer IP addresses were not displayed correctly in the services window of the dashboard ([**@IvanHunters**](https://github.com/IvanHunters) in #1884, #1887). + +## Dependencies + +* **Update Talos Linux v1.11.6**: Updated Talos Linux to v1.11.6 with latest security patches and improvements ([**@kvaps**](https://github.com/kvaps) in #1879). + +--- + +**Full Changelog**: [v0.40.3...v0.40.4](https://github.com/cozystack/cozystack/compare/v0.40.3...v0.40.4) diff --git a/docs/changelogs/v0.41.0.md b/docs/changelogs/v0.41.0.md new file mode 100644 index 00000000..a25dfe38 --- /dev/null +++ b/docs/changelogs/v0.41.0.md @@ -0,0 +1,33 @@ + + +## Improvements + +* **[linstor] Update piraeus-server patches with critical fixes**: Backported critical patches to piraeus-server that address storage stability issues and improve DRBD resource handling. These patches fix edge cases in device management and ensure more reliable storage operations ([**@kvaps**](https://github.com/kvaps) in #1850, #1852). + +* **[linstor] Refactor node-level RWX validation**: Refactored the node-level ReadWriteMany (RWX) validation logic in LINSTOR CSI. The validation has been moved to the CSI driver level with a custom linstor-csi image build, providing more reliable RWX volume handling and clearer error messages when RWX requirements cannot be satisfied ([**@kvaps**](https://github.com/kvaps) in #1856, #1857). + +* **[kubernetes] Increase default apiServer resourcesPreset to large**: Increased the default resource preset for kube-apiserver to `large` to ensure more reliable operation under higher workloads and prevent resource constraints ([**@kvaps**](https://github.com/kvaps) in #1875, #1882). + +* **[kubernetes] Increase kube-apiserver startup probe threshold**: Increased the startup probe threshold for kube-apiserver to allow more time for the API server to become ready, especially in scenarios with slow storage or high load ([**@kvaps**](https://github.com/kvaps) in #1876, #1883). + +* **[etcd] Increase probe thresholds for better recovery**: Increased etcd probe thresholds to provide more time for recovery operations, improving cluster resilience during network issues or temporary slowdowns ([**@kvaps**](https://github.com/kvaps) in #1874, #1878). + +## Fixes + +* **[linstor] Remove node-level RWX validation**: Removed the problematic node-level RWX validation that was causing issues with volume provisioning. The validation logic has been refactored and moved to a more appropriate location in the LINSTOR CSI driver ([**@kvaps**](https://github.com/kvaps) in #1851). + +* **[apiserver] Fix Watch resourceVersion and bookmark handling**: Fixed issues with Watch API handling of resourceVersion and bookmarks, ensuring proper event streaming and state synchronization for API clients ([**@kvaps**](https://github.com/kvaps) in #1860). + +* **[dashboard] Fix view of loadbalancer IP in services window**: Fixed an issue where load balancer IP addresses were not displayed correctly in the services window of the dashboard ([**@IvanHunters**](https://github.com/IvanHunters) in #1884, #1887). + +## Dependencies + +* **[cilium] Update cilium to v1.18.6**: Updated Cilium CNI to v1.18.6 with security fixes and performance improvements ([**@sircthulhu**](https://github.com/sircthulhu) in #1868, #1870). + +* **Update Talos Linux v1.11.6**: Updated Talos Linux to v1.11.6 with latest security patches and improvements ([**@kvaps**](https://github.com/kvaps) in #1879). + +--- + +**Full Changelog**: [v0.40.0...v0.41.0](https://github.com/cozystack/cozystack/compare/v0.40.0...v0.41.0) diff --git a/docs/changelogs/v1.0.0-alpha.2.md b/docs/changelogs/v1.0.0-alpha.2.md new file mode 100644 index 00000000..925d859f --- /dev/null +++ b/docs/changelogs/v1.0.0-alpha.2.md @@ -0,0 +1,58 @@ + + +> **⚠️ Alpha Release Warning**: This is a pre-release version intended for testing and early adoption. Breaking changes may occur before the stable v1.0.0 release. + +## Major Features and Improvements + +### New Applications + +* **[apps] Add MongoDB managed application**: Added MongoDB as a new managed application, providing a fully managed MongoDB database with automatic scaling, backups, and high availability support ([**@lexfrei**](https://github.com/lexfrei) in #1822). + +### Networking + +* **[kilo] Introduce kilo**: Added Kilo WireGuard mesh networking support. Kilo provides secure WireGuard-based VPN mesh for connecting Kubernetes nodes across different networks and regions ([**@kvaps**](https://github.com/kvaps) in #1691). + +* **[local-ccm] Add local-ccm package**: Added local cloud controller manager package for managing load balancer services in local/bare-metal environments without a cloud provider ([**@kvaps**](https://github.com/kvaps) in #1831). + +### Platform + +* **[platform] Add flux-plunger controller**: Added flux-plunger controller to automatically fix stuck HelmRelease errors by cleaning up failed resources and retrying reconciliation ([**@kvaps**](https://github.com/kvaps) in #1843). + +* **[platform] Split telemetry between operator and controller**: Separated telemetry collection between cozystack-operator and cozystack-controller for better metrics isolation and monitoring capabilities ([**@kvaps**](https://github.com/kvaps) in #1869). + +* **[platform] Remove cozystack.io/ui label**: Cleaned up deprecated `cozystack.io/ui` labels from platform components ([**@kvaps**](https://github.com/kvaps) in #1872). + +## Improvements + +* **[kubernetes] Increase default apiServer resourcesPreset to large**: Increased the default resource preset for kube-apiserver to `large` to ensure more reliable operation under higher workloads ([**@kvaps**](https://github.com/kvaps) in #1875). + +* **[kubernetes] Increase kube-apiserver startup probe threshold**: Increased the startup probe threshold for kube-apiserver to allow more time for the API server to become ready ([**@kvaps**](https://github.com/kvaps) in #1876). + +* **[etcd] Increase probe thresholds for better recovery**: Increased etcd probe thresholds to provide more time for recovery operations, improving cluster resilience ([**@kvaps**](https://github.com/kvaps) in #1874). + +## Fixes + +* **[apiserver] Fix Watch resourceVersion and bookmark handling**: Fixed issues with Watch API handling of resourceVersion and bookmarks, ensuring proper event streaming and state synchronization ([**@kvaps**](https://github.com/kvaps) in #1860). + +* **[dashboard] Fix view of loadbalancer IP in services window**: Fixed an issue where load balancer IP addresses were not displayed correctly in the services window of the dashboard ([**@IvanHunters**](https://github.com/IvanHunters) in #1884). + +## Dependencies + +* **[cilium] Update cilium to v1.18.6**: Updated Cilium CNI to v1.18.6 with security fixes and performance improvements ([**@sircthulhu**](https://github.com/sircthulhu) in #1868). + +* **Update Talos Linux v1.12.1**: Updated Talos Linux to v1.12.1 with latest features, security patches and improvements ([**@kvaps**](https://github.com/kvaps) in #1877). + +--- + +## Contributors + +* [**@IvanHunters**](https://github.com/IvanHunters) +* [**@kvaps**](https://github.com/kvaps) +* [**@lexfrei**](https://github.com/lexfrei) +* [**@sircthulhu**](https://github.com/sircthulhu) + +--- + +**Full Changelog**: [v1.0.0-alpha.1...v1.0.0-alpha.2](https://github.com/cozystack/cozystack/compare/v1.0.0-alpha.1...v1.0.0-alpha.2) From 0510ff1e2d3100c0a7aa17abcb6dd618fa176672 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Tue, 20 Jan 2026 03:56:27 +0100 Subject: [PATCH 047/125] [docs] Add MongoDB highlight and documentation to v0.41.0 and v1.0.0-alpha.2 changelogs Co-Authored-By: Claude Signed-off-by: Andrei Kvapil --- docs/changelogs/v0.41.0.md | 30 ++++++++++++++++++++++++++++++ docs/changelogs/v1.0.0-alpha.2.md | 13 +++++++++++++ 2 files changed, 43 insertions(+) diff --git a/docs/changelogs/v0.41.0.md b/docs/changelogs/v0.41.0.md index a25dfe38..3084e493 100644 --- a/docs/changelogs/v0.41.0.md +++ b/docs/changelogs/v0.41.0.md @@ -2,6 +2,24 @@ https://github.com/cozystack/cozystack/releases/tag/v0.41.0 --> +# Cozystack v0.41.0 — "MongoDB" + +This release introduces MongoDB as a new managed application, expanding Cozystack's database offerings alongside existing PostgreSQL, MySQL, and Redis services. The release also includes storage improvements, Kubernetes stability enhancements, and updated documentation. + +## Feature Highlights + +### MongoDB Managed Application + +Cozystack now includes MongoDB as a fully managed database service. Users can deploy production-ready MongoDB instances directly from the application catalog with minimal configuration. + +Key capabilities: +- **Replica Set deployment**: Automatic configuration of MongoDB replica sets for high availability +- **Persistent storage**: Integration with Cozystack storage backends for reliable data persistence +- **Resource management**: Configurable CPU, memory, and storage resources +- **Monitoring integration**: Built-in metrics export for platform monitoring + +Deploy MongoDB through the Cozystack dashboard or using the standard application deployment workflow ([**@lexfrei**](https://github.com/lexfrei) in #1822, #1881). + ## Improvements * **[linstor] Update piraeus-server patches with critical fixes**: Backported critical patches to piraeus-server that address storage stability issues and improve DRBD resource handling. These patches fix edge cases in device management and ensure more reliable storage operations ([**@kvaps**](https://github.com/kvaps) in #1850, #1852). @@ -28,6 +46,18 @@ https://github.com/cozystack/cozystack/releases/tag/v0.41.0 * **Update Talos Linux v1.11.6**: Updated Talos Linux to v1.11.6 with latest security patches and improvements ([**@kvaps**](https://github.com/kvaps) in #1879). +## Documentation + +* **[website] Add documentation for creating and managing cloned virtual machines**: Added comprehensive guide for VM cloning operations ([**@sircthulhu**](https://github.com/sircthulhu) in [cozystack/website#401](https://github.com/cozystack/website/pull/401)). + +* **[website] Simplify NFS driver setup instructions**: Improved NFS driver setup documentation with clearer instructions ([**@kvaps**](https://github.com/kvaps) in [cozystack/website#399](https://github.com/cozystack/website/pull/399)). + +* **[website] Update Talos installation docs for Hetzner and Servers.com**: Updated installation documentation with improved instructions for Hetzner and Servers.com environments ([**@kvaps**](https://github.com/kvaps) in [cozystack/website#395](https://github.com/cozystack/website/pull/395)). + +* **[website] Add Hetzner RobotLB documentation**: Added documentation for configuring public IP with Hetzner RobotLB ([**@kvaps**](https://github.com/kvaps) in [cozystack/website#394](https://github.com/cozystack/website/pull/394)). + +* **[website] Add Hidora organization support details**: Added Hidora to the support page with organization details ([**@matthieu-robin**](https://github.com/matthieu-robin) in [cozystack/website#397](https://github.com/cozystack/website/pull/397), [cozystack/website#398](https://github.com/cozystack/website/pull/398)). + --- **Full Changelog**: [v0.40.0...v0.41.0](https://github.com/cozystack/cozystack/compare/v0.40.0...v0.41.0) diff --git a/docs/changelogs/v1.0.0-alpha.2.md b/docs/changelogs/v1.0.0-alpha.2.md index 925d859f..14bd89a2 100644 --- a/docs/changelogs/v1.0.0-alpha.2.md +++ b/docs/changelogs/v1.0.0-alpha.2.md @@ -44,6 +44,18 @@ https://github.com/cozystack/cozystack/releases/tag/v1.0.0-alpha.2 * **Update Talos Linux v1.12.1**: Updated Talos Linux to v1.12.1 with latest features, security patches and improvements ([**@kvaps**](https://github.com/kvaps) in #1877). +## Documentation + +* **[website] Add documentation for creating and managing cloned virtual machines**: Added comprehensive guide for VM cloning operations ([**@sircthulhu**](https://github.com/sircthulhu) in [cozystack/website#401](https://github.com/cozystack/website/pull/401)). + +* **[website] Simplify NFS driver setup instructions**: Improved NFS driver setup documentation with clearer instructions ([**@kvaps**](https://github.com/kvaps) in [cozystack/website#399](https://github.com/cozystack/website/pull/399)). + +* **[website] Update Talos installation docs for Hetzner and Servers.com**: Updated installation documentation with improved instructions for Hetzner and Servers.com environments ([**@kvaps**](https://github.com/kvaps) in [cozystack/website#395](https://github.com/cozystack/website/pull/395)). + +* **[website] Add Hetzner RobotLB documentation**: Added documentation for configuring public IP with Hetzner RobotLB ([**@kvaps**](https://github.com/kvaps) in [cozystack/website#394](https://github.com/cozystack/website/pull/394)). + +* **[website] Add Hidora organization support details**: Added Hidora to the support page with organization details ([**@matthieu-robin**](https://github.com/matthieu-robin) in [cozystack/website#397](https://github.com/cozystack/website/pull/397), [cozystack/website#398](https://github.com/cozystack/website/pull/398)). + --- ## Contributors @@ -51,6 +63,7 @@ https://github.com/cozystack/cozystack/releases/tag/v1.0.0-alpha.2 * [**@IvanHunters**](https://github.com/IvanHunters) * [**@kvaps**](https://github.com/kvaps) * [**@lexfrei**](https://github.com/lexfrei) +* [**@matthieu-robin**](https://github.com/matthieu-robin) * [**@sircthulhu**](https://github.com/sircthulhu) --- From 0880bb107ebb8161222dcb075b4dbf7336242d8c Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Tue, 20 Jan 2026 08:21:43 +0100 Subject: [PATCH 048/125] Fix ApplicationDefinition for MongoDB Signed-off-by: Andrei Kvapil --- packages/system/mongodb-rd/cozyrds/mongodb.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/system/mongodb-rd/cozyrds/mongodb.yaml b/packages/system/mongodb-rd/cozyrds/mongodb.yaml index 2ad11696..996dde18 100644 --- a/packages/system/mongodb-rd/cozyrds/mongodb.yaml +++ b/packages/system/mongodb-rd/cozyrds/mongodb.yaml @@ -1,5 +1,5 @@ apiVersion: cozystack.io/v1alpha1 -kind: CozystackResourceDefinition +kind: ApplicationDefinition metadata: name: mongodb spec: From bb72dd885c8ac2662bf9ea35480457f55fdab564 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Tue, 20 Jan 2026 08:46:21 +0100 Subject: [PATCH 049/125] [kilo] rename joinCIDR to transitCIDR Signed-off-by: Andrei Kvapil --- packages/system/kilo/templates/kilo.yaml | 2 +- packages/system/kilo/values.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/system/kilo/templates/kilo.yaml b/packages/system/kilo/templates/kilo.yaml index 8fc362c0..298c0400 100644 --- a/packages/system/kilo/templates/kilo.yaml +++ b/packages/system/kilo/templates/kilo.yaml @@ -38,7 +38,7 @@ spec: {{- with .Values.kilo.serviceCIDR }} - --service-cidr={{ . }} {{- end }} - {{- with .Values.kilo.joinCIDR }} + {{- with .Values.kilo.transitCIDR }} - --subnet={{ . }} {{- end }} - --internal-cidr=$(NODE_IP)/32 diff --git a/packages/system/kilo/values.yaml b/packages/system/kilo/values.yaml index 9fde46a0..d2ef28ab 100644 --- a/packages/system/kilo/values.yaml +++ b/packages/system/kilo/values.yaml @@ -5,6 +5,6 @@ kilo: repository: 999669/kilo podCIDR: 10.244.0.0/16 serviceCIDR: 10.96.0.0/16 - joinCIDR: 100.64.0.0/16 + transitCIDR: 100.66.0.0/16 meshGranularity: location cleanUpInterface: false From 4ffe4533518f2fc7c3d7d4ed9b578c119369f925 Mon Sep 17 00:00:00 2001 From: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com> Date: Tue, 20 Jan 2026 07:50:59 +0000 Subject: [PATCH 050/125] Prepare release v1.0.0-alpha.2 Signed-off-by: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com> --- packages/apps/http-cache/images/nginx-cache.tag | 2 +- packages/apps/kubernetes/images/kubevirt-csi-driver.tag | 2 +- packages/apps/kubernetes/images/ubuntu-container-disk.tag | 2 +- packages/core/installer/values.yaml | 4 ++-- packages/core/testing/values.yaml | 2 +- packages/extra/bootbox/images/matchbox.tag | 2 +- packages/extra/seaweedfs/images/objectstorage-sidecar.tag | 2 +- packages/system/backup-controller/values.yaml | 2 +- packages/system/backupstrategy-controller/values.yaml | 2 +- packages/system/bucket/images/s3manager.tag | 2 +- packages/system/cilium/values.yaml | 6 +++--- packages/system/cozystack-api/values.yaml | 2 +- packages/system/cozystack-controller/values.yaml | 2 +- packages/system/dashboard/templates/configmap.yaml | 2 +- packages/system/dashboard/values.yaml | 6 +++--- .../system/grafana-operator/images/grafana-dashboards.tag | 2 +- packages/system/kamaji/values.yaml | 4 ++-- packages/system/kilo/values.yaml | 4 ++-- packages/system/kubeovn-plunger/values.yaml | 2 +- packages/system/kubeovn-webhook/values.yaml | 2 +- packages/system/kubevirt-csi-node/values.yaml | 2 +- packages/system/lineage-controller-webhook/values.yaml | 2 +- packages/system/linstor/values.yaml | 2 +- packages/system/objectstorage-controller/values.yaml | 2 +- packages/system/seaweedfs/values.yaml | 2 +- 25 files changed, 32 insertions(+), 32 deletions(-) diff --git a/packages/apps/http-cache/images/nginx-cache.tag b/packages/apps/http-cache/images/nginx-cache.tag index 3f0bbd33..ee4d8890 100644 --- a/packages/apps/http-cache/images/nginx-cache.tag +++ b/packages/apps/http-cache/images/nginx-cache.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/nginx-cache:0.0.0@sha256:31ebc09cfa11d8b438d2bbb32fa61b133aaf4b48b1a1282c9e59b5c127af61c1 +ghcr.io/cozystack/cozystack/nginx-cache:0.0.0@sha256:9e34fd50393b418d9516aadb488067a3a63675b045811beb1c0afc9c61e149e8 diff --git a/packages/apps/kubernetes/images/kubevirt-csi-driver.tag b/packages/apps/kubernetes/images/kubevirt-csi-driver.tag index ea6c26b9..420fbc66 100644 --- a/packages/apps/kubernetes/images/kubevirt-csi-driver.tag +++ b/packages/apps/kubernetes/images/kubevirt-csi-driver.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:15b94dca216b73336e7f39f4ea1b76b7656890d6be8a8cf0d9a786b4006781f9 +ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:31aee6f63c25a443974b56011d2907cedfeba245aa7caf732d7fc437b2b3ed50 diff --git a/packages/apps/kubernetes/images/ubuntu-container-disk.tag b/packages/apps/kubernetes/images/ubuntu-container-disk.tag index 06de29fa..930a3fd2 100644 --- a/packages/apps/kubernetes/images/ubuntu-container-disk.tag +++ b/packages/apps/kubernetes/images/ubuntu-container-disk.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.33@sha256:d25e567bc8b17b596e050f5ff410e36112c7966e33f4b372c752e7350bacc894 +ghcr.io/cozystack/cozystack/ubuntu-container-disk:v1.33@sha256:71a74ca30f75967bae309be2758f19aa3d37c60b19426b9b622ff1c33a80362f diff --git a/packages/core/installer/values.yaml b/packages/core/installer/values.yaml index 0d618fdb..6d244871 100644 --- a/packages/core/installer/values.yaml +++ b/packages/core/installer/values.yaml @@ -1,4 +1,4 @@ cozystackOperator: - image: ghcr.io/cozystack/cozystack/cozystack-operator:v1.0.0-alpha.1@sha256:d1beb9728b5be3ff6ee59eab071ac739cbb69d95f4bae173baa56323e97b0e5a + image: ghcr.io/cozystack/cozystack/cozystack-operator:v1.0.0-alpha.2@sha256:5bd4c8bb7bddb9e32555ef72ee3a04b5d8e0f488d7dce904ac2d7c9e0706debf platformSourceUrl: 'oci://ghcr.io/cozystack/cozystack/cozystack-packages' - platformSourceRef: 'digest=sha256:ab03446b256c0faa9de0656854319f3b56e1252dab6f7b51bc6c95609d192556' + platformSourceRef: 'digest=sha256:1b860c13b99da6fde37f2af3a5375aca234200836e2fa39a839ac30fa237962e' diff --git a/packages/core/testing/values.yaml b/packages/core/testing/values.yaml index af105f30..9e577ed3 100644 --- a/packages/core/testing/values.yaml +++ b/packages/core/testing/values.yaml @@ -1,2 +1,2 @@ e2e: - image: ghcr.io/cozystack/cozystack/e2e-sandbox:v1.0.0-alpha.1@sha256:78a556a059411cac89249c2e22596223128a1c74fbfcac4787fd74e4a66f66a6 + image: ghcr.io/cozystack/cozystack/e2e-sandbox:v1.0.0-alpha.2@sha256:fde7616aacaf5939388bbe74eb6d946147ce855b9cceb47092f620b75ba2c98a diff --git a/packages/extra/bootbox/images/matchbox.tag b/packages/extra/bootbox/images/matchbox.tag index 8ec9a817..ac9a2f76 100644 --- a/packages/extra/bootbox/images/matchbox.tag +++ b/packages/extra/bootbox/images/matchbox.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/matchbox:v1.0.0-alpha.1@sha256:fa4b9a4d2e94648e5f17d9e46f8dfaddee423652676052b1976f5be998fa9cad +ghcr.io/cozystack/cozystack/matchbox:v1.0.0-alpha.2@sha256:6a96746d43fde16db12bb2e0e6fc39823f4a26f41fc0be785bf6716c759009e1 diff --git a/packages/extra/seaweedfs/images/objectstorage-sidecar.tag b/packages/extra/seaweedfs/images/objectstorage-sidecar.tag index cc735976..dd3c00a1 100644 --- a/packages/extra/seaweedfs/images/objectstorage-sidecar.tag +++ b/packages/extra/seaweedfs/images/objectstorage-sidecar.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/objectstorage-sidecar:v1.0.0-alpha.1@sha256:d342ac197b97b81ec42712ecd9d762c6c7710a401736e9d09c64a5b8d59774cc +ghcr.io/cozystack/cozystack/objectstorage-sidecar:v1.0.0-alpha.2@sha256:aebbe6efe34c0a0cc2e87dafbf9dbb021d7e44796ef9378a47ca81cf72a407bb diff --git a/packages/system/backup-controller/values.yaml b/packages/system/backup-controller/values.yaml index 7d7d8740..0c20d434 100644 --- a/packages/system/backup-controller/values.yaml +++ b/packages/system/backup-controller/values.yaml @@ -1,5 +1,5 @@ backupController: - image: "ghcr.io/cozystack/cozystack/backup-controller:v1.0.0-alpha.1@sha256:17502897a36a0723b2ca65e88b336e6d64d67d2ff578d6aa22165f2e0c718cc6" + image: "ghcr.io/cozystack/cozystack/backup-controller:v1.0.0-alpha.2@sha256:3914624192b4d420b79f0d985d37a03acb13411c694e23c160de16b1d877c52f" replicas: 2 debug: false metrics: diff --git a/packages/system/backupstrategy-controller/values.yaml b/packages/system/backupstrategy-controller/values.yaml index 752af01a..cd71b6e1 100644 --- a/packages/system/backupstrategy-controller/values.yaml +++ b/packages/system/backupstrategy-controller/values.yaml @@ -1,5 +1,5 @@ backupStrategyController: - image: "ghcr.io/cozystack/cozystack/backupstrategy-controller:v1.0.0-alpha.1@sha256:d13aa9b1221484e6e70cb9d773b0d1cc0644eeaae2e78470cd5a89061dff9e05" + image: "ghcr.io/cozystack/cozystack/backupstrategy-controller:v1.0.0-alpha.2@sha256:a39b76c99f252916ca9b6606a68fbd5b0861988dd65426803855c2884117e49c" replicas: 2 debug: false metrics: diff --git a/packages/system/bucket/images/s3manager.tag b/packages/system/bucket/images/s3manager.tag index 88b26eac..1e30a0e1 100644 --- a/packages/system/bucket/images/s3manager.tag +++ b/packages/system/bucket/images/s3manager.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/s3manager:v0.5.0@sha256:79463ccddbbd2a4048863944a858b2aced3f6e1c2d77f7ef4fa57e30624f6e35 +ghcr.io/cozystack/cozystack/s3manager:v0.5.0@sha256:db548ff13f27f98e888b8f3ffb0f859a8f44334f1502d567d1881b79b6e5eb09 diff --git a/packages/system/cilium/values.yaml b/packages/system/cilium/values.yaml index 8cec970c..49a028f0 100644 --- a/packages/system/cilium/values.yaml +++ b/packages/system/cilium/values.yaml @@ -15,10 +15,10 @@ cilium: mode: "kubernetes" image: repository: ghcr.io/cozystack/cozystack/cilium - tag: 1.18.5 - digest: "sha256:c14a0bcb1a1531c72725b3a4c40aefa2bcd5c129810bf58ea8e37d3fcff2a326" + tag: 1.18.6 + digest: "sha256:4f4585f8adc3b8becd15d3999f3900a4d3d650f2ab7f85ca8c661f3807113d01" envoy: enabled: false rollOutCiliumPods: true operator: - rollOutPods: true \ No newline at end of file + rollOutPods: true diff --git a/packages/system/cozystack-api/values.yaml b/packages/system/cozystack-api/values.yaml index 40b8ca78..75f0d6d1 100644 --- a/packages/system/cozystack-api/values.yaml +++ b/packages/system/cozystack-api/values.yaml @@ -1,5 +1,5 @@ cozystackAPI: - image: ghcr.io/cozystack/cozystack/cozystack-api:v1.0.0-alpha.1@sha256:ad2d688c29b15d6cc77c526e9dd57bd4b2887a79ac1fb3fea636e69bc11d3b28 + image: ghcr.io/cozystack/cozystack/cozystack-api:v1.0.0-alpha.2@sha256:5bde4dd3e8d3a880475c9e872925f841f6131f51f9a5017ec5c2a0f0bd288838 localK8sAPIEndpoint: enabled: true replicas: 2 diff --git a/packages/system/cozystack-controller/values.yaml b/packages/system/cozystack-controller/values.yaml index e5110294..633d0b28 100644 --- a/packages/system/cozystack-controller/values.yaml +++ b/packages/system/cozystack-controller/values.yaml @@ -1,5 +1,5 @@ cozystackController: - image: ghcr.io/cozystack/cozystack/cozystack-controller:v1.0.0-alpha.1@sha256:b9c3f3ca0a65a7ed43d690e805ec42e1a7b3091418b7082e60478a82a5c8ddc3 + image: ghcr.io/cozystack/cozystack/cozystack-controller:v1.0.0-alpha.2@sha256:9cc3d4b05709d3e7495abcbe4c792467a9a439fd089a67df0e4a75fd3bb8ee81 debug: false disableTelemetry: false cozystackAPIKind: "DaemonSet" diff --git a/packages/system/dashboard/templates/configmap.yaml b/packages/system/dashboard/templates/configmap.yaml index dd739a5c..675a396d 100644 --- a/packages/system/dashboard/templates/configmap.yaml +++ b/packages/system/dashboard/templates/configmap.yaml @@ -1,6 +1,6 @@ {{- $brandingConfig := .Values._cluster.branding | default dict }} -{{- $tenantText := "v1.0.0-alpha.1" }} +{{- $tenantText := "v1.0.0-alpha.2" }} {{- $footerText := "Cozystack" }} {{- $titleText := "Cozystack Dashboard" }} {{- $logoText := "" }} diff --git a/packages/system/dashboard/values.yaml b/packages/system/dashboard/values.yaml index ec896d8b..fcbaeb8c 100644 --- a/packages/system/dashboard/values.yaml +++ b/packages/system/dashboard/values.yaml @@ -1,6 +1,6 @@ openapiUI: - image: ghcr.io/cozystack/cozystack/openapi-ui:v1.0.0-alpha.1@sha256:7a3cf1a20dac3bf5f7ecad6cf2ebd5be034814ec6f8ca9ea5b467596481ae3ec + image: ghcr.io/cozystack/cozystack/openapi-ui:v1.0.0-alpha.2@sha256:5661e877919c8625c27b89ee5dfda4a93d43e660d033fc4dbeb27f336c05d908 openapiUIK8sBff: - image: ghcr.io/cozystack/cozystack/openapi-ui-k8s-bff:v1.0.0-alpha.1@sha256:6e16fb9bad7ec5f05fdfe5be83f21846b6cd0cedd4a692319b0106257683330b + image: ghcr.io/cozystack/cozystack/openapi-ui-k8s-bff:v1.0.0-alpha.2@sha256:d33583995dc81a47c1dcbe45dbd866fa9097f88f4b6eb78b408dca432f15bd38 tokenProxy: - image: ghcr.io/cozystack/cozystack/token-proxy:v1.0.0-alpha.1@sha256:73887f80d96e7e3c16f1cebab521b05b4308bf4662ccc6724e6a8a9745ed8254 + image: ghcr.io/cozystack/cozystack/token-proxy:v1.0.0-alpha.2@sha256:73887f80d96e7e3c16f1cebab521b05b4308bf4662ccc6724e6a8a9745ed8254 diff --git a/packages/system/grafana-operator/images/grafana-dashboards.tag b/packages/system/grafana-operator/images/grafana-dashboards.tag index 41aeb19c..892bb131 100644 --- a/packages/system/grafana-operator/images/grafana-dashboards.tag +++ b/packages/system/grafana-operator/images/grafana-dashboards.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/grafana-dashboards:v1.0.0-alpha.1@sha256:e320863f0b04a71b0eccd5a4608aad34a882c79c66f3b4c2c4d1089f9ee3bb25 +ghcr.io/cozystack/cozystack/grafana-dashboards:v1.0.0-alpha.2@sha256:57051daa5686b7dcb0799b6a7c01e676b910a2ac224437ac0ad639a0e66566c2 diff --git a/packages/system/kamaji/values.yaml b/packages/system/kamaji/values.yaml index 24d5ed70..0995afc7 100644 --- a/packages/system/kamaji/values.yaml +++ b/packages/system/kamaji/values.yaml @@ -3,7 +3,7 @@ kamaji: deploy: false image: pullPolicy: IfNotPresent - tag: v1.0.0-alpha.1@sha256:777f1df253dc97da7f42019857e7a893c34bd98da1cf36e38533adbdf601acf7 + tag: v1.0.0-alpha.2@sha256:d5f2fa2972ba33cd2ccb855256e4bda4734d7e250638811b77f2e0dc72ad6b19 repository: ghcr.io/cozystack/cozystack/kamaji resources: limits: @@ -13,4 +13,4 @@ kamaji: cpu: 100m memory: 100Mi extraArgs: - - --migrate-image=ghcr.io/cozystack/cozystack/kamaji:v1.0.0-alpha.1@sha256:777f1df253dc97da7f42019857e7a893c34bd98da1cf36e38533adbdf601acf7 + - --migrate-image=ghcr.io/cozystack/cozystack/kamaji:v1.0.0-alpha.2@sha256:d5f2fa2972ba33cd2ccb855256e4bda4734d7e250638811b77f2e0dc72ad6b19 diff --git a/packages/system/kilo/values.yaml b/packages/system/kilo/values.yaml index d2ef28ab..565191a9 100644 --- a/packages/system/kilo/values.yaml +++ b/packages/system/kilo/values.yaml @@ -1,8 +1,8 @@ kilo: image: pullPolicy: IfNotPresent - tag: 0.6.0@sha256:5115b6ea3f7f492763e8bfe5b4ae23d0b65bde20162b00d5a42420918b20d328 - repository: 999669/kilo + tag: v1.0.0-alpha.2@sha256:161fec69f38f536529139318babbd7d654bd47e7bc654828c26eb0ddb516eb79 + repository: ghcr.io/cozystack/cozystack/kilo podCIDR: 10.244.0.0/16 serviceCIDR: 10.96.0.0/16 transitCIDR: 100.66.0.0/16 diff --git a/packages/system/kubeovn-plunger/values.yaml b/packages/system/kubeovn-plunger/values.yaml index c3917ffb..d821bdac 100644 --- a/packages/system/kubeovn-plunger/values.yaml +++ b/packages/system/kubeovn-plunger/values.yaml @@ -1,4 +1,4 @@ portSecurity: true routes: "" -image: ghcr.io/cozystack/cozystack/kubeovn-plunger:v1.0.0-alpha.1@sha256:dc5fc9d7a54b2608e088b24c8bef03693e450e8952cf0476266a1b68eeb7e455 +image: ghcr.io/cozystack/cozystack/kubeovn-plunger:v1.0.0-alpha.2@sha256:81d7e64b67b1e4fefa88ab0cf69c113a3fb44b04810074b616116102adcc3104 ovnCentralName: ovn-central diff --git a/packages/system/kubeovn-webhook/values.yaml b/packages/system/kubeovn-webhook/values.yaml index 34a48ef1..df35d690 100644 --- a/packages/system/kubeovn-webhook/values.yaml +++ b/packages/system/kubeovn-webhook/values.yaml @@ -1,3 +1,3 @@ portSecurity: true routes: "" -image: ghcr.io/cozystack/cozystack/kubeovn-webhook:v1.0.0-alpha.1@sha256:e18f9fd679e38f65362a8d0042f25468272f6d081136ad47027168d8e7e07a4a +image: ghcr.io/cozystack/cozystack/kubeovn-webhook:v1.0.0-alpha.2@sha256:e18f9fd679e38f65362a8d0042f25468272f6d081136ad47027168d8e7e07a4a diff --git a/packages/system/kubevirt-csi-node/values.yaml b/packages/system/kubevirt-csi-node/values.yaml index 5644e6d8..1fb152da 100644 --- a/packages/system/kubevirt-csi-node/values.yaml +++ b/packages/system/kubevirt-csi-node/values.yaml @@ -1,3 +1,3 @@ storageClass: replicated csiDriver: - image: ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:15b94dca216b73336e7f39f4ea1b76b7656890d6be8a8cf0d9a786b4006781f9 + image: ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:31aee6f63c25a443974b56011d2907cedfeba245aa7caf732d7fc437b2b3ed50 diff --git a/packages/system/lineage-controller-webhook/values.yaml b/packages/system/lineage-controller-webhook/values.yaml index 41d00955..0c4a1694 100644 --- a/packages/system/lineage-controller-webhook/values.yaml +++ b/packages/system/lineage-controller-webhook/values.yaml @@ -1,5 +1,5 @@ lineageControllerWebhook: - image: ghcr.io/cozystack/cozystack/lineage-controller-webhook:v1.0.0-alpha.1@sha256:887c264efecb31cb7ca38034ecf325cf461e2302d703422364d8b20e4f18c56b + image: ghcr.io/cozystack/cozystack/lineage-controller-webhook:v1.0.0-alpha.2@sha256:e3ed98fce423359de7dd25094de959213342f5664d266c8d1041ecd2e9c82d59 debug: false localK8sAPIEndpoint: enabled: true diff --git a/packages/system/linstor/values.yaml b/packages/system/linstor/values.yaml index 684167cb..9f7f022e 100644 --- a/packages/system/linstor/values.yaml +++ b/packages/system/linstor/values.yaml @@ -10,4 +10,4 @@ linstor: linstorCSI: image: repository: ghcr.io/cozystack/cozystack/linstor-csi - tag: v1.10.5@sha256:68465f120cfeec3d7ccbb389dd9bdbf7df1675da3ab9ba91c3feff21a799bc36 + tag: v1.10.5@sha256:353a8bea0b41f832975132da3569da7d4fce85980474edce41a2a37097c7c3a9 diff --git a/packages/system/objectstorage-controller/values.yaml b/packages/system/objectstorage-controller/values.yaml index 1d56ae5d..86ec9cd4 100644 --- a/packages/system/objectstorage-controller/values.yaml +++ b/packages/system/objectstorage-controller/values.yaml @@ -1,3 +1,3 @@ objectstorage: controller: - image: "ghcr.io/cozystack/cozystack/objectstorage-controller:v1.0.0-alpha.1@sha256:76756860145d49abe1585c8ca600267a07fcdbb0b359bc9c127baf9efb8e006b" + image: "ghcr.io/cozystack/cozystack/objectstorage-controller:v1.0.0-alpha.2@sha256:e535bd383e2ec275cc403cd557996e26df3d206a5541c53037246641cf6a4f2e" diff --git a/packages/system/seaweedfs/values.yaml b/packages/system/seaweedfs/values.yaml index 941755aa..88a05e9c 100644 --- a/packages/system/seaweedfs/values.yaml +++ b/packages/system/seaweedfs/values.yaml @@ -177,7 +177,7 @@ seaweedfs: bucketClassName: "seaweedfs" region: "" sidecar: - image: "ghcr.io/cozystack/cozystack/objectstorage-sidecar:v1.0.0-alpha.1@sha256:d342ac197b97b81ec42712ecd9d762c6c7710a401736e9d09c64a5b8d59774cc" + image: "ghcr.io/cozystack/cozystack/objectstorage-sidecar:v1.0.0-alpha.2@sha256:aebbe6efe34c0a0cc2e87dafbf9dbb021d7e44796ef9378a47ca81cf72a407bb" certificates: commonName: "SeaweedFS CA" ipAddresses: [] From 207a5171f0c0ef4680a4ddd9e073beeaa023c324 Mon Sep 17 00:00:00 2001 From: Kirill Ilin Date: Tue, 20 Jan 2026 18:00:13 +0500 Subject: [PATCH 051/125] [monitoring-agents] Set minReplicas to 1 for VPA for VMAgent Signed-off-by: Kirill Ilin --- packages/system/monitoring-agents/templates/vpa.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/system/monitoring-agents/templates/vpa.yaml b/packages/system/monitoring-agents/templates/vpa.yaml index fa672d82..4da978e7 100644 --- a/packages/system/monitoring-agents/templates/vpa.yaml +++ b/packages/system/monitoring-agents/templates/vpa.yaml @@ -9,6 +9,7 @@ spec: name: vmagent updatePolicy: updateMode: Auto + minReplicas: 1 resourcePolicy: containerPolicies: - containerName: config-reloader From 505b693c359849d4a2d928e050f6660da96ea9db Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Sat, 3 Jan 2026 01:01:04 +0100 Subject: [PATCH 052/125] [ci] Run e2e tests on shared runners Signed-off-by: Andrei Kvapil --- .github/workflows/pull-requests.yaml | 189 +++++++++------------------ hack/e2e-prepare-cluster.bats | 46 ++++--- 2 files changed, 91 insertions(+), 144 deletions(-) diff --git a/.github/workflows/pull-requests.yaml b/.github/workflows/pull-requests.yaml index 53787ad3..347bfd30 100644 --- a/.github/workflows/pull-requests.yaml +++ b/.github/workflows/pull-requests.yaml @@ -151,9 +151,10 @@ jobs: core.setOutput('disk_id', diskId); - prepare_env: - name: "Prepare environment" - runs-on: [self-hosted] + e2e: + name: "E2E Tests" + runs-on: [oracle-vm-24cpu-96gb-x86-64] + #runs-on: [oracle-vm-32cpu-128gb-x86-64] permissions: contents: read packages: read @@ -173,6 +174,20 @@ jobs: name: talos-image path: _out/assets + - name: "Download CRDs (regular PR)" + if: "!contains(github.event.pull_request.labels.*.name, 'release')" + uses: actions/download-artifact@v4 + with: + name: cozystack-crds + path: _out/assets + + - name: "Download operator (regular PR)" + if: "!contains(github.event.pull_request.labels.*.name, 'release')" + uses: actions/download-artifact@v4 + with: + name: cozystack-operator + path: _out/assets + - name: Download PR patch if: "!contains(github.event.pull_request.labels.*.name, 'release')" uses: actions/download-artifact@v4 @@ -193,13 +208,19 @@ jobs: curl -sSL -H "Authorization: token ${GH_PAT}" -H "Accept: application/octet-stream" \ -o _out/assets/nocloud-amd64.raw.xz \ "https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/assets/${{ needs.resolve_assets.outputs.disk_id }}" + curl -sSL -H "Authorization: token ${GH_PAT}" -H "Accept: application/octet-stream" \ + -o _out/assets/cozystack-crds.yaml \ + "https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/assets/${{ needs.resolve_assets.outputs.crds_id }}" + curl -sSL -H "Authorization: token ${GH_PAT}" -H "Accept: application/octet-stream" \ + -o _out/assets/cozystack-operator.yaml \ + "https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/assets/${{ needs.resolve_assets.outputs.operator_id }}" env: GH_PAT: ${{ secrets.GH_PAT }} - name: Set sandbox ID run: echo "SANDBOX_NAME=cozy-e2e-sandbox-$(echo "${GITHUB_REPOSITORY}:${GITHUB_WORKFLOW}:${GITHUB_REF}" | sha256sum | cut -c1-10)" >> $GITHUB_ENV - # ▸ Start actual job steps + # ▸ Prepare environment - name: Prepare workspace run: | rm -rf /tmp/$SANDBOX_NAME @@ -219,57 +240,7 @@ jobs: done echo "✅ The task completed successfully after $attempt attempts" - install_cozystack: - name: "Install Cozystack" - runs-on: [self-hosted] - permissions: - contents: read - packages: read - needs: ["prepare_env", "resolve_assets"] - if: ${{ always() && needs.prepare_env.result == 'success' }} - - steps: - - name: Prepare _out/assets directory - run: mkdir -p _out/assets - - # ▸ Regular PR path – download artefacts produced by the *build* job - - name: "Download CRDs (regular PR)" - if: "!contains(github.event.pull_request.labels.*.name, 'release')" - uses: actions/download-artifact@v4 - with: - name: cozystack-crds - path: _out/assets - - - name: "Download operator (regular PR)" - if: "!contains(github.event.pull_request.labels.*.name, 'release')" - uses: actions/download-artifact@v4 - with: - name: cozystack-operator - path: _out/assets - - # ▸ Release PR path – fetch artefacts from the corresponding draft release - - name: Download assets from draft release (release PR) - if: contains(github.event.pull_request.labels.*.name, 'release') - run: | - mkdir -p _out/assets - curl -sSL -H "Authorization: token ${GH_PAT}" -H "Accept: application/octet-stream" \ - -o _out/assets/cozystack-crds.yaml \ - "https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/assets/${{ needs.resolve_assets.outputs.crds_id }}" - curl -sSL -H "Authorization: token ${GH_PAT}" -H "Accept: application/octet-stream" \ - -o _out/assets/cozystack-operator.yaml \ - "https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/assets/${{ needs.resolve_assets.outputs.operator_id }}" - env: - GH_PAT: ${{ secrets.GH_PAT }} - - # ▸ Start actual job steps - - name: Set sandbox ID - run: echo "SANDBOX_NAME=cozy-e2e-sandbox-$(echo "${GITHUB_REPOSITORY}:${GITHUB_WORKFLOW}:${GITHUB_REF}" | sha256sum | cut -c1-10)" >> $GITHUB_ENV - - - name: Sync _out/assets directory - run: | - mkdir -p /tmp/$SANDBOX_NAME/_out/assets - mv _out/assets/* /tmp/$SANDBOX_NAME/_out/assets/ - + # ▸ Install Cozystack - name: Install Cozystack into sandbox run: | cd /tmp/$SANDBOX_NAME @@ -282,107 +253,77 @@ jobs: fi echo "❌ Attempt $attempt failed, retrying..." done - echo "✅ The task completed successfully after $attempt attempts." + echo "✅ The task completed successfully after $attempt attempts" - name: Run OpenAPI tests run: | cd /tmp/$SANDBOX_NAME make -C packages/core/testing SANDBOX_NAME=$SANDBOX_NAME test-openapi - detect_test_matrix: - name: "Detect e2e test matrix" - runs-on: ubuntu-latest - outputs: - matrix: ${{ steps.set.outputs.matrix }} - - steps: - - uses: actions/checkout@v4 - - id: set - run: | - apps=$(ls hack/e2e-apps/*.bats | cut -f3 -d/ | cut -f1 -d. | jq -R | jq -cs) - echo "matrix={\"app\":$apps}" >> "$GITHUB_OUTPUT" - - test_apps: - strategy: - fail-fast: false - matrix: ${{ fromJson(needs.detect_test_matrix.outputs.matrix) }} - name: Test ${{ matrix.app }} - runs-on: [self-hosted] - needs: [install_cozystack,detect_test_matrix] - if: ${{ always() && (needs.install_cozystack.result == 'success' && needs.detect_test_matrix.result == 'success') }} - - steps: - - name: Set sandbox ID - run: echo "SANDBOX_NAME=cozy-e2e-sandbox-$(echo "${GITHUB_REPOSITORY}:${GITHUB_WORKFLOW}:${GITHUB_REF}" | sha256sum | cut -c1-10)" >> $GITHUB_ENV - - - name: E2E Apps + # ▸ Run E2E tests + - name: Run E2E tests + id: e2e_tests run: | cd /tmp/$SANDBOX_NAME - attempt=0 - until make -C packages/core/testing SANDBOX_NAME=$SANDBOX_NAME test-apps-${{ matrix.app }}; do - attempt=$((attempt + 1)) - if [ $attempt -ge 3 ]; then - echo "❌ Attempt $attempt failed, exiting..." - exit 1 + failed_tests="" + for app in $(ls hack/e2e-apps/*.bats | xargs -n1 basename | cut -d. -f1); do + echo "::group::Testing $app" + attempt=0 + success=false + until [ $attempt -ge 3 ]; do + if make -C packages/core/testing SANDBOX_NAME=$SANDBOX_NAME test-apps-$app; then + success=true + break + fi + attempt=$((attempt + 1)) + echo "❌ Attempt $attempt failed, retrying..." + done + if [ "$success" = true ]; then + echo "✅ Test $app completed successfully" + else + echo "❌ Test $app failed after $attempt attempts" + failed_tests="$failed_tests $app" fi - echo "❌ Attempt $attempt failed, retrying..." + echo "::endgroup::" done - echo "✅ The task completed successfully after $attempt attempts" - - collect_debug_information: - name: Collect debug information - runs-on: [self-hosted] - needs: [test_apps] - if: ${{ always() }} - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set sandbox ID - run: echo "SANDBOX_NAME=cozy-e2e-sandbox-$(echo "${GITHUB_REPOSITORY}:${GITHUB_WORKFLOW}:${GITHUB_REF}" | sha256sum | cut -c1-10)" >> $GITHUB_ENV + if [ -n "$failed_tests" ]; then + echo "❌ Failed tests:$failed_tests" + exit 1 + fi + echo "✅ All E2E tests passed" + # ▸ Collect debug information (always runs) - name: Collect report + if: always() run: | cd /tmp/$SANDBOX_NAME - make -C packages/core/testing SANDBOX_NAME=$SANDBOX_NAME collect-report + make -C packages/core/testing SANDBOX_NAME=$SANDBOX_NAME collect-report || true - name: Upload cozyreport.tgz + if: always() uses: actions/upload-artifact@v4 with: name: cozyreport path: /tmp/${{ env.SANDBOX_NAME }}/_out/cozyreport.tgz - name: Collect images list + if: always() run: | cd /tmp/$SANDBOX_NAME - make -C packages/core/testing SANDBOX_NAME=$SANDBOX_NAME collect-images + make -C packages/core/testing SANDBOX_NAME=$SANDBOX_NAME collect-images || true - name: Upload image list + if: always() uses: actions/upload-artifact@v4 with: name: image-list path: /tmp/${{ env.SANDBOX_NAME }}/_out/images.txt - cleanup: - name: Tear down environment - runs-on: [self-hosted] - needs: [collect_debug_information] - if: ${{ always() && needs.test_apps.result == 'success' }} - - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - fetch-depth: 0 - fetch-tags: true - - - name: Set sandbox ID - run: echo "SANDBOX_NAME=cozy-e2e-sandbox-$(echo "${GITHUB_REPOSITORY}:${GITHUB_WORKFLOW}:${GITHUB_REF}" | sha256sum | cut -c1-10)" >> $GITHUB_ENV - + # ▸ Tear down environment (always runs) - name: Tear down sandbox - run: make -C packages/core/testing SANDBOX_NAME=$SANDBOX_NAME delete + if: always() + run: make -C packages/core/testing SANDBOX_NAME=$SANDBOX_NAME delete || true - name: Remove workspace + if: always() run: rm -rf /tmp/$SANDBOX_NAME - - diff --git a/hack/e2e-prepare-cluster.bats b/hack/e2e-prepare-cluster.bats index 650ca643..df90b91a 100644 --- a/hack/e2e-prepare-cluster.bats +++ b/hack/e2e-prepare-cluster.bats @@ -136,25 +136,28 @@ machine: mirrors: docker.io: endpoints: - - https://dockerio.nexus.aenix.org - cr.fluentbit.io: - endpoints: - - https://fluentbit.nexus.aenix.org - docker-registry3.mariadb.com: - endpoints: - - https://mariadb.nexus.aenix.org - gcr.io: - endpoints: - - https://gcr.nexus.aenix.org - ghcr.io: - endpoints: - - https://ghcr.nexus.aenix.org - quay.io: - endpoints: - - https://quay.nexus.aenix.org - registry.k8s.io: - endpoints: - - https://k8s.nexus.aenix.org + - https://mirror.gcr.io + #docker.io: + # endpoints: + # - https://dockerio.nexus.aenix.org + #cr.fluentbit.io: + # endpoints: + # - https://fluentbit.nexus.aenix.org + #docker-registry3.mariadb.com: + # endpoints: + # - https://mariadb.nexus.aenix.org + #gcr.io: + # endpoints: + # - https://gcr.nexus.aenix.org + #ghcr.io: + # endpoints: + # - https://ghcr.nexus.aenix.org + #quay.io: + # endpoints: + # - https://quay.nexus.aenix.org + #registry.k8s.io: + # endpoints: + # - https://k8s.nexus.aenix.org files: - content: | [plugins] @@ -236,7 +239,10 @@ EOF timeout 10 sh -ec 'until talosctl bootstrap -n 192.168.123.11 -e 192.168.123.11; do sleep 1; done' # Wait until etcd is healthy - timeout 180 sh -ec 'until talosctl etcd members -n 192.168.123.11,192.168.123.12,192.168.123.13 -e 192.168.123.10 >/dev/null 2>&1; do sleep 1; done' + if ! timeout 180 sh -ec 'until talosctl etcd members -n 192.168.123.11,192.168.123.12,192.168.123.13 -e 192.168.123.10 >/dev/null 2>&1; do sleep 1; done'; then + talosctl dmesg -n 192.168.123.11,192.168.123.12,192.168.123.13 -e 192.168.123.10 || true + exit 1 + fi timeout 60 sh -ec 'while talosctl etcd members -n 192.168.123.11,192.168.123.12,192.168.123.13 -e 192.168.123.10 2>&1 | grep -q "rpc error"; do sleep 1; done' # Retrieve kubeconfig From 0b95a72fa332164038bb2ad2afaa9a87b2494a3c Mon Sep 17 00:00:00 2001 From: Kirill Ilin Date: Wed, 21 Jan 2026 16:49:13 +0500 Subject: [PATCH 053/125] [kubernetes] Add enum validation for IngressNginx exposeMethod Signed-off-by: Kirill Ilin --- packages/apps/kubernetes/values.schema.json | 6 +++++- packages/apps/kubernetes/values.yaml | 6 +++++- packages/system/kubernetes-rd/cozyrds/kubernetes.yaml | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/apps/kubernetes/values.schema.json b/packages/apps/kubernetes/values.schema.json index 69b5145d..1f6c9361 100644 --- a/packages/apps/kubernetes/values.schema.json +++ b/packages/apps/kubernetes/values.schema.json @@ -150,7 +150,11 @@ "exposeMethod": { "description": "Method to expose the controller. Allowed values: `Proxied`, `LoadBalancer`.", "type": "string", - "default": "Proxied" + "default": "Proxied", + "enum": [ + "Proxied", + "LoadBalancer" + ] }, "hosts": { "description": "Domains routed to this tenant cluster when `exposeMethod` is `Proxied`.", diff --git a/packages/apps/kubernetes/values.yaml b/packages/apps/kubernetes/values.yaml index ba7d3f6d..7f15752f 100644 --- a/packages/apps/kubernetes/values.yaml +++ b/packages/apps/kubernetes/values.yaml @@ -76,9 +76,13 @@ host: "" ## @typedef {struct} GatewayAPIAddon - Gateway API addon. ## @field {bool} enabled - Enable Gateway API. +## @enum {string} IngressNginxExposeMethod - Method to expose the controller +## @value Proxied +## @value LoadBalancer + ## @typedef {struct} IngressNginxAddon - Ingress-NGINX controller. ## @field {bool} enabled - Enable the controller (requires nodes labeled `ingress-nginx`). -## @field {string} exposeMethod - Method to expose the controller. Allowed values: `Proxied`, `LoadBalancer`. +## @field {IngressNginxExposeMethod} exposeMethod - Method to expose the controller. Allowed values: `Proxied`, `LoadBalancer`. ## @field {[]string} hosts - Domains routed to this tenant cluster when `exposeMethod` is `Proxied`. ## @field {object} valuesOverride - Custom Helm values overrides. diff --git a/packages/system/kubernetes-rd/cozyrds/kubernetes.yaml b/packages/system/kubernetes-rd/cozyrds/kubernetes.yaml index 6c84f5e4..289ef25e 100644 --- a/packages/system/kubernetes-rd/cozyrds/kubernetes.yaml +++ b/packages/system/kubernetes-rd/cozyrds/kubernetes.yaml @@ -8,7 +8,7 @@ spec: singular: kubernetes plural: kuberneteses openAPISchema: |- - {"title":"Chart Values","type":"object","properties":{"addons":{"description":"Cluster addons configuration.","type":"object","default":{},"required":["certManager","cilium","coredns","fluxcd","gatewayAPI","gpuOperator","ingressNginx","monitoringAgents","velero","verticalPodAutoscaler"],"properties":{"certManager":{"description":"Cert-manager addon.","type":"object","default":{},"required":["enabled","valuesOverride"],"properties":{"enabled":{"description":"Enable cert-manager.","type":"boolean","default":false},"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"cilium":{"description":"Cilium CNI plugin.","type":"object","default":{},"required":["valuesOverride"],"properties":{"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"coredns":{"description":"CoreDNS addon.","type":"object","default":{},"required":["valuesOverride"],"properties":{"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"fluxcd":{"description":"FluxCD GitOps operator.","type":"object","default":{},"required":["enabled","valuesOverride"],"properties":{"enabled":{"description":"Enable FluxCD.","type":"boolean","default":false},"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"gatewayAPI":{"description":"Gateway API addon.","type":"object","default":{},"required":["enabled"],"properties":{"enabled":{"description":"Enable Gateway API.","type":"boolean","default":false}}},"gpuOperator":{"description":"NVIDIA GPU Operator.","type":"object","default":{},"required":["enabled","valuesOverride"],"properties":{"enabled":{"description":"Enable GPU Operator.","type":"boolean","default":false},"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"ingressNginx":{"description":"Ingress-NGINX controller.","type":"object","default":{},"required":["enabled","exposeMethod","valuesOverride"],"properties":{"enabled":{"description":"Enable the controller (requires nodes labeled `ingress-nginx`).","type":"boolean","default":false},"exposeMethod":{"description":"Method to expose the controller. Allowed values: `Proxied`, `LoadBalancer`.","type":"string","default":"Proxied"},"hosts":{"description":"Domains routed to this tenant cluster when `exposeMethod` is `Proxied`.","type":"array","default":[],"items":{"type":"string"}},"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"monitoringAgents":{"description":"Monitoring agents.","type":"object","default":{},"required":["enabled","valuesOverride"],"properties":{"enabled":{"description":"Enable monitoring agents.","type":"boolean","default":false},"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"velero":{"description":"Velero backup/restore addon.","type":"object","default":{},"required":["enabled","valuesOverride"],"properties":{"enabled":{"description":"Enable Velero.","type":"boolean","default":false},"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"verticalPodAutoscaler":{"description":"Vertical Pod Autoscaler.","type":"object","default":{},"required":["valuesOverride"],"properties":{"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}}}},"controlPlane":{"description":"Kubernetes control-plane configuration.","type":"object","default":{},"required":["apiServer","controllerManager","konnectivity","replicas","scheduler"],"properties":{"apiServer":{"description":"API Server configuration.","type":"object","default":{},"required":["resources","resourcesPreset"],"properties":{"resources":{"description":"CPU and memory resources for API Server.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available.","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":"Memory (RAM) available.","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}}},"resourcesPreset":{"description":"Preset if `resources` omitted.","type":"string","default":"large","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]}}},"controllerManager":{"description":"Controller Manager configuration.","type":"object","default":{},"required":["resources","resourcesPreset"],"properties":{"resources":{"description":"CPU and memory resources for Controller Manager.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available.","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":"Memory (RAM) available.","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}}},"resourcesPreset":{"description":"Preset if `resources` omitted.","type":"string","default":"micro","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]}}},"konnectivity":{"description":"Konnectivity configuration.","type":"object","default":{},"required":["server"],"properties":{"server":{"description":"Konnectivity Server configuration.","type":"object","default":{},"required":["resources","resourcesPreset"],"properties":{"resources":{"description":"CPU and memory resources for Konnectivity.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available.","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":"Memory (RAM) available.","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}}},"resourcesPreset":{"description":"Preset if `resources` omitted.","type":"string","default":"micro","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]}}}}},"replicas":{"description":"Number of control-plane replicas.","type":"integer","default":2},"scheduler":{"description":"Scheduler configuration.","type":"object","default":{},"required":["resources","resourcesPreset"],"properties":{"resources":{"description":"CPU and memory resources for Scheduler.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available.","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":"Memory (RAM) available.","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}}},"resourcesPreset":{"description":"Preset if `resources` omitted.","type":"string","default":"micro","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]}}}}},"host":{"description":"External hostname for Kubernetes cluster. Defaults to `.` if empty.","type":"string","default":""},"nodeGroups":{"description":"Worker nodes configuration map.","type":"object","default":{"md0":{"ephemeralStorage":"20Gi","gpus":[],"instanceType":"u1.medium","maxReplicas":10,"minReplicas":0,"resources":{},"roles":["ingress-nginx"]}},"additionalProperties":{"type":"object","required":["ephemeralStorage","instanceType","maxReplicas","minReplicas","resources"],"properties":{"ephemeralStorage":{"description":"Ephemeral storage size.","default":"20Gi","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},"gpus":{"description":"List of GPUs to attach (NVIDIA driver requires at least 4 GiB RAM).","type":"array","items":{"type":"object","required":["name"],"properties":{"name":{"description":"Name of GPU, such as \"nvidia.com/AD102GL_L40S\".","type":"string"}}}},"instanceType":{"description":"Virtual machine instance type.","type":"string","default":"u1.medium"},"maxReplicas":{"description":"Maximum number of replicas.","type":"integer","default":10},"minReplicas":{"description":"Minimum number of replicas.","type":"integer","default":0},"resources":{"description":"CPU and memory resources for each worker node.","type":"object","properties":{"cpu":{"description":"CPU available.","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":"Memory (RAM) available.","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}}},"roles":{"description":"List of node roles.","type":"array","items":{"type":"string"}}}}},"storageClass":{"description":"StorageClass used to store the data.","type":"string","default":"replicated"},"version":{"description":"Kubernetes major.minor version to deploy","type":"string","default":"v1.33","enum":["v1.33","v1.32","v1.31","v1.30","v1.29","v1.28"]}}} + {"title":"Chart Values","type":"object","properties":{"addons":{"description":"Cluster addons configuration.","type":"object","default":{},"required":["certManager","cilium","coredns","fluxcd","gatewayAPI","gpuOperator","ingressNginx","monitoringAgents","velero","verticalPodAutoscaler"],"properties":{"certManager":{"description":"Cert-manager addon.","type":"object","default":{},"required":["enabled","valuesOverride"],"properties":{"enabled":{"description":"Enable cert-manager.","type":"boolean","default":false},"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"cilium":{"description":"Cilium CNI plugin.","type":"object","default":{},"required":["valuesOverride"],"properties":{"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"coredns":{"description":"CoreDNS addon.","type":"object","default":{},"required":["valuesOverride"],"properties":{"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"fluxcd":{"description":"FluxCD GitOps operator.","type":"object","default":{},"required":["enabled","valuesOverride"],"properties":{"enabled":{"description":"Enable FluxCD.","type":"boolean","default":false},"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"gatewayAPI":{"description":"Gateway API addon.","type":"object","default":{},"required":["enabled"],"properties":{"enabled":{"description":"Enable Gateway API.","type":"boolean","default":false}}},"gpuOperator":{"description":"NVIDIA GPU Operator.","type":"object","default":{},"required":["enabled","valuesOverride"],"properties":{"enabled":{"description":"Enable GPU Operator.","type":"boolean","default":false},"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"ingressNginx":{"description":"Ingress-NGINX controller.","type":"object","default":{},"required":["enabled","exposeMethod","valuesOverride"],"properties":{"enabled":{"description":"Enable the controller (requires nodes labeled `ingress-nginx`).","type":"boolean","default":false},"exposeMethod":{"description":"Method to expose the controller. Allowed values: `Proxied`, `LoadBalancer`.","type":"string","default":"Proxied","enum":["Proxied","LoadBalancer"]},"hosts":{"description":"Domains routed to this tenant cluster when `exposeMethod` is `Proxied`.","type":"array","default":[],"items":{"type":"string"}},"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"monitoringAgents":{"description":"Monitoring agents.","type":"object","default":{},"required":["enabled","valuesOverride"],"properties":{"enabled":{"description":"Enable monitoring agents.","type":"boolean","default":false},"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"velero":{"description":"Velero backup/restore addon.","type":"object","default":{},"required":["enabled","valuesOverride"],"properties":{"enabled":{"description":"Enable Velero.","type":"boolean","default":false},"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}},"verticalPodAutoscaler":{"description":"Vertical Pod Autoscaler.","type":"object","default":{},"required":["valuesOverride"],"properties":{"valuesOverride":{"description":"Custom Helm values overrides.","type":"object","default":{},"x-kubernetes-preserve-unknown-fields":true}}}}},"controlPlane":{"description":"Kubernetes control-plane configuration.","type":"object","default":{},"required":["apiServer","controllerManager","konnectivity","replicas","scheduler"],"properties":{"apiServer":{"description":"API Server configuration.","type":"object","default":{},"required":["resources","resourcesPreset"],"properties":{"resources":{"description":"CPU and memory resources for API Server.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available.","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":"Memory (RAM) available.","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}}},"resourcesPreset":{"description":"Preset if `resources` omitted.","type":"string","default":"large","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]}}},"controllerManager":{"description":"Controller Manager configuration.","type":"object","default":{},"required":["resources","resourcesPreset"],"properties":{"resources":{"description":"CPU and memory resources for Controller Manager.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available.","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":"Memory (RAM) available.","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}}},"resourcesPreset":{"description":"Preset if `resources` omitted.","type":"string","default":"micro","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]}}},"konnectivity":{"description":"Konnectivity configuration.","type":"object","default":{},"required":["server"],"properties":{"server":{"description":"Konnectivity Server configuration.","type":"object","default":{},"required":["resources","resourcesPreset"],"properties":{"resources":{"description":"CPU and memory resources for Konnectivity.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available.","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":"Memory (RAM) available.","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}}},"resourcesPreset":{"description":"Preset if `resources` omitted.","type":"string","default":"micro","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]}}}}},"replicas":{"description":"Number of control-plane replicas.","type":"integer","default":2},"scheduler":{"description":"Scheduler configuration.","type":"object","default":{},"required":["resources","resourcesPreset"],"properties":{"resources":{"description":"CPU and memory resources for Scheduler.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available.","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":"Memory (RAM) available.","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}}},"resourcesPreset":{"description":"Preset if `resources` omitted.","type":"string","default":"micro","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]}}}}},"host":{"description":"External hostname for Kubernetes cluster. Defaults to `.` if empty.","type":"string","default":""},"nodeGroups":{"description":"Worker nodes configuration map.","type":"object","default":{"md0":{"ephemeralStorage":"20Gi","gpus":[],"instanceType":"u1.medium","maxReplicas":10,"minReplicas":0,"resources":{},"roles":["ingress-nginx"]}},"additionalProperties":{"type":"object","required":["ephemeralStorage","instanceType","maxReplicas","minReplicas","resources"],"properties":{"ephemeralStorage":{"description":"Ephemeral storage size.","default":"20Gi","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},"gpus":{"description":"List of GPUs to attach (NVIDIA driver requires at least 4 GiB RAM).","type":"array","items":{"type":"object","required":["name"],"properties":{"name":{"description":"Name of GPU, such as \"nvidia.com/AD102GL_L40S\".","type":"string"}}}},"instanceType":{"description":"Virtual machine instance type.","type":"string","default":"u1.medium"},"maxReplicas":{"description":"Maximum number of replicas.","type":"integer","default":10},"minReplicas":{"description":"Minimum number of replicas.","type":"integer","default":0},"resources":{"description":"CPU and memory resources for each worker node.","type":"object","properties":{"cpu":{"description":"CPU available.","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":"Memory (RAM) available.","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}}},"roles":{"description":"List of node roles.","type":"array","items":{"type":"string"}}}}},"storageClass":{"description":"StorageClass used to store the data.","type":"string","default":"replicated"},"version":{"description":"Kubernetes major.minor version to deploy","type":"string","default":"v1.33","enum":["v1.33","v1.32","v1.31","v1.30","v1.29","v1.28"]}}} release: prefix: kubernetes- labels: From 4eb3c36301034cf6ee95547b478ac9a3f11aa257 Mon Sep 17 00:00:00 2001 From: Kirill Ilin Date: Wed, 21 Jan 2026 17:07:08 +0500 Subject: [PATCH 054/125] [tenant] remove `isolated` flag and enforce isolation Signed-off-by: Kirill Ilin --- packages/apps/tenant/README.md | 1 - packages/apps/tenant/templates/networkpolicy.yaml | 2 -- packages/apps/tenant/values.schema.json | 5 ----- packages/apps/tenant/values.yaml | 3 --- packages/system/tenant-rd/cozyrds/tenant.yaml | 4 ++-- 5 files changed, 2 insertions(+), 13 deletions(-) diff --git a/packages/apps/tenant/README.md b/packages/apps/tenant/README.md index 222c98a5..30fa9429 100644 --- a/packages/apps/tenant/README.md +++ b/packages/apps/tenant/README.md @@ -76,6 +76,5 @@ tenant-u1 | `monitoring` | Deploy own Monitoring Stack. | `bool` | `false` | | `ingress` | Deploy own Ingress Controller. | `bool` | `false` | | `seaweedfs` | Deploy own SeaweedFS. | `bool` | `false` | -| `isolated` | Enforce tenant namespace with network policies (default: true). | `bool` | `true` | | `resourceQuotas` | Define resource quotas for the tenant. | `map[string]quantity` | `{}` | diff --git a/packages/apps/tenant/templates/networkpolicy.yaml b/packages/apps/tenant/templates/networkpolicy.yaml index 2c15a877..d9f87856 100644 --- a/packages/apps/tenant/templates/networkpolicy.yaml +++ b/packages/apps/tenant/templates/networkpolicy.yaml @@ -1,4 +1,3 @@ -{{- if .Values.isolated }} --- apiVersion: cilium.io/v2 kind: CiliumNetworkPolicy @@ -220,4 +219,3 @@ spec: - toEndpoints: - matchLabels: cozystack.io/service: ingress -{{- end }} diff --git a/packages/apps/tenant/values.schema.json b/packages/apps/tenant/values.schema.json index 7690e2b4..2c3d5f89 100644 --- a/packages/apps/tenant/values.schema.json +++ b/packages/apps/tenant/values.schema.json @@ -17,11 +17,6 @@ "type": "boolean", "default": false }, - "isolated": { - "description": "Enforce tenant namespace with network policies (default: true).", - "type": "boolean", - "default": true - }, "monitoring": { "description": "Deploy own Monitoring Stack.", "type": "boolean", diff --git a/packages/apps/tenant/values.yaml b/packages/apps/tenant/values.yaml index 4543bb6b..58bb451f 100644 --- a/packages/apps/tenant/values.yaml +++ b/packages/apps/tenant/values.yaml @@ -17,8 +17,5 @@ ingress: false ## @param {bool} seaweedfs - Deploy own SeaweedFS. seaweedfs: false -## @param {bool} isolated - Enforce tenant namespace with network policies (default: true). -isolated: true - ## @param {map[string]quantity} resourceQuotas - Define resource quotas for the tenant. resourceQuotas: {} diff --git a/packages/system/tenant-rd/cozyrds/tenant.yaml b/packages/system/tenant-rd/cozyrds/tenant.yaml index 398e9204..9b09692c 100644 --- a/packages/system/tenant-rd/cozyrds/tenant.yaml +++ b/packages/system/tenant-rd/cozyrds/tenant.yaml @@ -8,7 +8,7 @@ spec: singular: tenant plural: tenants openAPISchema: |- - {"title":"Chart Values","type":"object","properties":{"etcd":{"description":"Deploy own Etcd cluster.","type":"boolean","default":false},"host":{"description":"The hostname used to access tenant services (defaults to using the tenant name as a subdomain for its parent tenant host).","type":"string","default":""},"ingress":{"description":"Deploy own Ingress Controller.","type":"boolean","default":false},"isolated":{"description":"Enforce tenant namespace with network policies (default: true).","type":"boolean","default":true},"monitoring":{"description":"Deploy own Monitoring Stack.","type":"boolean","default":false},"resourceQuotas":{"description":"Define resource quotas for the tenant.","type":"object","default":{},"additionalProperties":{"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}},"seaweedfs":{"description":"Deploy own SeaweedFS.","type":"boolean","default":false}}} + {"title":"Chart Values","type":"object","properties":{"etcd":{"description":"Deploy own Etcd cluster.","type":"boolean","default":false},"host":{"description":"The hostname used to access tenant services (defaults to using the tenant name as a subdomain for its parent tenant host).","type":"string","default":""},"ingress":{"description":"Deploy own Ingress Controller.","type":"boolean","default":false},"monitoring":{"description":"Deploy own Monitoring Stack.","type":"boolean","default":false},"resourceQuotas":{"description":"Define resource quotas for the tenant.","type":"object","default":{},"additionalProperties":{"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}},"seaweedfs":{"description":"Deploy own SeaweedFS.","type":"boolean","default":false}}} release: prefix: tenant- labels: @@ -23,7 +23,7 @@ spec: plural: Tenants description: Separated tenant namespace icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxNDQiIGhlaWdodD0iMTQ0IiByeD0iMjQiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl82ODdfMzQwMykiLz4KPGcgY2xpcC1wYXRoPSJ1cmwoI2NsaXAwXzY4N18zNDAzKSI+CjxwYXRoIGQ9Ik03MiAyOUM2Ni4zOTI2IDI5IDYxLjAxNDggMzEuMjM4OCA1Ny4wNDk3IDM1LjIyNEM1My4wODQ3IDM5LjIwOTEgNTAuODU3MSA0NC42MTQxIDUwLjg1NzEgNTAuMjVDNTAuODU3MSA1NS44ODU5IDUzLjA4NDcgNjEuMjkwOSA1Ny4wNDk3IDY1LjI3NkM2MS4wMTQ4IDY5LjI2MTIgNjYuMzkyNiA3MS41IDcyIDcxLjVDNzcuNjA3NCA3MS41IDgyLjk4NTIgNjkuMjYxMiA4Ni45NTAzIDY1LjI3NkM5MC45MTUzIDYxLjI5MDkgOTMuMTQyOSA1NS44ODU5IDkzLjE0MjkgNTAuMjVDOTMuMTQyOSA0NC42MTQxIDkwLjkxNTMgMzkuMjA5MSA4Ni45NTAzIDM1LjIyNEM4Mi45ODUyIDMxLjIzODggNzcuNjA3NCAyOSA3MiAyOVpNNjAuOTgyNiA4My4zMDM3QzYwLjQ1NCA4Mi41ODk4IDU5LjU5NTEgODIuMTkxNCA1OC43MTk2IDgyLjI3NDRDNDUuMzg5NyA4My43MzU0IDM1IDk1LjEwNzQgMzUgMTA4LjkwM0MzNSAxMTEuNzI2IDM3LjI3OTUgMTE0IDQwLjA3MSAxMTRIMTAzLjkyOUMxMDYuNzM3IDExNCAxMDkgMTExLjcwOSAxMDkgMTA4LjkwM0MxMDkgOTUuMTA3NCA5OC42MTAzIDgzLjc1MiA4NS4yNjM4IDgyLjI5MUM4NC4zODg0IDgyLjE5MTQgODMuNTI5NSA4Mi42MDY0IDgzLjAwMDkgODMuMzIwM0w3NC4wOTc4IDk1LjI0MDJDNzMuMDQwNiA5Ni42NTE0IDcwLjkyNjMgOTYuNjUxNCA2OS44NjkyIDk1LjI0MDJMNjAuOTY2MSA4My4zMjAzTDYwLjk4MjYgODMuMzAzN1oiIGZpbGw9ImJsYWNrIi8+CjwvZz4KPGRlZnM+CjxsaW5lYXJHcmFkaWVudCBpZD0icGFpbnQwX2xpbmVhcl82ODdfMzQwMyIgeDE9IjcyIiB5MT0iMTQ0IiB4Mj0iLTEuMjgxN2UtMDUiIHkyPSI0IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIHN0b3AtY29sb3I9IiNDMEQ2RkYiLz4KPHN0b3Agb2Zmc2V0PSIwLjMiIHN0b3AtY29sb3I9IiNDNERBRkYiLz4KPHN0b3Agb2Zmc2V0PSIwLjY1IiBzdG9wLWNvbG9yPSIjRDNFOUZGIi8+CjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iI0U5RkZGRiIvPgo8L2xpbmVhckdyYWRpZW50Pgo8Y2xpcFBhdGggaWQ9ImNsaXAwXzY4N18zNDAzIj4KPHJlY3Qgd2lkdGg9Ijc0IiBoZWlnaHQ9Ijg1IiBmaWxsPSJ3aGl0ZSIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMzUgMjkpIi8+CjwvY2xpcFBhdGg+CjwvZGVmcz4KPC9zdmc+Cg== - keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "host"], ["spec", "etcd"], ["spec", "monitoring"], ["spec", "ingress"], ["spec", "seaweedfs"], ["spec", "isolated"], ["spec", "resourceQuotas"]] + keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "host"], ["spec", "etcd"], ["spec", "monitoring"], ["spec", "ingress"], ["spec", "seaweedfs"], ["spec", "resourceQuotas"]] secrets: exclude: [] include: [] From 9e94a699a0345db6a7dd5e31c5866f47ff359164 Mon Sep 17 00:00:00 2001 From: Andrey Kolkov Date: Sun, 18 Jan 2026 13:26:37 +0400 Subject: [PATCH 055/125] feat(backups): implement interface changes related backups class introduction Signed-off-by: Andrey Kolkov --- api/backups/v1alpha1/backup_types.go | 4 - api/backups/v1alpha1/backupclass_types.go | 85 +++++ api/backups/v1alpha1/backupjob_types.go | 12 +- api/backups/v1alpha1/plan_types.go | 12 +- api/backups/v1alpha1/zz_generated.deepcopy.go | 152 +++++++- .../backupcontroller/backupclass_resolver.go | 84 ++++ .../backupclass_resolver_test.go | 358 ++++++++++++++++++ .../backupcontroller/backupjob_controller.go | 35 +- .../backupcontroller/factory/backupjob.go | 23 +- .../factory/backupjob_test.go | 167 ++++++++ .../jobstrategy_controller.go | 3 +- internal/backupcontroller/plan_controller.go | 12 +- .../velerostrategy_controller.go | 328 ++-------------- 13 files changed, 929 insertions(+), 346 deletions(-) create mode 100644 api/backups/v1alpha1/backupclass_types.go create mode 100644 internal/backupcontroller/backupclass_resolver.go create mode 100644 internal/backupcontroller/backupclass_resolver_test.go create mode 100644 internal/backupcontroller/factory/backupjob_test.go diff --git a/api/backups/v1alpha1/backup_types.go b/api/backups/v1alpha1/backup_types.go index 2f18b34f..9371c27f 100644 --- a/api/backups/v1alpha1/backup_types.go +++ b/api/backups/v1alpha1/backup_types.go @@ -57,10 +57,6 @@ type BackupSpec struct { // +optional PlanRef *corev1.LocalObjectReference `json:"planRef,omitempty"` - // StorageRef refers to the Storage object that describes where the backup - // artifact is stored. - StorageRef corev1.TypedLocalObjectReference `json:"storageRef"` - // StrategyRef refers to the driver-specific BackupStrategy that was used // to create this backup. This allows the driver to later perform restores. StrategyRef corev1.TypedLocalObjectReference `json:"strategyRef"` diff --git a/api/backups/v1alpha1/backupclass_types.go b/api/backups/v1alpha1/backupclass_types.go new file mode 100644 index 00000000..19bcfd52 --- /dev/null +++ b/api/backups/v1alpha1/backupclass_types.go @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: Apache-2.0 +// Package v1alpha1 defines backups.cozystack.io API types. +// +// Group: backups.cozystack.io +// Version: v1alpha1 +package v1alpha1 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +func init() { + SchemeBuilder.Register(func(s *runtime.Scheme) error { + s.AddKnownTypes(GroupVersion, + &BackupClass{}, + &BackupClassList{}, + ) + return nil + }) +} + +// +kubebuilder:object:root=true +// +kubebuilder:resource:scope=Cluster +// +kubebuilder:subresource:status + +// BackupClass defines a class of backup configurations that can be referenced +// by BackupJob and Plan resources. It encapsulates strategy and storage configuration +// per application type. +type BackupClass struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec BackupClassSpec `json:"spec,omitempty"` + Status BackupClassStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// BackupClassList contains a list of BackupClasses. +type BackupClassList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []BackupClass `json:"items"` +} + +// BackupClassSpec defines the desired state of a BackupClass. +type BackupClassSpec struct { + // Strategies is a list of backup strategies, each matching a specific application type. + Strategies []BackupClassStrategy `json:"strategies"` +} + +// BackupClassStrategy defines a backup strategy for a specific application type. +type BackupClassStrategy struct { + // StrategyRef references the driver-specific BackupStrategy (e.g., Velero). + StrategyRef corev1.TypedLocalObjectReference `json:"strategyRef"` + + // Application specifies which application types this strategy applies to. + Application ApplicationSelector `json:"application"` + + // Parameters holds strategy-specific and storage-specific parameters. + // Common parameters include: + // - backupStorageLocationName: Name of Velero BackupStorageLocation + // +optional + Parameters map[string]string `json:"parameters,omitempty"` +} + +// ApplicationSelector specifies which application types a strategy applies to. +type ApplicationSelector struct { + // APIGroup is the API group of the application. + // If not specified, defaults to "apps.cozystack.io". + // +optional + APIGroup *string `json:"apiGroup,omitempty"` + + // Kind is the kind of the application (e.g., VirtualMachine, MySQL). + Kind string `json:"kind"` +} + +// BackupClassStatus defines the observed state of a BackupClass. +type BackupClassStatus struct { + // Conditions represents the latest available observations of a BackupClass's state. + // +optional + Conditions []metav1.Condition `json:"conditions,omitempty"` +} diff --git a/api/backups/v1alpha1/backupjob_types.go b/api/backups/v1alpha1/backupjob_types.go index c27b5347..f9aa42e6 100644 --- a/api/backups/v1alpha1/backupjob_types.go +++ b/api/backups/v1alpha1/backupjob_types.go @@ -46,15 +46,13 @@ type BackupJobSpec struct { // ApplicationRef holds a reference to the managed application whose state // is being backed up. + // If apiGroup is not specified, it defaults to "apps.cozystack.io". ApplicationRef corev1.TypedLocalObjectReference `json:"applicationRef"` - // StorageRef holds a reference to the Storage object that describes where - // the backup will be stored. - StorageRef corev1.TypedLocalObjectReference `json:"storageRef"` - - // StrategyRef holds a reference to the driver-specific BackupStrategy object - // that describes how the backup should be created. - StrategyRef corev1.TypedLocalObjectReference `json:"strategyRef"` + // BackupClassName references a BackupClass that contains strategy and storage configuration. + // The BackupClass will be resolved to determine the appropriate strategy and storage + // based on the ApplicationRef. + BackupClassName string `json:"backupClassName"` } // BackupJobStatus represents the observed state of a BackupJob. diff --git a/api/backups/v1alpha1/plan_types.go b/api/backups/v1alpha1/plan_types.go index c59d5eab..df807eb5 100644 --- a/api/backups/v1alpha1/plan_types.go +++ b/api/backups/v1alpha1/plan_types.go @@ -65,15 +65,13 @@ type PlanList struct { type PlanSpec struct { // ApplicationRef holds a reference to the managed application, // whose state and configuration must be backed up. + // If apiGroup is not specified, it defaults to "apps.cozystack.io". ApplicationRef corev1.TypedLocalObjectReference `json:"applicationRef"` - // StorageRef holds a reference to the Storage object that - // describes the location where the backup will be stored. - StorageRef corev1.TypedLocalObjectReference `json:"storageRef"` - - // StrategyRef holds a reference to the Strategy object that - // describes, how a backup copy is to be created. - StrategyRef corev1.TypedLocalObjectReference `json:"strategyRef"` + // BackupClassName references a BackupClass that contains strategy and storage configuration. + // The BackupClass will be resolved to determine the appropriate strategy and storage + // based on the ApplicationRef. + BackupClassName string `json:"backupClassName"` // Schedule specifies when backup copies are created. Schedule PlanSchedule `json:"schedule"` diff --git a/api/backups/v1alpha1/zz_generated.deepcopy.go b/api/backups/v1alpha1/zz_generated.deepcopy.go index fe825947..6fbc1d16 100644 --- a/api/backups/v1alpha1/zz_generated.deepcopy.go +++ b/api/backups/v1alpha1/zz_generated.deepcopy.go @@ -136,8 +136,6 @@ func (in *BackupJobSpec) DeepCopyInto(out *BackupJobSpec) { **out = **in } in.ApplicationRef.DeepCopyInto(&out.ApplicationRef) - in.StorageRef.DeepCopyInto(&out.StorageRef) - in.StrategyRef.DeepCopyInto(&out.StrategyRef) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupJobSpec. @@ -226,7 +224,6 @@ func (in *BackupSpec) DeepCopyInto(out *BackupSpec) { *out = new(v1.LocalObjectReference) **out = **in } - in.StorageRef.DeepCopyInto(&out.StorageRef) in.StrategyRef.DeepCopyInto(&out.StrategyRef) in.TakenAt.DeepCopyInto(&out.TakenAt) if in.DriverMetadata != nil { @@ -353,8 +350,6 @@ func (in *PlanSchedule) DeepCopy() *PlanSchedule { func (in *PlanSpec) DeepCopyInto(out *PlanSpec) { *out = *in in.ApplicationRef.DeepCopyInto(&out.ApplicationRef) - in.StorageRef.DeepCopyInto(&out.StorageRef) - in.StrategyRef.DeepCopyInto(&out.StrategyRef) out.Schedule = in.Schedule } @@ -499,3 +494,150 @@ func (in *RestoreJobStatus) DeepCopy() *RestoreJobStatus { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackupClass) DeepCopyInto(out *BackupClass) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupClass. +func (in *BackupClass) DeepCopy() *BackupClass { + if in == nil { + return nil + } + out := new(BackupClass) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *BackupClass) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackupClassList) DeepCopyInto(out *BackupClassList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]BackupClass, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupClassList. +func (in *BackupClassList) DeepCopy() *BackupClassList { + if in == nil { + return nil + } + out := new(BackupClassList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *BackupClassList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackupClassSpec) DeepCopyInto(out *BackupClassSpec) { + *out = *in + if in.Strategies != nil { + in, out := &in.Strategies, &out.Strategies + *out = make([]BackupClassStrategy, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupClassSpec. +func (in *BackupClassSpec) DeepCopy() *BackupClassSpec { + if in == nil { + return nil + } + out := new(BackupClassSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackupClassStrategy) DeepCopyInto(out *BackupClassStrategy) { + *out = *in + in.StrategyRef.DeepCopyInto(&out.StrategyRef) + in.Application.DeepCopyInto(&out.Application) + if in.Parameters != nil { + in, out := &in.Parameters, &out.Parameters + *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 BackupClassStrategy. +func (in *BackupClassStrategy) DeepCopy() *BackupClassStrategy { + if in == nil { + return nil + } + out := new(BackupClassStrategy) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ApplicationSelector) DeepCopyInto(out *ApplicationSelector) { + *out = *in + if in.APIGroup != nil { + in, out := &in.APIGroup, &out.APIGroup + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationSelector. +func (in *ApplicationSelector) DeepCopy() *ApplicationSelector { + if in == nil { + return nil + } + out := new(ApplicationSelector) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackupClassStatus) DeepCopyInto(out *BackupClassStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]metav1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupClassStatus. +func (in *BackupClassStatus) DeepCopy() *BackupClassStatus { + if in == nil { + return nil + } + out := new(BackupClassStatus) + in.DeepCopyInto(out) + return out +} diff --git a/internal/backupcontroller/backupclass_resolver.go b/internal/backupcontroller/backupclass_resolver.go new file mode 100644 index 00000000..c07e778c --- /dev/null +++ b/internal/backupcontroller/backupclass_resolver.go @@ -0,0 +1,84 @@ +package backupcontroller + +import ( + "context" + "fmt" + + corev1 "k8s.io/api/core/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + + backupsv1alpha1 "github.com/cozystack/cozystack/api/backups/v1alpha1" +) + +const ( + // DefaultApplicationAPIGroup is the default API group for applications + // when not specified in ApplicationRef or ApplicationSelector. + DefaultApplicationAPIGroup = "apps.cozystack.io" +) + +// NormalizeApplicationRef sets the default apiGroup to "apps.cozystack.io" if it's not specified. +// This function is exported so it can be used by other packages (e.g., factory). +func NormalizeApplicationRef(ref corev1.TypedLocalObjectReference) corev1.TypedLocalObjectReference { + if ref.APIGroup == nil || *ref.APIGroup == "" { + defaultGroup := DefaultApplicationAPIGroup + ref.APIGroup = &defaultGroup + } + return ref +} + +// normalizeApplicationRef is an internal alias for consistency within this package. +func normalizeApplicationRef(ref corev1.TypedLocalObjectReference) corev1.TypedLocalObjectReference { + return NormalizeApplicationRef(ref) +} + +// ResolvedBackupConfig contains the resolved strategy and storage configuration +// from a BackupClass. +type ResolvedBackupConfig struct { + StrategyRef corev1.TypedLocalObjectReference + StorageRef *corev1.TypedLocalObjectReference // Optional, may come from parameters + Parameters map[string]string +} + +// ResolveBackupClass resolves a BackupClass and finds the matching strategy for the given application. +// It normalizes the applicationRef's apiGroup (defaults to apps.cozystack.io if not specified) +// and matches it against the strategies in the BackupClass. +func ResolveBackupClass( + ctx context.Context, + c client.Client, + backupClassName string, + applicationRef corev1.TypedLocalObjectReference, +) (*ResolvedBackupConfig, error) { + // Normalize applicationRef (default apiGroup if not specified) + applicationRef = normalizeApplicationRef(applicationRef) + + // Get BackupClass + backupClass := &backupsv1alpha1.BackupClass{} + if err := c.Get(ctx, client.ObjectKey{Name: backupClassName}, backupClass); err != nil { + return nil, fmt.Errorf("failed to get BackupClass %s: %w", backupClassName, err) + } + + // Determine application API group (already normalized, but extract for matching) + appAPIGroup := DefaultApplicationAPIGroup + if applicationRef.APIGroup != nil { + appAPIGroup = *applicationRef.APIGroup + } + + // Find matching strategy + for _, strategy := range backupClass.Spec.Strategies { + // Normalize strategy's application selector (default apiGroup if not specified) + strategyAPIGroup := DefaultApplicationAPIGroup + if strategy.Application.APIGroup != nil && *strategy.Application.APIGroup != "" { + strategyAPIGroup = *strategy.Application.APIGroup + } + + if strategyAPIGroup == appAPIGroup && strategy.Application.Kind == applicationRef.Kind { + return &ResolvedBackupConfig{ + StrategyRef: strategy.StrategyRef, + Parameters: strategy.Parameters, + }, nil + } + } + + return nil, fmt.Errorf("no matching strategy found in BackupClass %s for application %s/%s", + backupClassName, appAPIGroup, applicationRef.Kind) +} diff --git a/internal/backupcontroller/backupclass_resolver_test.go b/internal/backupcontroller/backupclass_resolver_test.go new file mode 100644 index 00000000..4c2b03fc --- /dev/null +++ b/internal/backupcontroller/backupclass_resolver_test.go @@ -0,0 +1,358 @@ +package backupcontroller + +import ( + "context" + "testing" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + + backupsv1alpha1 "github.com/cozystack/cozystack/api/backups/v1alpha1" + strategyv1alpha1 "github.com/cozystack/cozystack/api/backups/strategy/v1alpha1" +) + +func TestNormalizeApplicationRef(t *testing.T) { + tests := []struct { + name string + input corev1.TypedLocalObjectReference + expected string + }{ + { + name: "apiGroup not specified - should default to apps.cozystack.io", + input: corev1.TypedLocalObjectReference{ + Kind: "VirtualMachine", + Name: "vm1", + }, + expected: DefaultApplicationAPIGroup, + }, + { + name: "apiGroup is nil - should default to apps.cozystack.io", + input: corev1.TypedLocalObjectReference{ + APIGroup: nil, + Kind: "VirtualMachine", + Name: "vm1", + }, + expected: DefaultApplicationAPIGroup, + }, + { + name: "apiGroup is empty string - should default to apps.cozystack.io", + input: corev1.TypedLocalObjectReference{ + APIGroup: stringPtr(""), + Kind: "VirtualMachine", + Name: "vm1", + }, + expected: DefaultApplicationAPIGroup, + }, + { + name: "apiGroup is explicitly set - should keep it", + input: corev1.TypedLocalObjectReference{ + APIGroup: stringPtr("custom.api.group.io"), + Kind: "CustomApp", + Name: "custom-app", + }, + expected: "custom.api.group.io", + }, + { + name: "apiGroup is apps.cozystack.io - should keep it", + input: corev1.TypedLocalObjectReference{ + APIGroup: stringPtr(DefaultApplicationAPIGroup), + Kind: "VirtualMachine", + Name: "vm1", + }, + expected: DefaultApplicationAPIGroup, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := NormalizeApplicationRef(tt.input) + if result.APIGroup == nil { + t.Errorf("NormalizeApplicationRef() returned nil APIGroup, expected %s", tt.expected) + return + } + if *result.APIGroup != tt.expected { + t.Errorf("NormalizeApplicationRef() APIGroup = %v, want %v", *result.APIGroup, tt.expected) + } + // Verify other fields are preserved + if result.Kind != tt.input.Kind { + t.Errorf("NormalizeApplicationRef() Kind = %v, want %v", result.Kind, tt.input.Kind) + } + if result.Name != tt.input.Name { + t.Errorf("NormalizeApplicationRef() Name = %v, want %v", result.Name, tt.input.Name) + } + }) + } +} + +func TestResolveBackupClass(t *testing.T) { + scheme := runtime.NewScheme() + err := backupsv1alpha1.AddToScheme(scheme) + if err != nil { + t.Fatalf("Failed to add backupsv1alpha1 to scheme: %v", err) + } + err = strategyv1alpha1.AddToScheme(scheme) + if err != nil { + t.Fatalf("Failed to add strategyv1alpha1 to scheme: %v", err) + } + + tests := []struct { + name string + backupClass *backupsv1alpha1.BackupClass + applicationRef corev1.TypedLocalObjectReference + backupClassName string + wantErr bool + expectedKind string + expectedParams map[string]string + }{ + { + name: "successful resolution - matches VirtualMachine strategy", + backupClass: &backupsv1alpha1.BackupClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: "velero", + }, + Spec: backupsv1alpha1.BackupClassSpec{ + Strategies: []backupsv1alpha1.BackupClassStrategy{ + { + StrategyRef: corev1.TypedLocalObjectReference{ + APIGroup: stringPtr("strategy.backups.cozystack.io"), + Kind: "Velero", + Name: "velero-strategy-vm", + }, + Application: backupsv1alpha1.ApplicationSelector{ + Kind: "VirtualMachine", + }, + Parameters: map[string]string{ + "backupStorageLocationName": "default", + }, + }, + { + StrategyRef: corev1.TypedLocalObjectReference{ + APIGroup: stringPtr("strategy.backups.cozystack.io"), + Kind: "Velero", + Name: "velero-strategy-mysql", + }, + Application: backupsv1alpha1.ApplicationSelector{ + Kind: "MySQL", + }, + Parameters: map[string]string{ + "backupStorageLocationName": "mysql-storage", + }, + }, + }, + }, + }, + applicationRef: corev1.TypedLocalObjectReference{ + Kind: "VirtualMachine", + Name: "vm1", + }, + backupClassName: "velero", + wantErr: false, + expectedKind: "VirtualMachine", + expectedParams: map[string]string{ + "backupStorageLocationName": "default", + }, + }, + { + name: "successful resolution - matches MySQL strategy with explicit apiGroup", + backupClass: &backupsv1alpha1.BackupClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: "velero", + }, + Spec: backupsv1alpha1.BackupClassSpec{ + Strategies: []backupsv1alpha1.BackupClassStrategy{ + { + StrategyRef: corev1.TypedLocalObjectReference{ + APIGroup: stringPtr("strategy.backups.cozystack.io"), + Kind: "Velero", + Name: "velero-strategy-mysql", + }, + Application: backupsv1alpha1.ApplicationSelector{ + APIGroup: stringPtr("apps.cozystack.io"), + Kind: "MySQL", + }, + Parameters: map[string]string{ + "backupStorageLocationName": "mysql-storage", + }, + }, + }, + }, + }, + applicationRef: corev1.TypedLocalObjectReference{ + APIGroup: stringPtr("apps.cozystack.io"), + Kind: "MySQL", + Name: "mysql1", + }, + backupClassName: "velero", + wantErr: false, + expectedKind: "MySQL", + expectedParams: map[string]string{ + "backupStorageLocationName": "mysql-storage", + }, + }, + { + name: "successful resolution - applicationRef without apiGroup defaults correctly", + backupClass: &backupsv1alpha1.BackupClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: "velero", + }, + Spec: backupsv1alpha1.BackupClassSpec{ + Strategies: []backupsv1alpha1.BackupClassStrategy{ + { + StrategyRef: corev1.TypedLocalObjectReference{ + APIGroup: stringPtr("strategy.backups.cozystack.io"), + Kind: "Velero", + Name: "velero-strategy-vm", + }, + Application: backupsv1alpha1.ApplicationSelector{ + Kind: "VirtualMachine", + }, + Parameters: map[string]string{ + "backupStorageLocationName": "default", + }, + }, + }, + }, + }, + applicationRef: corev1.TypedLocalObjectReference{ + // No APIGroup specified + Kind: "VirtualMachine", + Name: "vm1", + }, + backupClassName: "velero", + wantErr: false, + expectedKind: "VirtualMachine", + expectedParams: map[string]string{ + "backupStorageLocationName": "default", + }, + }, + { + name: "error - BackupClass not found", + backupClass: &backupsv1alpha1.BackupClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: "velero", + }, + Spec: backupsv1alpha1.BackupClassSpec{ + Strategies: []backupsv1alpha1.BackupClassStrategy{}, + }, + }, + applicationRef: corev1.TypedLocalObjectReference{ + Kind: "VirtualMachine", + Name: "vm1", + }, + backupClassName: "nonexistent", + wantErr: true, + }, + { + name: "error - no matching strategy found", + backupClass: &backupsv1alpha1.BackupClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: "velero", + }, + Spec: backupsv1alpha1.BackupClassSpec{ + Strategies: []backupsv1alpha1.BackupClassStrategy{ + { + StrategyRef: corev1.TypedLocalObjectReference{ + APIGroup: stringPtr("strategy.backups.cozystack.io"), + Kind: "Velero", + Name: "velero-strategy-vm", + }, + Application: backupsv1alpha1.ApplicationSelector{ + Kind: "VirtualMachine", + }, + }, + }, + }, + }, + applicationRef: corev1.TypedLocalObjectReference{ + Kind: "PostgreSQL", // Not in BackupClass + Name: "pg1", + }, + backupClassName: "velero", + wantErr: true, + }, + { + name: "error - apiGroup mismatch", + backupClass: &backupsv1alpha1.BackupClass{ + ObjectMeta: metav1.ObjectMeta{ + Name: "velero", + }, + Spec: backupsv1alpha1.BackupClassSpec{ + Strategies: []backupsv1alpha1.BackupClassStrategy{ + { + StrategyRef: corev1.TypedLocalObjectReference{ + APIGroup: stringPtr("strategy.backups.cozystack.io"), + Kind: "Velero", + Name: "velero-strategy-vm", + }, + Application: backupsv1alpha1.ApplicationSelector{ + APIGroup: stringPtr("custom.api.group.io"), + Kind: "VirtualMachine", + }, + }, + }, + }, + }, + applicationRef: corev1.TypedLocalObjectReference{ + APIGroup: stringPtr("apps.cozystack.io"), // Different apiGroup + Kind: "VirtualMachine", + Name: "vm1", + }, + backupClassName: "velero", + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctx := context.Background() + fakeClient := fake.NewClientBuilder(). + WithScheme(scheme). + WithObjects(tt.backupClass). + Build() + + resolved, err := ResolveBackupClass(ctx, fakeClient, tt.backupClassName, tt.applicationRef) + + if tt.wantErr { + if err == nil { + t.Errorf("ResolveBackupClass() expected error but got none") + } + return + } + + if err != nil { + t.Errorf("ResolveBackupClass() error = %v, wantErr %v", err, tt.wantErr) + return + } + + if resolved == nil { + t.Errorf("ResolveBackupClass() returned nil, expected ResolvedBackupConfig") + return + } + + // Verify strategy ref + if resolved.StrategyRef.Kind != "Velero" { + t.Errorf("ResolveBackupClass() StrategyRef.Kind = %v, want Velero", resolved.StrategyRef.Kind) + } + + // Verify parameters + if tt.expectedParams != nil { + for key, expectedValue := range tt.expectedParams { + actualValue, ok := resolved.Parameters[key] + if !ok { + t.Errorf("ResolveBackupClass() Parameters[%s] not found", key) + continue + } + if actualValue != expectedValue { + t.Errorf("ResolveBackupClass() Parameters[%s] = %v, want %v", key, actualValue, expectedValue) + } + } + } + }) + } +} + +func stringPtr(s string) *string { + return &s +} diff --git a/internal/backupcontroller/backupjob_controller.go b/internal/backupcontroller/backupjob_controller.go index 0db2e1dd..636b73c7 100644 --- a/internal/backupcontroller/backupjob_controller.go +++ b/internal/backupcontroller/backupjob_controller.go @@ -45,29 +45,42 @@ func (r *BackupJobReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( return ctrl.Result{}, err } - if j.Spec.StrategyRef.APIGroup == nil { - logger.V(1).Info("BackupJob has nil StrategyRef.APIGroup, skipping", "backupjob", j.Name) + // Normalize ApplicationRef (default apiGroup if not specified) + normalizedAppRef := normalizeApplicationRef(j.Spec.ApplicationRef) + + // Resolve BackupClass + resolved, err := ResolveBackupClass(ctx, r.Client, j.Spec.BackupClassName, normalizedAppRef) + if err != nil { + logger.Error(err, "failed to resolve BackupClass", "backupClassName", j.Spec.BackupClassName) + return ctrl.Result{}, err + } + + strategyRef := resolved.StrategyRef + + // Validate strategyRef + if strategyRef.APIGroup == nil { + logger.V(1).Info("BackupJob resolved StrategyRef has nil APIGroup, skipping", "backupjob", j.Name) return ctrl.Result{}, nil } - if *j.Spec.StrategyRef.APIGroup != strategyv1alpha1.GroupVersion.Group { - logger.V(1).Info("BackupJob StrategyRef.APIGroup doesn't match, skipping", + if *strategyRef.APIGroup != strategyv1alpha1.GroupVersion.Group { + logger.V(1).Info("BackupJob resolved StrategyRef.APIGroup doesn't match, skipping", "backupjob", j.Name, "expected", strategyv1alpha1.GroupVersion.Group, - "got", *j.Spec.StrategyRef.APIGroup) + "got", *strategyRef.APIGroup) return ctrl.Result{}, nil } - logger.Info("processing BackupJob", "backupjob", j.Name, "strategyKind", j.Spec.StrategyRef.Kind) - switch j.Spec.StrategyRef.Kind { + logger.Info("processing BackupJob", "backupjob", j.Name, "strategyKind", strategyRef.Kind, "backupClassName", j.Spec.BackupClassName) + switch strategyRef.Kind { case strategyv1alpha1.JobStrategyKind: - return r.reconcileJob(ctx, j) + return r.reconcileJob(ctx, j, resolved) case strategyv1alpha1.VeleroStrategyKind: - return r.reconcileVelero(ctx, j) + return r.reconcileVelero(ctx, j, resolved) default: - logger.V(1).Info("BackupJob StrategyRef.Kind not supported, skipping", + logger.V(1).Info("BackupJob resolved StrategyRef.Kind not supported, skipping", "backupjob", j.Name, - "kind", j.Spec.StrategyRef.Kind, + "kind", strategyRef.Kind, "supported", []string{strategyv1alpha1.JobStrategyKind, strategyv1alpha1.VeleroStrategyKind}) return ctrl.Result{}, nil } diff --git a/internal/backupcontroller/factory/backupjob.go b/internal/backupcontroller/factory/backupjob.go index 722e5d99..11741946 100644 --- a/internal/backupcontroller/factory/backupjob.go +++ b/internal/backupcontroller/factory/backupjob.go @@ -9,7 +9,25 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +const ( + // defaultApplicationAPIGroup is the default API group for applications + // when not specified in ApplicationRef. + defaultApplicationAPIGroup = "apps.cozystack.io" +) + +// normalizeApplicationRef sets the default apiGroup to "apps.cozystack.io" if it's not specified. +func normalizeApplicationRef(ref corev1.TypedLocalObjectReference) corev1.TypedLocalObjectReference { + if ref.APIGroup == nil || *ref.APIGroup == "" { + defaultGroup := defaultApplicationAPIGroup + ref.APIGroup = &defaultGroup + } + return ref +} + func BackupJob(p *backupsv1alpha1.Plan, scheduledFor time.Time) *backupsv1alpha1.BackupJob { + // Normalize ApplicationRef (default apiGroup if not specified) + appRef := normalizeApplicationRef(*p.Spec.ApplicationRef.DeepCopy()) + job := &backupsv1alpha1.BackupJob{ ObjectMeta: metav1.ObjectMeta{ Name: fmt.Sprintf("%s-%d", p.Name, scheduledFor.Unix()/60), @@ -19,9 +37,8 @@ func BackupJob(p *backupsv1alpha1.Plan, scheduledFor time.Time) *backupsv1alpha1 PlanRef: &corev1.LocalObjectReference{ Name: p.Name, }, - ApplicationRef: *p.Spec.ApplicationRef.DeepCopy(), - StorageRef: *p.Spec.StorageRef.DeepCopy(), - StrategyRef: *p.Spec.StrategyRef.DeepCopy(), + ApplicationRef: appRef, + BackupClassName: p.Spec.BackupClassName, }, } return job diff --git a/internal/backupcontroller/factory/backupjob_test.go b/internal/backupcontroller/factory/backupjob_test.go new file mode 100644 index 00000000..2fab5ff8 --- /dev/null +++ b/internal/backupcontroller/factory/backupjob_test.go @@ -0,0 +1,167 @@ +package factory + +import ( + "testing" + "time" + + backupsv1alpha1 "github.com/cozystack/cozystack/api/backups/v1alpha1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestBackupJob(t *testing.T) { + tests := []struct { + name string + plan *backupsv1alpha1.Plan + scheduled time.Time + validate func(*testing.T, *backupsv1alpha1.BackupJob) + }{ + { + name: "creates BackupJob with BackupClassName", + plan: &backupsv1alpha1.Plan{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-plan", + Namespace: "default", + }, + Spec: backupsv1alpha1.PlanSpec{ + ApplicationRef: corev1.TypedLocalObjectReference{ + Kind: "VirtualMachine", + Name: "vm1", + }, + BackupClassName: "velero", + Schedule: backupsv1alpha1.PlanSchedule{ + Type: backupsv1alpha1.PlanScheduleTypeCron, + Cron: "0 2 * * *", + }, + }, + }, + scheduled: time.Date(2024, 1, 1, 2, 0, 0, 0, time.UTC), + validate: func(t *testing.T, job *backupsv1alpha1.BackupJob) { + if job.Name == "" { + t.Error("BackupJob name should be set") + } + if job.Namespace != "default" { + t.Errorf("BackupJob namespace = %v, want default", job.Namespace) + } + if job.Spec.BackupClassName != "velero" { + t.Errorf("BackupJob BackupClassName = %v, want velero", job.Spec.BackupClassName) + } + if job.Spec.ApplicationRef.Kind != "VirtualMachine" { + t.Errorf("BackupJob ApplicationRef.Kind = %v, want VirtualMachine", job.Spec.ApplicationRef.Kind) + } + if job.Spec.ApplicationRef.Name != "vm1" { + t.Errorf("BackupJob ApplicationRef.Name = %v, want vm1", job.Spec.ApplicationRef.Name) + } + if job.Spec.PlanRef == nil || job.Spec.PlanRef.Name != "test-plan" { + t.Errorf("BackupJob PlanRef = %v, want {Name: test-plan}", job.Spec.PlanRef) + } + }, + }, + { + name: "normalizes ApplicationRef apiGroup", + plan: &backupsv1alpha1.Plan{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-plan", + Namespace: "default", + }, + Spec: backupsv1alpha1.PlanSpec{ + ApplicationRef: corev1.TypedLocalObjectReference{ + // No APIGroup specified + Kind: "MySQL", + Name: "mysql1", + }, + BackupClassName: "velero", + Schedule: backupsv1alpha1.PlanSchedule{ + Type: backupsv1alpha1.PlanScheduleTypeCron, + Cron: "0 2 * * *", + }, + }, + }, + scheduled: time.Date(2024, 1, 1, 2, 0, 0, 0, time.UTC), + validate: func(t *testing.T, job *backupsv1alpha1.BackupJob) { + if job.Spec.ApplicationRef.APIGroup == nil { + t.Error("BackupJob ApplicationRef.APIGroup should be set (normalized)") + return + } + if *job.Spec.ApplicationRef.APIGroup != "apps.cozystack.io" { + t.Errorf("BackupJob ApplicationRef.APIGroup = %v, want apps.cozystack.io", *job.Spec.ApplicationRef.APIGroup) + } + }, + }, + { + name: "preserves explicit ApplicationRef apiGroup", + plan: &backupsv1alpha1.Plan{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-plan", + Namespace: "default", + }, + Spec: backupsv1alpha1.PlanSpec{ + ApplicationRef: corev1.TypedLocalObjectReference{ + APIGroup: stringPtr("custom.api.group.io"), + Kind: "CustomApp", + Name: "custom1", + }, + BackupClassName: "velero", + Schedule: backupsv1alpha1.PlanSchedule{ + Type: backupsv1alpha1.PlanScheduleTypeCron, + Cron: "0 2 * * *", + }, + }, + }, + scheduled: time.Date(2024, 1, 1, 2, 0, 0, 0, time.UTC), + validate: func(t *testing.T, job *backupsv1alpha1.BackupJob) { + if job.Spec.ApplicationRef.APIGroup == nil { + t.Error("BackupJob ApplicationRef.APIGroup should be preserved") + return + } + if *job.Spec.ApplicationRef.APIGroup != "custom.api.group.io" { + t.Errorf("BackupJob ApplicationRef.APIGroup = %v, want custom.api.group.io", *job.Spec.ApplicationRef.APIGroup) + } + }, + }, + { + name: "generates unique job name based on timestamp", + plan: &backupsv1alpha1.Plan{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-plan", + Namespace: "default", + }, + Spec: backupsv1alpha1.PlanSpec{ + ApplicationRef: corev1.TypedLocalObjectReference{ + Kind: "VirtualMachine", + Name: "vm1", + }, + BackupClassName: "velero", + Schedule: backupsv1alpha1.PlanSchedule{ + Type: backupsv1alpha1.PlanScheduleTypeCron, + Cron: "0 2 * * *", + }, + }, + }, + scheduled: time.Date(2024, 1, 1, 2, 0, 0, 0, time.UTC), + validate: func(t *testing.T, job *backupsv1alpha1.BackupJob) { + if job.Name == "" { + t.Error("BackupJob name should be generated") + } + // Name should start with plan name + if len(job.Name) < len("test-plan") { + t.Errorf("BackupJob name = %v, should start with test-plan", job.Name) + } + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + job := BackupJob(tt.plan, tt.scheduled) + if job == nil { + t.Fatal("BackupJob() returned nil") + } + tt.validate(t, job) + }) + } +} + +func stringPtr(s string) *string { + return &s +} diff --git a/internal/backupcontroller/jobstrategy_controller.go b/internal/backupcontroller/jobstrategy_controller.go index e52e38f2..1ff4df59 100644 --- a/internal/backupcontroller/jobstrategy_controller.go +++ b/internal/backupcontroller/jobstrategy_controller.go @@ -9,7 +9,8 @@ import ( backupsv1alpha1 "github.com/cozystack/cozystack/api/backups/v1alpha1" ) -func (r *BackupJobReconciler) reconcileJob(ctx context.Context, j *backupsv1alpha1.BackupJob) (ctrl.Result, error) { +func (r *BackupJobReconciler) reconcileJob(ctx context.Context, j *backupsv1alpha1.BackupJob, resolved *ResolvedBackupConfig) (ctrl.Result, error) { _ = log.FromContext(ctx) + _ = resolved // TODO: Use resolved config when implementing job strategy return ctrl.Result{}, nil } diff --git a/internal/backupcontroller/plan_controller.go b/internal/backupcontroller/plan_controller.go index 046fdce4..4945802d 100644 --- a/internal/backupcontroller/plan_controller.go +++ b/internal/backupcontroller/plan_controller.go @@ -20,8 +20,8 @@ import ( ) const ( - minRequeueDelay = 30 * time.Second - startingDeadlineSeconds = 300 * time.Second + minRequeueDelay = 30 * time.Second + startingDeadline = 300 * time.Second ) // PlanReconciler reconciles a Plan object @@ -45,7 +45,7 @@ func (r *PlanReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl. return ctrl.Result{}, err } - tCheck := time.Now().Add(-startingDeadlineSeconds) + tCheck := time.Now().Add(-startingDeadline) sch, err := cron.ParseStandard(p.Spec.Schedule.Cron) if err != nil { errWrapped := fmt.Errorf("could not parse cron %s: %w", p.Spec.Schedule.Cron, err) @@ -78,7 +78,7 @@ func (r *PlanReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl. tNext := sch.Next(tCheck) if time.Now().Before(tNext) { - return ctrl.Result{RequeueAfter: tNext.Sub(time.Now())}, nil + return ctrl.Result{RequeueAfter: time.Until(tNext)}, nil } job := factory.BackupJob(p, tNext) @@ -88,12 +88,12 @@ func (r *PlanReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl. if err := r.Create(ctx, job); err != nil { if apierrors.IsAlreadyExists(err) { - return ctrl.Result{RequeueAfter: startingDeadlineSeconds}, nil + return ctrl.Result{RequeueAfter: startingDeadline}, nil } return ctrl.Result{}, err } - return ctrl.Result{RequeueAfter: startingDeadlineSeconds}, nil + return ctrl.Result{RequeueAfter: startingDeadline}, nil } // SetupWithManager registers our controller with the Manager and sets up watches. diff --git a/internal/backupcontroller/velerostrategy_controller.go b/internal/backupcontroller/velerostrategy_controller.go index 066325c0..0a023356 100644 --- a/internal/backupcontroller/velerostrategy_controller.go +++ b/internal/backupcontroller/velerostrategy_controller.go @@ -2,16 +2,13 @@ package backupcontroller import ( "context" - "encoding/json" "fmt" - "reflect" "time" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -49,19 +46,6 @@ type S3Credentials struct { AccessSecretKey string } -// bucketInfo represents the structure of BucketInfo stored in the secret -type bucketInfo struct { - Spec struct { - BucketName string `json:"bucketName"` - SecretS3 struct { - Endpoint string `json:"endpoint"` - Region string `json:"region"` - AccessKeyID string `json:"accessKeyID"` - AccessSecretKey string `json:"accessSecretKey"` - } `json:"secretS3"` - } `json:"spec"` -} - const ( defaultRequeueAfter = 5 * time.Second defaultActiveJobPollingInterval = defaultRequeueAfter @@ -70,15 +54,11 @@ const ( virtualMachinePrefix = "virtual-machine-" ) -func storageS3SecretName(namespace, backupJobName string) string { - return fmt.Sprintf("backup-%s-%s-s3-credentials", namespace, backupJobName) -} - func boolPtr(b bool) *bool { return &b } -func (r *BackupJobReconciler) reconcileVelero(ctx context.Context, j *backupsv1alpha1.BackupJob) (ctrl.Result, error) { +func (r *BackupJobReconciler) reconcileVelero(ctx context.Context, j *backupsv1alpha1.BackupJob, resolved *ResolvedBackupConfig) (ctrl.Result, error) { logger := getLogger(ctx) logger.Debug("reconciling Velero strategy", "backupjob", j.Name, "phase", j.Status.Phase) @@ -105,13 +85,13 @@ func (r *BackupJobReconciler) reconcileVelero(ctx context.Context, j *backupsv1a logger.Debug("BackupJob already started", "startedAt", j.Status.StartedAt, "phase", j.Status.Phase) } - // Step 2: Resolve inputs - Read Strategy, Storage, Application, optionally Plan - logger.Debug("fetching Velero strategy", "strategyName", j.Spec.StrategyRef.Name) + // Step 2: Resolve inputs - Read Strategy from resolved config + logger.Debug("fetching Velero strategy", "strategyName", resolved.StrategyRef.Name) veleroStrategy := &strategyv1alpha1.Velero{} - if err := r.Get(ctx, client.ObjectKey{Name: j.Spec.StrategyRef.Name}, veleroStrategy); err != nil { + if err := r.Get(ctx, client.ObjectKey{Name: resolved.StrategyRef.Name}, veleroStrategy); err != nil { if errors.IsNotFound(err) { - logger.Error(err, "Velero strategy not found", "strategyName", j.Spec.StrategyRef.Name) - return r.markBackupJobFailed(ctx, j, fmt.Sprintf("Velero strategy not found: %s", j.Spec.StrategyRef.Name)) + logger.Error(err, "Velero strategy not found", "strategyName", resolved.StrategyRef.Name) + return r.markBackupJobFailed(ctx, j, fmt.Sprintf("Velero strategy not found: %s", resolved.StrategyRef.Name)) } logger.Error(err, "failed to get Velero strategy") return ctrl.Result{}, err @@ -195,7 +175,7 @@ func (r *BackupJobReconciler) reconcileVelero(ctx context.Context, j *backupsv1a if phase == "Completed" { // Check if we already created the Backup resource if j.Status.BackupRef == nil { - backup, err := r.createBackupResource(ctx, j, veleroBackup) + backup, err := r.createBackupResource(ctx, j, veleroBackup, resolved) if err != nil { return r.markBackupJobFailed(ctx, j, fmt.Sprintf("failed to create Backup resource: %v", err)) } @@ -226,267 +206,12 @@ func (r *BackupJobReconciler) reconcileVelero(ctx context.Context, j *backupsv1a return ctrl.Result{RequeueAfter: 5 * time.Second}, nil } -// resolveBucketStorageRef discovers S3 credentials from a Bucket storageRef -// It follows this flow: -// 1. Get the Bucket resource (apps.cozystack.io/v1alpha1) -// 2. Find the BucketAccess that references this bucket -// 3. Get the secret from BucketAccess.spec.credentialsSecretName -// 4. Decode BucketInfo from secret.data.BucketInfo and extract S3 credentials -func (r *BackupJobReconciler) resolveBucketStorageRef(ctx context.Context, storageRef corev1.TypedLocalObjectReference, namespace string) (*S3Credentials, error) { - logger := getLogger(ctx) - - // Step 1: Get the Bucket resource - bucket := &unstructured.Unstructured{} - bucket.SetGroupVersionKind(schema.GroupVersionKind{ - Group: *storageRef.APIGroup, - Version: "v1alpha1", - Kind: storageRef.Kind, - }) - - if *storageRef.APIGroup != "apps.cozystack.io" { - return nil, fmt.Errorf("Unsupported storage APIGroup: %v, expected apps.cozystack.io", storageRef.APIGroup) - } - bucketKey := client.ObjectKey{Namespace: namespace, Name: storageRef.Name} - - if err := r.Get(ctx, bucketKey, bucket); err != nil { - return nil, fmt.Errorf("failed to get Bucket %s: %w", storageRef.Name, err) - } - - // Step 2: Determine the bucket claim name - // For apps.cozystack.io Bucket, the BucketClaim name is typically the same as the Bucket name - // or follows a pattern. Based on the templates, it's usually the Release.Name which equals the Bucket name - bucketName := storageRef.Name - - // Step 3: Get BucketAccess by name (assuming BucketAccess name matches bucketName) - bucketAccess := &unstructured.Unstructured{} - bucketAccess.SetGroupVersionKind(schema.GroupVersionKind{ - Group: "objectstorage.k8s.io", - Version: "v1alpha1", - Kind: "BucketAccess", - }) - - bucketAccessKey := client.ObjectKey{Name: "bucket-" + bucketName, Namespace: namespace} - if err := r.Get(ctx, bucketAccessKey, bucketAccess); err != nil { - return nil, fmt.Errorf("failed to get BucketAccess %s in namespace %s: %w", bucketName, namespace, err) - } - - // Step 4: Get the secret name from BucketAccess - secretName, found, err := unstructured.NestedString(bucketAccess.Object, "spec", "credentialsSecretName") - if err != nil { - return nil, fmt.Errorf("failed to get credentialsSecretName from BucketAccess: %w", err) - } - if !found || secretName == "" { - return nil, fmt.Errorf("credentialsSecretName not found in BucketAccess %s", bucketAccessKey.Name) - } - - // Step 5: Get the secret - secret := &corev1.Secret{} - secretKey := client.ObjectKey{Namespace: namespace, Name: secretName} - if err := r.Get(ctx, secretKey, secret); err != nil { - return nil, fmt.Errorf("failed to get secret %s: %w", secretName, err) - } - - // Step 6: Decode BucketInfo from secret.data.BucketInfo - bucketInfoData, found := secret.Data["BucketInfo"] - if !found { - return nil, fmt.Errorf("BucketInfo key not found in secret %s", secretName) - } - - // Parse JSON value - var info bucketInfo - if err := json.Unmarshal(bucketInfoData, &info); err != nil { - return nil, fmt.Errorf("failed to unmarshal BucketInfo from secret %s: %w", secretName, err) - } - - // Step 7: Extract and return S3 credentials - creds := &S3Credentials{ - BucketName: info.Spec.BucketName, - Endpoint: info.Spec.SecretS3.Endpoint, - Region: info.Spec.SecretS3.Region, - AccessKeyID: info.Spec.SecretS3.AccessKeyID, - AccessSecretKey: info.Spec.SecretS3.AccessSecretKey, - } - - logger.Debug("resolved S3 credentials from Bucket storageRef", - "bucket", storageRef.Name, - "bucketName", creds.BucketName, - "endpoint", creds.Endpoint) - - return creds, nil -} - -// createS3CredsForVelero creates or updates a Kubernetes Secret containing -// Velero S3 credentials in the format expected by Velero's cloud-credentials plugin. -func (r *BackupJobReconciler) createS3CredsForVelero(ctx context.Context, backupJob *backupsv1alpha1.BackupJob, creds *S3Credentials) error { - logger := getLogger(ctx) - secretName := storageS3SecretName(backupJob.Namespace, backupJob.Name) - secretNamespace := veleroNamespace - - secret := &corev1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: secretName, - Namespace: secretNamespace, - }, - Type: corev1.SecretTypeOpaque, - StringData: map[string]string{ - "cloud": fmt.Sprintf(`[default] -aws_access_key_id=%s -aws_secret_access_key=%s - -services = seaweed-s3 -[services seaweed-s3] -s3 = - endpoint_url = %s -`, creds.AccessKeyID, creds.AccessSecretKey, creds.Endpoint), - }, - } - - foundSecret := &corev1.Secret{} - secretKey := client.ObjectKey{Name: secretName, Namespace: secretNamespace} - err := r.Get(ctx, secretKey, foundSecret) - if err != nil && errors.IsNotFound(err) { - // Create the Secret - if err := r.Create(ctx, secret); err != nil { - r.Recorder.Event(backupJob, corev1.EventTypeWarning, "SecretCreationFailed", - fmt.Sprintf("Failed to create Velero credentials secret %s/%s: %v", secretNamespace, secretName, err)) - return fmt.Errorf("failed to create Velero credentials secret: %w", err) - } - logger.Debug("created Velero credentials secret", "secret", secretName) - r.Recorder.Event(backupJob, corev1.EventTypeNormal, "SecretCreated", - fmt.Sprintf("Created Velero credentials secret %s/%s", secretNamespace, secretName)) - } else if err == nil { - // Update if necessary - only update if the secret data has actually changed - // Compare the new secret data with existing secret data - existingData := foundSecret.Data - if existingData == nil { - existingData = make(map[string][]byte) - } - newData := make(map[string][]byte) - for k, v := range secret.StringData { - newData[k] = []byte(v) - } - - // Check if data has changed - dataChanged := false - if len(existingData) != len(newData) { - dataChanged = true - } else { - for k, newVal := range newData { - existingVal, exists := existingData[k] - if !exists || !reflect.DeepEqual(existingVal, newVal) { - dataChanged = true - break - } - } - } - - if dataChanged { - foundSecret.StringData = secret.StringData - foundSecret.Data = nil // Clear .Data so .StringData will be used - if err := r.Update(ctx, foundSecret); err != nil { - r.Recorder.Event(backupJob, corev1.EventTypeWarning, "SecretUpdateFailed", - fmt.Sprintf("Failed to update Velero credentials secret %s/%s: %v", secretNamespace, secretName, err)) - return fmt.Errorf("failed to update Velero credentials secret: %w", err) - } - logger.Debug("updated Velero credentials secret", "secret", secretName) - r.Recorder.Event(backupJob, corev1.EventTypeNormal, "SecretUpdated", - fmt.Sprintf("Updated Velero credentials secret %s/%s", secretNamespace, secretName)) - } else { - logger.Debug("Velero credentials secret data unchanged, skipping update", "secret", secretName) - } - } else if err != nil { - return fmt.Errorf("error checking for existing Velero credentials secret: %w", err) - } - - return nil -} - -// createBackupStorageLocation creates or updates a Velero BackupStorageLocation resource. -func (r *BackupJobReconciler) createBackupStorageLocation(ctx context.Context, bsl *velerov1.BackupStorageLocation) error { - logger := getLogger(ctx) - foundBSL := &velerov1.BackupStorageLocation{} - bslKey := client.ObjectKey{Name: bsl.Name, Namespace: bsl.Namespace} - - err := r.Get(ctx, bslKey, foundBSL) - if err != nil && errors.IsNotFound(err) { - // Create the BackupStorageLocation - if err := r.Create(ctx, bsl); err != nil { - return fmt.Errorf("failed to create BackupStorageLocation: %w", err) - } - logger.Debug("created BackupStorageLocation", "name", bsl.Name, "namespace", bsl.Namespace) - } else if err == nil { - // Update if necessary - use patch to avoid conflicts with Velero's status updates - // Only update if the spec has actually changed - if !reflect.DeepEqual(foundBSL.Spec, bsl.Spec) { - // Retry on conflict since Velero may be updating status concurrently - for i := 0; i < 3; i++ { - if err := r.Get(ctx, bslKey, foundBSL); err != nil { - return fmt.Errorf("failed to get BackupStorageLocation for update: %w", err) - } - foundBSL.Spec = bsl.Spec - if err := r.Update(ctx, foundBSL); err != nil { - if errors.IsConflict(err) && i < 2 { - logger.Debug("conflict updating BackupStorageLocation, retrying", "attempt", i+1) - time.Sleep(100 * time.Millisecond) - continue - } - return fmt.Errorf("failed to update BackupStorageLocation: %w", err) - } - logger.Debug("updated BackupStorageLocation", "name", bsl.Name, "namespace", bsl.Namespace) - return nil - } - } else { - logger.Debug("BackupStorageLocation spec unchanged, skipping update", "name", bsl.Name, "namespace", bsl.Namespace) - } - } else if err != nil { - return fmt.Errorf("error checking for existing BackupStorageLocation: %w", err) - } - - return nil -} - -// createVolumeSnapshotLocation creates or updates a Velero VolumeSnapshotLocation resource. -func (r *BackupJobReconciler) createVolumeSnapshotLocation(ctx context.Context, vsl *velerov1.VolumeSnapshotLocation) error { - logger := getLogger(ctx) - foundVSL := &velerov1.VolumeSnapshotLocation{} - vslKey := client.ObjectKey{Name: vsl.Name, Namespace: vsl.Namespace} - - err := r.Get(ctx, vslKey, foundVSL) - if err != nil && errors.IsNotFound(err) { - // Create the VolumeSnapshotLocation - if err := r.Create(ctx, vsl); err != nil { - return fmt.Errorf("failed to create VolumeSnapshotLocation: %w", err) - } - logger.Debug("created VolumeSnapshotLocation", "name", vsl.Name, "namespace", vsl.Namespace) - } else if err == nil { - // Update if necessary - only update if the spec has actually changed - if !reflect.DeepEqual(foundVSL.Spec, vsl.Spec) { - // Retry on conflict since Velero may be updating status concurrently - for i := 0; i < 3; i++ { - if err := r.Get(ctx, vslKey, foundVSL); err != nil { - return fmt.Errorf("failed to get VolumeSnapshotLocation for update: %w", err) - } - foundVSL.Spec = vsl.Spec - if err := r.Update(ctx, foundVSL); err != nil { - if errors.IsConflict(err) && i < 2 { - logger.Debug("conflict updating VolumeSnapshotLocation, retrying", "attempt", i+1) - time.Sleep(100 * time.Millisecond) - continue - } - return fmt.Errorf("failed to update VolumeSnapshotLocation: %w", err) - } - logger.Debug("updated VolumeSnapshotLocation", "name", vsl.Name, "namespace", vsl.Namespace) - return nil - } - } else { - logger.Debug("VolumeSnapshotLocation spec unchanged, skipping update", "name", vsl.Name, "namespace", vsl.Namespace) - } - } else if err != nil { - return fmt.Errorf("error checking for existing VolumeSnapshotLocation: %w", err) - } - - return nil -} +// NOTE: The following functions were removed as they are no longer used after migrating to BackupClass API: +// - resolveBucketStorageRef: Previously resolved S3 credentials from Bucket storageRef +// - createS3CredsForVelero: Previously created Velero S3 credentials secrets +// - createBackupStorageLocation: Previously created Velero BackupStorageLocation resources +// - createVolumeSnapshotLocation: Previously created Velero VolumeSnapshotLocation resources +// These functions may be needed in the future if we decide to support StorageRef resolution through BackupClass. func (r *BackupJobReconciler) markBackupJobFailed(ctx context.Context, backupJob *backupsv1alpha1.BackupJob, message string) (ctrl.Result, error) { logger := getLogger(ctx) @@ -561,13 +286,9 @@ func (r *BackupJobReconciler) createVeleroBackup(ctx context.Context, backupJob return nil } -func (r *BackupJobReconciler) createBackupResource(ctx context.Context, backupJob *backupsv1alpha1.BackupJob, veleroBackup *velerov1.Backup) (*backupsv1alpha1.Backup, error) { +func (r *BackupJobReconciler) createBackupResource(ctx context.Context, backupJob *backupsv1alpha1.BackupJob, veleroBackup *velerov1.Backup, resolved *ResolvedBackupConfig) (*backupsv1alpha1.Backup, error) { logger := getLogger(ctx) - // Extract artifact information from Velero Backup - // Create a basic artifact referencing the Velero backup - artifact := &backupsv1alpha1.BackupArtifact{ - URI: fmt.Sprintf("velero://%s/%s", backupJob.Namespace, veleroBackup.Name), - } + _ = logger // logger may be used in future // Get takenAt from Velero Backup creation timestamp or status takenAt := metav1.Now() @@ -583,9 +304,14 @@ func (r *BackupJobReconciler) createBackupResource(ctx context.Context, backupJo "velero.io/backup-namespace": veleroBackup.Namespace, } + // Create a basic artifact referencing the Velero backup + artifact := &backupsv1alpha1.BackupArtifact{ + URI: fmt.Sprintf("velero://%s/%s", backupJob.Namespace, veleroBackup.Name), + } + backup := &backupsv1alpha1.Backup{ ObjectMeta: metav1.ObjectMeta{ - Name: fmt.Sprintf("%s", backupJob.Name), + Name: backupJob.Name, Namespace: backupJob.Namespace, OwnerReferences: []metav1.OwnerReference{ { @@ -599,13 +325,15 @@ func (r *BackupJobReconciler) createBackupResource(ctx context.Context, backupJo }, Spec: backupsv1alpha1.BackupSpec{ ApplicationRef: backupJob.Spec.ApplicationRef, - StorageRef: backupJob.Spec.StorageRef, - StrategyRef: backupJob.Spec.StrategyRef, + // StorageRef is not set as it's now resolved from BackupClass parameters + // The storage location is managed via Velero's BackupStorageLocation + StrategyRef: resolved.StrategyRef, TakenAt: takenAt, DriverMetadata: driverMetadata, }, Status: backupsv1alpha1.BackupStatus{ - Phase: backupsv1alpha1.BackupPhaseReady, + Phase: backupsv1alpha1.BackupPhaseReady, + Artifact: artifact, }, } @@ -613,10 +341,6 @@ func (r *BackupJobReconciler) createBackupResource(ctx context.Context, backupJo backup.Spec.PlanRef = backupJob.Spec.PlanRef } - if artifact != nil { - backup.Status.Artifact = artifact - } - if err := r.Create(ctx, backup); err != nil { logger.Error(err, "failed to create Backup resource") return nil, err From 41646b253e60726a06dae16a0f3fbeb272e6b7f9 Mon Sep 17 00:00:00 2001 From: Andrey Kolkov Date: Mon, 19 Jan 2026 13:09:25 +0400 Subject: [PATCH 056/125] use everywhere NormalizeApplicationRef Signed-off-by: Andrey Kolkov --- .../backupcontroller/backupclass_resolver.go | 11 +++------- .../backupcontroller/backupjob_controller.go | 2 +- .../backupcontroller/factory/backupjob.go | 20 +++---------------- 3 files changed, 7 insertions(+), 26 deletions(-) diff --git a/internal/backupcontroller/backupclass_resolver.go b/internal/backupcontroller/backupclass_resolver.go index c07e778c..20a14ba4 100644 --- a/internal/backupcontroller/backupclass_resolver.go +++ b/internal/backupcontroller/backupclass_resolver.go @@ -20,17 +20,12 @@ const ( // This function is exported so it can be used by other packages (e.g., factory). func NormalizeApplicationRef(ref corev1.TypedLocalObjectReference) corev1.TypedLocalObjectReference { if ref.APIGroup == nil || *ref.APIGroup == "" { - defaultGroup := DefaultApplicationAPIGroup - ref.APIGroup = &defaultGroup + apiGroup := DefaultApplicationAPIGroup + ref.APIGroup = &apiGroup } return ref } -// normalizeApplicationRef is an internal alias for consistency within this package. -func normalizeApplicationRef(ref corev1.TypedLocalObjectReference) corev1.TypedLocalObjectReference { - return NormalizeApplicationRef(ref) -} - // ResolvedBackupConfig contains the resolved strategy and storage configuration // from a BackupClass. type ResolvedBackupConfig struct { @@ -49,7 +44,7 @@ func ResolveBackupClass( applicationRef corev1.TypedLocalObjectReference, ) (*ResolvedBackupConfig, error) { // Normalize applicationRef (default apiGroup if not specified) - applicationRef = normalizeApplicationRef(applicationRef) + applicationRef = NormalizeApplicationRef(applicationRef) // Get BackupClass backupClass := &backupsv1alpha1.BackupClass{} diff --git a/internal/backupcontroller/backupjob_controller.go b/internal/backupcontroller/backupjob_controller.go index 636b73c7..db7e701a 100644 --- a/internal/backupcontroller/backupjob_controller.go +++ b/internal/backupcontroller/backupjob_controller.go @@ -46,7 +46,7 @@ func (r *BackupJobReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( } // Normalize ApplicationRef (default apiGroup if not specified) - normalizedAppRef := normalizeApplicationRef(j.Spec.ApplicationRef) + normalizedAppRef := NormalizeApplicationRef(j.Spec.ApplicationRef) // Resolve BackupClass resolved, err := ResolveBackupClass(ctx, r.Client, j.Spec.BackupClassName, normalizedAppRef) diff --git a/internal/backupcontroller/factory/backupjob.go b/internal/backupcontroller/factory/backupjob.go index 11741946..cf8ae544 100644 --- a/internal/backupcontroller/factory/backupjob.go +++ b/internal/backupcontroller/factory/backupjob.go @@ -5,28 +5,14 @@ import ( "time" backupsv1alpha1 "github.com/cozystack/cozystack/api/backups/v1alpha1" + "github.com/cozystack/cozystack/internal/backupcontroller" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -const ( - // defaultApplicationAPIGroup is the default API group for applications - // when not specified in ApplicationRef. - defaultApplicationAPIGroup = "apps.cozystack.io" -) - -// normalizeApplicationRef sets the default apiGroup to "apps.cozystack.io" if it's not specified. -func normalizeApplicationRef(ref corev1.TypedLocalObjectReference) corev1.TypedLocalObjectReference { - if ref.APIGroup == nil || *ref.APIGroup == "" { - defaultGroup := defaultApplicationAPIGroup - ref.APIGroup = &defaultGroup - } - return ref -} - func BackupJob(p *backupsv1alpha1.Plan, scheduledFor time.Time) *backupsv1alpha1.BackupJob { // Normalize ApplicationRef (default apiGroup if not specified) - appRef := normalizeApplicationRef(*p.Spec.ApplicationRef.DeepCopy()) + appRef := backupcontroller.NormalizeApplicationRef(*p.Spec.ApplicationRef.DeepCopy()) job := &backupsv1alpha1.BackupJob{ ObjectMeta: metav1.ObjectMeta{ @@ -37,7 +23,7 @@ func BackupJob(p *backupsv1alpha1.Plan, scheduledFor time.Time) *backupsv1alpha1 PlanRef: &corev1.LocalObjectReference{ Name: p.Name, }, - ApplicationRef: appRef, + ApplicationRef: appRef, BackupClassName: p.Spec.BackupClassName, }, } From d7ae3213ff178c80de4e88e9ba463614b01ddb97 Mon Sep 17 00:00:00 2001 From: Andrey Kolkov Date: Mon, 19 Jan 2026 14:16:28 +0400 Subject: [PATCH 057/125] removed unused Signed-off-by: Andrey Kolkov --- internal/backupcontroller/backupclass_resolver.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/backupcontroller/backupclass_resolver.go b/internal/backupcontroller/backupclass_resolver.go index 20a14ba4..593e3bb0 100644 --- a/internal/backupcontroller/backupclass_resolver.go +++ b/internal/backupcontroller/backupclass_resolver.go @@ -30,7 +30,6 @@ func NormalizeApplicationRef(ref corev1.TypedLocalObjectReference) corev1.TypedL // from a BackupClass. type ResolvedBackupConfig struct { StrategyRef corev1.TypedLocalObjectReference - StorageRef *corev1.TypedLocalObjectReference // Optional, may come from parameters Parameters map[string]string } From 5924c484c95075d04e9571be8e0c557781c61814 Mon Sep 17 00:00:00 2001 From: Andrey Kolkov Date: Mon, 19 Jan 2026 14:26:10 +0400 Subject: [PATCH 058/125] clean-up comments Signed-off-by: Andrey Kolkov --- internal/backupcontroller/velerostrategy_controller.go | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/internal/backupcontroller/velerostrategy_controller.go b/internal/backupcontroller/velerostrategy_controller.go index 0a023356..0d43098c 100644 --- a/internal/backupcontroller/velerostrategy_controller.go +++ b/internal/backupcontroller/velerostrategy_controller.go @@ -206,13 +206,6 @@ func (r *BackupJobReconciler) reconcileVelero(ctx context.Context, j *backupsv1a return ctrl.Result{RequeueAfter: 5 * time.Second}, nil } -// NOTE: The following functions were removed as they are no longer used after migrating to BackupClass API: -// - resolveBucketStorageRef: Previously resolved S3 credentials from Bucket storageRef -// - createS3CredsForVelero: Previously created Velero S3 credentials secrets -// - createBackupStorageLocation: Previously created Velero BackupStorageLocation resources -// - createVolumeSnapshotLocation: Previously created Velero VolumeSnapshotLocation resources -// These functions may be needed in the future if we decide to support StorageRef resolution through BackupClass. - func (r *BackupJobReconciler) markBackupJobFailed(ctx context.Context, backupJob *backupsv1alpha1.BackupJob, message string) (ctrl.Result, error) { logger := getLogger(ctx) now := metav1.Now() @@ -288,7 +281,6 @@ func (r *BackupJobReconciler) createVeleroBackup(ctx context.Context, backupJob func (r *BackupJobReconciler) createBackupResource(ctx context.Context, backupJob *backupsv1alpha1.BackupJob, veleroBackup *velerov1.Backup, resolved *ResolvedBackupConfig) (*backupsv1alpha1.Backup, error) { logger := getLogger(ctx) - _ = logger // logger may be used in future // Get takenAt from Velero Backup creation timestamp or status takenAt := metav1.Now() @@ -325,8 +317,6 @@ func (r *BackupJobReconciler) createBackupResource(ctx context.Context, backupJo }, Spec: backupsv1alpha1.BackupSpec{ ApplicationRef: backupJob.Spec.ApplicationRef, - // StorageRef is not set as it's now resolved from BackupClass parameters - // The storage location is managed via Velero's BackupStorageLocation StrategyRef: resolved.StrategyRef, TakenAt: takenAt, DriverMetadata: driverMetadata, From ba04063662800d2713afa0b448b4736e8ee0182a Mon Sep 17 00:00:00 2001 From: Andrey Kolkov Date: Mon, 19 Jan 2026 14:41:19 +0400 Subject: [PATCH 059/125] buildfix: restore against cyclic dependency Signed-off-by: Andrey Kolkov --- .../backupcontroller/factory/backupjob.go | 19 +++++++++++++++++-- .../jobstrategy_controller.go | 2 +- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/internal/backupcontroller/factory/backupjob.go b/internal/backupcontroller/factory/backupjob.go index cf8ae544..6ca2ee21 100644 --- a/internal/backupcontroller/factory/backupjob.go +++ b/internal/backupcontroller/factory/backupjob.go @@ -5,14 +5,29 @@ import ( "time" backupsv1alpha1 "github.com/cozystack/cozystack/api/backups/v1alpha1" - "github.com/cozystack/cozystack/internal/backupcontroller" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) +const ( + // defaultApplicationAPIGroup is the default API group for applications + // when not specified in ApplicationRef. + defaultApplicationAPIGroup = "apps.cozystack.io" +) + +// normalizeApplicationRef sets the default apiGroup to "apps.cozystack.io" if it's not specified. +// This is a local copy to avoid import cycle with backupcontroller package. +func normalizeApplicationRef(ref corev1.TypedLocalObjectReference) corev1.TypedLocalObjectReference { + if ref.APIGroup == nil || *ref.APIGroup == "" { + apiGroup := defaultApplicationAPIGroup + ref.APIGroup = &apiGroup + } + return ref +} + func BackupJob(p *backupsv1alpha1.Plan, scheduledFor time.Time) *backupsv1alpha1.BackupJob { // Normalize ApplicationRef (default apiGroup if not specified) - appRef := backupcontroller.NormalizeApplicationRef(*p.Spec.ApplicationRef.DeepCopy()) + appRef := normalizeApplicationRef(*p.Spec.ApplicationRef.DeepCopy()) job := &backupsv1alpha1.BackupJob{ ObjectMeta: metav1.ObjectMeta{ diff --git a/internal/backupcontroller/jobstrategy_controller.go b/internal/backupcontroller/jobstrategy_controller.go index 1ff4df59..04b814dd 100644 --- a/internal/backupcontroller/jobstrategy_controller.go +++ b/internal/backupcontroller/jobstrategy_controller.go @@ -11,6 +11,6 @@ import ( func (r *BackupJobReconciler) reconcileJob(ctx context.Context, j *backupsv1alpha1.BackupJob, resolved *ResolvedBackupConfig) (ctrl.Result, error) { _ = log.FromContext(ctx) - _ = resolved // TODO: Use resolved config when implementing job strategy + _ = resolved // Use resolved BackupClass parameters when implementing your job strategy return ctrl.Result{}, nil } From 79bd3ad0d51e66d92a8eca6b2f1edc780f482810 Mon Sep 17 00:00:00 2001 From: Andrey Kolkov Date: Tue, 20 Jan 2026 10:07:46 +0400 Subject: [PATCH 060/125] fix templating Signed-off-by: Andrey Kolkov --- .../velerostrategy_controller.go | 11 +- .../velerostrategy_controller_test.go | 207 ++++++++++++++++++ 2 files changed, 215 insertions(+), 3 deletions(-) create mode 100644 internal/backupcontroller/velerostrategy_controller_test.go diff --git a/internal/backupcontroller/velerostrategy_controller.go b/internal/backupcontroller/velerostrategy_controller.go index 0d43098c..4a1225f6 100644 --- a/internal/backupcontroller/velerostrategy_controller.go +++ b/internal/backupcontroller/velerostrategy_controller.go @@ -123,7 +123,7 @@ func (r *BackupJobReconciler) reconcileVelero(ctx context.Context, j *backupsv1a if len(veleroBackupList.Items) == 0 { // Create Velero Backup logger.Debug("Velero Backup not found, creating new one") - if err := r.createVeleroBackup(ctx, j, veleroStrategy); err != nil { + if err := r.createVeleroBackup(ctx, j, veleroStrategy, resolved); err != nil { logger.Error(err, "failed to create Velero Backup") return r.markBackupJobFailed(ctx, j, fmt.Sprintf("failed to create Velero Backup: %v", err)) } @@ -230,7 +230,7 @@ func (r *BackupJobReconciler) markBackupJobFailed(ctx context.Context, backupJob return ctrl.Result{}, nil } -func (r *BackupJobReconciler) createVeleroBackup(ctx context.Context, backupJob *backupsv1alpha1.BackupJob, strategy *strategyv1alpha1.Velero) error { +func (r *BackupJobReconciler) createVeleroBackup(ctx context.Context, backupJob *backupsv1alpha1.BackupJob, strategy *strategyv1alpha1.Velero, resolved *ResolvedBackupConfig) error { logger := getLogger(ctx) logger.Debug("createVeleroBackup called", "strategy", strategy.Name) @@ -247,7 +247,12 @@ func (r *BackupJobReconciler) createVeleroBackup(ctx context.Context, backupJob return err } - veleroBackupSpec, err := template.Template(&strategy.Spec.Template.Spec, app.Object) + templateContext := map[string]interface{}{ + "Application": app.Object, + "Parameters": resolved.Parameters, + } + + veleroBackupSpec, err := template.Template(&strategy.Spec.Template.Spec, templateContext) if err != nil { return err } diff --git a/internal/backupcontroller/velerostrategy_controller_test.go b/internal/backupcontroller/velerostrategy_controller_test.go new file mode 100644 index 00000000..0575a681 --- /dev/null +++ b/internal/backupcontroller/velerostrategy_controller_test.go @@ -0,0 +1,207 @@ +package backupcontroller + +import ( + "context" + "testing" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + dynamicfake "k8s.io/client-go/dynamic/fake" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/tools/record" + "sigs.k8s.io/controller-runtime/pkg/client" + clientfake "sigs.k8s.io/controller-runtime/pkg/client/fake" + + strategyv1alpha1 "github.com/cozystack/cozystack/api/backups/strategy/v1alpha1" + backupsv1alpha1 "github.com/cozystack/cozystack/api/backups/v1alpha1" + velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" +) + +// mockRESTMapper implements meta.RESTMapper for testing +type mockRESTMapper struct { + mapping *meta.RESTMapping +} + +func (m *mockRESTMapper) RESTMapping(gk schema.GroupKind, versions ...string) (*meta.RESTMapping, error) { + return m.mapping, nil +} + +func (m *mockRESTMapper) RESTMappings(gk schema.GroupKind, versions ...string) ([]*meta.RESTMapping, error) { + return []*meta.RESTMapping{m.mapping}, nil +} + +func (m *mockRESTMapper) KindFor(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) { + return m.mapping.GroupVersionKind, nil +} + +func (m *mockRESTMapper) KindsFor(resource schema.GroupVersionResource) ([]schema.GroupVersionKind, error) { + return []schema.GroupVersionKind{m.mapping.GroupVersionKind}, nil +} + +func (m *mockRESTMapper) ResourceFor(input schema.GroupVersionResource) (schema.GroupVersionResource, error) { + return m.mapping.Resource, nil +} + +func (m *mockRESTMapper) ResourcesFor(input schema.GroupVersionResource) ([]schema.GroupVersionResource, error) { + return []schema.GroupVersionResource{m.mapping.Resource}, nil +} + +func (m *mockRESTMapper) ResourceSingularizer(resource string) (singular string, err error) { + return resource, nil +} + +func TestCreateVeleroBackup_TemplateContext(t *testing.T) { + // Setup scheme + testScheme := runtime.NewScheme() + _ = scheme.AddToScheme(testScheme) + _ = backupsv1alpha1.AddToScheme(testScheme) + _ = strategyv1alpha1.AddToScheme(testScheme) + _ = velerov1.AddToScheme(testScheme) + + // Create test application (VirtualMachine-like object) + testApp := &corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-vm", + Namespace: "default", + Labels: map[string]string{ + "apps.cozystack.io/application.Kind": "VirtualMachine", + }, + }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "test-container", + Image: "test-image:latest", + }, + }, + }, + } + + // Create dynamic client with the test application + dynamicClient := dynamicfake.NewSimpleDynamicClient(testScheme, testApp) + + // Create REST mapping + mapping := &meta.RESTMapping{ + Resource: schema.GroupVersionResource{ + Group: "", + Version: "v1", + Resource: "pods", + }, + GroupVersionKind: schema.GroupVersionKind{ + Group: "", + Version: "v1", + Kind: "Pod", + }, + Scope: meta.RESTScopeNamespace, + } + + // Create BackupJob + backupJob := &backupsv1alpha1.BackupJob{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-backup-job", + Namespace: "default", + }, + Spec: backupsv1alpha1.BackupJobSpec{ + ApplicationRef: corev1.TypedLocalObjectReference{ + APIGroup: stringPtr(""), + Kind: "Pod", + Name: "test-vm", + }, + BackupClassName: "velero", + }, + } + + // Create Velero strategy with template that uses Application and Parameters + veleroStrategy := &strategyv1alpha1.Velero{ + ObjectMeta: metav1.ObjectMeta{ + Name: "velero-strategy", + }, + Spec: strategyv1alpha1.VeleroSpec{ + Template: strategyv1alpha1.VeleroTemplate{ + Spec: velerov1.BackupSpec{ + // Use template variables to verify context is passed correctly + LabelSelector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "app": "{{ .Application.metadata.name }}", + }, + }, + // Use Parameters in template + StorageLocation: "{{ .Parameters.backupStorageLocationName }}", + }, + }, + }, + } + + // Create ResolvedBackupConfig with parameters + resolved := &ResolvedBackupConfig{ + StrategyRef: corev1.TypedLocalObjectReference{ + APIGroup: stringPtr("strategy.backups.cozystack.io"), + Kind: "Velero", + Name: "velero-strategy", + }, + Parameters: map[string]string{ + "backupStorageLocationName": "default-storage", + }, + } + + // Create fake client for controller + fakeClient := clientfake.NewClientBuilder(). + WithScheme(testScheme). + WithObjects(backupJob, veleroStrategy). + Build() + + // Create reconciler with fake event recorder + reconciler := &BackupJobReconciler{ + Client: fakeClient, + Interface: dynamicClient, + RESTMapper: &mockRESTMapper{mapping: mapping}, + Scheme: testScheme, + Recorder: record.NewFakeRecorder(10), + } + + // Create context with logger + ctx := context.Background() + + // Call createVeleroBackup + err := reconciler.createVeleroBackup(ctx, backupJob, veleroStrategy, resolved) + if err != nil { + t.Fatalf("createVeleroBackup() error = %v", err) + } + + // Verify that the template was executed correctly by checking the created Velero Backup + // The template should have replaced {{ .Application.metadata.name }} with "test-vm" + // and {{ .Parameters.backupStorageLocationName }} with "default-storage" + + // Get the created Velero Backup + veleroBackups := &velerov1.BackupList{} + err = fakeClient.List(ctx, veleroBackups, client.InNamespace(veleroNamespace)) + if err != nil { + t.Fatalf("Failed to list Velero Backups: %v", err) + } + + if len(veleroBackups.Items) == 0 { + t.Fatal("Expected Velero Backup to be created, but none found") + } + + veleroBackup := veleroBackups.Items[0] + + // Verify template context was used correctly: + // 1. Application.metadata.name should be replaced with "test-vm" + if veleroBackup.Spec.LabelSelector != nil { + if appLabel, ok := veleroBackup.Spec.LabelSelector.MatchLabels["app"]; ok { + if appLabel != "test-vm" { + t.Errorf("Template context Application.metadata.name not applied correctly. Expected 'test-vm', got '%s'", appLabel) + } + } else { + t.Error("Template context Application.metadata.name not found in label selector") + } + } + + // 2. Parameters.backupStorageLocationName should be replaced with "default-storage" + if veleroBackup.Spec.StorageLocation != "default-storage" { + t.Errorf("Template context Parameters.backupStorageLocationName not applied correctly. Expected 'default-storage', got '%s'", veleroBackup.Spec.StorageLocation) + } +} From 02ace2e482e7f6de48130b3cec4888c6824caad0 Mon Sep 17 00:00:00 2001 From: Andrey Kolkov Date: Tue, 20 Jan 2026 10:07:54 +0400 Subject: [PATCH 061/125] Simplify checks in tests Signed-off-by: Andrey Kolkov --- .../backupclass_resolver_test.go | 103 ++++++++++-------- 1 file changed, 60 insertions(+), 43 deletions(-) diff --git a/internal/backupcontroller/backupclass_resolver_test.go b/internal/backupcontroller/backupclass_resolver_test.go index 4c2b03fc..a606f241 100644 --- a/internal/backupcontroller/backupclass_resolver_test.go +++ b/internal/backupcontroller/backupclass_resolver_test.go @@ -5,19 +5,20 @@ import ( "testing" corev1 "k8s.io/api/core/v1" + apiequality "k8s.io/apimachinery/pkg/api/equality" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "sigs.k8s.io/controller-runtime/pkg/client/fake" - backupsv1alpha1 "github.com/cozystack/cozystack/api/backups/v1alpha1" strategyv1alpha1 "github.com/cozystack/cozystack/api/backups/strategy/v1alpha1" + backupsv1alpha1 "github.com/cozystack/cozystack/api/backups/v1alpha1" ) func TestNormalizeApplicationRef(t *testing.T) { tests := []struct { name string input corev1.TypedLocalObjectReference - expected string + expected corev1.TypedLocalObjectReference }{ { name: "apiGroup not specified - should default to apps.cozystack.io", @@ -25,7 +26,11 @@ func TestNormalizeApplicationRef(t *testing.T) { Kind: "VirtualMachine", Name: "vm1", }, - expected: DefaultApplicationAPIGroup, + expected: corev1.TypedLocalObjectReference{ + APIGroup: stringPtr(DefaultApplicationAPIGroup), + Kind: "VirtualMachine", + Name: "vm1", + }, }, { name: "apiGroup is nil - should default to apps.cozystack.io", @@ -34,7 +39,11 @@ func TestNormalizeApplicationRef(t *testing.T) { Kind: "VirtualMachine", Name: "vm1", }, - expected: DefaultApplicationAPIGroup, + expected: corev1.TypedLocalObjectReference{ + APIGroup: stringPtr(DefaultApplicationAPIGroup), + Kind: "VirtualMachine", + Name: "vm1", + }, }, { name: "apiGroup is empty string - should default to apps.cozystack.io", @@ -43,7 +52,11 @@ func TestNormalizeApplicationRef(t *testing.T) { Kind: "VirtualMachine", Name: "vm1", }, - expected: DefaultApplicationAPIGroup, + expected: corev1.TypedLocalObjectReference{ + APIGroup: stringPtr(DefaultApplicationAPIGroup), + Kind: "VirtualMachine", + Name: "vm1", + }, }, { name: "apiGroup is explicitly set - should keep it", @@ -52,7 +65,11 @@ func TestNormalizeApplicationRef(t *testing.T) { Kind: "CustomApp", Name: "custom-app", }, - expected: "custom.api.group.io", + expected: corev1.TypedLocalObjectReference{ + APIGroup: stringPtr("custom.api.group.io"), + Kind: "CustomApp", + Name: "custom-app", + }, }, { name: "apiGroup is apps.cozystack.io - should keep it", @@ -61,26 +78,19 @@ func TestNormalizeApplicationRef(t *testing.T) { Kind: "VirtualMachine", Name: "vm1", }, - expected: DefaultApplicationAPIGroup, + expected: corev1.TypedLocalObjectReference{ + APIGroup: stringPtr(DefaultApplicationAPIGroup), + Kind: "VirtualMachine", + Name: "vm1", + }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := NormalizeApplicationRef(tt.input) - if result.APIGroup == nil { - t.Errorf("NormalizeApplicationRef() returned nil APIGroup, expected %s", tt.expected) - return - } - if *result.APIGroup != tt.expected { - t.Errorf("NormalizeApplicationRef() APIGroup = %v, want %v", *result.APIGroup, tt.expected) - } - // Verify other fields are preserved - if result.Kind != tt.input.Kind { - t.Errorf("NormalizeApplicationRef() Kind = %v, want %v", result.Kind, tt.input.Kind) - } - if result.Name != tt.input.Name { - t.Errorf("NormalizeApplicationRef() Name = %v, want %v", result.Name, tt.input.Name) + if !apiequality.Semantic.DeepEqual(result, tt.expected) { + t.Errorf("NormalizeApplicationRef() = %v, want %v", result, tt.expected) } }) } @@ -98,13 +108,13 @@ func TestResolveBackupClass(t *testing.T) { } tests := []struct { - name string - backupClass *backupsv1alpha1.BackupClass - applicationRef corev1.TypedLocalObjectReference - backupClassName string - wantErr bool - expectedKind string - expectedParams map[string]string + name string + backupClass *backupsv1alpha1.BackupClass + applicationRef corev1.TypedLocalObjectReference + backupClassName string + wantErr bool + expectedStrategyRef *corev1.TypedLocalObjectReference + expectedParams map[string]string }{ { name: "successful resolution - matches VirtualMachine strategy", @@ -149,7 +159,11 @@ func TestResolveBackupClass(t *testing.T) { }, backupClassName: "velero", wantErr: false, - expectedKind: "VirtualMachine", + expectedStrategyRef: &corev1.TypedLocalObjectReference{ + APIGroup: stringPtr("strategy.backups.cozystack.io"), + Kind: "Velero", + Name: "velero-strategy-vm", + }, expectedParams: map[string]string{ "backupStorageLocationName": "default", }, @@ -186,7 +200,11 @@ func TestResolveBackupClass(t *testing.T) { }, backupClassName: "velero", wantErr: false, - expectedKind: "MySQL", + expectedStrategyRef: &corev1.TypedLocalObjectReference{ + APIGroup: stringPtr("strategy.backups.cozystack.io"), + Kind: "Velero", + Name: "velero-strategy-mysql", + }, expectedParams: map[string]string{ "backupStorageLocationName": "mysql-storage", }, @@ -222,7 +240,11 @@ func TestResolveBackupClass(t *testing.T) { }, backupClassName: "velero", wantErr: false, - expectedKind: "VirtualMachine", + expectedStrategyRef: &corev1.TypedLocalObjectReference{ + APIGroup: stringPtr("strategy.backups.cozystack.io"), + Kind: "Velero", + Name: "velero-strategy-vm", + }, expectedParams: map[string]string{ "backupStorageLocationName": "default", }, @@ -331,22 +353,17 @@ func TestResolveBackupClass(t *testing.T) { return } - // Verify strategy ref - if resolved.StrategyRef.Kind != "Velero" { - t.Errorf("ResolveBackupClass() StrategyRef.Kind = %v, want Velero", resolved.StrategyRef.Kind) + // Verify strategy ref using apimachinery equality + if tt.expectedStrategyRef != nil { + if !apiequality.Semantic.DeepEqual(resolved.StrategyRef, *tt.expectedStrategyRef) { + t.Errorf("ResolveBackupClass() StrategyRef = %v, want %v", resolved.StrategyRef, *tt.expectedStrategyRef) + } } - // Verify parameters + // Verify parameters using apimachinery equality if tt.expectedParams != nil { - for key, expectedValue := range tt.expectedParams { - actualValue, ok := resolved.Parameters[key] - if !ok { - t.Errorf("ResolveBackupClass() Parameters[%s] not found", key) - continue - } - if actualValue != expectedValue { - t.Errorf("ResolveBackupClass() Parameters[%s] = %v, want %v", key, actualValue, expectedValue) - } + if !apiequality.Semantic.DeepEqual(resolved.Parameters, tt.expectedParams) { + t.Errorf("ResolveBackupClass() Parameters = %v, want %v", resolved.Parameters, tt.expectedParams) } } }) From 272d2b7a206a15d7dab4e75eb9400f392687aa25 Mon Sep 17 00:00:00 2001 From: Andrey Kolkov Date: Tue, 20 Jan 2026 10:32:13 +0400 Subject: [PATCH 062/125] updated design doc Signed-off-by: Andrey Kolkov --- api/backups/v1alpha1/DESIGN.md | 115 ++++++++++++++++++++++++--------- 1 file changed, 85 insertions(+), 30 deletions(-) diff --git a/api/backups/v1alpha1/DESIGN.md b/api/backups/v1alpha1/DESIGN.md index 455dab59..abf93cfe 100644 --- a/api/backups/v1alpha1/DESIGN.md +++ b/api/backups/v1alpha1/DESIGN.md @@ -100,13 +100,13 @@ Describe **when**, **how**, and **where** to back up a specific managed applicat ```go type PlanSpec struct { // Application to back up. + // If apiGroup is not specified, it defaults to "apps.cozystack.io". ApplicationRef corev1.TypedLocalObjectReference `json:"applicationRef"` - // Where backups should be stored. - StorageRef corev1.TypedLocalObjectReference `json:"storageRef"` - - // Driver-specific BackupStrategy to use. - StrategyRef corev1.TypedLocalObjectReference `json:"strategyRef"` + // BackupClassName references a BackupClass that contains strategy and other parameters (e.g. storage reference). + // The BackupClass will be resolved to determine the appropriate strategy and parameters + // based on the ApplicationRef. + BackupClassName string `json:"backupClassName"` // When backups should run. Schedule PlanSchedule `json:"schedule"` @@ -145,12 +145,12 @@ Core Plan controller: * Create a `BackupJob` in the same namespace: * `spec.planRef.name = plan.Name` - * `spec.applicationRef = plan.spec.applicationRef` - * `spec.storageRef = plan.spec.storageRef` - * `spec.strategyRef = plan.spec.strategyRef` - * `spec.triggeredBy = "Plan"` + * `spec.applicationRef = plan.spec.applicationRef` (normalized with default apiGroup if not specified) + * `spec.backupClassName = plan.spec.backupClassName` * Set `ownerReferences` so the `BackupJob` is owned by the `Plan`. +**Note:** The `BackupJob` controller resolves the `BackupClass` to determine the appropriate strategy and parameters, based on the `ApplicationRef`. The strategy template is processed with a context containing the `Application` object and `Parameters` from the `BackupClass`. + The Plan controller does **not**: * Execute backups itself. @@ -159,17 +159,64 @@ The Plan controller does **not**: --- -### 4.2 Storage +### 4.2 BackupClass -**API Shape** +**Group/Kind** +`backups.cozystack.io/v1alpha1, Kind=BackupClass` -TBD +**Purpose** +Define a class of backup configurations that encapsulate strategy and parameters per application type. `BackupClass` is a cluster-scoped resource that allows admins to configure backup strategies and parameters in a reusable way. -**Storage usage** +**Key fields (spec)** -* `Plan` and `BackupJob` reference `Storage` via `TypedLocalObjectReference`. -* Drivers read `Storage` to know how/where to store or read artifacts. -* Core treats `Storage` spec as opaque; it does not directly talk to S3 or buckets. +```go +type BackupClassSpec struct { + // Strategies is a list of backup strategies, each matching a specific application type. + Strategies []BackupClassStrategy `json:"strategies"` +} + +type BackupClassStrategy struct { + // StrategyRef references the driver-specific BackupStrategy (e.g., Velero). + StrategyRef corev1.TypedLocalObjectReference `json:"strategyRef"` + + // Application specifies which application types this strategy applies to. + // If apiGroup is not specified, it defaults to "apps.cozystack.io". + Application ApplicationSelector `json:"application"` + + // Parameters holds strategy-specific parameters, like storage reference. + // Common parameters include: + // - backupStorageLocationName: Name of Velero BackupStorageLocation + // +optional + Parameters map[string]string `json:"parameters,omitempty"` +} + +type ApplicationSelector struct { + // APIGroup is the API group of the application. + // If not specified, defaults to "apps.cozystack.io". + // +optional + APIGroup *string `json:"apiGroup,omitempty"` + + // Kind is the kind of the application (e.g., VirtualMachine, MySQL). + Kind string `json:"kind"` +} +``` + +**BackupClass resolution** + +* When a `BackupJob` or `Plan` references a `BackupClass` via `backupClassName`, the controller: + 1. Fetches the `BackupClass` by name. + 2. Matches the `ApplicationRef` against strategies in the `BackupClass`: + * Normalizes `ApplicationRef.apiGroup` (defaults to `"apps.cozystack.io"` if not specified). + * Finds a strategy where `ApplicationSelector` matches the `ApplicationRef` (apiGroup and kind). + 3. Returns the matched `StrategyRef` and `Parameters`. +* Strategy templates (e.g., Velero's `backupTemplate.spec`) are processed with a context containing: + * `Application`: The application object being backed up. + * `Parameters`: The parameters from the matched `BackupClassStrategy`. + +**Parameters** + +* Parameters are passed via `Parameters` in the `BackupClass` (e.g., `backupStorageLocationName` for Velero). +* The driver uses these parameters to resolve the actual resources (e.g., Velero's `BackupStorageLocation` CRD). --- @@ -189,16 +236,13 @@ type BackupJobSpec struct { PlanRef *corev1.LocalObjectReference `json:"planRef,omitempty"` // Application to back up. + // If apiGroup is not specified, it defaults to "apps.cozystack.io". ApplicationRef corev1.TypedLocalObjectReference `json:"applicationRef"` - // Storage to use. - StorageRef corev1.TypedLocalObjectReference `json:"storageRef"` - - // Driver-specific BackupStrategy to use. - StrategyRef corev1.TypedLocalObjectReference `json:"strategyRef"` - - // Informational: what triggered this run ("Plan", "Manual", etc.). - TriggeredBy string `json:"triggeredBy,omitempty"` + // BackupClassName references a BackupClass that contains strategy and related parameters + // The BackupClass will be resolved to determine the appropriate strategy and parameters + // based on the ApplicationRef. + BackupClassName string `json:"backupClassName"` } ``` @@ -223,7 +267,9 @@ type BackupJobStatus struct { * Each driver controller: * Watches `BackupJob`. - * Reconciles runs where `spec.strategyRef.apiGroup/kind` matches its **strategy type(s)**. + * Resolves the `BackupClass` referenced by `spec.backupClassName`. + * Matches the `ApplicationRef` against strategies in the `BackupClass` to find the appropriate strategy. + * Reconciles runs where the resolved strategy's `apiGroup/kind` matches its **strategy type(s)**. * Driver responsibilities: 1. On first reconcile: @@ -232,7 +278,12 @@ type BackupJobStatus struct { * Set `status.phase = Running`. 2. Resolve inputs: - * Read `Strategy` (driver-owned CRD), `Storage`, `Application`, optionally `Plan`. + * Resolve `BackupClass` from `spec.backupClassName`. + * Match `ApplicationRef` against `BackupClass` strategies to get `StrategyRef` and `Parameters`. + * Read `Strategy` (driver-owned CRD) from `StrategyRef`. + * Read `Application` from `ApplicationRef`. + * Extract parameters from `Parameters` (e.g., `backupStorageLocationName` for Velero). + * Process strategy template with context: `Application` object and `Parameters` from `BackupClass`. 3. Execute backup logic (implementation-specific). 4. On success: @@ -264,13 +315,14 @@ Represent a single **backup artifact** for a given application, decoupled from a type BackupSpec struct { ApplicationRef corev1.TypedLocalObjectReference `json:"applicationRef"` PlanRef *corev1.LocalObjectReference `json:"planRef,omitempty"` - StorageRef corev1.TypedLocalObjectReference `json:"storageRef"` StrategyRef corev1.TypedLocalObjectReference `json:"strategyRef"` TakenAt metav1.Time `json:"takenAt"` DriverMetadata map[string]string `json:"driverMetadata,omitempty"` } ``` +**Note:** Parameters are no stored directly in `Backup`. Instead, they are resolved from `BackupClass` parameters when the backup was created. The storage location is managed by the driver (e.g., Velero's `BackupStorageLocation`) and referenced via parameters in the `BackupClass`. + **Key fields (status)** ```go @@ -290,7 +342,8 @@ type BackupStatus struct { * Creates a `Backup` in the same namespace (typically owned by the `BackupJob`). * Populates `spec` fields with: - * The application, storage, strategy references. + * The application reference. + * The strategy reference (resolved from `BackupClass` during `BackupJob` execution). * `takenAt`. * Optional `driverMetadata`. * Sets `status` with: @@ -306,6 +359,8 @@ type BackupStatus struct { * Anchor `RestoreJob` operations. * Implement higher-level policies (retention) if needed. +**Note:** Parameters are resolved from `BackupClass` when the `BackupJob` is created. The driver uses these parameters to determine where to store backups. The storage location itself is managed by the driver (e.g., Velero's `BackupStorageLocation` CRD) and is not directly referenced in the `Backup` resource. When restoring, the driver resolves the storage location from the original `BackupClass` parameters or from the driver's own metadata. + --- ### 4.5 RestoreJob @@ -353,13 +408,13 @@ type RestoreJobStatus struct { * Determines effective: * **Strategy**: `backup.spec.strategyRef`. - * **Storage**: `backup.spec.storageRef`. + * **Storage**: Resolved from driver metadata or `BackupClass` parameters (e.g., `backupStorageLocationName` stored in `driverMetadata` or resolved from the original `BackupClass`). * **Target application**: `spec.targetApplicationRef` or `backup.spec.applicationRef`. * If effective strategy’s GVK is one of its supported strategy types → driver is responsible. 3. Behaviour: * On first reconcile, set `status.startedAt` and `phase = Running`. - * Resolve `Backup`, `Storage`, `Strategy`, target application. + * Resolve `Backup`, storage location (from driver metadata or `BackupClass`), `Strategy`, target application. * Execute restore logic (implementation-specific). * On success: From e3aab2481032ee7f5d37d2dd9c06dd383808fd52 Mon Sep 17 00:00:00 2001 From: Andrey Kolkov Date: Tue, 20 Jan 2026 11:09:35 +0400 Subject: [PATCH 063/125] fixes Signed-off-by: Andrey Kolkov --- api/backups/v1alpha1/DESIGN.md | 8 +++++--- internal/backupcontroller/velerostrategy_controller.go | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/api/backups/v1alpha1/DESIGN.md b/api/backups/v1alpha1/DESIGN.md index abf93cfe..bab753b2 100644 --- a/api/backups/v1alpha1/DESIGN.md +++ b/api/backups/v1alpha1/DESIGN.md @@ -321,7 +321,7 @@ type BackupSpec struct { } ``` -**Note:** Parameters are no stored directly in `Backup`. Instead, they are resolved from `BackupClass` parameters when the backup was created. The storage location is managed by the driver (e.g., Velero's `BackupStorageLocation`) and referenced via parameters in the `BackupClass`. +**Note:** Parameters are not stored directly in `Backup`. Instead, they are resolved from `BackupClass` parameters when the backup was created. The storage location is managed by the driver (e.g., Velero's `BackupStorageLocation`) and referenced via parameters in the `BackupClass`. **Key fields (status)** @@ -469,8 +469,10 @@ The Cozystack backups core API: * Uses a single group, `backups.cozystack.io`, for all core CRDs. * Cleanly separates: - * **When & where** (Plan + Storage) – core-owned. + * **When** (Plan schedule) – core-owned. + * **How & where** (BackupClass) – central configuration unit that encapsulates strategy and parameters (e.g., storage reference) per application type, resolved per BackupJob/Plan. + * **Execution** (BackupJob) – created by Plan when schedule fires, resolves BackupClass to get strategy and parameters, then delegates to driver. * **What backup artifacts exist** (Backup) – driver-created but cluster-visible. - * **Execution lifecycle** (BackupJob, RestoreJob) – shared contract boundary. + * **Restore lifecycle** (RestoreJob) – shared contract boundary. * Allows multiple strategy drivers to implement backup/restore logic without entangling their implementation with the core API. diff --git a/internal/backupcontroller/velerostrategy_controller.go b/internal/backupcontroller/velerostrategy_controller.go index 4a1225f6..0986d648 100644 --- a/internal/backupcontroller/velerostrategy_controller.go +++ b/internal/backupcontroller/velerostrategy_controller.go @@ -303,7 +303,7 @@ func (r *BackupJobReconciler) createBackupResource(ctx context.Context, backupJo // Create a basic artifact referencing the Velero backup artifact := &backupsv1alpha1.BackupArtifact{ - URI: fmt.Sprintf("velero://%s/%s", backupJob.Namespace, veleroBackup.Name), + URI: fmt.Sprintf("velero://%s/%s", veleroBackup.Namespace, veleroBackup.Name), } backup := &backupsv1alpha1.Backup{ From f254c5f03e5866ff70385e3ef428bbbfa0207b84 Mon Sep 17 00:00:00 2001 From: Andrey Kolkov Date: Tue, 20 Jan 2026 11:12:48 +0400 Subject: [PATCH 064/125] fix test Signed-off-by: Andrey Kolkov --- .../velerostrategy_controller_test.go | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/internal/backupcontroller/velerostrategy_controller_test.go b/internal/backupcontroller/velerostrategy_controller_test.go index 0575a681..a7de2076 100644 --- a/internal/backupcontroller/velerostrategy_controller_test.go +++ b/internal/backupcontroller/velerostrategy_controller_test.go @@ -190,14 +190,15 @@ func TestCreateVeleroBackup_TemplateContext(t *testing.T) { // Verify template context was used correctly: // 1. Application.metadata.name should be replaced with "test-vm" - if veleroBackup.Spec.LabelSelector != nil { - if appLabel, ok := veleroBackup.Spec.LabelSelector.MatchLabels["app"]; ok { - if appLabel != "test-vm" { - t.Errorf("Template context Application.metadata.name not applied correctly. Expected 'test-vm', got '%s'", appLabel) - } - } else { - t.Error("Template context Application.metadata.name not found in label selector") + if veleroBackup.Spec.LabelSelector == nil { + t.Fatal("Expected LabelSelector to be set by template") + } + if appLabel, ok := veleroBackup.Spec.LabelSelector.MatchLabels["app"]; ok { + if appLabel != "test-vm" { + t.Errorf("Template context Application.metadata.name not applied correctly. Expected 'test-vm', got '%s'", appLabel) } + } else { + t.Error("Template context Application.metadata.name not found in label selector") } // 2. Parameters.backupStorageLocationName should be replaced with "default-storage" From f6641c15470ed0a97f3f502250dc8abafebf16a4 Mon Sep 17 00:00:00 2001 From: Andrey Kolkov Date: Tue, 20 Jan 2026 11:58:25 +0400 Subject: [PATCH 065/125] fixes Signed-off-by: Andrey Kolkov --- api/backups/v1alpha1/zz_generated.deepcopy.go | 294 +++++++++--------- .../backupcontroller/backupjob_controller.go | 36 +++ .../backups.cozystack.io_backupclasses.yaml | 171 ++++++++++ .../backups.cozystack.io_backupjobs.yaml | 54 +--- .../backups.cozystack.io_backups.yaml | 23 -- .../backups.cozystack.io_plans.yaml | 54 +--- 6 files changed, 370 insertions(+), 262 deletions(-) create mode 100644 packages/system/backup-controller/definitions/backups.cozystack.io_backupclasses.yaml diff --git a/api/backups/v1alpha1/zz_generated.deepcopy.go b/api/backups/v1alpha1/zz_generated.deepcopy.go index 6fbc1d16..b2e4a680 100644 --- a/api/backups/v1alpha1/zz_generated.deepcopy.go +++ b/api/backups/v1alpha1/zz_generated.deepcopy.go @@ -26,6 +26,26 @@ import ( "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ApplicationSelector) DeepCopyInto(out *ApplicationSelector) { + *out = *in + if in.APIGroup != nil { + in, out := &in.APIGroup, &out.APIGroup + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationSelector. +func (in *ApplicationSelector) DeepCopy() *ApplicationSelector { + if in == nil { + return nil + } + out := new(ApplicationSelector) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Backup) DeepCopyInto(out *Backup) { *out = *in @@ -68,6 +88,133 @@ func (in *BackupArtifact) DeepCopy() *BackupArtifact { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackupClass) DeepCopyInto(out *BackupClass) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupClass. +func (in *BackupClass) DeepCopy() *BackupClass { + if in == nil { + return nil + } + out := new(BackupClass) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *BackupClass) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackupClassList) DeepCopyInto(out *BackupClassList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]BackupClass, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupClassList. +func (in *BackupClassList) DeepCopy() *BackupClassList { + if in == nil { + return nil + } + out := new(BackupClassList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *BackupClassList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackupClassSpec) DeepCopyInto(out *BackupClassSpec) { + *out = *in + if in.Strategies != nil { + in, out := &in.Strategies, &out.Strategies + *out = make([]BackupClassStrategy, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupClassSpec. +func (in *BackupClassSpec) DeepCopy() *BackupClassSpec { + if in == nil { + return nil + } + out := new(BackupClassSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackupClassStatus) DeepCopyInto(out *BackupClassStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]metav1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupClassStatus. +func (in *BackupClassStatus) DeepCopy() *BackupClassStatus { + if in == nil { + return nil + } + out := new(BackupClassStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *BackupClassStrategy) DeepCopyInto(out *BackupClassStrategy) { + *out = *in + in.StrategyRef.DeepCopyInto(&out.StrategyRef) + in.Application.DeepCopyInto(&out.Application) + if in.Parameters != nil { + in, out := &in.Parameters, &out.Parameters + *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 BackupClassStrategy. +func (in *BackupClassStrategy) DeepCopy() *BackupClassStrategy { + if in == nil { + return nil + } + out := new(BackupClassStrategy) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *BackupJob) DeepCopyInto(out *BackupJob) { *out = *in @@ -494,150 +641,3 @@ func (in *RestoreJobStatus) DeepCopy() *RestoreJobStatus { in.DeepCopyInto(out) return out } - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BackupClass) DeepCopyInto(out *BackupClass) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupClass. -func (in *BackupClass) DeepCopy() *BackupClass { - if in == nil { - return nil - } - out := new(BackupClass) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *BackupClass) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BackupClassList) DeepCopyInto(out *BackupClassList) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ListMeta.DeepCopyInto(&out.ListMeta) - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]BackupClass, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupClassList. -func (in *BackupClassList) DeepCopy() *BackupClassList { - if in == nil { - return nil - } - out := new(BackupClassList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *BackupClassList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BackupClassSpec) DeepCopyInto(out *BackupClassSpec) { - *out = *in - if in.Strategies != nil { - in, out := &in.Strategies, &out.Strategies - *out = make([]BackupClassStrategy, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupClassSpec. -func (in *BackupClassSpec) DeepCopy() *BackupClassSpec { - if in == nil { - return nil - } - out := new(BackupClassSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BackupClassStrategy) DeepCopyInto(out *BackupClassStrategy) { - *out = *in - in.StrategyRef.DeepCopyInto(&out.StrategyRef) - in.Application.DeepCopyInto(&out.Application) - if in.Parameters != nil { - in, out := &in.Parameters, &out.Parameters - *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 BackupClassStrategy. -func (in *BackupClassStrategy) DeepCopy() *BackupClassStrategy { - if in == nil { - return nil - } - out := new(BackupClassStrategy) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ApplicationSelector) DeepCopyInto(out *ApplicationSelector) { - *out = *in - if in.APIGroup != nil { - in, out := &in.APIGroup, &out.APIGroup - *out = new(string) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ApplicationSelector. -func (in *ApplicationSelector) DeepCopy() *ApplicationSelector { - if in == nil { - return nil - } - out := new(ApplicationSelector) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BackupClassStatus) DeepCopyInto(out *BackupClassStatus) { - *out = *in - if in.Conditions != nil { - in, out := &in.Conditions, &out.Conditions - *out = make([]metav1.Condition, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BackupClassStatus. -func (in *BackupClassStatus) DeepCopy() *BackupClassStatus { - if in == nil { - return nil - } - out := new(BackupClassStatus) - in.DeepCopyInto(out) - return out -} diff --git a/internal/backupcontroller/backupjob_controller.go b/internal/backupcontroller/backupjob_controller.go index db7e701a..c04ec85d 100644 --- a/internal/backupcontroller/backupjob_controller.go +++ b/internal/backupcontroller/backupjob_controller.go @@ -14,7 +14,10 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/apiutil" + "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" strategyv1alpha1 "github.com/cozystack/cozystack/api/backups/strategy/v1alpha1" backupsv1alpha1 "github.com/cozystack/cozystack/api/backups/v1alpha1" @@ -88,6 +91,17 @@ func (r *BackupJobReconciler) Reconcile(ctx context.Context, req ctrl.Request) ( // SetupWithManager registers our controller with the Manager and sets up watches. func (r *BackupJobReconciler) SetupWithManager(mgr ctrl.Manager) error { + // index BackupJob by backupClassName for efficient lookups when BackupClass changes + if err := mgr.GetFieldIndexer().IndexField(context.Background(), &backupsv1alpha1.BackupJob{}, "spec.backupClassName", func(obj client.Object) []string { + job := obj.(*backupsv1alpha1.BackupJob) + if job.Spec.BackupClassName == "" { + return []string{} + } + return []string{job.Spec.BackupClassName} + }); err != nil { + return err + } + cfg := mgr.GetConfig() var err error if r.Interface, err = dynamic.NewForConfig(cfg); err != nil { @@ -102,5 +116,27 @@ func (r *BackupJobReconciler) SetupWithManager(mgr ctrl.Manager) error { } return ctrl.NewControllerManagedBy(mgr). For(&backupsv1alpha1.BackupJob{}). + // Requeue BackupJobs when their referenced BackupClass changes + WatchesRawSource(source.Kind( + mgr.GetCache(), + &backupsv1alpha1.BackupClass{}, + handler.TypedEnqueueRequestsFromMapFunc(func(ctx context.Context, bc *backupsv1alpha1.BackupClass) []reconcile.Request { + var jobs backupsv1alpha1.BackupJobList + if err := r.List(ctx, &jobs, client.MatchingFields{"spec.backupClassName": bc.Name}); err != nil { + return nil + } + + reqs := make([]reconcile.Request, 0, len(jobs.Items)) + for _, job := range jobs.Items { + reqs = append(reqs, reconcile.Request{ + NamespacedName: types.NamespacedName{ + Namespace: job.Namespace, + Name: job.Name, + }, + }) + } + return reqs + }), + )). Complete(r) } diff --git a/packages/system/backup-controller/definitions/backups.cozystack.io_backupclasses.yaml b/packages/system/backup-controller/definitions/backups.cozystack.io_backupclasses.yaml new file mode 100644 index 00000000..b0f0fec2 --- /dev/null +++ b/packages/system/backup-controller/definitions/backups.cozystack.io_backupclasses.yaml @@ -0,0 +1,171 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.4 + name: backupclasses.backups.cozystack.io +spec: + group: backups.cozystack.io + names: + kind: BackupClass + listKind: BackupClassList + plural: backupclasses + singular: backupclass + scope: Cluster + versions: + - name: v1alpha1 + schema: + openAPIV3Schema: + description: |- + BackupClass defines a class of backup configurations that can be referenced + by BackupJob and Plan resources. It encapsulates strategy and storage configuration + per application type. + properties: + apiVersion: + description: |- + APIVersion defines the versioned schema of this representation of an object. + Servers should convert recognized schemas to the latest internal value, and + may reject unrecognized values. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources + type: string + kind: + description: |- + Kind is a string value representing the REST resource this object represents. + Servers may infer this from the endpoint the client submits requests to. + Cannot be updated. + In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds + type: string + metadata: + type: object + spec: + description: BackupClassSpec defines the desired state of a BackupClass. + properties: + strategies: + description: Strategies is a list of backup strategies, each matching + a specific application type. + items: + description: BackupClassStrategy defines a backup strategy for a + specific application type. + properties: + application: + description: Application specifies which application types this + strategy applies to. + properties: + apiGroup: + description: |- + APIGroup is the API group of the application. + If not specified, defaults to "apps.cozystack.io". + type: string + kind: + description: Kind is the kind of the application (e.g., + VirtualMachine, MySQL). + type: string + required: + - kind + type: object + parameters: + additionalProperties: + type: string + description: |- + Parameters holds strategy-specific and storage-specific parameters. + Common parameters include: + - backupStorageLocationName: Name of Velero BackupStorageLocation + type: object + strategyRef: + description: StrategyRef references the driver-specific BackupStrategy + (e.g., Velero). + properties: + apiGroup: + description: |- + APIGroup is the group for the resource being referenced. + If APIGroup is not specified, the specified Kind must be in the core API group. + For any other third-party types, APIGroup is required. + type: string + kind: + description: Kind is the type of resource being referenced + type: string + name: + description: Name is the name of resource being referenced + type: string + required: + - kind + - name + type: object + x-kubernetes-map-type: atomic + required: + - application + - strategyRef + type: object + type: array + required: + - strategies + type: object + status: + description: BackupClassStatus defines the observed state of a BackupClass. + properties: + conditions: + description: Conditions represents the latest available observations + of a BackupClass's state. + items: + description: Condition contains details for one aspect of the current + state of this API Resource. + properties: + lastTransitionTime: + description: |- + lastTransitionTime is the last time the condition transitioned from one status to another. + This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable. + format: date-time + type: string + message: + description: |- + message is a human readable message indicating details about the transition. + This may be an empty string. + maxLength: 32768 + type: string + observedGeneration: + description: |- + observedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + minimum: 0 + type: integer + reason: + description: |- + reason contains a programmatic identifier indicating the reason for the condition's last transition. + Producers of specific condition types may define expected values and meanings for this field, + and whether the values are considered a guaranteed API. + The value should be a CamelCase string. + This field may not be empty. + maxLength: 1024 + minLength: 1 + pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ + type: string + status: + description: status of the condition, one of True, False, Unknown. + enum: + - "True" + - "False" + - Unknown + type: string + type: + description: type of condition in CamelCase or in foo.example.com/CamelCase. + maxLength: 316 + pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ + type: string + required: + - lastTransitionTime + - message + - reason + - status + - type + type: object + type: array + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/packages/system/backup-controller/definitions/backups.cozystack.io_backupjobs.yaml b/packages/system/backup-controller/definitions/backups.cozystack.io_backupjobs.yaml index 74349181..686c079f 100644 --- a/packages/system/backup-controller/definitions/backups.cozystack.io_backupjobs.yaml +++ b/packages/system/backup-controller/definitions/backups.cozystack.io_backupjobs.yaml @@ -50,6 +50,7 @@ spec: description: |- ApplicationRef holds a reference to the managed application whose state is being backed up. + If apiGroup is not specified, it defaults to "apps.cozystack.io". properties: apiGroup: description: |- @@ -68,6 +69,12 @@ spec: - name type: object x-kubernetes-map-type: atomic + backupClassName: + description: |- + BackupClassName references a BackupClass that contains strategy and storage configuration. + The BackupClass will be resolved to determine the appropriate strategy and storage + based on the ApplicationRef. + type: string planRef: description: |- PlanRef refers to the Plan that requested this backup run. @@ -84,54 +91,9 @@ spec: type: string type: object x-kubernetes-map-type: atomic - storageRef: - description: |- - StorageRef holds a reference to the Storage object that describes where - the backup will be stored. - properties: - apiGroup: - description: |- - APIGroup is the group for the resource being referenced. - If APIGroup is not specified, the specified Kind must be in the core API group. - For any other third-party types, APIGroup is required. - type: string - kind: - description: Kind is the type of resource being referenced - type: string - name: - description: Name is the name of resource being referenced - type: string - required: - - kind - - name - type: object - x-kubernetes-map-type: atomic - strategyRef: - description: |- - StrategyRef holds a reference to the driver-specific BackupStrategy object - that describes how the backup should be created. - properties: - apiGroup: - description: |- - APIGroup is the group for the resource being referenced. - If APIGroup is not specified, the specified Kind must be in the core API group. - For any other third-party types, APIGroup is required. - type: string - kind: - description: Kind is the type of resource being referenced - type: string - name: - description: Name is the name of resource being referenced - type: string - required: - - kind - - name - type: object - x-kubernetes-map-type: atomic required: - applicationRef - - storageRef - - strategyRef + - backupClassName type: object status: description: BackupJobStatus represents the observed state of a BackupJob. diff --git a/packages/system/backup-controller/definitions/backups.cozystack.io_backups.yaml b/packages/system/backup-controller/definitions/backups.cozystack.io_backups.yaml index 13d9bc62..6d55cb84 100644 --- a/packages/system/backup-controller/definitions/backups.cozystack.io_backups.yaml +++ b/packages/system/backup-controller/definitions/backups.cozystack.io_backups.yaml @@ -85,28 +85,6 @@ spec: type: string type: object x-kubernetes-map-type: atomic - storageRef: - description: |- - StorageRef refers to the Storage object that describes where the backup - artifact is stored. - properties: - apiGroup: - description: |- - APIGroup is the group for the resource being referenced. - If APIGroup is not specified, the specified Kind must be in the core API group. - For any other third-party types, APIGroup is required. - type: string - kind: - description: Kind is the type of resource being referenced - type: string - name: - description: Name is the name of resource being referenced - type: string - required: - - kind - - name - type: object - x-kubernetes-map-type: atomic strategyRef: description: |- StrategyRef refers to the driver-specific BackupStrategy that was used @@ -137,7 +115,6 @@ spec: type: string required: - applicationRef - - storageRef - strategyRef - takenAt type: object diff --git a/packages/system/backup-controller/definitions/backups.cozystack.io_plans.yaml b/packages/system/backup-controller/definitions/backups.cozystack.io_plans.yaml index 1c6829cc..94c09334 100644 --- a/packages/system/backup-controller/definitions/backups.cozystack.io_plans.yaml +++ b/packages/system/backup-controller/definitions/backups.cozystack.io_plans.yaml @@ -47,6 +47,7 @@ spec: description: |- ApplicationRef holds a reference to the managed application, whose state and configuration must be backed up. + If apiGroup is not specified, it defaults to "apps.cozystack.io". properties: apiGroup: description: |- @@ -65,6 +66,12 @@ spec: - name type: object x-kubernetes-map-type: atomic + backupClassName: + description: |- + BackupClassName references a BackupClass that contains strategy and storage configuration. + The BackupClass will be resolved to determine the appropriate strategy and storage + based on the ApplicationRef. + type: string schedule: description: Schedule specifies when backup copies are created. properties: @@ -80,55 +87,10 @@ spec: [`cron`]. If omitted, defaults to `cron`. type: string type: object - storageRef: - description: |- - StorageRef holds a reference to the Storage object that - describes the location where the backup will be stored. - properties: - apiGroup: - description: |- - APIGroup is the group for the resource being referenced. - If APIGroup is not specified, the specified Kind must be in the core API group. - For any other third-party types, APIGroup is required. - type: string - kind: - description: Kind is the type of resource being referenced - type: string - name: - description: Name is the name of resource being referenced - type: string - required: - - kind - - name - type: object - x-kubernetes-map-type: atomic - strategyRef: - description: |- - StrategyRef holds a reference to the Strategy object that - describes, how a backup copy is to be created. - properties: - apiGroup: - description: |- - APIGroup is the group for the resource being referenced. - If APIGroup is not specified, the specified Kind must be in the core API group. - For any other third-party types, APIGroup is required. - type: string - kind: - description: Kind is the type of resource being referenced - type: string - name: - description: Name is the name of resource being referenced - type: string - required: - - kind - - name - type: object - x-kubernetes-map-type: atomic required: - applicationRef + - backupClassName - schedule - - storageRef - - strategyRef type: object status: properties: From 2cca1bc8d85ae016b36c0cf5ffcbc8c030edd2a7 Mon Sep 17 00:00:00 2001 From: Andrey Kolkov Date: Thu, 22 Jan 2026 12:30:41 +0400 Subject: [PATCH 066/125] add webhook for validation backupClass is immutable in BackupJob Signed-off-by: Andrey Kolkov --- api/backups/v1alpha1/backupjob_types.go | 2 + api/backups/v1alpha1/backupjob_webhook.go | 71 +++++++++++++++++++++++ cmd/backup-controller/main.go | 6 ++ 3 files changed, 79 insertions(+) create mode 100644 api/backups/v1alpha1/backupjob_webhook.go diff --git a/api/backups/v1alpha1/backupjob_types.go b/api/backups/v1alpha1/backupjob_types.go index f9aa42e6..6675c71d 100644 --- a/api/backups/v1alpha1/backupjob_types.go +++ b/api/backups/v1alpha1/backupjob_types.go @@ -52,6 +52,8 @@ type BackupJobSpec struct { // BackupClassName references a BackupClass that contains strategy and storage configuration. // The BackupClass will be resolved to determine the appropriate strategy and storage // based on the ApplicationRef. + // This field is immutable once the BackupJob is created. + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="backupClassName is immutable" BackupClassName string `json:"backupClassName"` } diff --git a/api/backups/v1alpha1/backupjob_webhook.go b/api/backups/v1alpha1/backupjob_webhook.go new file mode 100644 index 00000000..8c167c22 --- /dev/null +++ b/api/backups/v1alpha1/backupjob_webhook.go @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: Apache-2.0 +package v1alpha1 + +import ( + "context" + "fmt" + + apierrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/webhook" + "sigs.k8s.io/controller-runtime/pkg/webhook/admission" +) + +// SetupWebhookWithManager registers the BackupJob webhook with the manager. +func SetupBackupJobWebhookWithManager(mgr ctrl.Manager) error { + return ctrl.NewWebhookManagedBy(mgr). + For(&BackupJob{}). + Complete() +} + +// +kubebuilder:webhook:path=/mutate-backups-cozystack-io-v1alpha1-backupjob,mutating=true,failurePolicy=fail,sideEffects=None,groups=backups.cozystack.io,resources=backupjobs,verbs=create;update,versions=v1alpha1,name=mbackupjob.kb.io,admissionReviewVersions=v1 + +var _ webhook.Defaulter = &BackupJob{} + +// Default implements webhook.Defaulter so a webhook will be registered for the type +func (j *BackupJob) Default() { + // No defaults needed for BackupJob currently +} + +// +kubebuilder:webhook:path=/validate-backups-cozystack-io-v1alpha1-backupjob,mutating=false,failurePolicy=fail,sideEffects=None,groups=backups.cozystack.io,resources=backupjobs,verbs=create;update,versions=v1alpha1,name=vbackupjob.kb.io,admissionReviewVersions=v1 + +var _ webhook.Validator = &BackupJob{} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (j *BackupJob) ValidateCreate() (admission.Warnings, error) { + logger := log.FromContext(context.Background()) + logger.Info("validating BackupJob creation", "name", j.Name, "namespace", j.Namespace) + + // Validate that backupClassName is set + if j.Spec.BackupClassName == "" { + return nil, fmt.Errorf("backupClassName is required and cannot be empty") + } + + return nil, nil +} + +// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type +func (j *BackupJob) ValidateUpdate(old runtime.Object) (admission.Warnings, error) { + logger := log.FromContext(context.Background()) + logger.Info("validating BackupJob update", "name", j.Name, "namespace", j.Namespace) + + oldJob, ok := old.(*BackupJob) + if !ok { + return nil, apierrors.NewBadRequest(fmt.Sprintf("expected a BackupJob but got a %T", old)) + } + + // Enforce immutability of backupClassName + if oldJob.Spec.BackupClassName != "" && oldJob.Spec.BackupClassName != j.Spec.BackupClassName { + return nil, fmt.Errorf("backupClassName is immutable and cannot be changed from %q to %q", oldJob.Spec.BackupClassName, j.Spec.BackupClassName) + } + + return nil, nil +} + +// ValidateDelete implements webhook.Validator so a webhook will be registered for the type +func (j *BackupJob) ValidateDelete() (admission.Warnings, error) { + // No validation needed for deletion + return nil, nil +} diff --git a/cmd/backup-controller/main.go b/cmd/backup-controller/main.go index a99f9350..3278ecf7 100644 --- a/cmd/backup-controller/main.go +++ b/cmd/backup-controller/main.go @@ -168,6 +168,12 @@ func main() { os.Exit(1) } + // Register BackupJob webhook for validation (immutability of backupClassName) + if err = backupsv1alpha1.SetupBackupJobWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "BackupJob") + os.Exit(1) + } + // +kubebuilder:scaffold:builder if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { From 0e05578f81974c055882637a60563426b0f4b2a6 Mon Sep 17 00:00:00 2001 From: Andrey Kolkov Date: Thu, 22 Jan 2026 12:51:14 +0400 Subject: [PATCH 067/125] fix tests Signed-off-by: Andrey Kolkov --- api/backups/v1alpha1/backupjob_webhook.go | 5 - .../v1alpha1/backupjob_webhook_test.go | 332 ++++++++++++++++++ 2 files changed, 332 insertions(+), 5 deletions(-) create mode 100644 api/backups/v1alpha1/backupjob_webhook_test.go diff --git a/api/backups/v1alpha1/backupjob_webhook.go b/api/backups/v1alpha1/backupjob_webhook.go index 8c167c22..06ec81b9 100644 --- a/api/backups/v1alpha1/backupjob_webhook.go +++ b/api/backups/v1alpha1/backupjob_webhook.go @@ -9,7 +9,6 @@ import ( "k8s.io/apimachinery/pkg/runtime" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/webhook" "sigs.k8s.io/controller-runtime/pkg/webhook/admission" ) @@ -22,8 +21,6 @@ func SetupBackupJobWebhookWithManager(mgr ctrl.Manager) error { // +kubebuilder:webhook:path=/mutate-backups-cozystack-io-v1alpha1-backupjob,mutating=true,failurePolicy=fail,sideEffects=None,groups=backups.cozystack.io,resources=backupjobs,verbs=create;update,versions=v1alpha1,name=mbackupjob.kb.io,admissionReviewVersions=v1 -var _ webhook.Defaulter = &BackupJob{} - // Default implements webhook.Defaulter so a webhook will be registered for the type func (j *BackupJob) Default() { // No defaults needed for BackupJob currently @@ -31,8 +28,6 @@ func (j *BackupJob) Default() { // +kubebuilder:webhook:path=/validate-backups-cozystack-io-v1alpha1-backupjob,mutating=false,failurePolicy=fail,sideEffects=None,groups=backups.cozystack.io,resources=backupjobs,verbs=create;update,versions=v1alpha1,name=vbackupjob.kb.io,admissionReviewVersions=v1 -var _ webhook.Validator = &BackupJob{} - // ValidateCreate implements webhook.Validator so a webhook will be registered for the type func (j *BackupJob) ValidateCreate() (admission.Warnings, error) { logger := log.FromContext(context.Background()) diff --git a/api/backups/v1alpha1/backupjob_webhook_test.go b/api/backups/v1alpha1/backupjob_webhook_test.go new file mode 100644 index 00000000..5d85a455 --- /dev/null +++ b/api/backups/v1alpha1/backupjob_webhook_test.go @@ -0,0 +1,332 @@ +// SPDX-License-Identifier: Apache-2.0 +package v1alpha1 + +import ( + "testing" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +func TestBackupJob_ValidateCreate(t *testing.T) { + tests := []struct { + name string + job *BackupJob + wantErr bool + errMsg string + }{ + { + name: "valid BackupJob with backupClassName", + job: &BackupJob{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-job", + Namespace: "default", + }, + Spec: BackupJobSpec{ + ApplicationRef: corev1.TypedLocalObjectReference{ + Kind: "VirtualMachine", + Name: "vm1", + }, + BackupClassName: "velero", + }, + }, + wantErr: false, + }, + { + name: "BackupJob with empty backupClassName should be rejected", + job: &BackupJob{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-job", + Namespace: "default", + }, + Spec: BackupJobSpec{ + ApplicationRef: corev1.TypedLocalObjectReference{ + Kind: "VirtualMachine", + Name: "vm1", + }, + BackupClassName: "", + }, + }, + wantErr: true, + errMsg: "backupClassName is required and cannot be empty", + }, + { + name: "BackupJob with whitespace-only backupClassName should be rejected", + job: &BackupJob{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-job", + Namespace: "default", + }, + Spec: BackupJobSpec{ + ApplicationRef: corev1.TypedLocalObjectReference{ + Kind: "VirtualMachine", + Name: "vm1", + }, + BackupClassName: " ", + }, + }, + wantErr: false, // Whitespace is technically not empty, but this is acceptable + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + warnings, err := tt.job.ValidateCreate() + if (err != nil) != tt.wantErr { + t.Errorf("ValidateCreate() error = %v, wantErr %v", err, tt.wantErr) + return + } + if tt.wantErr && err != nil { + if tt.errMsg != "" && err.Error() != tt.errMsg { + t.Errorf("ValidateCreate() error message = %v, want %v", err.Error(), tt.errMsg) + } + } + if warnings != nil && len(warnings) > 0 { + t.Logf("ValidateCreate() warnings = %v", warnings) + } + }) + } +} + +func TestBackupJob_ValidateUpdate(t *testing.T) { + baseJob := &BackupJob{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-job", + Namespace: "default", + }, + Spec: BackupJobSpec{ + ApplicationRef: corev1.TypedLocalObjectReference{ + Kind: "VirtualMachine", + Name: "vm1", + }, + BackupClassName: "velero", + }, + } + + tests := []struct { + name string + old runtime.Object + new *BackupJob + wantErr bool + errMsg string + }{ + { + name: "update with same backupClassName should succeed", + old: baseJob, + new: &BackupJob{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-job", + Namespace: "default", + }, + Spec: BackupJobSpec{ + ApplicationRef: corev1.TypedLocalObjectReference{ + Kind: "VirtualMachine", + Name: "vm1", + }, + BackupClassName: "velero", // Same as old + }, + }, + wantErr: false, + }, + { + name: "update changing backupClassName should be rejected", + old: baseJob, + new: &BackupJob{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-job", + Namespace: "default", + }, + Spec: BackupJobSpec{ + ApplicationRef: corev1.TypedLocalObjectReference{ + Kind: "VirtualMachine", + Name: "vm1", + }, + BackupClassName: "different-class", // Changed! + }, + }, + wantErr: true, + errMsg: "backupClassName is immutable and cannot be changed from \"velero\" to \"different-class\"", + }, + { + name: "update changing other fields but keeping backupClassName should succeed", + old: baseJob, + new: &BackupJob{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-job", + Namespace: "default", + Labels: map[string]string{ + "new-label": "value", + }, + }, + Spec: BackupJobSpec{ + ApplicationRef: corev1.TypedLocalObjectReference{ + Kind: "VirtualMachine", + Name: "vm2", // Changed application + }, + BackupClassName: "velero", // Same as old + }, + }, + wantErr: false, + }, + { + name: "update when old backupClassName is empty should allow setting it", + old: &BackupJob{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-job", + Namespace: "default", + }, + Spec: BackupJobSpec{ + ApplicationRef: corev1.TypedLocalObjectReference{ + Kind: "VirtualMachine", + Name: "vm1", + }, + BackupClassName: "", // Empty in old + }, + }, + new: &BackupJob{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-job", + Namespace: "default", + }, + Spec: BackupJobSpec{ + ApplicationRef: corev1.TypedLocalObjectReference{ + Kind: "VirtualMachine", + Name: "vm1", + }, + BackupClassName: "velero", // Setting it for the first time + }, + }, + wantErr: false, // Allowed because old was empty + }, + { + name: "update changing from non-empty to different non-empty should be rejected", + old: &BackupJob{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-job", + Namespace: "default", + }, + Spec: BackupJobSpec{ + ApplicationRef: corev1.TypedLocalObjectReference{ + Kind: "VirtualMachine", + Name: "vm1", + }, + BackupClassName: "class-a", + }, + }, + new: &BackupJob{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-job", + Namespace: "default", + }, + Spec: BackupJobSpec{ + ApplicationRef: corev1.TypedLocalObjectReference{ + Kind: "VirtualMachine", + Name: "vm1", + }, + BackupClassName: "class-b", // Changed from class-a + }, + }, + wantErr: true, + errMsg: "backupClassName is immutable and cannot be changed from \"class-a\" to \"class-b\"", + }, + { + name: "update with invalid old object type should be rejected", + old: &corev1.Pod{ // Wrong type - will be cast to runtime.Object in test + ObjectMeta: metav1.ObjectMeta{ + Name: "test-job", + Namespace: "default", + }, + }, + new: baseJob, + wantErr: true, + errMsg: "expected a BackupJob but got a", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + warnings, err := tt.new.ValidateUpdate(tt.old) + if (err != nil) != tt.wantErr { + t.Errorf("ValidateUpdate() error = %v, wantErr %v", err, tt.wantErr) + if err != nil { + t.Logf("Error message: %v", err.Error()) + } + return + } + if tt.wantErr && err != nil { + if tt.errMsg != "" { + if tt.errMsg != "" && !contains(err.Error(), tt.errMsg) { + t.Errorf("ValidateUpdate() error message = %v, want contains %v", err.Error(), tt.errMsg) + } + } + } + if warnings != nil && len(warnings) > 0 { + t.Logf("ValidateUpdate() warnings = %v", warnings) + } + }) + } +} + +func TestBackupJob_ValidateDelete(t *testing.T) { + job := &BackupJob{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-job", + Namespace: "default", + }, + Spec: BackupJobSpec{ + ApplicationRef: corev1.TypedLocalObjectReference{ + Kind: "VirtualMachine", + Name: "vm1", + }, + BackupClassName: "velero", + }, + } + + warnings, err := job.ValidateDelete() + if err != nil { + t.Errorf("ValidateDelete() should never return an error, got %v", err) + } + if warnings != nil && len(warnings) > 0 { + t.Logf("ValidateDelete() warnings = %v", warnings) + } +} + +func TestBackupJob_Default(t *testing.T) { + job := &BackupJob{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test-job", + Namespace: "default", + }, + Spec: BackupJobSpec{ + ApplicationRef: corev1.TypedLocalObjectReference{ + Kind: "VirtualMachine", + Name: "vm1", + }, + BackupClassName: "velero", + }, + } + + // Default() should not panic and should not modify the object + originalClassName := job.Spec.BackupClassName + job.Default() + if job.Spec.BackupClassName != originalClassName { + t.Errorf("Default() should not modify backupClassName, got %v, want %v", job.Spec.BackupClassName, originalClassName) + } +} + +// Helper function to check if a string contains a substring +func contains(s, substr string) bool { + if len(substr) == 0 { + return true + } + if len(s) < len(substr) { + return false + } + for i := 0; i <= len(s)-len(substr); i++ { + if s[i:i+len(substr)] == substr { + return true + } + } + return false +} From ffde02c9924aa3f7f9406f48274263a6837b8901 Mon Sep 17 00:00:00 2001 From: Andrey Kolkov Date: Thu, 22 Jan 2026 12:59:42 +0400 Subject: [PATCH 068/125] fix setup cache for backupClasses Signed-off-by: Andrey Kolkov --- cmd/backup-controller/main.go | 7 ++++++ cmd/backupstrategy-controller/main.go | 7 ++++++ .../backupcontroller/backupjob_controller.go | 25 ------------------- 3 files changed, 14 insertions(+), 25 deletions(-) diff --git a/cmd/backup-controller/main.go b/cmd/backup-controller/main.go index 3278ecf7..bbb92790 100644 --- a/cmd/backup-controller/main.go +++ b/cmd/backup-controller/main.go @@ -29,6 +29,8 @@ import ( utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/cache" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/log/zap" "sigs.k8s.io/controller-runtime/pkg/metrics/filters" @@ -134,6 +136,11 @@ func main() { HealthProbeBindAddress: probeAddr, LeaderElection: enableLeaderElection, LeaderElectionID: "core.backups.cozystack.io", + Cache: cache.Options{ + ByObject: map[client.Object]cache.ByObject{ + &backupsv1alpha1.BackupClass{}: {}, + }, + }, // LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily // when the Manager ends. This requires the binary to immediately end when the // Manager is stopped, otherwise, this setting is unsafe. Setting this significantly diff --git a/cmd/backupstrategy-controller/main.go b/cmd/backupstrategy-controller/main.go index 61edc40a..68b33902 100644 --- a/cmd/backupstrategy-controller/main.go +++ b/cmd/backupstrategy-controller/main.go @@ -29,6 +29,8 @@ import ( utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/cache" + "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/log/zap" "sigs.k8s.io/controller-runtime/pkg/metrics/filters" @@ -130,6 +132,11 @@ func main() { HealthProbeBindAddress: probeAddr, LeaderElection: enableLeaderElection, LeaderElectionID: "strategy.backups.cozystack.io", + Cache: cache.Options{ + ByObject: map[client.Object]cache.ByObject{ + &backupsv1alpha1.BackupClass{}: {}, + }, + }, // LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily // when the Manager ends. This requires the binary to immediately end when the // Manager is stopped, otherwise, this setting is unsafe. Setting this significantly diff --git a/internal/backupcontroller/backupjob_controller.go b/internal/backupcontroller/backupjob_controller.go index c04ec85d..58294972 100644 --- a/internal/backupcontroller/backupjob_controller.go +++ b/internal/backupcontroller/backupjob_controller.go @@ -14,10 +14,7 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/apiutil" - "sigs.k8s.io/controller-runtime/pkg/handler" "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/controller-runtime/pkg/source" strategyv1alpha1 "github.com/cozystack/cozystack/api/backups/strategy/v1alpha1" backupsv1alpha1 "github.com/cozystack/cozystack/api/backups/v1alpha1" @@ -116,27 +113,5 @@ func (r *BackupJobReconciler) SetupWithManager(mgr ctrl.Manager) error { } return ctrl.NewControllerManagedBy(mgr). For(&backupsv1alpha1.BackupJob{}). - // Requeue BackupJobs when their referenced BackupClass changes - WatchesRawSource(source.Kind( - mgr.GetCache(), - &backupsv1alpha1.BackupClass{}, - handler.TypedEnqueueRequestsFromMapFunc(func(ctx context.Context, bc *backupsv1alpha1.BackupClass) []reconcile.Request { - var jobs backupsv1alpha1.BackupJobList - if err := r.List(ctx, &jobs, client.MatchingFields{"spec.backupClassName": bc.Name}); err != nil { - return nil - } - - reqs := make([]reconcile.Request, 0, len(jobs.Items)) - for _, job := range jobs.Items { - reqs = append(reqs, reconcile.Request{ - NamespacedName: types.NamespacedName{ - Namespace: job.Namespace, - Name: job.Name, - }, - }) - } - return reqs - }), - )). Complete(r) } From 875549786924db8ae2c4240b262fb9b731369437 Mon Sep 17 00:00:00 2001 From: Andrey Kolkov Date: Thu, 22 Jan 2026 13:55:00 +0400 Subject: [PATCH 069/125] fixes Signed-off-by: Andrey Kolkov --- api/backups/v1alpha1/backupjob_types.go | 14 ++++++++++++++ api/backups/v1alpha1/backupjob_webhook.go | 7 ++++--- api/backups/v1alpha1/backupjob_webhook_test.go | 8 +++++--- .../backupcontroller/backupclass_resolver.go | 17 +++++++---------- internal/backupcontroller/factory/backupjob.go | 18 +----------------- 5 files changed, 31 insertions(+), 33 deletions(-) diff --git a/api/backups/v1alpha1/backupjob_types.go b/api/backups/v1alpha1/backupjob_types.go index 6675c71d..7b251ceb 100644 --- a/api/backups/v1alpha1/backupjob_types.go +++ b/api/backups/v1alpha1/backupjob_types.go @@ -24,6 +24,10 @@ func init() { const ( OwningJobNameLabel = thisGroup + "/owned-by.BackupJobName" OwningJobNamespaceLabel = thisGroup + "/owned-by.BackupJobNamespace" + + // DefaultApplicationAPIGroup is the default API group for applications + // when not specified in ApplicationRef or ApplicationSelector. + DefaultApplicationAPIGroup = "apps.cozystack.io" ) // BackupJobPhase represents the lifecycle phase of a BackupJob. @@ -114,3 +118,13 @@ type BackupJobList struct { metav1.ListMeta `json:"metadata,omitempty"` Items []BackupJob `json:"items"` } + +// NormalizeApplicationRef sets the default apiGroup to DefaultApplicationAPIGroup if it's not specified. +// This function is exported so it can be used by other packages (e.g., controllers, factories). +func NormalizeApplicationRef(ref corev1.TypedLocalObjectReference) corev1.TypedLocalObjectReference { + if ref.APIGroup == nil || *ref.APIGroup == "" { + apiGroup := DefaultApplicationAPIGroup + ref.APIGroup = &apiGroup + } + return ref +} diff --git a/api/backups/v1alpha1/backupjob_webhook.go b/api/backups/v1alpha1/backupjob_webhook.go index 06ec81b9..064605c2 100644 --- a/api/backups/v1alpha1/backupjob_webhook.go +++ b/api/backups/v1alpha1/backupjob_webhook.go @@ -4,6 +4,7 @@ package v1alpha1 import ( "context" "fmt" + "strings" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" @@ -23,7 +24,7 @@ func SetupBackupJobWebhookWithManager(mgr ctrl.Manager) error { // Default implements webhook.Defaulter so a webhook will be registered for the type func (j *BackupJob) Default() { - // No defaults needed for BackupJob currently + j.Spec.ApplicationRef = NormalizeApplicationRef(j.Spec.ApplicationRef) } // +kubebuilder:webhook:path=/validate-backups-cozystack-io-v1alpha1-backupjob,mutating=false,failurePolicy=fail,sideEffects=None,groups=backups.cozystack.io,resources=backupjobs,verbs=create;update,versions=v1alpha1,name=vbackupjob.kb.io,admissionReviewVersions=v1 @@ -34,7 +35,7 @@ func (j *BackupJob) ValidateCreate() (admission.Warnings, error) { logger.Info("validating BackupJob creation", "name", j.Name, "namespace", j.Namespace) // Validate that backupClassName is set - if j.Spec.BackupClassName == "" { + if strings.TrimSpace(j.Spec.BackupClassName) == "" { return nil, fmt.Errorf("backupClassName is required and cannot be empty") } @@ -52,7 +53,7 @@ func (j *BackupJob) ValidateUpdate(old runtime.Object) (admission.Warnings, erro } // Enforce immutability of backupClassName - if oldJob.Spec.BackupClassName != "" && oldJob.Spec.BackupClassName != j.Spec.BackupClassName { + if oldJob.Spec.BackupClassName != j.Spec.BackupClassName { return nil, fmt.Errorf("backupClassName is immutable and cannot be changed from %q to %q", oldJob.Spec.BackupClassName, j.Spec.BackupClassName) } diff --git a/api/backups/v1alpha1/backupjob_webhook_test.go b/api/backups/v1alpha1/backupjob_webhook_test.go index 5d85a455..d143c5be 100644 --- a/api/backups/v1alpha1/backupjob_webhook_test.go +++ b/api/backups/v1alpha1/backupjob_webhook_test.go @@ -66,7 +66,8 @@ func TestBackupJob_ValidateCreate(t *testing.T) { BackupClassName: " ", }, }, - wantErr: false, // Whitespace is technically not empty, but this is acceptable + wantErr: true, + errMsg: "backupClassName is required and cannot be empty", }, } @@ -170,7 +171,7 @@ func TestBackupJob_ValidateUpdate(t *testing.T) { wantErr: false, }, { - name: "update when old backupClassName is empty should allow setting it", + name: "update when old backupClassName is empty should be rejected", old: &BackupJob{ ObjectMeta: metav1.ObjectMeta{ Name: "test-job", @@ -197,7 +198,8 @@ func TestBackupJob_ValidateUpdate(t *testing.T) { BackupClassName: "velero", // Setting it for the first time }, }, - wantErr: false, // Allowed because old was empty + wantErr: true, + errMsg: "backupClassName is immutable", }, { name: "update changing from non-empty to different non-empty should be rejected", diff --git a/internal/backupcontroller/backupclass_resolver.go b/internal/backupcontroller/backupclass_resolver.go index 593e3bb0..a1449c49 100644 --- a/internal/backupcontroller/backupclass_resolver.go +++ b/internal/backupcontroller/backupclass_resolver.go @@ -13,17 +13,14 @@ import ( const ( // DefaultApplicationAPIGroup is the default API group for applications // when not specified in ApplicationRef or ApplicationSelector. - DefaultApplicationAPIGroup = "apps.cozystack.io" + // Deprecated: Use backupsv1alpha1.DefaultApplicationAPIGroup instead. + DefaultApplicationAPIGroup = backupsv1alpha1.DefaultApplicationAPIGroup ) -// NormalizeApplicationRef sets the default apiGroup to "apps.cozystack.io" if it's not specified. -// This function is exported so it can be used by other packages (e.g., factory). +// NormalizeApplicationRef sets the default apiGroup to DefaultApplicationAPIGroup if it's not specified. +// Deprecated: Use backupsv1alpha1.NormalizeApplicationRef instead. func NormalizeApplicationRef(ref corev1.TypedLocalObjectReference) corev1.TypedLocalObjectReference { - if ref.APIGroup == nil || *ref.APIGroup == "" { - apiGroup := DefaultApplicationAPIGroup - ref.APIGroup = &apiGroup - } - return ref + return backupsv1alpha1.NormalizeApplicationRef(ref) } // ResolvedBackupConfig contains the resolved strategy and storage configuration @@ -52,7 +49,7 @@ func ResolveBackupClass( } // Determine application API group (already normalized, but extract for matching) - appAPIGroup := DefaultApplicationAPIGroup + appAPIGroup := backupsv1alpha1.DefaultApplicationAPIGroup if applicationRef.APIGroup != nil { appAPIGroup = *applicationRef.APIGroup } @@ -60,7 +57,7 @@ func ResolveBackupClass( // Find matching strategy for _, strategy := range backupClass.Spec.Strategies { // Normalize strategy's application selector (default apiGroup if not specified) - strategyAPIGroup := DefaultApplicationAPIGroup + strategyAPIGroup := backupsv1alpha1.DefaultApplicationAPIGroup if strategy.Application.APIGroup != nil && *strategy.Application.APIGroup != "" { strategyAPIGroup = *strategy.Application.APIGroup } diff --git a/internal/backupcontroller/factory/backupjob.go b/internal/backupcontroller/factory/backupjob.go index 6ca2ee21..1c51d434 100644 --- a/internal/backupcontroller/factory/backupjob.go +++ b/internal/backupcontroller/factory/backupjob.go @@ -9,25 +9,9 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -const ( - // defaultApplicationAPIGroup is the default API group for applications - // when not specified in ApplicationRef. - defaultApplicationAPIGroup = "apps.cozystack.io" -) - -// normalizeApplicationRef sets the default apiGroup to "apps.cozystack.io" if it's not specified. -// This is a local copy to avoid import cycle with backupcontroller package. -func normalizeApplicationRef(ref corev1.TypedLocalObjectReference) corev1.TypedLocalObjectReference { - if ref.APIGroup == nil || *ref.APIGroup == "" { - apiGroup := defaultApplicationAPIGroup - ref.APIGroup = &apiGroup - } - return ref -} - func BackupJob(p *backupsv1alpha1.Plan, scheduledFor time.Time) *backupsv1alpha1.BackupJob { // Normalize ApplicationRef (default apiGroup if not specified) - appRef := normalizeApplicationRef(*p.Spec.ApplicationRef.DeepCopy()) + appRef := backupsv1alpha1.NormalizeApplicationRef(*p.Spec.ApplicationRef.DeepCopy()) job := &backupsv1alpha1.BackupJob{ ObjectMeta: metav1.ObjectMeta{ From 73d6e3013ef6f0a5484061328585260976bee134 Mon Sep 17 00:00:00 2001 From: Andrey Kolkov Date: Thu, 22 Jan 2026 14:00:17 +0400 Subject: [PATCH 070/125] fix Signed-off-by: Andrey Kolkov --- api/backups/v1alpha1/backup_types.go | 3 +- api/backups/v1alpha1/backupclass_types.go | 41 ++++++++++++++++++- .../backupcontroller/backupclass_resolver.go | 2 +- .../backupclass_resolver_test.go | 20 ++++----- .../velerostrategy_controller_test.go | 2 +- 5 files changed, 53 insertions(+), 15 deletions(-) diff --git a/api/backups/v1alpha1/backup_types.go b/api/backups/v1alpha1/backup_types.go index 9371c27f..da4cf02e 100644 --- a/api/backups/v1alpha1/backup_types.go +++ b/api/backups/v1alpha1/backup_types.go @@ -59,7 +59,8 @@ type BackupSpec struct { // StrategyRef refers to the driver-specific BackupStrategy that was used // to create this backup. This allows the driver to later perform restores. - StrategyRef corev1.TypedLocalObjectReference `json:"strategyRef"` + // This references a cluster-scoped resource, so it does not include a namespace. + StrategyRef TypedClusterObjectReference `json:"strategyRef"` // TakenAt is the time at which the backup was taken (as reported by the // driver). It may differ slightly from metadata.creationTimestamp. diff --git a/api/backups/v1alpha1/backupclass_types.go b/api/backups/v1alpha1/backupclass_types.go index 19bcfd52..a7a2714d 100644 --- a/api/backups/v1alpha1/backupclass_types.go +++ b/api/backups/v1alpha1/backupclass_types.go @@ -6,7 +6,6 @@ package v1alpha1 import ( - corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ) @@ -54,7 +53,8 @@ type BackupClassSpec struct { // BackupClassStrategy defines a backup strategy for a specific application type. type BackupClassStrategy struct { // StrategyRef references the driver-specific BackupStrategy (e.g., Velero). - StrategyRef corev1.TypedLocalObjectReference `json:"strategyRef"` + // This references a cluster-scoped resource, so it does not include a namespace. + StrategyRef TypedClusterObjectReference `json:"strategyRef"` // Application specifies which application types this strategy applies to. Application ApplicationSelector `json:"application"` @@ -66,6 +66,43 @@ type BackupClassStrategy struct { Parameters map[string]string `json:"parameters,omitempty"` } +// TypedClusterObjectReference contains enough information to let you locate a +// cluster-scoped typed resource. It does not include a namespace because +// cluster-scoped resources do not have namespaces. +type TypedClusterObjectReference struct { + // APIGroup is the group for the resource being referenced. + // If APIGroup is not specified, the specified Kind must be in the core API group. + // For any other third-party types, APIGroup is required. + // +optional + APIGroup *string `json:"apiGroup,omitempty"` + + // Kind is the type of resource being referenced. + Kind string `json:"kind"` + + // Name is the name of resource being referenced. + Name string `json:"name"` +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *TypedClusterObjectReference) DeepCopyInto(out *TypedClusterObjectReference) { + *out = *in + if in.APIGroup != nil { + in, out := &in.APIGroup, &out.APIGroup + *out = new(string) + **out = **in + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TypedClusterObjectReference. +func (in *TypedClusterObjectReference) DeepCopy() *TypedClusterObjectReference { + if in == nil { + return nil + } + out := new(TypedClusterObjectReference) + in.DeepCopyInto(out) + return out +} + // ApplicationSelector specifies which application types a strategy applies to. type ApplicationSelector struct { // APIGroup is the API group of the application. diff --git a/internal/backupcontroller/backupclass_resolver.go b/internal/backupcontroller/backupclass_resolver.go index a1449c49..b28c16cb 100644 --- a/internal/backupcontroller/backupclass_resolver.go +++ b/internal/backupcontroller/backupclass_resolver.go @@ -26,7 +26,7 @@ func NormalizeApplicationRef(ref corev1.TypedLocalObjectReference) corev1.TypedL // ResolvedBackupConfig contains the resolved strategy and storage configuration // from a BackupClass. type ResolvedBackupConfig struct { - StrategyRef corev1.TypedLocalObjectReference + StrategyRef backupsv1alpha1.TypedClusterObjectReference Parameters map[string]string } diff --git a/internal/backupcontroller/backupclass_resolver_test.go b/internal/backupcontroller/backupclass_resolver_test.go index a606f241..2fe77006 100644 --- a/internal/backupcontroller/backupclass_resolver_test.go +++ b/internal/backupcontroller/backupclass_resolver_test.go @@ -113,7 +113,7 @@ func TestResolveBackupClass(t *testing.T) { applicationRef corev1.TypedLocalObjectReference backupClassName string wantErr bool - expectedStrategyRef *corev1.TypedLocalObjectReference + expectedStrategyRef *backupsv1alpha1.TypedClusterObjectReference expectedParams map[string]string }{ { @@ -125,7 +125,7 @@ func TestResolveBackupClass(t *testing.T) { Spec: backupsv1alpha1.BackupClassSpec{ Strategies: []backupsv1alpha1.BackupClassStrategy{ { - StrategyRef: corev1.TypedLocalObjectReference{ + StrategyRef: backupsv1alpha1.TypedClusterObjectReference{ APIGroup: stringPtr("strategy.backups.cozystack.io"), Kind: "Velero", Name: "velero-strategy-vm", @@ -138,7 +138,7 @@ func TestResolveBackupClass(t *testing.T) { }, }, { - StrategyRef: corev1.TypedLocalObjectReference{ + StrategyRef: backupsv1alpha1.TypedClusterObjectReference{ APIGroup: stringPtr("strategy.backups.cozystack.io"), Kind: "Velero", Name: "velero-strategy-mysql", @@ -159,7 +159,7 @@ func TestResolveBackupClass(t *testing.T) { }, backupClassName: "velero", wantErr: false, - expectedStrategyRef: &corev1.TypedLocalObjectReference{ + expectedStrategyRef: &backupsv1alpha1.TypedClusterObjectReference{ APIGroup: stringPtr("strategy.backups.cozystack.io"), Kind: "Velero", Name: "velero-strategy-vm", @@ -177,7 +177,7 @@ func TestResolveBackupClass(t *testing.T) { Spec: backupsv1alpha1.BackupClassSpec{ Strategies: []backupsv1alpha1.BackupClassStrategy{ { - StrategyRef: corev1.TypedLocalObjectReference{ + StrategyRef: backupsv1alpha1.TypedClusterObjectReference{ APIGroup: stringPtr("strategy.backups.cozystack.io"), Kind: "Velero", Name: "velero-strategy-mysql", @@ -200,7 +200,7 @@ func TestResolveBackupClass(t *testing.T) { }, backupClassName: "velero", wantErr: false, - expectedStrategyRef: &corev1.TypedLocalObjectReference{ + expectedStrategyRef: &backupsv1alpha1.TypedClusterObjectReference{ APIGroup: stringPtr("strategy.backups.cozystack.io"), Kind: "Velero", Name: "velero-strategy-mysql", @@ -218,7 +218,7 @@ func TestResolveBackupClass(t *testing.T) { Spec: backupsv1alpha1.BackupClassSpec{ Strategies: []backupsv1alpha1.BackupClassStrategy{ { - StrategyRef: corev1.TypedLocalObjectReference{ + StrategyRef: backupsv1alpha1.TypedClusterObjectReference{ APIGroup: stringPtr("strategy.backups.cozystack.io"), Kind: "Velero", Name: "velero-strategy-vm", @@ -240,7 +240,7 @@ func TestResolveBackupClass(t *testing.T) { }, backupClassName: "velero", wantErr: false, - expectedStrategyRef: &corev1.TypedLocalObjectReference{ + expectedStrategyRef: &backupsv1alpha1.TypedClusterObjectReference{ APIGroup: stringPtr("strategy.backups.cozystack.io"), Kind: "Velero", Name: "velero-strategy-vm", @@ -275,7 +275,7 @@ func TestResolveBackupClass(t *testing.T) { Spec: backupsv1alpha1.BackupClassSpec{ Strategies: []backupsv1alpha1.BackupClassStrategy{ { - StrategyRef: corev1.TypedLocalObjectReference{ + StrategyRef: backupsv1alpha1.TypedClusterObjectReference{ APIGroup: stringPtr("strategy.backups.cozystack.io"), Kind: "Velero", Name: "velero-strategy-vm", @@ -303,7 +303,7 @@ func TestResolveBackupClass(t *testing.T) { Spec: backupsv1alpha1.BackupClassSpec{ Strategies: []backupsv1alpha1.BackupClassStrategy{ { - StrategyRef: corev1.TypedLocalObjectReference{ + StrategyRef: backupsv1alpha1.TypedClusterObjectReference{ APIGroup: stringPtr("strategy.backups.cozystack.io"), Kind: "Velero", Name: "velero-strategy-vm", diff --git a/internal/backupcontroller/velerostrategy_controller_test.go b/internal/backupcontroller/velerostrategy_controller_test.go index a7de2076..0241a2b9 100644 --- a/internal/backupcontroller/velerostrategy_controller_test.go +++ b/internal/backupcontroller/velerostrategy_controller_test.go @@ -137,7 +137,7 @@ func TestCreateVeleroBackup_TemplateContext(t *testing.T) { // Create ResolvedBackupConfig with parameters resolved := &ResolvedBackupConfig{ - StrategyRef: corev1.TypedLocalObjectReference{ + StrategyRef: backupsv1alpha1.TypedClusterObjectReference{ APIGroup: stringPtr("strategy.backups.cozystack.io"), Kind: "Velero", Name: "velero-strategy", From beb6e1a0ba3c8759c04236a6c28a6c06e3e93d0a Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Thu, 22 Jan 2026 17:53:52 +0100 Subject: [PATCH 071/125] [mongodb] Remove user-configurable images from MongoDB chart Remove the ability for users to specify custom container images (images.pmm and images.backup) in the MongoDB application values. This is a security hardening measure - allowing users to specify arbitrary container images could lead to running malicious or compromised images, supply chain attacks, or privilege escalation. The images are now hardcoded in the template: - percona/pmm-client:2.44.1 - percona/percona-backup-mongodb:2.11.0 Co-Authored-By: Claude Signed-off-by: Andrei Kvapil --- packages/apps/mongodb/README.md | 9 -------- packages/apps/mongodb/templates/mongodb.yaml | 4 ++-- packages/apps/mongodb/values.schema.json | 21 ------------------- packages/apps/mongodb/values.yaml | 13 ------------ .../system/mongodb-rd/cozyrds/mongodb.yaml | 4 ++-- 5 files changed, 4 insertions(+), 47 deletions(-) diff --git a/packages/apps/mongodb/README.md b/packages/apps/mongodb/README.md index 84f2bf3f..a5fb17fb 100644 --- a/packages/apps/mongodb/README.md +++ b/packages/apps/mongodb/README.md @@ -52,15 +52,6 @@ Run `helm upgrade` after MongoDB is ready to populate the credentials secret wit | `version` | MongoDB major version to deploy. | `string` | `v8` | -### Image configuration - -| Name | Description | Type | Value | -| --------------- | -------------------------------------- | -------- | --------------------------------------- | -| `images` | Container images used by the operator. | `object` | `{}` | -| `images.pmm` | PMM client image for monitoring. | `string` | `percona/pmm-client:2.44.1` | -| `images.backup` | Percona Backup for MongoDB image. | `string` | `percona/percona-backup-mongodb:2.11.0` | - - ### Sharding configuration | Name | Description | Type | Value | diff --git a/packages/apps/mongodb/templates/mongodb.yaml b/packages/apps/mongodb/templates/mongodb.yaml index ffd358f4..dabb51d4 100644 --- a/packages/apps/mongodb/templates/mongodb.yaml +++ b/packages/apps/mongodb/templates/mongodb.yaml @@ -23,7 +23,7 @@ spec: pmm: enabled: false - image: {{ .Values.images.pmm }} + image: percona/pmm-client:2.44.1 serverHost: "" sharding: @@ -123,7 +123,7 @@ spec: backup: enabled: {{ .Values.backup.enabled | default false }} - image: {{ .Values.images.backup }} + image: percona/percona-backup-mongodb:2.11.0 {{- if .Values.backup.enabled }} storages: s3-storage: diff --git a/packages/apps/mongodb/values.schema.json b/packages/apps/mongodb/values.schema.json index 74ffec7c..817c3463 100644 --- a/packages/apps/mongodb/values.schema.json +++ b/packages/apps/mongodb/values.schema.json @@ -78,27 +78,6 @@ "type": "boolean", "default": false }, - "images": { - "description": "Container images used by the operator.", - "type": "object", - "default": {}, - "required": [ - "backup", - "pmm" - ], - "properties": { - "backup": { - "description": "Percona Backup for MongoDB image.", - "type": "string", - "default": "percona/percona-backup-mongodb:2.11.0" - }, - "pmm": { - "description": "PMM client image for monitoring.", - "type": "string", - "default": "percona/pmm-client:2.44.1" - } - } - }, "replicas": { "description": "Number of MongoDB replicas in replica set.", "type": "integer", diff --git a/packages/apps/mongodb/values.yaml b/packages/apps/mongodb/values.yaml index ea3a18dd..003a0c9e 100644 --- a/packages/apps/mongodb/values.yaml +++ b/packages/apps/mongodb/values.yaml @@ -42,19 +42,6 @@ external: false ## @param {Version} version - MongoDB major version to deploy. version: v8 -## -## @section Image configuration -## - -## @typedef {struct} Images - Container image configuration. -## @field {string} pmm - PMM client image for monitoring. -## @field {string} backup - Percona Backup for MongoDB image. - -## @param {Images} images - Container images used by the operator. -images: - pmm: "percona/pmm-client:2.44.1" - backup: "percona/percona-backup-mongodb:2.11.0" - ## ## @section Sharding configuration ## diff --git a/packages/system/mongodb-rd/cozyrds/mongodb.yaml b/packages/system/mongodb-rd/cozyrds/mongodb.yaml index 996dde18..0edb0d00 100644 --- a/packages/system/mongodb-rd/cozyrds/mongodb.yaml +++ b/packages/system/mongodb-rd/cozyrds/mongodb.yaml @@ -8,7 +8,7 @@ spec: singular: mongodb plural: mongodbs openAPISchema: |- - {"title":"Chart Values","type":"object","properties":{"backup":{"description":"Backup configuration.","type":"object","default":{},"required":["enabled"],"properties":{"destinationPath":{"description":"Destination path for backups (e.g. s3://bucket/path/).","type":"string","default":"s3://bucket/path/to/folder/"},"enabled":{"description":"Enable regular backups.","type":"boolean","default":false},"endpointURL":{"description":"S3 endpoint URL for uploads.","type":"string","default":"http://minio-gateway-service:9000"},"retentionPolicy":{"description":"Retention policy (e.g. \"30d\").","type":"string","default":"30d"},"s3AccessKey":{"description":"Access key for S3 authentication.","type":"string","default":""},"s3SecretKey":{"description":"Secret key for S3 authentication.","type":"string","default":""},"schedule":{"description":"Cron schedule for automated backups.","type":"string","default":"0 2 * * *"}}},"bootstrap":{"description":"Bootstrap configuration.","type":"object","default":{},"required":["backupName","enabled"],"properties":{"backupName":{"description":"Name of backup to restore from.","type":"string","default":""},"enabled":{"description":"Whether to restore from a backup.","type":"boolean","default":false},"recoveryTime":{"description":"Timestamp for point-in-time recovery; empty means latest.","type":"string","default":""}}},"external":{"description":"Enable external access from outside the cluster.","type":"boolean","default":false},"images":{"description":"Container images used by the operator.","type":"object","default":{},"required":["backup","pmm"],"properties":{"backup":{"description":"Percona Backup for MongoDB image.","type":"string","default":"percona/percona-backup-mongodb:2.11.0"},"pmm":{"description":"PMM client image for monitoring.","type":"string","default":"percona/pmm-client:2.44.1"}}},"replicas":{"description":"Number of MongoDB replicas in replica set.","type":"integer","default":3},"resources":{"description":"Explicit CPU and memory configuration for each MongoDB replica. When omitted, the preset defined in `resourcesPreset` is applied.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available to each replica.","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":"Memory (RAM) available to each replica.","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}}},"resourcesPreset":{"description":"Default sizing preset used when `resources` is omitted.","type":"string","default":"small","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]},"sharding":{"description":"Enable sharded cluster mode. When disabled, deploys a replica set.","type":"boolean","default":false},"shardingConfig":{"description":"Configuration for sharded cluster mode.","type":"object","default":{},"required":["configServerSize","configServers","mongos"],"properties":{"configServerSize":{"description":"PVC size for config servers.","default":"3Gi","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},"configServers":{"description":"Number of config server replicas.","type":"integer","default":3},"mongos":{"description":"Number of mongos router replicas.","type":"integer","default":2},"shards":{"description":"List of shard configurations.","type":"array","default":[{"name":"rs0","replicas":3,"size":"10Gi"}],"items":{"type":"object","required":["name","replicas","size"],"properties":{"name":{"description":"Shard name.","type":"string"},"replicas":{"description":"Number of replicas in this shard.","type":"integer"},"size":{"description":"PVC size for this shard.","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}}}}}},"size":{"description":"Persistent Volume Claim size available for application data.","default":"10Gi","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},"storageClass":{"description":"StorageClass used to store the data.","type":"string","default":""},"users":{"description":"Custom MongoDB users configuration map.","type":"object","default":{},"additionalProperties":{"type":"object","required":["db"],"properties":{"db":{"description":"Database to authenticate against.","type":"string"},"password":{"description":"Password for the user (auto-generated if omitted).","type":"string"},"roles":{"description":"List of MongoDB roles with database scope.","type":"array","items":{"type":"object","required":["db","name"],"properties":{"db":{"description":"Database the role applies to.","type":"string"},"name":{"description":"Role name (e.g., readWrite, dbAdmin, clusterAdmin).","type":"string"}}}}}}},"version":{"description":"MongoDB major version to deploy.","type":"string","default":"v8","enum":["v8","v7","v6"]}}} + {"title":"Chart Values","type":"object","properties":{"backup":{"description":"Backup configuration.","type":"object","default":{},"required":["enabled"],"properties":{"destinationPath":{"description":"Destination path for backups (e.g. s3://bucket/path/).","type":"string","default":"s3://bucket/path/to/folder/"},"enabled":{"description":"Enable regular backups.","type":"boolean","default":false},"endpointURL":{"description":"S3 endpoint URL for uploads.","type":"string","default":"http://minio-gateway-service:9000"},"retentionPolicy":{"description":"Retention policy (e.g. \"30d\").","type":"string","default":"30d"},"s3AccessKey":{"description":"Access key for S3 authentication.","type":"string","default":""},"s3SecretKey":{"description":"Secret key for S3 authentication.","type":"string","default":""},"schedule":{"description":"Cron schedule for automated backups.","type":"string","default":"0 2 * * *"}}},"bootstrap":{"description":"Bootstrap configuration.","type":"object","default":{},"required":["backupName","enabled"],"properties":{"backupName":{"description":"Name of backup to restore from.","type":"string","default":""},"enabled":{"description":"Whether to restore from a backup.","type":"boolean","default":false},"recoveryTime":{"description":"Timestamp for point-in-time recovery; empty means latest.","type":"string","default":""}}},"external":{"description":"Enable external access from outside the cluster.","type":"boolean","default":false},"replicas":{"description":"Number of MongoDB replicas in replica set.","type":"integer","default":3},"resources":{"description":"Explicit CPU and memory configuration for each MongoDB replica. When omitted, the preset defined in `resourcesPreset` is applied.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available to each replica.","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":"Memory (RAM) available to each replica.","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}}},"resourcesPreset":{"description":"Default sizing preset used when `resources` is omitted.","type":"string","default":"small","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]},"sharding":{"description":"Enable sharded cluster mode. When disabled, deploys a replica set.","type":"boolean","default":false},"shardingConfig":{"description":"Configuration for sharded cluster mode.","type":"object","default":{},"required":["configServerSize","configServers","mongos"],"properties":{"configServerSize":{"description":"PVC size for config servers.","default":"3Gi","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},"configServers":{"description":"Number of config server replicas.","type":"integer","default":3},"mongos":{"description":"Number of mongos router replicas.","type":"integer","default":2},"shards":{"description":"List of shard configurations.","type":"array","default":[{"name":"rs0","replicas":3,"size":"10Gi"}],"items":{"type":"object","required":["name","replicas","size"],"properties":{"name":{"description":"Shard name.","type":"string"},"replicas":{"description":"Number of replicas in this shard.","type":"integer"},"size":{"description":"PVC size for this shard.","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}}}}}},"size":{"description":"Persistent Volume Claim size available for application data.","default":"10Gi","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},"storageClass":{"description":"StorageClass used to store the data.","type":"string","default":""},"users":{"description":"Custom MongoDB users configuration map.","type":"object","default":{},"additionalProperties":{"type":"object","required":["db"],"properties":{"db":{"description":"Database to authenticate against.","type":"string"},"password":{"description":"Password for the user (auto-generated if omitted).","type":"string"},"roles":{"description":"List of MongoDB roles with database scope.","type":"array","items":{"type":"object","required":["db","name"],"properties":{"db":{"description":"Database the role applies to.","type":"string"},"name":{"description":"Role name (e.g., readWrite, dbAdmin, clusterAdmin).","type":"string"}}}}}}},"version":{"description":"MongoDB major version to deploy.","type":"string","default":"v8","enum":["v8","v7","v6"]}}} release: prefix: mongodb- labels: @@ -25,7 +25,7 @@ spec: tags: - database icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxNDQiIGhlaWdodD0iMTQ0IiByeD0iMjQiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl9tb25nb2RiKSIvPgo8cGF0aCBkPSJNNzIgMjRDNzIgMjQgNzIgMjQgNzIgMjRDNzIgMjQgNTggNDAgNTggNjJDNTggODQgNzIgMTIwIDcyIDEyMEM3MiAxMjAgODYgODQgODYgNjJDODYgNDAgNzIgMjQgNzIgMjRaIiBmaWxsPSIjMDBFRDY0Ii8+CjxwYXRoIGQ9Ik03MiAxMjBDNzIgMTIwIDg2IDg0IDg2IDYyQzg2IDQwIDcyIDI0IDcyIDI0IiBzdHJva2U9IiMwMDY4NEEiIHN0cm9rZS13aWR0aD0iNCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+CjxwYXRoIGQ9Ik03MiAyNEM3MiAyNCA1OCA0MCA1OCA2MkM1OCA4NCA3MiAxMjAgNzIgMTIwIiBzdHJva2U9IiMwMDFFMkIiIHN0cm9rZS13aWR0aD0iNCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+CjxyZWN0IHg9IjY5IiB5PSIxMDgiIHdpZHRoPSI2IiBoZWlnaHQ9IjE2IiByeD0iMiIgZmlsbD0iIzAwNjg0QSIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyX21vbmdvZGIiIHgxPSIxNDAiIHkxPSIxMzAuNSIgeDI9IjQiIHkyPSI5LjQ5OTk5IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIHN0b3AtY29sb3I9IiMwMDFFMkIiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjMDIzNDMwIi8+CjwvbGluZWFyR3JhZGllbnQ+CjwvZGVmcz4KPC9zdmc+Cg== - keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "replicas"], ["spec", "resources"], ["spec", "resourcesPreset"], ["spec", "size"], ["spec", "storageClass"], ["spec", "external"], ["spec", "version"], ["spec", "images"], ["spec", "images", "pmm"], ["spec", "images", "backup"], ["spec", "sharding"], ["spec", "shardingConfig"], ["spec", "shardingConfig", "configServers"], ["spec", "shardingConfig", "configServerSize"], ["spec", "shardingConfig", "mongos"], ["spec", "shardingConfig", "shards"], ["spec", "users"], ["spec", "backup"], ["spec", "backup", "enabled"], ["spec", "backup", "schedule"], ["spec", "backup", "retentionPolicy"], ["spec", "backup", "destinationPath"], ["spec", "backup", "endpointURL"], ["spec", "backup", "s3AccessKey"], ["spec", "backup", "s3SecretKey"], ["spec", "bootstrap"], ["spec", "bootstrap", "enabled"], ["spec", "bootstrap", "recoveryTime"], ["spec", "bootstrap", "backupName"]] + keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "replicas"], ["spec", "resources"], ["spec", "resourcesPreset"], ["spec", "size"], ["spec", "storageClass"], ["spec", "external"], ["spec", "version"], ["spec", "sharding"], ["spec", "shardingConfig"], ["spec", "shardingConfig", "configServers"], ["spec", "shardingConfig", "configServerSize"], ["spec", "shardingConfig", "mongos"], ["spec", "shardingConfig", "shards"], ["spec", "users"], ["spec", "backup"], ["spec", "backup", "enabled"], ["spec", "backup", "schedule"], ["spec", "backup", "retentionPolicy"], ["spec", "backup", "destinationPath"], ["spec", "backup", "endpointURL"], ["spec", "backup", "s3AccessKey"], ["spec", "backup", "s3SecretKey"], ["spec", "bootstrap"], ["spec", "bootstrap", "enabled"], ["spec", "bootstrap", "recoveryTime"], ["spec", "bootstrap", "backupName"]] secrets: exclude: [] include: From b4271c4702b179724676d8c0f8334f4aafc0b201 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Thu, 22 Jan 2026 20:30:01 +0100 Subject: [PATCH 072/125] [tenant] Document resourceQuotas configuration Add documentation for the resourceQuotas parameter explaining supported resource types and their usage. Co-Authored-By: Claude Signed-off-by: Andrei Kvapil --- packages/apps/tenant/README.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/packages/apps/tenant/README.md b/packages/apps/tenant/README.md index 222c98a5..6b407cfb 100644 --- a/packages/apps/tenant/README.md +++ b/packages/apps/tenant/README.md @@ -79,3 +79,34 @@ tenant-u1 | `isolated` | Enforce tenant namespace with network policies (default: true). | `bool` | `true` | | `resourceQuotas` | Define resource quotas for the tenant. | `map[string]quantity` | `{}` | + +## Configuration + +### Resource Quotas + +The `resourceQuotas` parameter allows you to limit resources available to the tenant. Supported keys include: + +**Compute resources** (converted to `requests.X` and `limits.X`): +- `cpu` - Total CPU cores (e.g., `"4"` or `"500m"`) +- `memory` - Total memory (e.g., `"4Gi"` or `"512Mi"`) +- `ephemeral-storage` - Ephemeral storage limit (e.g., `"10Gi"`) +- `storage` - Total persistent storage (e.g., `"100Gi"`) + +**Object count quotas** (passed as-is): +- `pods` - Maximum number of pods +- `services` - Maximum number of services +- `services.loadbalancers` - Maximum number of LoadBalancer services +- `services.nodeports` - Maximum number of NodePort services +- `configmaps` - Maximum number of ConfigMaps +- `secrets` - Maximum number of Secrets +- `persistentvolumeclaims` - Maximum number of PVCs + +**Example:** +```yaml +resourceQuotas: + cpu: 4 + memory: 4Gi + storage: 10Gi + services.loadbalancers: "3" + pods: "50" +``` From d1d0627f0e79de7558b9e4bbb64e2300b204707e Mon Sep 17 00:00:00 2001 From: Andrey Kolkov Date: Fri, 23 Jan 2026 00:26:28 +0400 Subject: [PATCH 073/125] Revert "fix about wrong strategy ref type" This reverts commit 73d6e3013ef6f0a5484061328585260976bee134. Signed-off-by: Andrey Kolkov --- api/backups/v1alpha1/backup_types.go | 3 +- api/backups/v1alpha1/backupclass_types.go | 41 +------------------ .../backupcontroller/backupclass_resolver.go | 2 +- .../backupclass_resolver_test.go | 20 ++++----- .../velerostrategy_controller_test.go | 2 +- 5 files changed, 15 insertions(+), 53 deletions(-) diff --git a/api/backups/v1alpha1/backup_types.go b/api/backups/v1alpha1/backup_types.go index da4cf02e..9371c27f 100644 --- a/api/backups/v1alpha1/backup_types.go +++ b/api/backups/v1alpha1/backup_types.go @@ -59,8 +59,7 @@ type BackupSpec struct { // StrategyRef refers to the driver-specific BackupStrategy that was used // to create this backup. This allows the driver to later perform restores. - // This references a cluster-scoped resource, so it does not include a namespace. - StrategyRef TypedClusterObjectReference `json:"strategyRef"` + StrategyRef corev1.TypedLocalObjectReference `json:"strategyRef"` // TakenAt is the time at which the backup was taken (as reported by the // driver). It may differ slightly from metadata.creationTimestamp. diff --git a/api/backups/v1alpha1/backupclass_types.go b/api/backups/v1alpha1/backupclass_types.go index a7a2714d..19bcfd52 100644 --- a/api/backups/v1alpha1/backupclass_types.go +++ b/api/backups/v1alpha1/backupclass_types.go @@ -6,6 +6,7 @@ package v1alpha1 import ( + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ) @@ -53,8 +54,7 @@ type BackupClassSpec struct { // BackupClassStrategy defines a backup strategy for a specific application type. type BackupClassStrategy struct { // StrategyRef references the driver-specific BackupStrategy (e.g., Velero). - // This references a cluster-scoped resource, so it does not include a namespace. - StrategyRef TypedClusterObjectReference `json:"strategyRef"` + StrategyRef corev1.TypedLocalObjectReference `json:"strategyRef"` // Application specifies which application types this strategy applies to. Application ApplicationSelector `json:"application"` @@ -66,43 +66,6 @@ type BackupClassStrategy struct { Parameters map[string]string `json:"parameters,omitempty"` } -// TypedClusterObjectReference contains enough information to let you locate a -// cluster-scoped typed resource. It does not include a namespace because -// cluster-scoped resources do not have namespaces. -type TypedClusterObjectReference struct { - // APIGroup is the group for the resource being referenced. - // If APIGroup is not specified, the specified Kind must be in the core API group. - // For any other third-party types, APIGroup is required. - // +optional - APIGroup *string `json:"apiGroup,omitempty"` - - // Kind is the type of resource being referenced. - Kind string `json:"kind"` - - // Name is the name of resource being referenced. - Name string `json:"name"` -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TypedClusterObjectReference) DeepCopyInto(out *TypedClusterObjectReference) { - *out = *in - if in.APIGroup != nil { - in, out := &in.APIGroup, &out.APIGroup - *out = new(string) - **out = **in - } -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TypedClusterObjectReference. -func (in *TypedClusterObjectReference) DeepCopy() *TypedClusterObjectReference { - if in == nil { - return nil - } - out := new(TypedClusterObjectReference) - in.DeepCopyInto(out) - return out -} - // ApplicationSelector specifies which application types a strategy applies to. type ApplicationSelector struct { // APIGroup is the API group of the application. diff --git a/internal/backupcontroller/backupclass_resolver.go b/internal/backupcontroller/backupclass_resolver.go index b28c16cb..a1449c49 100644 --- a/internal/backupcontroller/backupclass_resolver.go +++ b/internal/backupcontroller/backupclass_resolver.go @@ -26,7 +26,7 @@ func NormalizeApplicationRef(ref corev1.TypedLocalObjectReference) corev1.TypedL // ResolvedBackupConfig contains the resolved strategy and storage configuration // from a BackupClass. type ResolvedBackupConfig struct { - StrategyRef backupsv1alpha1.TypedClusterObjectReference + StrategyRef corev1.TypedLocalObjectReference Parameters map[string]string } diff --git a/internal/backupcontroller/backupclass_resolver_test.go b/internal/backupcontroller/backupclass_resolver_test.go index 2fe77006..a606f241 100644 --- a/internal/backupcontroller/backupclass_resolver_test.go +++ b/internal/backupcontroller/backupclass_resolver_test.go @@ -113,7 +113,7 @@ func TestResolveBackupClass(t *testing.T) { applicationRef corev1.TypedLocalObjectReference backupClassName string wantErr bool - expectedStrategyRef *backupsv1alpha1.TypedClusterObjectReference + expectedStrategyRef *corev1.TypedLocalObjectReference expectedParams map[string]string }{ { @@ -125,7 +125,7 @@ func TestResolveBackupClass(t *testing.T) { Spec: backupsv1alpha1.BackupClassSpec{ Strategies: []backupsv1alpha1.BackupClassStrategy{ { - StrategyRef: backupsv1alpha1.TypedClusterObjectReference{ + StrategyRef: corev1.TypedLocalObjectReference{ APIGroup: stringPtr("strategy.backups.cozystack.io"), Kind: "Velero", Name: "velero-strategy-vm", @@ -138,7 +138,7 @@ func TestResolveBackupClass(t *testing.T) { }, }, { - StrategyRef: backupsv1alpha1.TypedClusterObjectReference{ + StrategyRef: corev1.TypedLocalObjectReference{ APIGroup: stringPtr("strategy.backups.cozystack.io"), Kind: "Velero", Name: "velero-strategy-mysql", @@ -159,7 +159,7 @@ func TestResolveBackupClass(t *testing.T) { }, backupClassName: "velero", wantErr: false, - expectedStrategyRef: &backupsv1alpha1.TypedClusterObjectReference{ + expectedStrategyRef: &corev1.TypedLocalObjectReference{ APIGroup: stringPtr("strategy.backups.cozystack.io"), Kind: "Velero", Name: "velero-strategy-vm", @@ -177,7 +177,7 @@ func TestResolveBackupClass(t *testing.T) { Spec: backupsv1alpha1.BackupClassSpec{ Strategies: []backupsv1alpha1.BackupClassStrategy{ { - StrategyRef: backupsv1alpha1.TypedClusterObjectReference{ + StrategyRef: corev1.TypedLocalObjectReference{ APIGroup: stringPtr("strategy.backups.cozystack.io"), Kind: "Velero", Name: "velero-strategy-mysql", @@ -200,7 +200,7 @@ func TestResolveBackupClass(t *testing.T) { }, backupClassName: "velero", wantErr: false, - expectedStrategyRef: &backupsv1alpha1.TypedClusterObjectReference{ + expectedStrategyRef: &corev1.TypedLocalObjectReference{ APIGroup: stringPtr("strategy.backups.cozystack.io"), Kind: "Velero", Name: "velero-strategy-mysql", @@ -218,7 +218,7 @@ func TestResolveBackupClass(t *testing.T) { Spec: backupsv1alpha1.BackupClassSpec{ Strategies: []backupsv1alpha1.BackupClassStrategy{ { - StrategyRef: backupsv1alpha1.TypedClusterObjectReference{ + StrategyRef: corev1.TypedLocalObjectReference{ APIGroup: stringPtr("strategy.backups.cozystack.io"), Kind: "Velero", Name: "velero-strategy-vm", @@ -240,7 +240,7 @@ func TestResolveBackupClass(t *testing.T) { }, backupClassName: "velero", wantErr: false, - expectedStrategyRef: &backupsv1alpha1.TypedClusterObjectReference{ + expectedStrategyRef: &corev1.TypedLocalObjectReference{ APIGroup: stringPtr("strategy.backups.cozystack.io"), Kind: "Velero", Name: "velero-strategy-vm", @@ -275,7 +275,7 @@ func TestResolveBackupClass(t *testing.T) { Spec: backupsv1alpha1.BackupClassSpec{ Strategies: []backupsv1alpha1.BackupClassStrategy{ { - StrategyRef: backupsv1alpha1.TypedClusterObjectReference{ + StrategyRef: corev1.TypedLocalObjectReference{ APIGroup: stringPtr("strategy.backups.cozystack.io"), Kind: "Velero", Name: "velero-strategy-vm", @@ -303,7 +303,7 @@ func TestResolveBackupClass(t *testing.T) { Spec: backupsv1alpha1.BackupClassSpec{ Strategies: []backupsv1alpha1.BackupClassStrategy{ { - StrategyRef: backupsv1alpha1.TypedClusterObjectReference{ + StrategyRef: corev1.TypedLocalObjectReference{ APIGroup: stringPtr("strategy.backups.cozystack.io"), Kind: "Velero", Name: "velero-strategy-vm", diff --git a/internal/backupcontroller/velerostrategy_controller_test.go b/internal/backupcontroller/velerostrategy_controller_test.go index 0241a2b9..a7de2076 100644 --- a/internal/backupcontroller/velerostrategy_controller_test.go +++ b/internal/backupcontroller/velerostrategy_controller_test.go @@ -137,7 +137,7 @@ func TestCreateVeleroBackup_TemplateContext(t *testing.T) { // Create ResolvedBackupConfig with parameters resolved := &ResolvedBackupConfig{ - StrategyRef: backupsv1alpha1.TypedClusterObjectReference{ + StrategyRef: corev1.TypedLocalObjectReference{ APIGroup: stringPtr("strategy.backups.cozystack.io"), Kind: "Velero", Name: "velero-strategy", From f1a3f4db290f5b7d424fc88199bb3256fd7f6723 Mon Sep 17 00:00:00 2001 From: Kirill Ilin Date: Fri, 23 Jan 2026 11:50:48 +0500 Subject: [PATCH 074/125] [dashboard] Add External IPs tab for LoadBalancer services Introduce a new "External IPs" sidebar and associated dashboard factory to display services of type LoadBalancer. Signed-off-by: Kirill Ilin --- internal/controller/dashboard/sidebar.go | 8 ++++ .../controller/dashboard/static_refactored.go | 41 +++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/internal/controller/dashboard/sidebar.go b/internal/controller/dashboard/sidebar.go index 5bb378c2..a6ade387 100644 --- a/internal/controller/dashboard/sidebar.go +++ b/internal/controller/dashboard/sidebar.go @@ -111,6 +111,8 @@ func (m *Manager) ensureSidebar(ctx context.Context, crd *cozyv1alpha1.Applicati keysAndTags["services"] = []any{"service-sidebar"} keysAndTags["secrets"] = []any{"secret-sidebar"} keysAndTags["ingresses"] = []any{"ingress-sidebar"} + // Add sidebar for v1/services type loadbalancer + keysAndTags["loadbalancer-services"] = []any{"external-ips-sidebar"} // Add sidebar for backups.cozystack.io Plan resource keysAndTags["plans"] = []any{"plan-sidebar"} @@ -210,6 +212,11 @@ func (m *Manager) ensureSidebar(ctx context.Context, crd *cozyv1alpha1.Applicati "label": "Modules", "link": "/openapi-ui/{clusterName}/{namespace}/api-table/core.cozystack.io/v1alpha1/tenantmodules", }, + map[string]any{ + "key": "loadbalancer-services", + "label": "External IPs", + "link": "/openapi-ui/{clusterName}/{namespace}/factory/external-ips", + }, map[string]any{ "key": "tenants", "label": "Tenants", @@ -236,6 +243,7 @@ func (m *Manager) ensureSidebar(ctx context.Context, crd *cozyv1alpha1.Applicati "stock-project-factory-plan-details", "stock-project-factory-backupjob-details", "stock-project-factory-backup-details", + "stock-project-factory-external-ips", "stock-project-api-form", "stock-project-api-table", "stock-project-builtin-form", diff --git a/internal/controller/dashboard/static_refactored.go b/internal/controller/dashboard/static_refactored.go index 4039898d..daaa36b4 100644 --- a/internal/controller/dashboard/static_refactored.go +++ b/internal/controller/dashboard/static_refactored.go @@ -1885,6 +1885,46 @@ func CreateAllFactories() []*dashboardv1alpha1.Factory { } backupSpec := createUnifiedFactory(backupConfig, backupTabs, []any{"/api/clusters/{2}/k8s/apis/backups.cozystack.io/v1alpha1/namespaces/{3}/backups/{6}"}) + // External IPs factory (filtered services) + externalIPsTabs := []any{ + map[string]any{ + "key": "services", + "label": "Services", + "children": []any{ + map[string]any{ + "type": "EnrichedTable", + "data": map[string]any{ + "id": "external-ips-table", + "fetchUrl": "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/services", + "clusterNamePartOfUrl": "{2}", + "baseprefix": "/openapi-ui", + "customizationId": "factory-details-v1.services", + "pathToItems": []any{"items"}, + "fieldSelector": map[string]any{ + "spec.type": "LoadBalancer", + }, + }, + }, + }, + }, + } + externalIPsSpec := map[string]any{ + "key": "external-ips", + "sidebarTags": []any{"external-ips-sidebar"}, + "withScrollableMainContentCard": true, + "urlsToFetch": []any{}, + "data": []any{ + map[string]any{ + "type": "antdTabs", + "data": map[string]any{ + "id": "tabs-root", + "defaultActiveKey": "services", + "items": externalIPsTabs, + }, + }, + }, + } + return []*dashboardv1alpha1.Factory{ createFactory("marketplace", marketplaceSpec), createFactory("namespace-details", namespaceSpec), @@ -1897,6 +1937,7 @@ func CreateAllFactories() []*dashboardv1alpha1.Factory { createFactory("plan-details", planSpec), createFactory("backupjob-details", backupJobSpec), createFactory("backup-details", backupSpec), + createFactory("external-ips", externalIPsSpec), } } From ee759dd11e68058bf8a81c44cbcd56f0f801bfc4 Mon Sep 17 00:00:00 2001 From: Kirill Ilin Date: Fri, 23 Jan 2026 14:28:23 +0500 Subject: [PATCH 075/125] [dashboard] Fix filtering on Pods tab for Service Signed-off-by: Kirill Ilin --- internal/controller/dashboard/static_refactored.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/controller/dashboard/static_refactored.go b/internal/controller/dashboard/static_refactored.go index 4039898d..d7954ed8 100644 --- a/internal/controller/dashboard/static_refactored.go +++ b/internal/controller/dashboard/static_refactored.go @@ -1144,7 +1144,7 @@ func CreateAllFactories() []*dashboardv1alpha1.Factory { "clusterNamePartOfUrl": "{2}", "customizationId": "factory-node-details-/v1/pods", "fetchUrl": "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/pods", - "labelsSelectorFull": map[string]any{ + "labelSelectorFull": map[string]any{ "pathToLabels": ".spec.selector", "reqIndex": 0, }, From befbdf0964ba5efa18fe86d7220bf198c9b120e7 Mon Sep 17 00:00:00 2001 From: Kirill Ilin Date: Mon, 26 Jan 2026 18:13:06 +0500 Subject: [PATCH 076/125] [kubernetes] show Service and Ingress resources for kubernetes app in dashboard Signed-off-by: Kirill Ilin --- packages/apps/kubernetes/templates/cloud-config.yaml | 5 +++++ packages/apps/kubernetes/templates/ingress.yaml | 10 ++++++++++ packages/system/kubernetes-rd/cozyrds/kubernetes.yaml | 4 ++++ 3 files changed, 19 insertions(+) diff --git a/packages/apps/kubernetes/templates/cloud-config.yaml b/packages/apps/kubernetes/templates/cloud-config.yaml index b1399b11..a4b7f2c9 100644 --- a/packages/apps/kubernetes/templates/cloud-config.yaml +++ b/packages/apps/kubernetes/templates/cloud-config.yaml @@ -10,3 +10,8 @@ data: enableEPSController: true selectorless: true namespace: {{ .Release.Namespace }} + infraLabels: + apps.cozystack.io/application.group: apps.cozystack.io + apps.cozystack.io/application.kind: Kubernetes + apps.cozystack.io/application.name: {{ .Release.Name | trimPrefix "kubernetes-" }} + internal.cozystack.io/tenantresource: "true" diff --git a/packages/apps/kubernetes/templates/ingress.yaml b/packages/apps/kubernetes/templates/ingress.yaml index 7993dba8..2e7f9933 100644 --- a/packages/apps/kubernetes/templates/ingress.yaml +++ b/packages/apps/kubernetes/templates/ingress.yaml @@ -14,6 +14,11 @@ metadata: } nginx.ingress.kubernetes.io/ssl-passthrough: "true" nginx.ingress.kubernetes.io/ssl-redirect: "false" + labels: + apps.cozystack.io/application.group: apps.cozystack.io + apps.cozystack.io/application.kind: Kubernetes + apps.cozystack.io/application.name: {{ .Release.Name | trimPrefix "kubernetes-" }} + internal.cozystack.io/tenantresource: "true" spec: ingressClassName: "{{ $ingress }}" rules: @@ -41,6 +46,11 @@ apiVersion: v1 kind: Service metadata: name: {{ .Release.Name }}-ingress-nginx + labels: + apps.cozystack.io/application.group: apps.cozystack.io + apps.cozystack.io/application.kind: Kubernetes + apps.cozystack.io/application.name: {{ .Release.Name | trimPrefix "kubernetes-" }} + internal.cozystack.io/tenantresource: "true" spec: ports: - appProtocol: http diff --git a/packages/system/kubernetes-rd/cozyrds/kubernetes.yaml b/packages/system/kubernetes-rd/cozyrds/kubernetes.yaml index 289ef25e..1b4c7361 100644 --- a/packages/system/kubernetes-rd/cozyrds/kubernetes.yaml +++ b/packages/system/kubernetes-rd/cozyrds/kubernetes.yaml @@ -37,8 +37,12 @@ spec: include: - resourceNames: - kubernetes-{{ .name }} + - kubernetes-{{ .name }}-ingress-nginx + - matchLabels: + cluster.x-k8s.io/cluster-name: kubernetes-{{ .name }} ingresses: exclude: [] include: - resourceNames: - kubernetes-{{ .name }} + - kubernetes-{{ .name }}-ingress-nginx From 7cebafbafd138d45ae0413158a4060a53ace3ad7 Mon Sep 17 00:00:00 2001 From: Timofei Larkin Date: Mon, 26 Jan 2026 19:44:17 +0300 Subject: [PATCH 077/125] [dashboard] Improve dashboard session params ## What this PR does This patch enables the `offline_access` scope for the dashbord keycloak client, so that users get a refresh token which gatekeeper can use to automatically refresh an expiring access token. Also session timeouts were increased. ### Release note ```release-note [dashboard] Increase session timeouts, add the offline_access scope, enable refresh tokens to improve the overall user experience when working with the dashboard. ``` Signed-off-by: Timofei Larkin --- packages/system/dashboard/templates/gatekeeper.yaml | 1 + packages/system/dashboard/templates/keycloakclient.yaml | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/packages/system/dashboard/templates/gatekeeper.yaml b/packages/system/dashboard/templates/gatekeeper.yaml index 40f2565f..984ec03e 100644 --- a/packages/system/dashboard/templates/gatekeeper.yaml +++ b/packages/system/dashboard/templates/gatekeeper.yaml @@ -64,6 +64,7 @@ spec: - --cookie-secure=true - --cookie-secret=$(OAUTH2_PROXY_COOKIE_SECRET) - --skip-provider-button + - --scope=openid email profile offline_access env: - name: OAUTH2_PROXY_CLIENT_ID value: dashboard diff --git a/packages/system/dashboard/templates/keycloakclient.yaml b/packages/system/dashboard/templates/keycloakclient.yaml index e1caea71..d8e47e8a 100644 --- a/packages/system/dashboard/templates/keycloakclient.yaml +++ b/packages/system/dashboard/templates/keycloakclient.yaml @@ -66,6 +66,12 @@ spec: defaultClientScopes: - groups - kubernetes-client + optionalClientScopes: + - offline_access + attributes: + post.logout.redirect.uris: "+" + client.session.idle.timeout: "86400" + client.session.max.lifespan: "604800" redirectUris: - "https://dashboard.{{ $host }}/oauth2/callback/*" {{- range $i, $v := $extraRedirectUris }} From 17a5dadd63d460ef3489a7d65ee4f076b3c07989 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Tue, 27 Jan 2026 16:44:20 +0100 Subject: [PATCH 078/125] Remove apply-locally target from kilo Signed-off-by: Andrei Kvapil --- packages/system/kilo/Makefile | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/system/kilo/Makefile b/packages/system/kilo/Makefile index b0d23033..3d278db3 100644 --- a/packages/system/kilo/Makefile +++ b/packages/system/kilo/Makefile @@ -4,9 +4,6 @@ export NAMESPACE=cozy-$(NAME) include ../../../hack/common-envs.mk include ../../../hack/package.mk -apply-locally: - cozypkg apply --plain -n $(NAMESPACE) $(NAME) - update: wget https://raw.githubusercontent.com/squat/kilo/refs/heads/main/manifests/crds.yaml -O templates/crds.yaml wget https://raw.githubusercontent.com/squat/kilo/refs/heads/main/manifests/kilo-typhoon-flannel.yaml -O templates/kilo.yaml From b864c040698ab6165772f5b9836b71cc2b2d98b6 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Tue, 27 Jan 2026 16:44:47 +0100 Subject: [PATCH 079/125] Update cozyhr v1.6.1 Signed-off-by: Andrei Kvapil --- packages/core/platform/images/migrations/Dockerfile | 2 +- packages/core/testing/images/e2e-sandbox/Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/platform/images/migrations/Dockerfile b/packages/core/platform/images/migrations/Dockerfile index 137c2768..b35808d5 100644 --- a/packages/core/platform/images/migrations/Dockerfile +++ b/packages/core/platform/images/migrations/Dockerfile @@ -1,6 +1,6 @@ FROM alpine:3.22 -RUN wget -O- https://github.com/cozystack/cozyhr/raw/refs/heads/main/hack/install.sh | sh -s -- -v 1.5.0 +RUN wget -O- https://github.com/cozystack/cozyhr/raw/refs/heads/main/hack/install.sh | sh -s -- -v 1.6.1 RUN apk add --no-cache kubectl helm coreutils git jq ca-certificates bash curl diff --git a/packages/core/testing/images/e2e-sandbox/Dockerfile b/packages/core/testing/images/e2e-sandbox/Dockerfile index ee57f193..2f94991f 100644 --- a/packages/core/testing/images/e2e-sandbox/Dockerfile +++ b/packages/core/testing/images/e2e-sandbox/Dockerfile @@ -3,7 +3,7 @@ FROM ubuntu:22.04 ARG KUBECTL_VERSION=1.33.2 ARG TALOSCTL_VERSION=1.10.4 ARG HELM_VERSION=3.18.3 -ARG COZYHR_VERSION=1.5.0 +ARG COZYHR_VERSION=1.6.1 ARG TARGETOS ARG TARGETARCH From af320a86a0a8d06d2107148bc8d5ee7ca0682391 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Tue, 27 Jan 2026 18:06:30 +0100 Subject: [PATCH 080/125] fix(telemetry): use APIReader instead of cached client in operator The cozystack-operator's manager cache is filtered to only include specific secrets and namespaces with certain labels. This caused telemetry collection to fail because resources like kube-system namespace, nodes, services, and PVs were not in the cache. Switch to using mgr.GetAPIReader() which bypasses the cache and queries the API server directly. This is appropriate for telemetry since it only runs every 15 minutes. Co-Authored-By: Claude Signed-off-by: Andrei Kvapil --- cmd/cozystack-operator/main.go | 4 +++- internal/telemetry/operator_collector.go | 16 ++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/cmd/cozystack-operator/main.go b/cmd/cozystack-operator/main.go index f835aba1..188ce160 100644 --- a/cmd/cozystack-operator/main.go +++ b/cmd/cozystack-operator/main.go @@ -262,7 +262,9 @@ func main() { } // Initialize telemetry collector - collector, err := telemetry.NewOperatorCollector(mgr.GetClient(), &telemetryConfig, config) + // Use APIReader (non-cached) because the manager's cache is filtered + // and doesn't include resources needed for telemetry (e.g., kube-system namespace, nodes, etc.) + collector, err := telemetry.NewOperatorCollector(mgr.GetAPIReader(), &telemetryConfig, config) if err != nil { setupLog.V(1).Info("unable to create telemetry collector, telemetry will be disabled", "error", err) } diff --git a/internal/telemetry/operator_collector.go b/internal/telemetry/operator_collector.go index aecb5551..8b4ec302 100644 --- a/internal/telemetry/operator_collector.go +++ b/internal/telemetry/operator_collector.go @@ -22,7 +22,7 @@ import ( // OperatorCollector handles telemetry data collection for cozystack-operator type OperatorCollector struct { - client client.Client + reader client.Reader discoveryClient discovery.DiscoveryInterface config *Config ticker *time.Ticker @@ -30,13 +30,13 @@ type OperatorCollector struct { } // NewOperatorCollector creates a new telemetry collector for cozystack-operator -func NewOperatorCollector(c client.Client, config *Config, kubeConfig *rest.Config) (*OperatorCollector, error) { +func NewOperatorCollector(r client.Reader, config *Config, kubeConfig *rest.Config) (*OperatorCollector, error) { discoveryClient, err := discovery.NewDiscoveryClientForConfig(kubeConfig) if err != nil { return nil, fmt.Errorf("failed to create discovery client: %w", err) } return &OperatorCollector{ - client: c, + reader: r, discoveryClient: discoveryClient, config: config, }, nil @@ -108,7 +108,7 @@ func (c *OperatorCollector) collect(ctx context.Context) { // Get cluster ID from kube-system namespace var kubeSystemNS corev1.Namespace - if err := c.client.Get(ctx, types.NamespacedName{Name: "kube-system"}, &kubeSystemNS); err != nil { + if err := c.reader.Get(ctx, types.NamespacedName{Name: "kube-system"}, &kubeSystemNS); err != nil { logger.Info(fmt.Sprintf("Failed to get kube-system namespace: %v", err)) return } @@ -124,7 +124,7 @@ func (c *OperatorCollector) collect(ctx context.Context) { // Get nodes var nodeList corev1.NodeList - if err := c.client.List(ctx, &nodeList); err != nil { + if err := c.reader.List(ctx, &nodeList); err != nil { logger.Info(fmt.Sprintf("Failed to list nodes: %v", err)) return } @@ -183,7 +183,7 @@ func (c *OperatorCollector) collect(ctx context.Context) { // Collect LoadBalancer services metrics var serviceList corev1.ServiceList - if err := c.client.List(ctx, &serviceList); err != nil { + if err := c.reader.List(ctx, &serviceList); err != nil { logger.Info(fmt.Sprintf("Failed to list Services: %v", err)) } else { lbCount := 0 @@ -197,7 +197,7 @@ func (c *OperatorCollector) collect(ctx context.Context) { // Collect PV metrics grouped by driver and size var pvList corev1.PersistentVolumeList - if err := c.client.List(ctx, &pvList); err != nil { + if err := c.reader.List(ctx, &pvList); err != nil { logger.Info(fmt.Sprintf("Failed to list PVs: %v", err)) } else { pvMetrics := make(map[string]map[string]int) // size -> driver -> count @@ -236,7 +236,7 @@ func (c *OperatorCollector) collect(ctx context.Context) { // Collect installed packages var packageList cozyv1alpha1.PackageList - if err := c.client.List(ctx, &packageList); err != nil { + if err := c.reader.List(ctx, &packageList); err != nil { logger.Info(fmt.Sprintf("Failed to list Packages: %v", err)) } else { for _, pkg := range packageList.Items { From 0de4755d56aee62dabdf44da34473dac2b06e7be Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Tue, 27 Jan 2026 23:06:44 +0100 Subject: [PATCH 081/125] docs(changelogs): add changelogs for v0.40.3, v0.41.1, v0.41.2, v0.41.3 Add missing changelog documentation for recent patch releases. Co-Authored-By: Claude Signed-off-by: Andrei Kvapil --- docs/changelogs/v0.40.3.md | 15 +++++++++++++++ docs/changelogs/v0.41.1.md | 11 +++++++++++ docs/changelogs/v0.41.2.md | 13 +++++++++++++ docs/changelogs/v0.41.3.md | 15 +++++++++++++++ 4 files changed, 54 insertions(+) create mode 100644 docs/changelogs/v0.40.3.md create mode 100644 docs/changelogs/v0.41.1.md create mode 100644 docs/changelogs/v0.41.2.md create mode 100644 docs/changelogs/v0.41.3.md diff --git a/docs/changelogs/v0.40.3.md b/docs/changelogs/v0.40.3.md new file mode 100644 index 00000000..a62a70bc --- /dev/null +++ b/docs/changelogs/v0.40.3.md @@ -0,0 +1,15 @@ + + +## Fixes + +* **[apiserver] Fix Watch resourceVersion and bookmark handling**: Fixed issues with Watch API handling of resourceVersion and bookmarks, ensuring proper event streaming and state synchronization for API clients ([**@kvaps**](https://github.com/kvaps) in #1860). + +## Dependencies + +* **[cilium] Update Cilium to v1.18.6**: Updated Cilium CNI to v1.18.6 with security fixes and performance improvements ([**@sircthulhu**](https://github.com/sircthulhu) in #1868, #1870). + +--- + +**Full Changelog**: [v0.40.2...v0.40.3](https://github.com/cozystack/cozystack/compare/v0.40.2...v0.40.3) diff --git a/docs/changelogs/v0.41.1.md b/docs/changelogs/v0.41.1.md new file mode 100644 index 00000000..d084d644 --- /dev/null +++ b/docs/changelogs/v0.41.1.md @@ -0,0 +1,11 @@ + + +## Improvements + +* **[kubernetes] Add enum validation for IngressNginx exposeMethod**: Added enum validation for the `exposeMethod` field in IngressNginx configuration, preventing invalid values and improving user experience with clear valid options ([**@sircthulhu**](https://github.com/sircthulhu) in #1895, #1897). + +--- + +**Full Changelog**: [v0.41.0...v0.41.1](https://github.com/cozystack/cozystack/compare/v0.41.0...v0.41.1) diff --git a/docs/changelogs/v0.41.2.md b/docs/changelogs/v0.41.2.md new file mode 100644 index 00000000..8b3a8261 --- /dev/null +++ b/docs/changelogs/v0.41.2.md @@ -0,0 +1,13 @@ + + +## Improvements + +* **[monitoring-agents] Set minReplicas to 1 for VPA for VMAgent**: Configured VPA (Vertical Pod Autoscaler) to maintain at least 1 replica for VMAgent, ensuring monitoring availability during scaling operations ([**@sircthulhu**](https://github.com/sircthulhu) in #1894, #1905). + +* **[mongodb] Remove user-configurable images from MongoDB chart**: Removed user-configurable image options from the MongoDB chart to simplify configuration and ensure consistency with tested image versions ([**@kvaps**](https://github.com/kvaps) in #1901, #1904). + +--- + +**Full Changelog**: [v0.41.1...v0.41.2](https://github.com/cozystack/cozystack/compare/v0.41.1...v0.41.2) diff --git a/docs/changelogs/v0.41.3.md b/docs/changelogs/v0.41.3.md new file mode 100644 index 00000000..6d04dad3 --- /dev/null +++ b/docs/changelogs/v0.41.3.md @@ -0,0 +1,15 @@ + + +## Improvements + +* **[kubernetes] Show Service and Ingress resources for Kubernetes app in dashboard**: Added visibility of Service and Ingress resources for Kubernetes applications in the dashboard, improving resource management and monitoring capabilities ([**@sircthulhu**](https://github.com/sircthulhu) in #1912, #1915). + +## Fixes + +* **[dashboard] Fix filtering on Pods tab for Service**: Fixed an issue where pod filtering was not working correctly on the Pods tab when viewing Services in the dashboard ([**@sircthulhu**](https://github.com/sircthulhu) in #1909, #1914). + +--- + +**Full Changelog**: [v0.41.2...v0.41.3](https://github.com/cozystack/cozystack/compare/v0.41.2...v0.41.3) From b0fa330d886f00b5fd77042f815040c987eb5bb5 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Tue, 27 Jan 2026 23:24:42 +0100 Subject: [PATCH 082/125] refactor(mongodb): unify users and databases configuration Align MongoDB configuration with postgres and mysql patterns: - Add separate `databases` section with `roles.admin` and `roles.readonly` - Simplify `users` to only contain optional password field - All users now authenticate via `admin` database (MongoDB best practice) - Admin role maps to readWrite + dbAdmin permissions - Readonly role maps to read permission Co-Authored-By: Claude Signed-off-by: Andrei Kvapil --- hack/e2e-apps/mongodb.bats | 8 +++ packages/apps/mongodb/templates/mongodb.yaml | 24 +++++-- packages/apps/mongodb/tests/mongodb_test.yaml | 70 +++++++++++++------ .../apps/mongodb/tests/user-secrets_test.yaml | 28 ++------ packages/apps/mongodb/values.schema.json | 56 ++++++++------- packages/apps/mongodb/values.yaml | 38 ++++++---- 6 files changed, 135 insertions(+), 89 deletions(-) diff --git a/hack/e2e-apps/mongodb.bats b/hack/e2e-apps/mongodb.bats index 61ebada8..794baf63 100644 --- a/hack/e2e-apps/mongodb.bats +++ b/hack/e2e-apps/mongodb.bats @@ -14,6 +14,14 @@ spec: replicas: 1 storageClass: "" resourcesPreset: "nano" + users: + testuser: + password: xai7Wepo + databases: + testdb: + roles: + admin: + - testuser backup: enabled: false EOF diff --git a/packages/apps/mongodb/templates/mongodb.yaml b/packages/apps/mongodb/templates/mongodb.yaml index dabb51d4..67969758 100644 --- a/packages/apps/mongodb/templates/mongodb.yaml +++ b/packages/apps/mongodb/templates/mongodb.yaml @@ -103,18 +103,34 @@ spec: {{- end }} {{- if .Values.users }} + {{- /* Build a map of username -> list of roles from databases config */}} + {{- $userRoles := dict }} + {{- range $dbname, $db := .Values.databases }} + {{- range $user := $db.roles.admin }} + {{- $roles := index $userRoles $user | default list }} + {{- $roles = append $roles (dict "name" "readWrite" "db" $dbname) }} + {{- $roles = append $roles (dict "name" "dbAdmin" "db" $dbname) }} + {{- $_ := set $userRoles $user $roles }} + {{- end }} + {{- range $user := $db.roles.readonly }} + {{- $roles := index $userRoles $user | default list }} + {{- $roles = append $roles (dict "name" "read" "db" $dbname) }} + {{- $_ := set $userRoles $user $roles }} + {{- end }} + {{- end }} users: {{- range $username, $user := .Values.users }} - {{- if not $user.roles }} - {{- fail (printf "users.%s.roles is required and cannot be empty" $username) }} + {{- $roles := index $userRoles $username }} + {{- if not $roles }} + {{- fail (printf "user '%s' is not assigned to any database role in databases.*.roles" $username) }} {{- end }} - name: {{ $username }} - db: {{ $user.db }} + db: admin passwordSecretRef: name: {{ $.Release.Name }}-user-{{ $username }} key: password roles: - {{- range $user.roles }} + {{- range $roles }} - name: {{ .name }} db: {{ .db }} {{- end }} diff --git a/packages/apps/mongodb/tests/mongodb_test.yaml b/packages/apps/mongodb/tests/mongodb_test.yaml index db46fe79..4154ca25 100644 --- a/packages/apps/mongodb/tests/mongodb_test.yaml +++ b/packages/apps/mongodb/tests/mongodb_test.yaml @@ -352,12 +352,13 @@ tests: _cluster: cluster-domain: cozy.local users: {} + databases: {} asserts: - notExists: path: spec.users documentIndex: 0 - - it: configures users when defined + - it: configures users with admin role release: name: test-mongodb namespace: tenant-test @@ -365,11 +366,12 @@ tests: _cluster: cluster-domain: cozy.local users: - appuser: - db: appdb + appuser: {} + databases: + appdb: roles: - - name: readWrite - db: appdb + admin: + - appuser asserts: - exists: path: spec.users @@ -380,7 +382,7 @@ tests: documentIndex: 0 - equal: path: spec.users[0].db - value: appdb + value: admin documentIndex: 0 - equal: path: spec.users[0].passwordSecretRef.name @@ -391,7 +393,7 @@ tests: value: password documentIndex: 0 - - it: configures user roles + - it: assigns readWrite and dbAdmin roles for admin users release: name: test-mongodb namespace: tenant-test @@ -399,28 +401,31 @@ tests: _cluster: cluster-domain: cozy.local users: - admin: - db: admin + appuser: {} + databases: + mydb: roles: - - name: clusterAdmin - db: admin - - name: userAdminAnyDatabase - db: admin + admin: + - appuser asserts: - equal: path: spec.users[0].roles[0].name - value: clusterAdmin + value: readWrite documentIndex: 0 - equal: path: spec.users[0].roles[0].db - value: admin + value: mydb documentIndex: 0 - equal: path: spec.users[0].roles[1].name - value: userAdminAnyDatabase + value: dbAdmin + documentIndex: 0 + - equal: + path: spec.users[0].roles[1].db + value: mydb documentIndex: 0 - - it: fails when user has empty roles + - it: assigns read role for readonly users release: name: test-mongodb namespace: tenant-test @@ -428,12 +433,35 @@ tests: _cluster: cluster-domain: cozy.local users: - myuser: - db: mydb - roles: [] + reader: {} + databases: + mydb: + roles: + readonly: + - reader + asserts: + - equal: + path: spec.users[0].roles[0].name + value: read + documentIndex: 0 + - equal: + path: spec.users[0].roles[0].db + value: mydb + documentIndex: 0 + + - it: fails when user is not assigned to any database role + release: + name: test-mongodb + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + users: + myuser: {} + databases: {} asserts: - failedTemplate: - errorMessage: "users.myuser.roles is required and cannot be empty" + errorMessage: "user 'myuser' is not assigned to any database role in databases.*.roles" ########################### # Backup configuration # diff --git a/packages/apps/mongodb/tests/user-secrets_test.yaml b/packages/apps/mongodb/tests/user-secrets_test.yaml index b16e4243..6d9fb388 100644 --- a/packages/apps/mongodb/tests/user-secrets_test.yaml +++ b/packages/apps/mongodb/tests/user-secrets_test.yaml @@ -22,11 +22,7 @@ tests: namespace: tenant-test set: users: - myuser: - db: mydb - roles: - - name: readWrite - db: mydb + myuser: {} asserts: - hasDocuments: count: 1 @@ -48,16 +44,8 @@ tests: namespace: tenant-test set: users: - user1: - db: db1 - roles: - - name: readWrite - db: db1 - user2: - db: db2 - roles: - - name: dbAdmin - db: db2 + user1: {} + user2: {} asserts: - hasDocuments: count: 2 @@ -71,10 +59,6 @@ tests: users: myuser: password: "mysecretpassword" - db: mydb - roles: - - name: readWrite - db: mydb asserts: - equal: path: stringData.password @@ -87,11 +71,7 @@ tests: namespace: tenant-prod set: users: - admin: - db: admin - roles: - - name: clusterAdmin - db: admin + admin: {} asserts: - equal: path: metadata.name diff --git a/packages/apps/mongodb/values.schema.json b/packages/apps/mongodb/values.schema.json index 817c3463..9b9d30aa 100644 --- a/packages/apps/mongodb/values.schema.json +++ b/packages/apps/mongodb/values.schema.json @@ -232,40 +232,28 @@ "type": "string", "default": "" }, - "users": { - "description": "Custom MongoDB users configuration map.", + "databases": { + "description": "Databases configuration map.", "type": "object", "default": {}, "additionalProperties": { "type": "object", - "required": [ - "db" - ], "properties": { - "db": { - "description": "Database to authenticate against.", - "type": "string" - }, - "password": { - "description": "Password for the user (auto-generated if omitted).", - "type": "string" - }, "roles": { - "description": "List of MongoDB roles with database scope.", - "type": "array", - "items": { - "type": "object", - "required": [ - "db", - "name" - ], - "properties": { - "db": { - "description": "Database the role applies to.", + "description": "Roles assigned to users.", + "type": "object", + "properties": { + "admin": { + "description": "List of users with admin privileges (readWrite + dbAdmin).", + "type": "array", + "items": { "type": "string" - }, - "name": { - "description": "Role name (e.g., readWrite, dbAdmin, clusterAdmin).", + } + }, + "readonly": { + "description": "List of users with read-only privileges.", + "type": "array", + "items": { "type": "string" } } @@ -274,6 +262,20 @@ } } }, + "users": { + "description": "Users configuration map.", + "type": "object", + "default": {}, + "additionalProperties": { + "type": "object", + "properties": { + "password": { + "description": "Password for the user (auto-generated if omitted).", + "type": "string" + } + } + } + }, "version": { "description": "MongoDB major version to deploy.", "type": "string", diff --git a/packages/apps/mongodb/values.yaml b/packages/apps/mongodb/values.yaml index 003a0c9e..df8fbe37 100644 --- a/packages/apps/mongodb/values.yaml +++ b/packages/apps/mongodb/values.yaml @@ -74,26 +74,38 @@ shardingConfig: ## @section Users configuration ## -## @typedef {struct} Role - MongoDB role configuration. -## @field {string} name - Role name (e.g., readWrite, dbAdmin, clusterAdmin). -## @field {string} db - Database the role applies to. - ## @typedef {struct} User - User configuration. ## @field {string} [password] - Password for the user (auto-generated if omitted). -## @field {string} db - Database to authenticate against. -## @field {[]Role} roles - List of MongoDB roles with database scope. -## @param {map[string]User} users - Custom MongoDB users configuration map. +## @param {map[string]User} users - Users configuration map. users: {} ## Example: ## users: -## myuser: -## db: mydb +## user1: +## password: strongpassword +## user2: {} + +## +## @section Databases configuration +## + +## @typedef {struct} DatabaseRoles - Role assignments for a database. +## @field {[]string} [admin] - List of users with admin privileges (readWrite + dbAdmin). +## @field {[]string} [readonly] - List of users with read-only privileges. + +## @typedef {struct} Database - Database configuration. +## @field {DatabaseRoles} [roles] - Roles assigned to users. + +## @param {map[string]Database} databases - Databases configuration map. +databases: {} +## Example: +## databases: +## myapp: ## roles: -## - name: readWrite -## db: mydb -## - name: dbAdmin -## db: mydb +## admin: +## - user1 +## readonly: +## - user2 ## ## @section Backup parameters From a56fc00c5cc6bb72595fd56cf1846caf88a1b8b1 Mon Sep 17 00:00:00 2001 From: Kirill Ilin Date: Wed, 28 Jan 2026 12:27:13 +0500 Subject: [PATCH 083/125] [dashboard] Add "Edit" button to all resources Signed-off-by: Kirill Ilin --- internal/controller/dashboard/ui_helpers.go | 16 ++++++++++++++++ internal/controller/dashboard/unified_helpers.go | 13 ++++++++++--- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/internal/controller/dashboard/ui_helpers.go b/internal/controller/dashboard/ui_helpers.go index fb29a608..6c7e3000 100644 --- a/internal/controller/dashboard/ui_helpers.go +++ b/internal/controller/dashboard/ui_helpers.go @@ -102,6 +102,22 @@ func antdFlex(id string, gap float64, children []any) map[string]any { } } +func antdFlexSpaceBetween(id string, children []any) map[string]any { + if id == "" { + id = generateContainerID("auto", "flex") + } + + return map[string]any{ + "type": "antdFlex", + "data": map[string]any{ + "id": id, + "align": "center", + "justify": "space-between", + }, + "children": children, + } +} + func antdFlexVertical(id string, gap float64, children []any) map[string]any { // Auto-generate ID if not provided if id == "" { diff --git a/internal/controller/dashboard/unified_helpers.go b/internal/controller/dashboard/unified_helpers.go index b25d5b65..d5edf207 100644 --- a/internal/controller/dashboard/unified_helpers.go +++ b/internal/controller/dashboard/unified_helpers.go @@ -237,9 +237,16 @@ func createUnifiedFactory(config UnifiedResourceConfig, tabs []any, urlsToFetch "lineHeight": "24px", }) - header := antdFlex(generateContainerID("header", "row"), float64(6), []any{ - badge, - nameText, + header := antdFlexSpaceBetween(generateContainerID("header", "row"), []any{ + antdFlex(generateContainerID("header", "title-text"), float64(6), []any{ + badge, + nameText, + }), + antdLink(generateLinkID("header", "edit"), + "Edit", + fmt.Sprintf("/openapi-ui/{2}/{3}/forms/apis/{reqsJsonPath[0]['.apiVersion']['-']}/%s/{reqsJsonPath[0]['.metadata.name']['-']}", + config.Plural), + ), }) // Add marginBottom style to header From 1741651b0cd64e8f0757739ee31a4378136e6523 Mon Sep 17 00:00:00 2001 From: nbykov0 <166552198+nbykov0@users.noreply.github.com> Date: Wed, 28 Jan 2026 20:08:35 +0300 Subject: [PATCH 084/125] Post upgrade fixes for v1.0 packagesources Signed-off-by: nbykov0 <166552198+nbykov0@users.noreply.github.com> --- packages/core/platform/sources/cozy-proxy.yaml | 2 +- packages/core/platform/sources/metrics-server.yaml | 2 +- packages/core/platform/sources/monitoring-agents.yaml | 1 + packages/core/platform/templates/bundles/system.yaml | 2 ++ .../system/vertical-pod-autoscaler/templates/vpa-for-vpa.yaml | 2 +- 5 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/core/platform/sources/cozy-proxy.yaml b/packages/core/platform/sources/cozy-proxy.yaml index fac0f5fc..041b968f 100644 --- a/packages/core/platform/sources/cozy-proxy.yaml +++ b/packages/core/platform/sources/cozy-proxy.yaml @@ -18,4 +18,4 @@ spec: path: system/cozy-proxy install: namespace: cozy-system - releaseName: cozystack + releaseName: cozy-proxy diff --git a/packages/core/platform/sources/metrics-server.yaml b/packages/core/platform/sources/metrics-server.yaml index 0bd17881..607cd7f0 100644 --- a/packages/core/platform/sources/metrics-server.yaml +++ b/packages/core/platform/sources/metrics-server.yaml @@ -18,5 +18,5 @@ spec: - name: metrics-server path: system/metrics-server install: - namespace: cozy-metrics-server + namespace: cozy-monitoring releaseName: metrics-server diff --git a/packages/core/platform/sources/monitoring-agents.yaml b/packages/core/platform/sources/monitoring-agents.yaml index 9585c5aa..206699b6 100644 --- a/packages/core/platform/sources/monitoring-agents.yaml +++ b/packages/core/platform/sources/monitoring-agents.yaml @@ -13,6 +13,7 @@ spec: - name: default dependsOn: - cozystack.networking + - cozystack.metrics-server - cozystack.victoria-metrics-operator - cozystack.vertical-pod-autoscaler components: diff --git a/packages/core/platform/templates/bundles/system.yaml b/packages/core/platform/templates/bundles/system.yaml index a1e6a195..96a88333 100644 --- a/packages/core/platform/templates/bundles/system.yaml +++ b/packages/core/platform/templates/bundles/system.yaml @@ -20,6 +20,7 @@ {{include "cozystack.platform.package.default" (list "cozystack.metallb" $) }} {{include "cozystack.platform.package.default" (list "cozystack.reloader" $) }} {{include "cozystack.platform.package.default" (list "cozystack.linstor" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.linstor-scheduler" $) }} {{include "cozystack.platform.package.default" (list "cozystack.snapshot-controller" $) }} {{- end }} @@ -54,6 +55,7 @@ {{include "cozystack.platform.package.default" (list "cozystack.backup-controller" $) }} {{include "cozystack.platform.package.optional.default" (list "cozystack.velero" $) }} {{include "cozystack.platform.package.default" (list "cozystack.vertical-pod-autoscaler" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.metrics-server" $) }} {{include "cozystack.platform.package.default" (list "cozystack.monitoring-agents" $) }} {{include "cozystack.platform.package.default" (list "cozystack.goldpinger" $) }} {{include "cozystack.platform.package.default" (list "cozystack.prometheus-operator-crds" $) }} diff --git a/packages/system/vertical-pod-autoscaler/templates/vpa-for-vpa.yaml b/packages/system/vertical-pod-autoscaler/templates/vpa-for-vpa.yaml index 00fcadd7..2df053b5 100644 --- a/packages/system/vertical-pod-autoscaler/templates/vpa-for-vpa.yaml +++ b/packages/system/vertical-pod-autoscaler/templates/vpa-for-vpa.yaml @@ -18,7 +18,7 @@ metadata: spec: chartRef: kind: ExternalArtifact - name: cozystack-system-default-vertical-pod-autoscaler + name: cozystack-vertical-pod-autoscaler-default-vpa-for-vpa namespace: cozy-system dependsOn: - name: monitoring-agents From 9e63bd533c0b36879a2b7f532dbc650beb53ce92 Mon Sep 17 00:00:00 2001 From: Kirill Ilin Date: Wed, 28 Jan 2026 18:11:59 +0500 Subject: [PATCH 085/125] [dashboard] Add resource quota usage to tenant info resource Signed-off-by: Kirill Ilin --- .../controller/dashboard/customcolumns.go | 3 -- internal/controller/dashboard/factory.go | 42 +++++++++++++++++++ .../controller/dashboard/static_refactored.go | 8 ++++ 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/internal/controller/dashboard/customcolumns.go b/internal/controller/dashboard/customcolumns.go index be1318f8..19a5a16a 100644 --- a/internal/controller/dashboard/customcolumns.go +++ b/internal/controller/dashboard/customcolumns.go @@ -34,9 +34,6 @@ func (m *Manager) ensureCustomColumnsOverride(ctx context.Context, crd *cozyv1al obj.SetName(name) href := fmt.Sprintf("/openapi-ui/{2}/{reqsJsonPath[0]['.metadata.namespace']['-']}/factory/%s/{reqsJsonPath[0]['.metadata.name']['-']}", detailsSegment) - if g == "apps.cozystack.io" && kind == "Tenant" && plural == "tenants" { - href = "/openapi-ui/{2}/{reqsJsonPath[0]['.status.namespace']['-']}/api-table/core.cozystack.io/v1alpha1/tenantmodules" - } desired := map[string]any{ "spec": map[string]any{ diff --git a/internal/controller/dashboard/factory.go b/internal/controller/dashboard/factory.go index 84578dd0..63bb7a82 100644 --- a/internal/controller/dashboard/factory.go +++ b/internal/controller/dashboard/factory.go @@ -174,6 +174,48 @@ func detailsTab(kind, endpoint, schemaJSON string, keysOrder [][]string) map[str }), ) } + if kind == "Info" { + rightColStack = append(rightColStack, + antdFlexVertical("resource-quotas-block", 4, []any{ + antdText("resource-quotas-label", true, "Resource Quotas", map[string]any{ + "fontSize": float64(20), + "marginBottom": float64(12), + }), + map[string]any{ + "type": "EnrichedTable", + "data": map[string]any{ + "id": "resource-quotas-table", + "baseprefix": "/openapi-ui", + "clusterNamePartOfUrl": "{2}", + "customizationId": "factory-resource-quotas", + "fetchUrl": "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/resourcequotas", + "pathToItems": []any{`items`}, + }, + }, + }), + ) + } + if kind == "Tenant" { + rightColStack = append(rightColStack, + antdFlexVertical("resource-quotas-block", 4, []any{ + antdText("resource-quotas-label", true, "Resource Quotas", map[string]any{ + "fontSize": float64(20), + "marginBottom": float64(12), + }), + map[string]any{ + "type": "EnrichedTable", + "data": map[string]any{ + "id": "resource-quotas-table", + "baseprefix": "/openapi-ui", + "clusterNamePartOfUrl": "{2}", + "customizationId": "factory-resource-quotas", + "fetchUrl": "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/resourcequotas", + "pathToItems": []any{`items`}, + }, + }, + }), + ) + } return map[string]any{ "key": "details", diff --git a/internal/controller/dashboard/static_refactored.go b/internal/controller/dashboard/static_refactored.go index b0e78e56..5b3acd61 100644 --- a/internal/controller/dashboard/static_refactored.go +++ b/internal/controller/dashboard/static_refactored.go @@ -189,6 +189,14 @@ func CreateAllCustomColumnsOverrides() []*dashboardv1alpha1.CustomColumnsOverrid createStringColumn("Values", "_flatMapData_Value"), }), + // Factory resource quotas + createCustomColumnsOverride("factory-resource-quotas", []any{ + createFlatMapColumn("Data", ".spec.hard"), + createStringColumn("Resource", "_flatMapData_Key"), + createStringColumn("Hard", "_flatMapData_Value"), + createStringColumn("Used", ".status.used['{_flatMapData_Key}']"), + }), + // Factory ingress details rules createCustomColumnsOverride("factory-kube-ingress-details-rules", []any{ createStringColumn("Host", ".host"), From f485b5b92a89c7d28837ee98a45acaa6ec9486a0 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Tue, 27 Jan 2026 23:26:45 +0100 Subject: [PATCH 086/125] feat(mongodb): add migration for users/databases config Add migration script to convert existing MongoDB HelmReleases from old users format to new users + databases format. Co-Authored-By: Claude Signed-off-by: Andrei Kvapil --- packages/apps/mongodb/README.md | 22 ++++-- packages/apps/mongodb/values.schema.json | 60 +++++++-------- .../platform/images/migrations/migrations/24 | 73 +++++++++++++++++++ .../system/mongodb-rd/cozyrds/mongodb.yaml | 4 +- 4 files changed, 119 insertions(+), 40 deletions(-) create mode 100755 packages/core/platform/images/migrations/migrations/24 diff --git a/packages/apps/mongodb/README.md b/packages/apps/mongodb/README.md index a5fb17fb..82127c59 100644 --- a/packages/apps/mongodb/README.md +++ b/packages/apps/mongodb/README.md @@ -69,14 +69,20 @@ Run `helm upgrade` after MongoDB is ready to populate the credentials secret wit ### Users configuration -| Name | Description | Type | Value | -| --------------------------- | --------------------------------------------------- | ------------------- | ----- | -| `users` | Custom MongoDB users configuration map. | `map[string]object` | `{}` | -| `users[name].password` | Password for the user (auto-generated if omitted). | `string` | `""` | -| `users[name].db` | Database to authenticate against. | `string` | `""` | -| `users[name].roles` | List of MongoDB roles with database scope. | `[]object` | `[]` | -| `users[name].roles[i].name` | Role name (e.g., readWrite, dbAdmin, clusterAdmin). | `string` | `""` | -| `users[name].roles[i].db` | Database the role applies to. | `string` | `""` | +| Name | Description | Type | Value | +| ---------------------- | -------------------------------------------------- | ------------------- | ----- | +| `users` | Users configuration map. | `map[string]object` | `{}` | +| `users[name].password` | Password for the user (auto-generated if omitted). | `string` | `""` | + + +### Databases configuration + +| Name | Description | Type | Value | +| -------------------------------- | ---------------------------------------------------------- | ------------------- | ----- | +| `databases` | Databases configuration map. | `map[string]object` | `{}` | +| `databases[name].roles` | Roles assigned to users. | `object` | `{}` | +| `databases[name].roles.admin` | List of users with admin privileges (readWrite + dbAdmin). | `[]string` | `[]` | +| `databases[name].roles.readonly` | List of users with read-only privileges. | `[]string` | `[]` | ### Backup parameters diff --git a/packages/apps/mongodb/values.schema.json b/packages/apps/mongodb/values.schema.json index 9b9d30aa..3a66d60d 100644 --- a/packages/apps/mongodb/values.schema.json +++ b/packages/apps/mongodb/values.schema.json @@ -73,6 +73,36 @@ } } }, + "databases": { + "description": "Databases configuration map.", + "type": "object", + "default": {}, + "additionalProperties": { + "type": "object", + "properties": { + "roles": { + "description": "Roles assigned to users.", + "type": "object", + "properties": { + "admin": { + "description": "List of users with admin privileges (readWrite + dbAdmin).", + "type": "array", + "items": { + "type": "string" + } + }, + "readonly": { + "description": "List of users with read-only privileges.", + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + }, "external": { "description": "Enable external access from outside the cluster.", "type": "boolean", @@ -232,36 +262,6 @@ "type": "string", "default": "" }, - "databases": { - "description": "Databases configuration map.", - "type": "object", - "default": {}, - "additionalProperties": { - "type": "object", - "properties": { - "roles": { - "description": "Roles assigned to users.", - "type": "object", - "properties": { - "admin": { - "description": "List of users with admin privileges (readWrite + dbAdmin).", - "type": "array", - "items": { - "type": "string" - } - }, - "readonly": { - "description": "List of users with read-only privileges.", - "type": "array", - "items": { - "type": "string" - } - } - } - } - } - } - }, "users": { "description": "Users configuration map.", "type": "object", diff --git a/packages/core/platform/images/migrations/migrations/24 b/packages/core/platform/images/migrations/migrations/24 new file mode 100755 index 00000000..cb34b041 --- /dev/null +++ b/packages/core/platform/images/migrations/migrations/24 @@ -0,0 +1,73 @@ +#!/bin/sh +# Migration 24 --> 25 +# Migrate MongoDB users configuration to new databases format + +set -euo pipefail + +echo "Migrating MongoDB HelmReleases: converting users to databases format" + +# Process all MongoDB HelmReleases +kubectl get helmreleases --all-namespaces -o json | \ + jq -r '.items[] | select(.metadata.name | startswith("mongodb-")) | "\(.metadata.namespace)|\(.metadata.name)"' | \ + while IFS='|' read -r namespace name; do + echo "Processing MongoDB HelmRelease $namespace/$name" + + # Get current spec.values + values=$(kubectl get helmrelease -n "$namespace" "$name" -o jsonpath='{.spec.values}') + + # Check if users exist and have old format (with db and roles fields) + has_old_format=$(echo "$values" | jq -r '.users // {} | to_entries[] | select(.value.db != null or .value.roles != null) | .key' | head -1) + + if [ -z "$has_old_format" ]; then + echo "Skipping $namespace/$name: no users with old format found" + continue + fi + + echo "Converting users configuration for $namespace/$name" + + # Build new configuration using jq + new_values=$(echo "$values" | jq ' + # Extract users and build new format + .users as $old_users | + + # Build databases from user roles + ($old_users // {} | to_entries | reduce .[] as $user ( + {}; + ($user.value.roles // []) as $roles | + reduce $roles[] as $role ( + .; + # Determine role type: readWrite/dbAdmin -> admin, read -> readonly + if ($role.name == "readWrite" or $role.name == "dbAdmin") then + .[$role.db].roles.admin = ((.[$role.db].roles.admin // []) + [$user.key] | unique) + elif ($role.name == "read") then + .[$role.db].roles.readonly = ((.[$role.db].roles.readonly // []) + [$user.key] | unique) + else + . + end + ) + )) as $databases | + + # Build new users (only keep password if present) + ($old_users // {} | to_entries | reduce .[] as $user ( + {}; + if $user.value.password then + .[$user.key] = {password: $user.value.password} + else + .[$user.key] = {} + end + )) as $new_users | + + # Update values + . + {users: $new_users} + (if ($databases | length) > 0 then {databases: $databases} else {} end) + ') + + # Patch the HelmRelease + kubectl patch helmrelease -n "$namespace" "$name" --type=merge -p "{\"spec\":{\"values\":$new_values}}" + echo "Successfully migrated $namespace/$name" + done + +echo "MongoDB migration completed" + +# Stamp version +kubectl create configmap -n cozy-system cozystack-version \ + --from-literal=version=25 --dry-run=client -o yaml | kubectl apply -f- diff --git a/packages/system/mongodb-rd/cozyrds/mongodb.yaml b/packages/system/mongodb-rd/cozyrds/mongodb.yaml index 0edb0d00..54909131 100644 --- a/packages/system/mongodb-rd/cozyrds/mongodb.yaml +++ b/packages/system/mongodb-rd/cozyrds/mongodb.yaml @@ -8,7 +8,7 @@ spec: singular: mongodb plural: mongodbs openAPISchema: |- - {"title":"Chart Values","type":"object","properties":{"backup":{"description":"Backup configuration.","type":"object","default":{},"required":["enabled"],"properties":{"destinationPath":{"description":"Destination path for backups (e.g. s3://bucket/path/).","type":"string","default":"s3://bucket/path/to/folder/"},"enabled":{"description":"Enable regular backups.","type":"boolean","default":false},"endpointURL":{"description":"S3 endpoint URL for uploads.","type":"string","default":"http://minio-gateway-service:9000"},"retentionPolicy":{"description":"Retention policy (e.g. \"30d\").","type":"string","default":"30d"},"s3AccessKey":{"description":"Access key for S3 authentication.","type":"string","default":""},"s3SecretKey":{"description":"Secret key for S3 authentication.","type":"string","default":""},"schedule":{"description":"Cron schedule for automated backups.","type":"string","default":"0 2 * * *"}}},"bootstrap":{"description":"Bootstrap configuration.","type":"object","default":{},"required":["backupName","enabled"],"properties":{"backupName":{"description":"Name of backup to restore from.","type":"string","default":""},"enabled":{"description":"Whether to restore from a backup.","type":"boolean","default":false},"recoveryTime":{"description":"Timestamp for point-in-time recovery; empty means latest.","type":"string","default":""}}},"external":{"description":"Enable external access from outside the cluster.","type":"boolean","default":false},"replicas":{"description":"Number of MongoDB replicas in replica set.","type":"integer","default":3},"resources":{"description":"Explicit CPU and memory configuration for each MongoDB replica. When omitted, the preset defined in `resourcesPreset` is applied.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available to each replica.","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":"Memory (RAM) available to each replica.","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}}},"resourcesPreset":{"description":"Default sizing preset used when `resources` is omitted.","type":"string","default":"small","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]},"sharding":{"description":"Enable sharded cluster mode. When disabled, deploys a replica set.","type":"boolean","default":false},"shardingConfig":{"description":"Configuration for sharded cluster mode.","type":"object","default":{},"required":["configServerSize","configServers","mongos"],"properties":{"configServerSize":{"description":"PVC size for config servers.","default":"3Gi","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},"configServers":{"description":"Number of config server replicas.","type":"integer","default":3},"mongos":{"description":"Number of mongos router replicas.","type":"integer","default":2},"shards":{"description":"List of shard configurations.","type":"array","default":[{"name":"rs0","replicas":3,"size":"10Gi"}],"items":{"type":"object","required":["name","replicas","size"],"properties":{"name":{"description":"Shard name.","type":"string"},"replicas":{"description":"Number of replicas in this shard.","type":"integer"},"size":{"description":"PVC size for this shard.","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}}}}}},"size":{"description":"Persistent Volume Claim size available for application data.","default":"10Gi","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},"storageClass":{"description":"StorageClass used to store the data.","type":"string","default":""},"users":{"description":"Custom MongoDB users configuration map.","type":"object","default":{},"additionalProperties":{"type":"object","required":["db"],"properties":{"db":{"description":"Database to authenticate against.","type":"string"},"password":{"description":"Password for the user (auto-generated if omitted).","type":"string"},"roles":{"description":"List of MongoDB roles with database scope.","type":"array","items":{"type":"object","required":["db","name"],"properties":{"db":{"description":"Database the role applies to.","type":"string"},"name":{"description":"Role name (e.g., readWrite, dbAdmin, clusterAdmin).","type":"string"}}}}}}},"version":{"description":"MongoDB major version to deploy.","type":"string","default":"v8","enum":["v8","v7","v6"]}}} + {"title":"Chart Values","type":"object","properties":{"backup":{"description":"Backup configuration.","type":"object","default":{},"required":["enabled"],"properties":{"destinationPath":{"description":"Destination path for backups (e.g. s3://bucket/path/).","type":"string","default":"s3://bucket/path/to/folder/"},"enabled":{"description":"Enable regular backups.","type":"boolean","default":false},"endpointURL":{"description":"S3 endpoint URL for uploads.","type":"string","default":"http://minio-gateway-service:9000"},"retentionPolicy":{"description":"Retention policy (e.g. \"30d\").","type":"string","default":"30d"},"s3AccessKey":{"description":"Access key for S3 authentication.","type":"string","default":""},"s3SecretKey":{"description":"Secret key for S3 authentication.","type":"string","default":""},"schedule":{"description":"Cron schedule for automated backups.","type":"string","default":"0 2 * * *"}}},"bootstrap":{"description":"Bootstrap configuration.","type":"object","default":{},"required":["backupName","enabled"],"properties":{"backupName":{"description":"Name of backup to restore from.","type":"string","default":""},"enabled":{"description":"Whether to restore from a backup.","type":"boolean","default":false},"recoveryTime":{"description":"Timestamp for point-in-time recovery; empty means latest.","type":"string","default":""}}},"databases":{"description":"Databases configuration map.","type":"object","default":{},"additionalProperties":{"type":"object","properties":{"roles":{"description":"Roles assigned to users.","type":"object","properties":{"admin":{"description":"List of users with admin privileges (readWrite + dbAdmin).","type":"array","items":{"type":"string"}},"readonly":{"description":"List of users with read-only privileges.","type":"array","items":{"type":"string"}}}}}}},"external":{"description":"Enable external access from outside the cluster.","type":"boolean","default":false},"replicas":{"description":"Number of MongoDB replicas in replica set.","type":"integer","default":3},"resources":{"description":"Explicit CPU and memory configuration for each MongoDB replica. When omitted, the preset defined in `resourcesPreset` is applied.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available to each replica.","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":"Memory (RAM) available to each replica.","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}}},"resourcesPreset":{"description":"Default sizing preset used when `resources` is omitted.","type":"string","default":"small","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]},"sharding":{"description":"Enable sharded cluster mode. When disabled, deploys a replica set.","type":"boolean","default":false},"shardingConfig":{"description":"Configuration for sharded cluster mode.","type":"object","default":{},"required":["configServerSize","configServers","mongos"],"properties":{"configServerSize":{"description":"PVC size for config servers.","default":"3Gi","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},"configServers":{"description":"Number of config server replicas.","type":"integer","default":3},"mongos":{"description":"Number of mongos router replicas.","type":"integer","default":2},"shards":{"description":"List of shard configurations.","type":"array","default":[{"name":"rs0","replicas":3,"size":"10Gi"}],"items":{"type":"object","required":["name","replicas","size"],"properties":{"name":{"description":"Shard name.","type":"string"},"replicas":{"description":"Number of replicas in this shard.","type":"integer"},"size":{"description":"PVC size for this shard.","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}}}}}},"size":{"description":"Persistent Volume Claim size available for application data.","default":"10Gi","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},"storageClass":{"description":"StorageClass used to store the data.","type":"string","default":""},"users":{"description":"Users configuration map.","type":"object","default":{},"additionalProperties":{"type":"object","properties":{"password":{"description":"Password for the user (auto-generated if omitted).","type":"string"}}}},"version":{"description":"MongoDB major version to deploy.","type":"string","default":"v8","enum":["v8","v7","v6"]}}} release: prefix: mongodb- labels: @@ -25,7 +25,7 @@ spec: tags: - database icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxNDQiIGhlaWdodD0iMTQ0IiByeD0iMjQiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl9tb25nb2RiKSIvPgo8cGF0aCBkPSJNNzIgMjRDNzIgMjQgNzIgMjQgNzIgMjRDNzIgMjQgNTggNDAgNTggNjJDNTggODQgNzIgMTIwIDcyIDEyMEM3MiAxMjAgODYgODQgODYgNjJDODYgNDAgNzIgMjQgNzIgMjRaIiBmaWxsPSIjMDBFRDY0Ii8+CjxwYXRoIGQ9Ik03MiAxMjBDNzIgMTIwIDg2IDg0IDg2IDYyQzg2IDQwIDcyIDI0IDcyIDI0IiBzdHJva2U9IiMwMDY4NEEiIHN0cm9rZS13aWR0aD0iNCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+CjxwYXRoIGQ9Ik03MiAyNEM3MiAyNCA1OCA0MCA1OCA2MkM1OCA4NCA3MiAxMjAgNzIgMTIwIiBzdHJva2U9IiMwMDFFMkIiIHN0cm9rZS13aWR0aD0iNCIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIi8+CjxyZWN0IHg9IjY5IiB5PSIxMDgiIHdpZHRoPSI2IiBoZWlnaHQ9IjE2IiByeD0iMiIgZmlsbD0iIzAwNjg0QSIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyX21vbmdvZGIiIHgxPSIxNDAiIHkxPSIxMzAuNSIgeDI9IjQiIHkyPSI5LjQ5OTk5IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIHN0b3AtY29sb3I9IiMwMDFFMkIiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjMDIzNDMwIi8+CjwvbGluZWFyR3JhZGllbnQ+CjwvZGVmcz4KPC9zdmc+Cg== - keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "replicas"], ["spec", "resources"], ["spec", "resourcesPreset"], ["spec", "size"], ["spec", "storageClass"], ["spec", "external"], ["spec", "version"], ["spec", "sharding"], ["spec", "shardingConfig"], ["spec", "shardingConfig", "configServers"], ["spec", "shardingConfig", "configServerSize"], ["spec", "shardingConfig", "mongos"], ["spec", "shardingConfig", "shards"], ["spec", "users"], ["spec", "backup"], ["spec", "backup", "enabled"], ["spec", "backup", "schedule"], ["spec", "backup", "retentionPolicy"], ["spec", "backup", "destinationPath"], ["spec", "backup", "endpointURL"], ["spec", "backup", "s3AccessKey"], ["spec", "backup", "s3SecretKey"], ["spec", "bootstrap"], ["spec", "bootstrap", "enabled"], ["spec", "bootstrap", "recoveryTime"], ["spec", "bootstrap", "backupName"]] + keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "replicas"], ["spec", "resources"], ["spec", "resourcesPreset"], ["spec", "size"], ["spec", "storageClass"], ["spec", "external"], ["spec", "version"], ["spec", "sharding"], ["spec", "shardingConfig"], ["spec", "shardingConfig", "configServers"], ["spec", "shardingConfig", "configServerSize"], ["spec", "shardingConfig", "mongos"], ["spec", "shardingConfig", "shards"], ["spec", "users"], ["spec", "databases"], ["spec", "backup"], ["spec", "backup", "enabled"], ["spec", "backup", "schedule"], ["spec", "backup", "retentionPolicy"], ["spec", "backup", "destinationPath"], ["spec", "backup", "endpointURL"], ["spec", "backup", "s3AccessKey"], ["spec", "backup", "s3SecretKey"], ["spec", "bootstrap"], ["spec", "bootstrap", "enabled"], ["spec", "bootstrap", "recoveryTime"], ["spec", "bootstrap", "backupName"]] secrets: exclude: [] include: From 68d8271ede5becb0d4213ce23cb9f602686b5342 Mon Sep 17 00:00:00 2001 From: Kirill Ilin Date: Thu, 29 Jan 2026 18:20:50 +0500 Subject: [PATCH 087/125] [dashboard] Fix resource quota table on Tenant page - Remove table from info - fix namespace selector - add patch to allow usage of flatMap in jsonpath Signed-off-by: Kirill Ilin --- internal/controller/dashboard/factory.go | 23 +---- .../controller/dashboard/static_refactored.go | 2 +- .../patches/flatmap-dynamic-key.diff | 90 +++++++++++++++++++ 3 files changed, 92 insertions(+), 23 deletions(-) create mode 100644 packages/system/dashboard/images/openapi-ui/openapi-k8s-toolkit/patches/flatmap-dynamic-key.diff diff --git a/internal/controller/dashboard/factory.go b/internal/controller/dashboard/factory.go index 63bb7a82..3fa61961 100644 --- a/internal/controller/dashboard/factory.go +++ b/internal/controller/dashboard/factory.go @@ -174,27 +174,6 @@ func detailsTab(kind, endpoint, schemaJSON string, keysOrder [][]string) map[str }), ) } - if kind == "Info" { - rightColStack = append(rightColStack, - antdFlexVertical("resource-quotas-block", 4, []any{ - antdText("resource-quotas-label", true, "Resource Quotas", map[string]any{ - "fontSize": float64(20), - "marginBottom": float64(12), - }), - map[string]any{ - "type": "EnrichedTable", - "data": map[string]any{ - "id": "resource-quotas-table", - "baseprefix": "/openapi-ui", - "clusterNamePartOfUrl": "{2}", - "customizationId": "factory-resource-quotas", - "fetchUrl": "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/resourcequotas", - "pathToItems": []any{`items`}, - }, - }, - }), - ) - } if kind == "Tenant" { rightColStack = append(rightColStack, antdFlexVertical("resource-quotas-block", 4, []any{ @@ -209,7 +188,7 @@ func detailsTab(kind, endpoint, schemaJSON string, keysOrder [][]string) map[str "baseprefix": "/openapi-ui", "clusterNamePartOfUrl": "{2}", "customizationId": "factory-resource-quotas", - "fetchUrl": "/api/clusters/{2}/k8s/api/v1/namespaces/{3}/resourcequotas", + "fetchUrl": "/api/clusters/{2}/k8s/api/v1/namespaces/{reqsJsonPath[0]['.status.namespace']}/resourcequotas", "pathToItems": []any{`items`}, }, }, diff --git a/internal/controller/dashboard/static_refactored.go b/internal/controller/dashboard/static_refactored.go index 5b3acd61..b2ace34b 100644 --- a/internal/controller/dashboard/static_refactored.go +++ b/internal/controller/dashboard/static_refactored.go @@ -194,7 +194,7 @@ func CreateAllCustomColumnsOverrides() []*dashboardv1alpha1.CustomColumnsOverrid createFlatMapColumn("Data", ".spec.hard"), createStringColumn("Resource", "_flatMapData_Key"), createStringColumn("Hard", "_flatMapData_Value"), - createStringColumn("Used", ".status.used['{_flatMapData_Key}']"), + createStringColumn("Used", ".status.used[_flatMapData_Key]"), }), // Factory ingress details rules diff --git a/packages/system/dashboard/images/openapi-ui/openapi-k8s-toolkit/patches/flatmap-dynamic-key.diff b/packages/system/dashboard/images/openapi-ui/openapi-k8s-toolkit/patches/flatmap-dynamic-key.diff new file mode 100644 index 00000000..03212014 --- /dev/null +++ b/packages/system/dashboard/images/openapi-ui/openapi-k8s-toolkit/patches/flatmap-dynamic-key.diff @@ -0,0 +1,90 @@ +diff --git a/src/components/molecules/EnrichedTable/organisms/EnrichedTableProvider/utils.ts b/src/components/molecules/EnrichedTable/organisms/EnrichedTableProvider/utils.ts +index 87a0f12..fb2e1cc 100644 +--- a/src/components/molecules/EnrichedTable/organisms/EnrichedTableProvider/utils.ts ++++ b/src/components/molecules/EnrichedTable/organisms/EnrichedTableProvider/utils.ts +@@ -134,22 +134,6 @@ export const prepare = ({ + // impossible in k8s + return {} + }) +- if (customFields.length > 0) { +- dataSource = dataSource.map((el: TJSON) => { +- const newFieldsForComplexJsonPath: Record = {} +- customFields.forEach(({ dataIndex, jsonPath }) => { +- const jpQueryResult = jp.query(el, `$${jsonPath}`) +- newFieldsForComplexJsonPath[dataIndex] = +- Array.isArray(jpQueryResult) && jpQueryResult.length === 1 ? jpQueryResult[0] : jpQueryResult +- }) +- if (typeof el === 'object') { +- return { ...el, ...newFieldsForComplexJsonPath } +- } +- // impossible in k8s +- return { ...newFieldsForComplexJsonPath } +- }) +- } +- + // Handle flatMap: expand rows for map objects + // Process all flatMap columns sequentially + if (flatMapColumns.length > 0 && dataSource) { +@@ -204,6 +188,62 @@ export const prepare = ({ + currentDataSource = expandedDataSource + }) + dataSource = currentDataSource ++ } ++ ++ if (customFields.length > 0) { ++ dataSource = dataSource.map((el: TJSON) => { ++ const newFieldsForComplexJsonPath: Record = {} ++ customFields.forEach(({ dataIndex, jsonPath }) => { ++ let fieldValue: TJSON = null ++ let handled = false ++ ++ const flatMapMatch = jsonPath.match(/^(.*)\[(_flatMap[^\]]+_Key)\](.*)$/) ++ if (flatMapMatch && el && typeof el === 'object' && !Array.isArray(el)) { ++ const basePath = flatMapMatch[1] ++ const keyField = flatMapMatch[2] ++ const tailPath = flatMapMatch[3] ++ const keyValue = (el as Record)[keyField] ++ if (keyValue !== null && keyValue !== undefined) { ++ const baseResult = jp.query(el, `$${basePath}`)[0] ++ if (baseResult && typeof baseResult === 'object' && !Array.isArray(baseResult)) { ++ const baseValue = (baseResult as Record)[String(keyValue)] ++ if (tailPath) { ++ const normalizedTailPath = ++ tailPath.startsWith('.') || tailPath.startsWith('[') ? tailPath : `.${tailPath}` ++ const tailResult = jp.query(baseValue, `$${normalizedTailPath}`) ++ fieldValue = Array.isArray(tailResult) && tailResult.length === 1 ? tailResult[0] : tailResult ++ } else { ++ fieldValue = baseValue as TJSON ++ } ++ handled = true ++ } ++ } ++ } ++ ++ if (!handled) { ++ let resolvedJsonPath = jsonPath ++ if (el && typeof el === 'object' && !Array.isArray(el)) { ++ resolvedJsonPath = jsonPath.replace(/\[(_flatMap[^\]]+_Key)\]/g, (match, keyField) => { ++ const keyValue = (el as Record)[keyField] ++ if (keyValue === null || keyValue === undefined) { ++ return match ++ } ++ const escaped = String(keyValue).replace(/'/g, "\\'") ++ return `['${escaped}']` ++ }) ++ } ++ const jpQueryResult = jp.query(el, `$${resolvedJsonPath}`) ++ fieldValue = Array.isArray(jpQueryResult) && jpQueryResult.length === 1 ? jpQueryResult[0] : jpQueryResult ++ } ++ ++ newFieldsForComplexJsonPath[dataIndex] = fieldValue ++ }) ++ if (typeof el === 'object') { ++ return { ...el, ...newFieldsForComplexJsonPath } ++ } ++ // impossible in k8s ++ return { ...newFieldsForComplexJsonPath } ++ }) + } + } else { + dataSource = dataItems.map((el: TJSON) => { From 063e9a49bdaab202d9e983a8d8dfa7ef3d5f596d Mon Sep 17 00:00:00 2001 From: nbykov0 <166552198+nbykov0@users.noreply.github.com> Date: Thu, 29 Jan 2026 18:02:03 +0300 Subject: [PATCH 088/125] Post upgrade fixes for v1.0 migration script Signed-off-by: nbykov0 <166552198+nbykov0@users.noreply.github.com> --- hack/migrate-to-version-1.0.sh | 49 +++++++++++++++++----- packages/core/platform/templates/apps.yaml | 1 + packages/core/platform/values.yaml | 9 ++-- 3 files changed, 42 insertions(+), 17 deletions(-) diff --git a/hack/migrate-to-version-1.0.sh b/hack/migrate-to-version-1.0.sh index 3985d6f9..743aaf30 100755 --- a/hack/migrate-to-version-1.0.sh +++ b/hack/migrate-to-version-1.0.sh @@ -7,8 +7,9 @@ set -e NAMESPACE="cozy-system" -echo "Cozystack Migration to v1.0" -echo "===========================" +echo "=============================" +echo " Cozystack Migration to v1.0 " +echo "=============================" echo "" echo "This script will convert existing ConfigMaps to a Package resource." echo "" @@ -19,6 +20,12 @@ if ! command -v kubectl &> /dev/null; then exit 1 fi +# Check if jq is available +if ! command -v jq &> /dev/null; then + echo "Error: jq is not installed or not in PATH" + exit 1 +fi + # Check if we can access the cluster if ! kubectl get namespace "$NAMESPACE" &> /dev/null; then echo "Error: Cannot access namespace $NAMESPACE" @@ -42,6 +49,7 @@ CLUSTER_DOMAIN=$(echo "$COZYSTACK_CM" | jq -r '.data["cluster-domain"] // "cozy. ROOT_HOST=$(echo "$COZYSTACK_CM" | jq -r '.data["root-host"] // "example.org"') API_SERVER_ENDPOINT=$(echo "$COZYSTACK_CM" | jq -r '.data["api-server-endpoint"] // ""') OIDC_ENABLED=$(echo "$COZYSTACK_CM" | jq -r '.data["oidc-enabled"] // "false"') +KEYCLOAK_REDIRECTS=$(echo "$COZYSTACK_CM" | jq -r '.data["extra-keycloak-redirect-uri-for-dashboard"] // ""' ) TELEMETRY_ENABLED=$(echo "$COZYSTACK_CM" | jq -r '.data["telemetry-enabled"] // "true"') BUNDLE_NAME=$(echo "$COZYSTACK_CM" | jq -r '.data["bundle-name"] // "paas-full"') @@ -51,6 +59,13 @@ POD_GATEWAY=$(echo "$COZYSTACK_CM" | jq -r '.data["ipv4-pod-gateway"] // "10.244 SVC_CIDR=$(echo "$COZYSTACK_CM" | jq -r '.data["ipv4-svc-cidr"] // "10.96.0.0/16"') JOIN_CIDR=$(echo "$COZYSTACK_CM" | jq -r '.data["ipv4-join-cidr"] // "100.64.0.0/16"') +EXTERNAL_IPS=$(echo "$COZYSTACK_CM" | jq -r '.data["expose-external-ips"] // ""') +if [ -z "$EXTERNAL_IPS" ]; then + EXTERNAL_IPS="[]" +else + EXTERNAL_IPS=$(echo "$EXTERNAL_IPS" | sed 's/,/\n/g' | awk 'BEGIN{print}{print " - "$0}') +fi + # Determine bundle type case "$BUNDLE_NAME" in paas-full|distro-full) @@ -67,12 +82,24 @@ case "$BUNDLE_NAME" in ;; esac +# Update bundle naming +BUNDLE_NAME=$(echo "$BUNDLE_NAME" | sed 's/paas/isp/') + # Extract branding if available -DASHBOARD_BRANDING=$(echo "$BRANDING_CM" | jq -r '.data["dashboard"] // "{}"') -KEYCLOAK_BRANDING=$(echo "$BRANDING_CM" | jq -r '.data["keycloak"] // "{}"') +BRANDING=$(echo "$BRANDING_CM" | jq -r '.data // {} | to_entries[] | "\(.key): \"\(.value)\""') +if [ -z "$BRANDING" ]; then + BRANDING="{}" +else + BRANDING=$(echo "$BRANDING" | awk 'BEGIN{print}{print " " $0}') +fi # Extract scheduling if available -SCHEDULING_CONSTRAINTS=$(echo "$SCHEDULING_CM" | jq -r '.data["topologySpreadConstraints"] // "[]"') +SCHEDULING_CONSTRAINTS=$(echo "$SCHEDULING_CM" | jq -r '.data.["globalAppTopologySpreadConstraints"] // ""') +if [ -z "$SCHEDULING_CONSTRAINTS" ]; then + SCHEDULING_CONSTRAINTS='""' +else + SCHEDULING_CONSTRAINTS=$(echo "$SCHEDULING_CONSTRAINTS" | awk 'BEGIN{print}{print " " $0}') +fi echo "" echo "Extracted configuration:" @@ -116,21 +143,21 @@ spec: publishing: host: "$ROOT_HOST" apiServerEndpoint: "$API_SERVER_ENDPOINT" + externalIPs: $EXTERNAL_IPS authentication: oidc: enabled: $OIDC_ENABLED + keycloakExtraRedirectUri: "$KEYCLOAK_REDIRECTS" scheduling: - topologySpreadConstraints: $SCHEDULING_CONSTRAINTS - branding: - dashboard: $DASHBOARD_BRANDING - keycloak: $KEYCLOAK_BRANDING + globalAppTopologySpreadConstraints: $SCHEDULING_CONSTRAINTS + branding: $BRANDING EOF ) echo "Generated Package resource:" echo "---" echo "$PACKAGE_YAML" -echo "---" +echo "..." echo "" read -p "Do you want to apply this Package? (y/N) " -n 1 -r @@ -149,4 +176,4 @@ else fi echo "" -echo "Migration complete!" +echo "All done!" diff --git a/packages/core/platform/templates/apps.yaml b/packages/core/platform/templates/apps.yaml index d3941067..8a716066 100644 --- a/packages/core/platform/templates/apps.yaml +++ b/packages/core/platform/templates/apps.yaml @@ -17,6 +17,7 @@ stringData: bundle-name: {{ .Values.bundles.system.variant | quote }} clusterissuer: {{ .Values.publishing.certificates.issuerType | quote }} oidc-enabled: {{ .Values.authentication.oidc.enabled | quote }} + extra-keycloak-redirect-uri-for-dashboard: {{ index .Values.authentication.oidc.keycloakExtraRedirectUri | quote }} expose-services: {{ .Values.publishing.exposedServices | join "," | quote }} expose-ingress: {{ .Values.publishing.ingressName | quote }} expose-external-ips: {{ .Values.publishing.externalIPs | join "," | quote }} diff --git a/packages/core/platform/values.yaml b/packages/core/platform/values.yaml index a358ef22..f8a0a9e4 100644 --- a/packages/core/platform/values.yaml +++ b/packages/core/platform/values.yaml @@ -44,15 +44,12 @@ publishing: authentication: oidc: enabled: false - dashboard: - extraRedirectUris: [] + keycloakExtraRedirectUri: "" # Pod scheduling configuration scheduling: - topologySpreadConstraints: [] + globalAppTopologySpreadConstraints: "" # UI branding configuration -branding: - dashboard: {} - keycloak: {} +branding: {} # Container registry mirrors configuration # # Example: From ded52c127972ad5ab2dcbc06d64eebc0f5832d88 Mon Sep 17 00:00:00 2001 From: Kirill Ilin Date: Fri, 30 Jan 2026 13:54:01 +0500 Subject: [PATCH 089/125] [dashboard] Add external ips count to Tenant details page Signed-off-by: Kirill Ilin --- internal/controller/dashboard/factory.go | 4 ++ pkg/apis/apps/v1alpha1/types.go | 3 ++ pkg/registry/apps/application/rest.go | 50 ++++++++++++++++++++---- 3 files changed, 49 insertions(+), 8 deletions(-) diff --git a/internal/controller/dashboard/factory.go b/internal/controller/dashboard/factory.go index 63bb7a82..1016b30a 100644 --- a/internal/controller/dashboard/factory.go +++ b/internal/controller/dashboard/factory.go @@ -196,6 +196,10 @@ func detailsTab(kind, endpoint, schemaJSON string, keysOrder [][]string) map[str ) } if kind == "Tenant" { + leftColStack = append(leftColStack, antdFlexVertical("tenant-external-ip-count", 4, []any{ + antdText("tenant-external-ip-count-label", true, "External IPs count", nil), + parsedText("tenant-external-ip-count-value", `{reqsJsonPath[0]['.status.externalIPsCount']['0']}`, nil), + })) rightColStack = append(rightColStack, antdFlexVertical("resource-quotas-block", 4, []any{ antdText("resource-quotas-label", true, "Resource Quotas", map[string]any{ diff --git a/pkg/apis/apps/v1alpha1/types.go b/pkg/apis/apps/v1alpha1/types.go index 0118b0de..51247efb 100644 --- a/pkg/apis/apps/v1alpha1/types.go +++ b/pkg/apis/apps/v1alpha1/types.go @@ -47,6 +47,9 @@ type ApplicationStatus struct { // Namespace holds the computed namespace for Tenant applications. // +optional Namespace string `json:"namespace,omitempty"` + // ExternalIPsCount holds the number of LoadBalancer services with assigned external IPs for Tenant applications. + // +optional + ExternalIPsCount int32 `json:"externalIPsCount,omitempty"` } // GetConditions returns the status conditions of the object. diff --git a/pkg/registry/apps/application/rest.go b/pkg/registry/apps/application/rest.go index b42f322c..a69e23db 100644 --- a/pkg/registry/apps/application/rest.go +++ b/pkg/registry/apps/application/rest.go @@ -27,6 +27,7 @@ import ( "time" helmv2 "github.com/fluxcd/helm-controller/api/v2" + corev1 "k8s.io/api/core/v1" metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" @@ -186,7 +187,7 @@ func (r *REST) Create(ctx context.Context, obj runtime.Object, createValidation } // Convert the created HelmRelease back to Application - convertedApp, err := r.ConvertHelmReleaseToApplication(helmRelease) + convertedApp, err := r.ConvertHelmReleaseToApplication(ctx, helmRelease) if err != nil { klog.Errorf("Conversion error from HelmRelease to Application for resource %s: %v", helmRelease.GetName(), err) return nil, fmt.Errorf("conversion error: %v", err) @@ -233,7 +234,7 @@ func (r *REST) Get(ctx context.Context, name string, options *metav1.GetOptions) } // Convert HelmRelease to Application - convertedApp, err := r.ConvertHelmReleaseToApplication(helmRelease) + convertedApp, err := r.ConvertHelmReleaseToApplication(ctx, helmRelease) if err != nil { klog.Errorf("Conversion error from HelmRelease to Application for resource %s: %v", name, err) return nil, fmt.Errorf("conversion error: %v", err) @@ -360,7 +361,7 @@ func (r *REST) List(ctx context.Context, options *metainternalversion.ListOption continue } - app, err := r.ConvertHelmReleaseToApplication(hr) + app, err := r.ConvertHelmReleaseToApplication(ctx, hr) if err != nil { klog.Errorf("Error converting HelmRelease %s to Application: %v", hr.GetName(), err) continue @@ -514,7 +515,7 @@ func (r *REST) Update(ctx context.Context, name string, objInfo rest.UpdatedObje } // Convert the updated HelmRelease back to Application - convertedApp, err := r.ConvertHelmReleaseToApplication(helmRelease) + convertedApp, err := r.ConvertHelmReleaseToApplication(ctx, helmRelease) if err != nil { klog.Errorf("Conversion error from HelmRelease to Application for resource %s: %v", helmRelease.GetName(), err) return nil, false, fmt.Errorf("conversion error: %v", err) @@ -796,7 +797,7 @@ func (r *REST) Watch(ctx context.Context, options *metainternalversion.ListOptio // Note: All HelmReleases already match the required labels due to server-side label selector filtering // Convert HelmRelease to Application - app, err := r.ConvertHelmReleaseToApplication(hr) + app, err := r.ConvertHelmReleaseToApplication(ctx, hr) if err != nil { klog.Errorf("Error converting HelmRelease to Application: %v", err) continue @@ -967,11 +968,11 @@ func filterPrefixedMap(original map[string]string, prefix string) map[string]str } // ConvertHelmReleaseToApplication converts a HelmRelease to an Application -func (r *REST) ConvertHelmReleaseToApplication(hr *helmv2.HelmRelease) (appsv1alpha1.Application, error) { +func (r *REST) ConvertHelmReleaseToApplication(ctx context.Context, hr *helmv2.HelmRelease) (appsv1alpha1.Application, error) { klog.V(6).Infof("Converting HelmRelease to Application for resource %s", hr.GetName()) // Convert HelmRelease struct to Application struct - app, err := r.convertHelmReleaseToApplication(hr) + app, err := r.convertHelmReleaseToApplication(ctx, hr) if err != nil { klog.Errorf("Error converting from HelmRelease to Application: %v", err) return appsv1alpha1.Application{}, err @@ -1029,7 +1030,7 @@ func validateNoInternalKeys(values *apiextv1.JSON) error { } // convertHelmReleaseToApplication implements the actual conversion logic -func (r *REST) convertHelmReleaseToApplication(hr *helmv2.HelmRelease) (appsv1alpha1.Application, error) { +func (r *REST) convertHelmReleaseToApplication(ctx context.Context, hr *helmv2.HelmRelease) (appsv1alpha1.Application, error) { // Filter out internal keys (starting with "_") from spec filteredSpec := filterInternalKeys(hr.Spec.Values) @@ -1071,6 +1072,12 @@ func (r *REST) convertHelmReleaseToApplication(hr *helmv2.HelmRelease) (appsv1al // Add namespace field for Tenant applications if r.kindName == "Tenant" { app.Status.Namespace = r.computeTenantNamespace(hr.Namespace, app.Name) + externalIPsCount, err := r.countTenantExternalIPs(ctx, app.Status.Namespace) + if err != nil { + klog.Warningf("Failed to count external IPs for tenant %s/%s: %v", hr.Namespace, app.Name, err) + } else { + app.Status.ExternalIPsCount = externalIPsCount + } } return app, nil @@ -1265,6 +1272,33 @@ func (r *REST) computeTenantNamespace(currentNamespace, tenantName string) strin } } +func (r *REST) countTenantExternalIPs(ctx context.Context, namespace string) (int32, error) { + if namespace == "" { + return 0, nil + } + + var services corev1.ServiceList + if err := r.c.List(ctx, &services, client.InNamespace(namespace)); err != nil { + return 0, err + } + + var count int32 + for i := range services.Items { + svc := &services.Items[i] + if svc.Spec.Type != corev1.ServiceTypeLoadBalancer { + continue + } + for _, ingress := range svc.Status.LoadBalancer.Ingress { + if ingress.IP != "" { + count++ + break + } + } + } + + return count, nil +} + // Destroy releases resources associated with REST func (r *REST) Destroy() { // No additional actions needed to release resources. From 40dc20f0f1bc0ee659e205efaec6c90dc692364e Mon Sep 17 00:00:00 2001 From: Kirill Ilin Date: Fri, 30 Jan 2026 15:13:24 +0500 Subject: [PATCH 090/125] [cozystack-api] Add field index for Service spec.type and filter by LoadBalancer type Signed-off-by: Kirill Ilin --- pkg/apiserver/apiserver.go | 11 +++++++++++ pkg/registry/apps/application/rest.go | 10 ++++++---- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/pkg/apiserver/apiserver.go b/pkg/apiserver/apiserver.go index fccbf9a9..bc2b4857 100644 --- a/pkg/apiserver/apiserver.go +++ b/pkg/apiserver/apiserver.go @@ -149,6 +149,17 @@ func (c completedConfig) New() (*CozyServer, error) { return nil, fmt.Errorf("failed to build manager: %w", err) } + if err := mgr.GetFieldIndexer().IndexField( + context.Background(), + &corev1.Service{}, + "spec.type", + func(rawObj client.Object) []string { + svc := rawObj.(*corev1.Service) + return []string{string(svc.Spec.Type)} + }); err != nil { + return nil, fmt.Errorf("failed to index service spec.type field: %w", err) + } + ctx := ctrl.SetupSignalHandler() if err = mustGetInformers(ctx, mgr, diff --git a/pkg/registry/apps/application/rest.go b/pkg/registry/apps/application/rest.go index a69e23db..00d22486 100644 --- a/pkg/registry/apps/application/rest.go +++ b/pkg/registry/apps/application/rest.go @@ -1278,16 +1278,18 @@ func (r *REST) countTenantExternalIPs(ctx context.Context, namespace string) (in } var services corev1.ServiceList - if err := r.c.List(ctx, &services, client.InNamespace(namespace)); err != nil { + if err := r.c.List( + ctx, + &services, + client.InNamespace(namespace), + client.MatchingFields{"spec.type": string(corev1.ServiceTypeLoadBalancer)}, + ); err != nil { return 0, err } var count int32 for i := range services.Items { svc := &services.Items[i] - if svc.Spec.Type != corev1.ServiceTypeLoadBalancer { - continue - } for _, ingress := range svc.Status.LoadBalancer.Ingress { if ingress.IP != "" { count++ From 083ce146094172b11cbc2ee09f2cdece83faa907 Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Thu, 29 Jan 2026 21:18:04 +0300 Subject: [PATCH 091/125] feat(platform): add configurable Kubernetes API server settings for non-Talos deployments Add networking.apiServer.host and networking.apiServer.port to platform values to allow overriding the default Talos KubePrism settings (localhost:7445). Also add networking.cilium.cgroup.autoMount for non-Talos clusters that need Cilium to mount cgroups automatically. Changes: - packages/core/platform/values.yaml: Add apiServer and cilium.cgroup settings - packages/core/platform/templates/bundles/system.yaml: Pass cilium values - packages/core/installer/values.yaml: Add kubernetesServiceHost/Port - packages/core/installer/templates/cozystack-operator.yaml: Template env vars Closes: #1933 Co-Authored-By: Claude Signed-off-by: Aleksei Sviridkin --- .../core/installer/templates/cozystack-operator.yaml | 4 ++-- packages/core/platform/templates/bundles/system.yaml | 5 +++++ packages/core/platform/values.yaml | 10 ++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/packages/core/installer/templates/cozystack-operator.yaml b/packages/core/installer/templates/cozystack-operator.yaml index a88b2590..31028757 100644 --- a/packages/core/installer/templates/cozystack-operator.yaml +++ b/packages/core/installer/templates/cozystack-operator.yaml @@ -65,9 +65,9 @@ spec: {{- end }} env: - name: KUBERNETES_SERVICE_HOST - value: localhost + value: {{ .Values.cozystackOperator.kubernetesServiceHost | quote }} - name: KUBERNETES_SERVICE_PORT - value: "7445" + value: {{ .Values.cozystackOperator.kubernetesServicePort | quote }} hostNetwork: true tolerations: - key: "node.kubernetes.io/not-ready" diff --git a/packages/core/platform/templates/bundles/system.yaml b/packages/core/platform/templates/bundles/system.yaml index 96a88333..f683bf03 100644 --- a/packages/core/platform/templates/bundles/system.yaml +++ b/packages/core/platform/templates/bundles/system.yaml @@ -11,6 +11,11 @@ "SVC_CIDR" .Values.networking.serviceCIDR "JOIN_CIDR" .Values.networking.joinCIDR)) -}} {{- $_ := set $networkingComponents "kubeovn" (dict "values" $kubeovnValues) -}} +{{- $ciliumValues := dict "cilium" (dict + "k8sServiceHost" .Values.networking.apiServer.host + "k8sServicePort" .Values.networking.apiServer.port + "cgroup" (dict "autoMount" (dict "enabled" .Values.networking.cilium.cgroup.autoMount))) -}} +{{- $_ := set $networkingComponents "cilium" (dict "values" $ciliumValues) -}} {{- end -}} {{include "cozystack.platform.package" (list "cozystack.networking" "kubeovn-cilium" $ $networkingComponents) }} {{include "cozystack.platform.package.default" (list "cozystack.kubeovn-webhook" $) }} diff --git a/packages/core/platform/values.yaml b/packages/core/platform/values.yaml index f8a0a9e4..4665e86b 100644 --- a/packages/core/platform/values.yaml +++ b/packages/core/platform/values.yaml @@ -27,6 +27,16 @@ networking: podGateway: "10.244.0.1" serviceCIDR: "10.96.0.0/16" joinCIDR: "100.64.0.0/16" + # Kubernetes API server configuration for Cilium + # Defaults are for Talos KubePrism; override for non-Talos clusters + apiServer: + host: "localhost" + port: "7445" + # Cilium cgroup configuration + # Set autoMount to true for non-Talos clusters + cilium: + cgroup: + autoMount: false # Service publishing and ingress configuration publishing: host: "example.org" From 28152b62ecae1c2438c98380622461b4f1bcb297 Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Fri, 30 Jan 2026 11:12:06 +0300 Subject: [PATCH 092/125] feat(platform): add MASTER_NODES support for KubeOVN on non-Talos clusters - Add networking.kubeovn.MASTER_NODES to platform values.yaml - Conditionally include MASTER_NODES in kubeovn values when set - Enables KubeOVN deployment on k3s/kubeadm without control-plane node labels Co-Authored-By: Claude Signed-off-by: Aleksei Sviridkin --- .../core/platform/templates/bundles/system.yaml | 16 ++++++++++------ packages/core/platform/values.yaml | 4 ++++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/packages/core/platform/templates/bundles/system.yaml b/packages/core/platform/templates/bundles/system.yaml index f683bf03..2ef10965 100644 --- a/packages/core/platform/templates/bundles/system.yaml +++ b/packages/core/platform/templates/bundles/system.yaml @@ -4,12 +4,16 @@ {{- if eq .Values.bundles.system.variant "isp-full" }} {{- $networkingComponents := dict -}} {{- if .Values.networking -}} -{{- $kubeovnValues := dict "kube-ovn" (dict - "ipv4" (dict - "POD_CIDR" .Values.networking.podCIDR - "POD_GATEWAY" .Values.networking.podGateway - "SVC_CIDR" .Values.networking.serviceCIDR - "JOIN_CIDR" .Values.networking.joinCIDR)) -}} +{{- $kubeovnIpv4 := dict + "POD_CIDR" .Values.networking.podCIDR + "POD_GATEWAY" .Values.networking.podGateway + "SVC_CIDR" .Values.networking.serviceCIDR + "JOIN_CIDR" .Values.networking.joinCIDR -}} +{{- $kubeovnDict := dict "ipv4" $kubeovnIpv4 -}} +{{- if .Values.networking.kubeovn.MASTER_NODES -}} +{{- $_ := set $kubeovnDict "MASTER_NODES" .Values.networking.kubeovn.MASTER_NODES -}} +{{- end -}} +{{- $kubeovnValues := dict "kube-ovn" $kubeovnDict -}} {{- $_ := set $networkingComponents "kubeovn" (dict "values" $kubeovnValues) -}} {{- $ciliumValues := dict "cilium" (dict "k8sServiceHost" .Values.networking.apiServer.host diff --git a/packages/core/platform/values.yaml b/packages/core/platform/values.yaml index 4665e86b..8ea9f27d 100644 --- a/packages/core/platform/values.yaml +++ b/packages/core/platform/values.yaml @@ -37,6 +37,10 @@ networking: cilium: cgroup: autoMount: false + # KubeOVN configuration for non-Talos clusters + # MASTER_NODES: comma-separated list of master node IPs (empty = use helm lookup) + kubeovn: + MASTER_NODES: "" # Service publishing and ingress configuration publishing: host: "example.org" From 72c7290351adec4115dea4a0590bc16bdbff7d2a Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Fri, 30 Jan 2026 12:38:53 +0300 Subject: [PATCH 093/125] feat(linstor): make Talos-specific configuration conditional Add talos.enabled value (default: true) to control whether LinstorSatelliteConfiguration cozystack-talos is created. This allows deploying linstor on non-Talos clusters (Ubuntu, Debian, etc.) where DRBD init containers are required. Co-Authored-By: Claude Signed-off-by: Aleksei Sviridkin --- packages/system/linstor/templates/satellites-talos.yaml | 2 ++ packages/system/linstor/values.yaml | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/packages/system/linstor/templates/satellites-talos.yaml b/packages/system/linstor/templates/satellites-talos.yaml index c5be9204..21da32a6 100644 --- a/packages/system/linstor/templates/satellites-talos.yaml +++ b/packages/system/linstor/templates/satellites-talos.yaml @@ -1,3 +1,4 @@ +{{- if .Values.talos.enabled }} apiVersion: piraeus.io/v1 kind: LinstorSatelliteConfiguration metadata: @@ -41,3 +42,4 @@ spec: hostPath: path: /var/etc/lvm/archive type: DirectoryOrCreate +{{- end }} diff --git a/packages/system/linstor/values.yaml b/packages/system/linstor/values.yaml index 9f7f022e..8c19ba2a 100644 --- a/packages/system/linstor/values.yaml +++ b/packages/system/linstor/values.yaml @@ -2,6 +2,11 @@ piraeusServer: image: repository: ghcr.io/cozystack/cozystack/piraeus-server tag: 1.32.3@sha256:0e78fa31a3fe4ec2af43d1e59a9fc0f6d765780e32d473e18e1c495714051802 + +# Talos-specific workarounds (disable for generic Linux like Ubuntu/Debian) +talos: + enabled: true + linstor: autoDiskful: enabled: true From a5de8379c5383bbc8828168d84ed96a0ecb8b15e Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Fri, 30 Jan 2026 12:39:02 +0300 Subject: [PATCH 094/125] fix(linstor-scheduler): strip distribution suffix from Kubernetes version k3s and RKE2 include distribution suffixes in version string (e.g., v1.35.0+k3s1) which are not valid container image tags. Strip everything after '+' using regexReplaceAll to produce clean version tags like v1.35.0. Co-Authored-By: Claude Signed-off-by: Aleksei Sviridkin --- .../charts/linstor-scheduler/templates/_helpers.tpl | 6 ++++-- .../charts/linstor-scheduler/templates/deployment.yaml | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/system/linstor-scheduler/charts/linstor-scheduler/templates/_helpers.tpl b/packages/system/linstor-scheduler/charts/linstor-scheduler/templates/_helpers.tpl index ad24453b..04c793f9 100644 --- a/packages/system/linstor-scheduler/charts/linstor-scheduler/templates/_helpers.tpl +++ b/packages/system/linstor-scheduler/charts/linstor-scheduler/templates/_helpers.tpl @@ -62,10 +62,12 @@ Create the name of the service account to use {{- end }} {{/* -Get the kubernetes version we should assume for creating scheduler configs +Get the kubernetes version we should assume for creating scheduler configs. +Strips distribution suffixes like +k3s1, +rke2r1 from version string. */}} {{- define "linstor-scheduler.kubeVersion" }} -{{- .Values.scheduler.image.compatibleKubernetesRelease | default .Capabilities.KubeVersion.Version }} +{{- $version := .Values.scheduler.image.compatibleKubernetesRelease | default .Capabilities.KubeVersion.Version }} +{{- regexReplaceAll "\\+.*$" $version "" }} {{- end }} {{/* diff --git a/packages/system/linstor-scheduler/charts/linstor-scheduler/templates/deployment.yaml b/packages/system/linstor-scheduler/charts/linstor-scheduler/templates/deployment.yaml index 49898b8d..ea43cb83 100644 --- a/packages/system/linstor-scheduler/charts/linstor-scheduler/templates/deployment.yaml +++ b/packages/system/linstor-scheduler/charts/linstor-scheduler/templates/deployment.yaml @@ -30,7 +30,7 @@ spec: {{- toYaml .Values.podSecurityContext | nindent 8 }} containers: - name: kube-scheduler - image: "{{ .Values.scheduler.image.repository }}:{{ .Values.scheduler.image.tag | default .Capabilities.KubeVersion.Version }}" + image: "{{ .Values.scheduler.image.repository }}:{{ .Values.scheduler.image.tag | default (include "linstor-scheduler.kubeVersion" .) }}" securityContext: {{- toYaml .Values.scheduler.securityContext | nindent 12 }} command: From b7028aaa2ae8c8396adfd51aa829ae9f837ffbdf Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Fri, 30 Jan 2026 12:39:07 +0300 Subject: [PATCH 095/125] feat(networking): add generic variants for non-Talos clusters Add cilium-generic and kubeovn-cilium-generic variants that exclude values-talos.yaml. These variants are suitable for kubeadm, k3s, RKE2 and other non-Talos Kubernetes distributions. Co-Authored-By: Claude Signed-off-by: Aleksei Sviridkin --- .../core/platform/sources/networking.yaml | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/packages/core/platform/sources/networking.yaml b/packages/core/platform/sources/networking.yaml index 57fff6fc..9694dcc3 100644 --- a/packages/core/platform/sources/networking.yaml +++ b/packages/core/platform/sources/networking.yaml @@ -33,6 +33,27 @@ spec: releaseName: cilium-networkpolicy dependsOn: - cilium + # Generic Cilium variant for non-Talos clusters (kubeadm, k3s, RKE2, etc.) + - name: cilium-generic + dependsOn: [] + components: + - name: cilium + path: system/cilium + valuesFiles: + - values.yaml + install: + privileged: true + namespace: cozy-cilium + releaseName: cilium + dependsOn: [] + - name: cilium-networkpolicy + path: system/cilium-networkpolicy + install: + privileged: true + namespace: cozy-cilium + releaseName: cilium-networkpolicy + dependsOn: + - cilium - name: kubeovn-cilium dependsOn: [] components: @@ -63,3 +84,33 @@ spec: releaseName: kubeovn dependsOn: - cilium + # Generic KubeOVN+Cilium variant for non-Talos clusters (kubeadm, k3s, RKE2, etc.) + - name: kubeovn-cilium-generic + dependsOn: [] + components: + - name: cilium + path: system/cilium + valuesFiles: + - values.yaml + - values-kubeovn.yaml + install: + privileged: true + namespace: cozy-cilium + releaseName: cilium + dependsOn: [] + - name: cilium-networkpolicy + path: system/cilium-networkpolicy + install: + privileged: true + namespace: cozy-cilium + releaseName: cilium-networkpolicy + dependsOn: + - cilium + - name: kubeovn + path: system/kubeovn + install: + privileged: true + namespace: cozy-kubeovn + releaseName: kubeovn + dependsOn: + - cilium From 47e69e58a06acc088677ecb858267dd8bce4f155 Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Fri, 30 Jan 2026 12:39:13 +0300 Subject: [PATCH 096/125] feat(platform): add isp-full-generic bundle variant Add isp-full-generic bundle variant for deploying Cozystack on non-Talos Kubernetes clusters (kubeadm, k3s, RKE2, etc.). This variant: - Uses kubeovn-cilium-generic networking (no values-talos.yaml) - Passes talos.enabled=false to linstor (keeps DRBD init containers) - Supports all standard networking configuration via platform values Co-Authored-By: Claude Signed-off-by: Aleksei Sviridkin --- .../platform/templates/bundles/system.yaml | 35 +++++++++++++++++++ packages/core/platform/values.yaml | 2 +- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/packages/core/platform/templates/bundles/system.yaml b/packages/core/platform/templates/bundles/system.yaml index 2ef10965..ab369892 100644 --- a/packages/core/platform/templates/bundles/system.yaml +++ b/packages/core/platform/templates/bundles/system.yaml @@ -37,6 +37,41 @@ {{include "cozystack.platform.package" (list "cozystack.networking" "noop" $) }} {{- end }} +{{- if eq .Values.bundles.system.variant "isp-full-generic" }} +{{- $networkingComponents := dict -}} +{{- if .Values.networking -}} +{{- $kubeovnIpv4 := dict + "POD_CIDR" .Values.networking.podCIDR + "POD_GATEWAY" .Values.networking.podGateway + "SVC_CIDR" .Values.networking.serviceCIDR + "JOIN_CIDR" .Values.networking.joinCIDR -}} +{{- $kubeovnDict := dict "ipv4" $kubeovnIpv4 -}} +{{- if .Values.networking.kubeovn.MASTER_NODES -}} +{{- $_ := set $kubeovnDict "MASTER_NODES" .Values.networking.kubeovn.MASTER_NODES -}} +{{- end -}} +{{- $kubeovnValues := dict "kube-ovn" $kubeovnDict -}} +{{- $_ := set $networkingComponents "kubeovn" (dict "values" $kubeovnValues) -}} +{{- $ciliumValues := dict "cilium" (dict + "k8sServiceHost" .Values.networking.apiServer.host + "k8sServicePort" .Values.networking.apiServer.port + "cgroup" (dict "autoMount" (dict "enabled" .Values.networking.cilium.cgroup.autoMount))) -}} +{{- $_ := set $networkingComponents "cilium" (dict "values" $ciliumValues) -}} +{{- end -}} +{{- /* Use kubeovn-cilium-generic variant (no values-talos.yaml) */ -}} +{{include "cozystack.platform.package" (list "cozystack.networking" "kubeovn-cilium-generic" $ $networkingComponents) }} +{{include "cozystack.platform.package.default" (list "cozystack.kubeovn-webhook" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.kubeovn-plunger" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.cozy-proxy" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.multus" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.metallb" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.reloader" $) }} +{{- /* Pass talos.enabled: false to linstor for generic Linux */ -}} +{{- $linstorComponents := dict "linstor" (dict "values" (dict "talos" (dict "enabled" false))) -}} +{{include "cozystack.platform.package" (list "cozystack.linstor" "default" $ $linstorComponents) }} +{{include "cozystack.platform.package.default" (list "cozystack.linstor-scheduler" $) }} +{{include "cozystack.platform.package.default" (list "cozystack.snapshot-controller" $) }} +{{- end }} + # Cozystack Engine {{- if .Values.authentication.oidc.enabled }} {{include "cozystack.platform.package" (list "cozystack.cozystack-engine" "oidc" $) }} diff --git a/packages/core/platform/values.yaml b/packages/core/platform/values.yaml index 8ea9f27d..34a251e6 100644 --- a/packages/core/platform/values.yaml +++ b/packages/core/platform/values.yaml @@ -11,7 +11,7 @@ migrations: bundles: system: enabled: false - variant: "isp-full" # Options: "isp-full", "isp-hosted", "distro-full" + variant: "isp-full" # Options: "isp-full", "isp-full-generic", "isp-hosted", "distro-full" iaas: enabled: false paas: From 9681387c9830af8b2cd54fd7ebf4fbaa0ae6a943 Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Fri, 30 Jan 2026 13:55:38 +0300 Subject: [PATCH 097/125] fix(platform): parse apiServerEndpoint URL for generic k8s bundle The isp-full-generic bundle now automatically extracts host and port from publishing.apiServerEndpoint URL for Cilium and KubeOVN configuration. This eliminates the need for manual networking.apiServer values when api-server-endpoint is set in the ConfigMap. Changes: - Parse apiServerEndpoint URL to extract host and port for Cilium - Auto-detect MASTER_NODES for KubeOVN from apiServerEndpoint if not explicitly set - Hardcode cgroup.autoMount=true for generic k8s (non-Talos) Co-Authored-By: Claude Signed-off-by: Aleksei Sviridkin --- .../platform/templates/bundles/system.yaml | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/packages/core/platform/templates/bundles/system.yaml b/packages/core/platform/templates/bundles/system.yaml index ab369892..ad3630a9 100644 --- a/packages/core/platform/templates/bundles/system.yaml +++ b/packages/core/platform/templates/bundles/system.yaml @@ -40,21 +40,41 @@ {{- if eq .Values.bundles.system.variant "isp-full-generic" }} {{- $networkingComponents := dict -}} {{- if .Values.networking -}} +{{- /* Parse apiServerEndpoint URL for generic k8s (used by both Cilium and KubeOVN) */ -}} +{{- $apiHost := .Values.networking.apiServer.host -}} +{{- $apiPort := .Values.networking.apiServer.port -}} +{{- if .Values.publishing.apiServerEndpoint -}} +{{- $parsed := urlParse .Values.publishing.apiServerEndpoint -}} +{{- $hostPort := splitList ":" $parsed.host -}} +{{- $apiHost = index $hostPort 0 -}} +{{- if gt (len $hostPort) 1 -}} +{{- $apiPort = index $hostPort 1 -}} +{{- else -}} +{{- $apiPort = "6443" -}} +{{- end -}} +{{- end -}} +{{- /* KubeOVN IPv4 configuration */ -}} {{- $kubeovnIpv4 := dict "POD_CIDR" .Values.networking.podCIDR "POD_GATEWAY" .Values.networking.podGateway "SVC_CIDR" .Values.networking.serviceCIDR "JOIN_CIDR" .Values.networking.joinCIDR -}} {{- $kubeovnDict := dict "ipv4" $kubeovnIpv4 -}} -{{- if .Values.networking.kubeovn.MASTER_NODES -}} -{{- $_ := set $kubeovnDict "MASTER_NODES" .Values.networking.kubeovn.MASTER_NODES -}} +{{- /* Set MASTER_NODES: explicit value > parsed from apiServerEndpoint > empty (use helm lookup) */ -}} +{{- $masterNodes := .Values.networking.kubeovn.MASTER_NODES -}} +{{- if and (not $masterNodes) .Values.publishing.apiServerEndpoint -}} +{{- $masterNodes = $apiHost -}} +{{- end -}} +{{- if $masterNodes -}} +{{- $_ := set $kubeovnDict "MASTER_NODES" $masterNodes -}} {{- end -}} {{- $kubeovnValues := dict "kube-ovn" $kubeovnDict -}} {{- $_ := set $networkingComponents "kubeovn" (dict "values" $kubeovnValues) -}} +{{- /* Cilium configuration - for generic k8s, always enable cgroup autoMount */ -}} {{- $ciliumValues := dict "cilium" (dict - "k8sServiceHost" .Values.networking.apiServer.host - "k8sServicePort" .Values.networking.apiServer.port - "cgroup" (dict "autoMount" (dict "enabled" .Values.networking.cilium.cgroup.autoMount))) -}} + "k8sServiceHost" $apiHost + "k8sServicePort" $apiPort + "cgroup" (dict "autoMount" (dict "enabled" true))) -}} {{- $_ := set $networkingComponents "cilium" (dict "values" $ciliumValues) -}} {{- end -}} {{- /* Use kubeovn-cilium-generic variant (no values-talos.yaml) */ -}} From f72860c827e3691679050792b92404df1414677e Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Fri, 30 Jan 2026 14:32:07 +0300 Subject: [PATCH 098/125] feat(platform): add values-isp-full-generic.yaml for generic k8s bundle Create dedicated values file for isp-full-generic bundle variant with bundles.system.variant set to "isp-full-generic" instead of "isp-full". This fixes the ArtifactGenerator generating identical digests for both isp-full and isp-full-generic variants, which caused the wrong bundle template to be selected. Also adds isp-full-generic variant to installer PackageSource definition. Co-Authored-By: Claude Signed-off-by: Aleksei Sviridkin --- packages/core/installer/templates/packagesource.yaml | 10 ++++++++++ packages/core/platform/values-isp-full-generic.yaml | 12 ++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 packages/core/platform/values-isp-full-generic.yaml diff --git a/packages/core/installer/templates/packagesource.yaml b/packages/core/installer/templates/packagesource.yaml index 7a3dd73e..f4a5f6d0 100644 --- a/packages/core/installer/templates/packagesource.yaml +++ b/packages/core/installer/templates/packagesource.yaml @@ -41,3 +41,13 @@ spec: valuesFiles: - values.yaml - values-isp-hosted.yaml + - name: isp-full-generic + components: + - install: + namespace: cozy-system + releaseName: cozystack-platform + name: platform + path: core/platform + valuesFiles: + - values.yaml + - values-isp-full-generic.yaml diff --git a/packages/core/platform/values-isp-full-generic.yaml b/packages/core/platform/values-isp-full-generic.yaml new file mode 100644 index 00000000..d5515bab --- /dev/null +++ b/packages/core/platform/values-isp-full-generic.yaml @@ -0,0 +1,12 @@ +migrations: + enabled: true +bundles: + system: + enabled: true + variant: "isp-full-generic" + iaas: + enabled: true + paas: + enabled: true + naas: + enabled: true From c3197d59fa175ef7f013dc32c21807b668d2e765 Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Fri, 30 Jan 2026 14:49:55 +0300 Subject: [PATCH 099/125] fix(platform): disable iaas/paas/naas bundles for generic variant The isp-full-generic variant should have iaas, paas, and naas bundles disabled by default since these bundles require 'isp-full' or 'isp-hosted' variants as per validation rules in their respective templates. Co-Authored-By: Claude Signed-off-by: Aleksei Sviridkin --- packages/core/platform/values-isp-full-generic.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/core/platform/values-isp-full-generic.yaml b/packages/core/platform/values-isp-full-generic.yaml index d5515bab..17ca19f3 100644 --- a/packages/core/platform/values-isp-full-generic.yaml +++ b/packages/core/platform/values-isp-full-generic.yaml @@ -5,8 +5,8 @@ bundles: enabled: true variant: "isp-full-generic" iaas: - enabled: true + enabled: false paas: - enabled: true + enabled: false naas: - enabled: true + enabled: false From 467b1eb350bff27d034e8854790b337b4e4d79a3 Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Fri, 30 Jan 2026 14:59:40 +0300 Subject: [PATCH 100/125] fix(platform): preserve trailing newlines in package helper templates Remove trailing dash from {{- end -}} constructs in _helpers.tpl to preserve trailing newlines after each rendered package YAML document. Without this fix, consecutive packages are rendered without proper separators: variant: default--- apiVersion: cozystack.io/v1alpha1 This causes YAML parse errors: "mapping key 'apiVersion' already defined" because the YAML parser sees a single document with duplicate keys instead of two separate documents. Co-Authored-By: Claude Signed-off-by: Aleksei Sviridkin --- packages/core/platform/templates/_helpers.tpl | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/core/platform/templates/_helpers.tpl b/packages/core/platform/templates/_helpers.tpl index 18105acf..2c454177 100644 --- a/packages/core/platform/templates/_helpers.tpl +++ b/packages/core/platform/templates/_helpers.tpl @@ -19,14 +19,14 @@ spec: components: {{ toYaml $components | indent 4 }} {{- end }} -{{- end -}} -{{- end -}} +{{- end }} +{{ end }} {{- define "cozystack.platform.package.default" -}} {{- $name := index . 0 -}} {{- $root := index . 1 -}} {{- include "cozystack.platform.package" (list $name "default" $root) }} -{{- end -}} +{{ end }} {{- define "cozystack.platform.package.optional" -}} {{- $name := index . 0 -}} @@ -42,11 +42,11 @@ metadata: name: {{ $name }} spec: variant: {{ $variant }} -{{- end -}} -{{- end -}} +{{- end }} +{{ end }} {{- define "cozystack.platform.package.optional.default" -}} {{- $name := index . 0 -}} {{- $root := index . 1 -}} {{- include "cozystack.platform.package.optional" (list $name "default" $root) }} -{{- end -}} +{{ end }} From 4132dff70a5e8fe734a44e962bee5b4dda449634 Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Fri, 30 Jan 2026 15:07:20 +0300 Subject: [PATCH 101/125] fix(platform): read values from cozystack ConfigMap in isp-full-generic For isp-full-generic bundle variant, the template now looks up the cozystack ConfigMap to read cluster-specific settings instead of only relying on chart default values. This fixes the issue where Cilium was trying to connect to localhost:7445 (Talos KubePrism default) instead of the actual API server endpoint. Values read from ConfigMap: - api-server-endpoint: parsed to extract host/port for Cilium - ipv4-pod-cidr, ipv4-pod-gateway, ipv4-svc-cidr, ipv4-join-cidr: for KubeOVN Co-Authored-By: Claude Signed-off-by: Aleksei Sviridkin --- .../platform/templates/bundles/system.yaml | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/packages/core/platform/templates/bundles/system.yaml b/packages/core/platform/templates/bundles/system.yaml index ad3630a9..12ce708b 100644 --- a/packages/core/platform/templates/bundles/system.yaml +++ b/packages/core/platform/templates/bundles/system.yaml @@ -41,10 +41,16 @@ {{- $networkingComponents := dict -}} {{- if .Values.networking -}} {{- /* Parse apiServerEndpoint URL for generic k8s (used by both Cilium and KubeOVN) */ -}} +{{- /* First try cozystack ConfigMap, then fall back to chart values */ -}} +{{- $cozystackCm := lookup "v1" "ConfigMap" "cozy-system" "cozystack" -}} +{{- $apiServerEndpoint := .Values.publishing.apiServerEndpoint -}} +{{- if and $cozystackCm $cozystackCm.data -}} +{{- $apiServerEndpoint = default $apiServerEndpoint (index $cozystackCm.data "api-server-endpoint") -}} +{{- end -}} {{- $apiHost := .Values.networking.apiServer.host -}} {{- $apiPort := .Values.networking.apiServer.port -}} -{{- if .Values.publishing.apiServerEndpoint -}} -{{- $parsed := urlParse .Values.publishing.apiServerEndpoint -}} +{{- if $apiServerEndpoint -}} +{{- $parsed := urlParse $apiServerEndpoint -}} {{- $hostPort := splitList ":" $parsed.host -}} {{- $apiHost = index $hostPort 0 -}} {{- if gt (len $hostPort) 1 -}} @@ -53,16 +59,26 @@ {{- $apiPort = "6443" -}} {{- end -}} {{- end -}} -{{- /* KubeOVN IPv4 configuration */ -}} +{{- /* KubeOVN IPv4 configuration - read from ConfigMap or fall back to chart values */ -}} +{{- $podCIDR := .Values.networking.podCIDR -}} +{{- $podGateway := .Values.networking.podGateway -}} +{{- $svcCIDR := .Values.networking.serviceCIDR -}} +{{- $joinCIDR := .Values.networking.joinCIDR -}} +{{- if and $cozystackCm $cozystackCm.data -}} +{{- $podCIDR = default $podCIDR (index $cozystackCm.data "ipv4-pod-cidr") -}} +{{- $podGateway = default $podGateway (index $cozystackCm.data "ipv4-pod-gateway") -}} +{{- $svcCIDR = default $svcCIDR (index $cozystackCm.data "ipv4-svc-cidr") -}} +{{- $joinCIDR = default $joinCIDR (index $cozystackCm.data "ipv4-join-cidr") -}} +{{- end -}} {{- $kubeovnIpv4 := dict - "POD_CIDR" .Values.networking.podCIDR - "POD_GATEWAY" .Values.networking.podGateway - "SVC_CIDR" .Values.networking.serviceCIDR - "JOIN_CIDR" .Values.networking.joinCIDR -}} + "POD_CIDR" $podCIDR + "POD_GATEWAY" $podGateway + "SVC_CIDR" $svcCIDR + "JOIN_CIDR" $joinCIDR -}} {{- $kubeovnDict := dict "ipv4" $kubeovnIpv4 -}} {{- /* Set MASTER_NODES: explicit value > parsed from apiServerEndpoint > empty (use helm lookup) */ -}} {{- $masterNodes := .Values.networking.kubeovn.MASTER_NODES -}} -{{- if and (not $masterNodes) .Values.publishing.apiServerEndpoint -}} +{{- if and (not $masterNodes) $apiServerEndpoint -}} {{- $masterNodes = $apiHost -}} {{- end -}} {{- if $masterNodes -}} From 0e207d466e79820c9c8001703d33102390ff8e9f Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Fri, 30 Jan 2026 15:22:26 +0300 Subject: [PATCH 102/125] fix(platform): set MASTER_NODES_LABEL for generic k8s in isp-full-generic k3s and kubeadm set node-role.kubernetes.io/control-plane=true, while Talos uses an empty value. KubeOVN node selector needs the exact label value to match. Co-Authored-By: Claude Signed-off-by: Aleksei Sviridkin --- packages/core/platform/templates/bundles/system.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/core/platform/templates/bundles/system.yaml b/packages/core/platform/templates/bundles/system.yaml index 12ce708b..48837105 100644 --- a/packages/core/platform/templates/bundles/system.yaml +++ b/packages/core/platform/templates/bundles/system.yaml @@ -84,6 +84,8 @@ {{- if $masterNodes -}} {{- $_ := set $kubeovnDict "MASTER_NODES" $masterNodes -}} {{- end -}} +{{- /* For generic k8s (k3s, kubeadm), control-plane label has value "true" */ -}} +{{- $_ := set $kubeovnDict "MASTER_NODES_LABEL" "node-role.kubernetes.io/control-plane=true" -}} {{- $kubeovnValues := dict "kube-ovn" $kubeovnDict -}} {{- $_ := set $networkingComponents "kubeovn" (dict "values" $kubeovnValues) -}} {{- /* Cilium configuration - for generic k8s, always enable cgroup autoMount */ -}} From d384c6faf6df8d03d5d58c7733a6ee31876f2b8b Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Fri, 30 Jan 2026 15:50:10 +0300 Subject: [PATCH 103/125] fix(lineage-webhook): make nodeSelector configurable for generic k8s On Talos, control-plane nodes have label node-role.kubernetes.io/control-plane with empty value. On generic k8s (k3s, kubeadm), the same label has value "true". The lineage-controller-webhook DaemonSet was hardcoded to use empty value, causing 0 pods scheduled on k3s clusters. Changes: - Add nodeSelector to lineage-controller-webhook values (default: empty for Talos) - Update DaemonSet template to use configurable nodeSelector - Pass nodeSelector with value "true" for isp-full-generic bundle variant Co-Authored-By: Claude Signed-off-by: Aleksei Sviridkin --- packages/core/platform/templates/bundles/system.yaml | 11 +++++++++-- .../templates/daemonset.yaml | 4 +++- .../system/lineage-controller-webhook/values.yaml | 5 +++++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/packages/core/platform/templates/bundles/system.yaml b/packages/core/platform/templates/bundles/system.yaml index 48837105..515cffb7 100644 --- a/packages/core/platform/templates/bundles/system.yaml +++ b/packages/core/platform/templates/bundles/system.yaml @@ -111,12 +111,19 @@ {{- end }} # Cozystack Engine +{{- $cozystackEngineComponents := dict -}} +{{- /* For generic k8s, lineage-controller-webhook needs nodeSelector with value "true" */ -}} +{{- if eq .Values.bundles.system.variant "isp-full-generic" -}} +{{- $lineageNodeSelector := dict "node-role.kubernetes.io/control-plane" "true" -}} +{{- $lineageValues := dict "lineageControllerWebhook" (dict "nodeSelector" $lineageNodeSelector) -}} +{{- $_ := set $cozystackEngineComponents "lineage-controller-webhook" (dict "values" $lineageValues) -}} +{{- end -}} {{- if .Values.authentication.oidc.enabled }} -{{include "cozystack.platform.package" (list "cozystack.cozystack-engine" "oidc" $) }} +{{include "cozystack.platform.package" (list "cozystack.cozystack-engine" "oidc" $ $cozystackEngineComponents) }} {{include "cozystack.platform.package.default" (list "cozystack.keycloak" $) }} {{include "cozystack.platform.package.default" (list "cozystack.keycloak-operator" $) }} {{- else }} -{{include "cozystack.platform.package" (list "cozystack.cozystack-engine" "default" $) }} +{{include "cozystack.platform.package" (list "cozystack.cozystack-engine" "default" $ $cozystackEngineComponents) }} {{- end }} # Common Packages diff --git a/packages/system/lineage-controller-webhook/templates/daemonset.yaml b/packages/system/lineage-controller-webhook/templates/daemonset.yaml index b6b73ac7..860aee6e 100644 --- a/packages/system/lineage-controller-webhook/templates/daemonset.yaml +++ b/packages/system/lineage-controller-webhook/templates/daemonset.yaml @@ -13,8 +13,10 @@ spec: labels: app: lineage-controller-webhook spec: + {{- with .Values.lineageControllerWebhook.nodeSelector }} nodeSelector: - node-role.kubernetes.io/control-plane: "" + {{- toYaml . | nindent 8 }} + {{- end }} tolerations: - operator: Exists serviceAccountName: lineage-controller-webhook diff --git a/packages/system/lineage-controller-webhook/values.yaml b/packages/system/lineage-controller-webhook/values.yaml index 0c4a1694..8225bd3c 100644 --- a/packages/system/lineage-controller-webhook/values.yaml +++ b/packages/system/lineage-controller-webhook/values.yaml @@ -3,3 +3,8 @@ lineageControllerWebhook: debug: false localK8sAPIEndpoint: enabled: true + # nodeSelector for the DaemonSet + # Talos uses empty value: "node-role.kubernetes.io/control-plane": "" + # Generic k8s (k3s, kubeadm) uses: "node-role.kubernetes.io/control-plane": "true" + nodeSelector: + node-role.kubernetes.io/control-plane: "" From 58476a4d4afe06fe6d3f36b1029c5911ed93c651 Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Fri, 30 Jan 2026 15:57:24 +0300 Subject: [PATCH 104/125] fix(cozystack-api): make nodeSelector configurable for generic k8s Same issue as lineage-controller-webhook: DaemonSet uses hardcoded nodeSelector with empty value, but k3s/kubeadm use value "true". Changes: - Add nodeSelector to cozystack-api values (default: empty for Talos) - Update deployment template to use configurable nodeSelector - Pass nodeSelector for both cozystack-api and lineage-controller-webhook in isp-full-generic bundle variant Co-Authored-By: Claude Signed-off-by: Aleksei Sviridkin --- packages/core/platform/templates/bundles/system.yaml | 10 +++++++--- .../system/cozystack-api/templates/deployment.yaml | 4 +++- packages/system/cozystack-api/values.yaml | 5 +++++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/packages/core/platform/templates/bundles/system.yaml b/packages/core/platform/templates/bundles/system.yaml index 515cffb7..9a8665b0 100644 --- a/packages/core/platform/templates/bundles/system.yaml +++ b/packages/core/platform/templates/bundles/system.yaml @@ -112,10 +112,14 @@ # Cozystack Engine {{- $cozystackEngineComponents := dict -}} -{{- /* For generic k8s, lineage-controller-webhook needs nodeSelector with value "true" */ -}} +{{- /* For generic k8s, DaemonSets with control-plane nodeSelector need value "true" */ -}} {{- if eq .Values.bundles.system.variant "isp-full-generic" -}} -{{- $lineageNodeSelector := dict "node-role.kubernetes.io/control-plane" "true" -}} -{{- $lineageValues := dict "lineageControllerWebhook" (dict "nodeSelector" $lineageNodeSelector) -}} +{{- $genericNodeSelector := dict "node-role.kubernetes.io/control-plane" "true" -}} +{{- /* cozystack-api DaemonSet */ -}} +{{- $apiValues := dict "cozystackAPI" (dict "nodeSelector" $genericNodeSelector) -}} +{{- $_ := set $cozystackEngineComponents "cozystack-api" (dict "values" $apiValues) -}} +{{- /* lineage-controller-webhook DaemonSet */ -}} +{{- $lineageValues := dict "lineageControllerWebhook" (dict "nodeSelector" $genericNodeSelector) -}} {{- $_ := set $cozystackEngineComponents "lineage-controller-webhook" (dict "values" $lineageValues) -}} {{- end -}} {{- if .Values.authentication.oidc.enabled }} diff --git a/packages/system/cozystack-api/templates/deployment.yaml b/packages/system/cozystack-api/templates/deployment.yaml index ee7e532f..aa877266 100644 --- a/packages/system/cozystack-api/templates/deployment.yaml +++ b/packages/system/cozystack-api/templates/deployment.yaml @@ -25,8 +25,10 @@ spec: - operator: Exists serviceAccountName: cozystack-api {{- if .Values.cozystackAPI.localK8sAPIEndpoint.enabled }} + {{- with .Values.cozystackAPI.nodeSelector }} nodeSelector: - node-role.kubernetes.io/control-plane: "" + {{- toYaml . | nindent 8 }} + {{- end }} {{- end }} containers: - name: cozystack-api diff --git a/packages/system/cozystack-api/values.yaml b/packages/system/cozystack-api/values.yaml index 75f0d6d1..7876981a 100644 --- a/packages/system/cozystack-api/values.yaml +++ b/packages/system/cozystack-api/values.yaml @@ -3,3 +3,8 @@ cozystackAPI: localK8sAPIEndpoint: enabled: true replicas: 2 + # nodeSelector for DaemonSet mode (localK8sAPIEndpoint.enabled: true) + # Talos uses empty value: "node-role.kubernetes.io/control-plane": "" + # Generic k8s (k3s, kubeadm) uses: "node-role.kubernetes.io/control-plane": "true" + nodeSelector: + node-role.kubernetes.io/control-plane: "" From 4f297eb2627ab40427a59e6299635d678c364805 Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Fri, 30 Jan 2026 16:04:24 +0300 Subject: [PATCH 105/125] fix(platform): read root-host from ConfigMap in apps.yaml The cozystack-values secret was using the default publishing.host value from values.yaml (example.org) instead of reading from the cozystack ConfigMap where the actual root-host is configured. Add ConfigMap lookup to apps.yaml to read root-host from cozystack ConfigMap, falling back to chart values if not present. Co-Authored-By: Claude Signed-off-by: Aleksei Sviridkin --- packages/core/platform/templates/apps.yaml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/core/platform/templates/apps.yaml b/packages/core/platform/templates/apps.yaml index 8a716066..ee3b35cf 100644 --- a/packages/core/platform/templates/apps.yaml +++ b/packages/core/platform/templates/apps.yaml @@ -1,6 +1,12 @@ {{- $kubeRootCa := lookup "v1" "ConfigMap" "kube-system" "kube-root-ca.crt" }} +{{- $cozystackCm := lookup "v1" "ConfigMap" "cozy-system" "cozystack" }} {{- $bundleName := .Values.bundles.system.variant }} {{- $bundle := tpl (.Files.Get (printf "bundles/%s.yaml" $bundleName)) . | fromYaml }} +{{- /* Read root-host from ConfigMap if available, else use chart values */ -}} +{{- $rootHost := .Values.publishing.host -}} +{{- if and $cozystackCm $cozystackCm.data -}} +{{- $rootHost = default $rootHost (index $cozystackCm.data "root-host") -}} +{{- end -}} --- apiVersion: v1 kind: Secret @@ -13,7 +19,7 @@ type: Opaque stringData: values.yaml: | _cluster: - root-host: {{ .Values.publishing.host | quote }} + root-host: {{ $rootHost | quote }} bundle-name: {{ .Values.bundles.system.variant | quote }} clusterissuer: {{ .Values.publishing.certificates.issuerType | quote }} oidc-enabled: {{ .Values.authentication.oidc.enabled | quote }} From f32f40f645d08cc3fd17b2ccf9d3be9cc46196ee Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Fri, 30 Jan 2026 18:16:52 +0300 Subject: [PATCH 106/125] feat(platform): enable iaas, paas, naas bundles for isp-full-generic variant - Allow iaas, paas, naas bundles when using isp-full-generic variant - Enable all bundles by default in values-isp-full-generic.yaml - Update bundle templates to accept isp-full-generic alongside isp-full Co-Authored-By: Claude Signed-off-by: Aleksei Sviridkin --- packages/core/platform/templates/bundles/iaas.yaml | 6 +++--- packages/core/platform/templates/bundles/naas.yaml | 6 +++--- packages/core/platform/templates/bundles/paas.yaml | 6 +++--- packages/core/platform/values-isp-full-generic.yaml | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/core/platform/templates/bundles/iaas.yaml b/packages/core/platform/templates/bundles/iaas.yaml index 529ec67a..7c856b8c 100644 --- a/packages/core/platform/templates/bundles/iaas.yaml +++ b/packages/core/platform/templates/bundles/iaas.yaml @@ -1,7 +1,7 @@ -{{- if and .Values.bundles.iaas.enabled (ne .Values.bundles.system.variant "isp-full") }} -{{- fail "bundles.iaas.enabled can only be true when bundles.system.variant is 'isp-full'" }} +{{- if and .Values.bundles.iaas.enabled (not (or (eq .Values.bundles.system.variant "isp-full") (eq .Values.bundles.system.variant "isp-full-generic"))) }} +{{- fail "bundles.iaas.enabled can only be true when bundles.system.variant is 'isp-full' or 'isp-full-generic'" }} {{- end }} -{{- if and .Values.bundles.iaas.enabled (eq .Values.bundles.system.variant "isp-full") }} +{{- if and .Values.bundles.iaas.enabled (or (eq .Values.bundles.system.variant "isp-full") (eq .Values.bundles.system.variant "isp-full-generic")) }} {{include "cozystack.platform.package.default" (list "cozystack.kubevirt" $) }} {{include "cozystack.platform.package.default" (list "cozystack.kubevirt-cdi" $) }} {{include "cozystack.platform.package.optional.default" (list "cozystack.gpu-operator" $) }} diff --git a/packages/core/platform/templates/bundles/naas.yaml b/packages/core/platform/templates/bundles/naas.yaml index 7c40a4aa..9682d447 100644 --- a/packages/core/platform/templates/bundles/naas.yaml +++ b/packages/core/platform/templates/bundles/naas.yaml @@ -1,7 +1,7 @@ -{{- if and .Values.bundles.naas.enabled (not (or (eq .Values.bundles.system.variant "isp-full") (eq .Values.bundles.system.variant "isp-hosted"))) }} -{{- fail "bundles.naas.enabled can only be true when bundles.system.variant is 'isp-full' or 'isp-hosted'" }} +{{- if and .Values.bundles.naas.enabled (not (or (eq .Values.bundles.system.variant "isp-full") (eq .Values.bundles.system.variant "isp-full-generic") (eq .Values.bundles.system.variant "isp-hosted"))) }} +{{- fail "bundles.naas.enabled can only be true when bundles.system.variant is 'isp-full', 'isp-full-generic', or 'isp-hosted'" }} {{- end }} -{{- if and .Values.bundles.naas.enabled (or (eq .Values.bundles.system.variant "isp-full") (eq .Values.bundles.system.variant "isp-hosted")) }} +{{- if and .Values.bundles.naas.enabled (or (eq .Values.bundles.system.variant "isp-full") (eq .Values.bundles.system.variant "isp-full-generic") (eq .Values.bundles.system.variant "isp-hosted")) }} {{include "cozystack.platform.package.default" (list "cozystack.http-cache-application" $) }} {{include "cozystack.platform.package.default" (list "cozystack.tcp-balancer-application" $) }} {{include "cozystack.platform.package.default" (list "cozystack.vpn-application" $) }} diff --git a/packages/core/platform/templates/bundles/paas.yaml b/packages/core/platform/templates/bundles/paas.yaml index 890ca514..359f0959 100644 --- a/packages/core/platform/templates/bundles/paas.yaml +++ b/packages/core/platform/templates/bundles/paas.yaml @@ -1,7 +1,7 @@ -{{- if and .Values.bundles.paas.enabled (not (or (eq .Values.bundles.system.variant "isp-full") (eq .Values.bundles.system.variant "isp-hosted"))) }} -{{- fail "bundles.paas.enabled can only be true when bundles.system.variant is 'isp-full' or 'isp-hosted'" }} +{{- if and .Values.bundles.paas.enabled (not (or (eq .Values.bundles.system.variant "isp-full") (eq .Values.bundles.system.variant "isp-full-generic") (eq .Values.bundles.system.variant "isp-hosted"))) }} +{{- fail "bundles.paas.enabled can only be true when bundles.system.variant is 'isp-full', 'isp-full-generic', or 'isp-hosted'" }} {{- end }} -{{- if and .Values.bundles.paas.enabled (or (eq .Values.bundles.system.variant "isp-full") (eq .Values.bundles.system.variant "isp-hosted")) }} +{{- if and .Values.bundles.paas.enabled (or (eq .Values.bundles.system.variant "isp-full") (eq .Values.bundles.system.variant "isp-full-generic") (eq .Values.bundles.system.variant "isp-hosted")) }} {{include "cozystack.platform.package.default" (list "cozystack.mariadb-operator" $) }} {{include "cozystack.platform.package.default" (list "cozystack.kafka-operator" $) }} {{include "cozystack.platform.package.default" (list "cozystack.clickhouse-operator" $) }} diff --git a/packages/core/platform/values-isp-full-generic.yaml b/packages/core/platform/values-isp-full-generic.yaml index 17ca19f3..d5515bab 100644 --- a/packages/core/platform/values-isp-full-generic.yaml +++ b/packages/core/platform/values-isp-full-generic.yaml @@ -5,8 +5,8 @@ bundles: enabled: true variant: "isp-full-generic" iaas: - enabled: false + enabled: true paas: - enabled: false + enabled: true naas: - enabled: false + enabled: true From 7927033864670af7afe8dfb37867fe34abaf8d18 Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Fri, 30 Jan 2026 18:24:25 +0300 Subject: [PATCH 107/125] feat(platform): enable velero in isp-full-generic variant Add velero to enabledPackages list for isp-full-generic to enable backup functionality by default. Co-Authored-By: Claude Signed-off-by: Aleksei Sviridkin --- packages/core/platform/values-isp-full-generic.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/core/platform/values-isp-full-generic.yaml b/packages/core/platform/values-isp-full-generic.yaml index d5515bab..7f96507c 100644 --- a/packages/core/platform/values-isp-full-generic.yaml +++ b/packages/core/platform/values-isp-full-generic.yaml @@ -10,3 +10,5 @@ bundles: enabled: true naas: enabled: true + enabledPackages: + - velero From cb90df4969e5caefec8110a99997cbbaf4129271 Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Fri, 30 Jan 2026 18:49:35 +0300 Subject: [PATCH 108/125] fix(platform): use correct package names in enabledPackages The enabledPackages list must use fully qualified package names (e.g., cozystack.velero) to match the helper template check. Also add backupstrategy-controller which is required by backup-controller. Co-Authored-By: Claude Signed-off-by: Aleksei Sviridkin --- packages/core/platform/values-isp-full-generic.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/core/platform/values-isp-full-generic.yaml b/packages/core/platform/values-isp-full-generic.yaml index 7f96507c..4fd5ff59 100644 --- a/packages/core/platform/values-isp-full-generic.yaml +++ b/packages/core/platform/values-isp-full-generic.yaml @@ -11,4 +11,5 @@ bundles: naas: enabled: true enabledPackages: - - velero + - cozystack.velero + - cozystack.backupstrategy-controller From 39fbb374aaae79ccf9b9bfc0ca70419fdda3e1b9 Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Fri, 30 Jan 2026 18:54:00 +0300 Subject: [PATCH 109/125] fix(velero): replace bitnami kubectl image with rancher/kubectl Bitnami images are forbidden and bitnamilegacy/kubectl:1.35 doesn't exist. Co-Authored-By: Claude Signed-off-by: Aleksei Sviridkin --- packages/system/velero/charts/velero/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/system/velero/charts/velero/values.yaml b/packages/system/velero/charts/velero/values.yaml index 30eb7b5f..0630cc6f 100644 --- a/packages/system/velero/charts/velero/values.yaml +++ b/packages/system/velero/charts/velero/values.yaml @@ -338,7 +338,7 @@ metrics: kubectl: image: - repository: docker.io/bitnamilegacy/kubectl + repository: rancher/kubectl # Digest value example: sha256:d238835e151cec91c6a811fe3a89a66d3231d9f64d09e5f3c49552672d271f38. # If used, it will take precedence over the kubectl.image.tag. # digest: From 052935a0422f337aad88dd3fd6dc99f4c9723e77 Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Fri, 30 Jan 2026 19:14:11 +0300 Subject: [PATCH 110/125] fix(velero): set explicit kubectl tag v1.35.0 rancher/kubectl uses 'v' prefix for tags (v1.35.0 vs 1.35). Co-Authored-By: Claude Signed-off-by: Aleksei Sviridkin --- packages/system/velero/charts/velero/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/system/velero/charts/velero/values.yaml b/packages/system/velero/charts/velero/values.yaml index 0630cc6f..9c14fd3c 100644 --- a/packages/system/velero/charts/velero/values.yaml +++ b/packages/system/velero/charts/velero/values.yaml @@ -343,7 +343,7 @@ kubectl: # If used, it will take precedence over the kubectl.image.tag. # digest: # kubectl image tag. If used, it will take precedence over the cluster Kubernetes version. - # tag: 1.16.15 + tag: v1.35.0 # Container Level Security Context for the 'kubectl' container of the crd jobs. Optional. # See: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container containerSecurityContext: {} From 5e7087a1600cad28ff1e1f51732b61b9711fa93d Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Fri, 30 Jan 2026 19:18:10 +0300 Subject: [PATCH 111/125] fix(velero): use alpine/k8s for kubectl with shell rancher/kubectl is a minimal image without shell. alpine/k8s includes kubectl and shell for running upgrade jobs. Co-Authored-By: Claude Signed-off-by: Aleksei Sviridkin --- packages/system/velero/charts/velero/values.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/system/velero/charts/velero/values.yaml b/packages/system/velero/charts/velero/values.yaml index 9c14fd3c..00ec9380 100644 --- a/packages/system/velero/charts/velero/values.yaml +++ b/packages/system/velero/charts/velero/values.yaml @@ -338,12 +338,12 @@ metrics: kubectl: image: - repository: rancher/kubectl + repository: alpine/k8s # Digest value example: sha256:d238835e151cec91c6a811fe3a89a66d3231d9f64d09e5f3c49552672d271f38. # If used, it will take precedence over the kubectl.image.tag. # digest: # kubectl image tag. If used, it will take precedence over the cluster Kubernetes version. - tag: v1.35.0 + tag: "1.35.0" # Container Level Security Context for the 'kubectl' container of the crd jobs. Optional. # See: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container containerSecurityContext: {} From ce24ddf7a5a9cf3e96a75f6555f4068366b09de4 Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Fri, 30 Jan 2026 19:21:59 +0300 Subject: [PATCH 112/125] fix(velero): disable upgradeCRDs job The CRD upgrade pre-install job has image compatibility issues. CRDs are installed as part of the Helm chart install anyway. Co-Authored-By: Claude Signed-off-by: Aleksei Sviridkin --- packages/system/velero/values.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/system/velero/values.yaml b/packages/system/velero/values.yaml index 62f13c1f..4d503543 100644 --- a/packages/system/velero/values.yaml +++ b/packages/system/velero/values.yaml @@ -1,4 +1,7 @@ velero: + # Disable CRD upgrade job - CRDs are installed as part of helm chart + # The upgrade job has issues with kubectl image compatibility + upgradeCRDs: false initContainers: - name: velero-plugin-for-aws image: velero/velero-plugin-for-aws:v1.12.1 From 11eb255640dbadfee9a6591739f90afda418b908 Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Fri, 30 Jan 2026 19:26:17 +0300 Subject: [PATCH 113/125] fix(backup-controller): add required template spec to Velero strategy The Velero CRD requires spec.template.spec field. Co-Authored-By: Claude Signed-off-by: Aleksei Sviridkin --- packages/system/backup-controller/templates/strategy.yaml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/system/backup-controller/templates/strategy.yaml b/packages/system/backup-controller/templates/strategy.yaml index 68109332..a8396d66 100644 --- a/packages/system/backup-controller/templates/strategy.yaml +++ b/packages/system/backup-controller/templates/strategy.yaml @@ -2,4 +2,9 @@ apiVersion: strategy.backups.cozystack.io/v1alpha1 kind: Velero metadata: name: velero-strategy-default -spec: {} +spec: + template: + spec: + ttl: 720h + includedNamespaces: + - "*" From 395a57bc1b084d464a49bc6831c9d8102058d91b Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Fri, 30 Jan 2026 21:18:03 +0300 Subject: [PATCH 114/125] refactor(platform): simplify networking values, hardcode CNI-specific config - Remove apiServer and cilium.cgroup from user-facing values.yaml - Hardcode Talos-specific values (localhost:7445, cgroup autoMount: false) directly in isp-full bundle template - isp-full-generic already hardcodes its own values - Improve MASTER_NODES comment explaining helm lookup behavior This hides implementation details from users as requested in review. Co-Authored-By: Claude Signed-off-by: Aleksei Sviridkin --- .../core/platform/templates/bundles/system.yaml | 7 ++++--- packages/core/platform/values.yaml | 17 +++++------------ 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/packages/core/platform/templates/bundles/system.yaml b/packages/core/platform/templates/bundles/system.yaml index 9a8665b0..d272962b 100644 --- a/packages/core/platform/templates/bundles/system.yaml +++ b/packages/core/platform/templates/bundles/system.yaml @@ -15,10 +15,11 @@ {{- end -}} {{- $kubeovnValues := dict "kube-ovn" $kubeovnDict -}} {{- $_ := set $networkingComponents "kubeovn" (dict "values" $kubeovnValues) -}} +{{- /* For Talos (isp-full): use KubePrism endpoint and disable cgroup autoMount */ -}} {{- $ciliumValues := dict "cilium" (dict - "k8sServiceHost" .Values.networking.apiServer.host - "k8sServicePort" .Values.networking.apiServer.port - "cgroup" (dict "autoMount" (dict "enabled" .Values.networking.cilium.cgroup.autoMount))) -}} + "k8sServiceHost" "localhost" + "k8sServicePort" "7445" + "cgroup" (dict "autoMount" (dict "enabled" false))) -}} {{- $_ := set $networkingComponents "cilium" (dict "values" $ciliumValues) -}} {{- end -}} {{include "cozystack.platform.package" (list "cozystack.networking" "kubeovn-cilium" $ $networkingComponents) }} diff --git a/packages/core/platform/values.yaml b/packages/core/platform/values.yaml index 34a251e6..f7fec687 100644 --- a/packages/core/platform/values.yaml +++ b/packages/core/platform/values.yaml @@ -27,18 +27,11 @@ networking: podGateway: "10.244.0.1" serviceCIDR: "10.96.0.0/16" joinCIDR: "100.64.0.0/16" - # Kubernetes API server configuration for Cilium - # Defaults are for Talos KubePrism; override for non-Talos clusters - apiServer: - host: "localhost" - port: "7445" - # Cilium cgroup configuration - # Set autoMount to true for non-Talos clusters - cilium: - cgroup: - autoMount: false - # KubeOVN configuration for non-Talos clusters - # MASTER_NODES: comma-separated list of master node IPs (empty = use helm lookup) + # KubeOVN master nodes override (optional) + # By default, KubeOVN helm chart uses `lookup` to find control-plane nodes + # by label `node-role.kubernetes.io/control-plane`. On fresh clusters or + # during initial deployment, lookup may return empty results. + # Set this to comma-separated list of master node IPs to override. kubeovn: MASTER_NODES: "" # Service publishing and ingress configuration From 77ce8227b4ee4a79b10773af5e50e61a11e6df5c Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Fri, 30 Jan 2026 21:20:22 +0300 Subject: [PATCH 115/125] feat(installer): split operator manifest into talos/generic/hosted variants Per review feedback, create separate manifest files for different deployment targets instead of using templated values: - cozystack-operator.yaml (Talos): hardcoded localhost:7445 (KubePrism) - cozystack-operator-generic.yaml: reads from cozystack-operator-config ConfigMap (user must create before applying) - cozystack-operator-hosted.yaml: no env override (uses in-cluster SA) This keeps installation flow clean - users apply the manifest matching their deployment target without needing to modify original files. Build system updated to generate all three variants. Co-Authored-By: Claude Signed-off-by: Aleksei Sviridkin --- Makefile | 11 +++ hack/upload-assets.sh | 2 + .../templates/cozystack-operator-generic.yaml | 90 +++++++++++++++++++ .../templates/cozystack-operator-hosted.yaml | 77 ++++++++++++++++ .../templates/cozystack-operator.yaml | 5 +- 5 files changed, 183 insertions(+), 2 deletions(-) create mode 100644 packages/core/installer/templates/cozystack-operator-generic.yaml create mode 100644 packages/core/installer/templates/cozystack-operator-hosted.yaml diff --git a/Makefile b/Makefile index 0cf71d0b..c66f8c03 100644 --- a/Makefile +++ b/Makefile @@ -41,10 +41,21 @@ manifests: helm template installer packages/core/installer -n cozy-system \ -s templates/crds.yaml \ > _out/assets/cozystack-crds.yaml + # Talos variant (default) helm template installer packages/core/installer -n cozy-system \ -s templates/cozystack-operator.yaml \ -s templates/packagesource.yaml \ > _out/assets/cozystack-operator.yaml + # Generic Kubernetes variant (k3s, kubeadm, RKE2) + helm template installer packages/core/installer -n cozy-system \ + -s templates/cozystack-operator-generic.yaml \ + -s templates/packagesource.yaml \ + > _out/assets/cozystack-operator-generic.yaml + # Hosted variant (managed Kubernetes) + helm template installer packages/core/installer -n cozy-system \ + -s templates/cozystack-operator-hosted.yaml \ + -s templates/packagesource.yaml \ + > _out/assets/cozystack-operator-hosted.yaml cozypkg: go build -ldflags "-X github.com/cozystack/cozystack/cmd/cozypkg/cmd.Version=v$(COZYSTACK_VERSION)" -o _out/bin/cozypkg ./cmd/cozypkg diff --git a/hack/upload-assets.sh b/hack/upload-assets.sh index 0f1e9aeb..e846261c 100755 --- a/hack/upload-assets.sh +++ b/hack/upload-assets.sh @@ -5,6 +5,8 @@ version=${VERSION:-$(git describe --tags)} gh release upload --clobber $version _out/assets/cozystack-crds.yaml gh release upload --clobber $version _out/assets/cozystack-operator.yaml +gh release upload --clobber $version _out/assets/cozystack-operator-generic.yaml +gh release upload --clobber $version _out/assets/cozystack-operator-hosted.yaml gh release upload --clobber $version _out/assets/metal-amd64.iso gh release upload --clobber $version _out/assets/metal-amd64.raw.xz gh release upload --clobber $version _out/assets/nocloud-amd64.raw.xz diff --git a/packages/core/installer/templates/cozystack-operator-generic.yaml b/packages/core/installer/templates/cozystack-operator-generic.yaml new file mode 100644 index 00000000..510a92c8 --- /dev/null +++ b/packages/core/installer/templates/cozystack-operator-generic.yaml @@ -0,0 +1,90 @@ +--- +apiVersion: v1 +kind: Namespace +metadata: + name: cozy-system + labels: + cozystack.io/system: "true" + pod-security.kubernetes.io/enforce: privileged +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: cozystack + namespace: cozy-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: cozystack +subjects: +- kind: ServiceAccount + name: cozystack + namespace: cozy-system +roleRef: + kind: ClusterRole + name: cluster-admin + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: cozystack-operator + namespace: cozy-system +spec: + replicas: 1 + selector: + matchLabels: + app: cozystack-operator + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 0 + maxUnavailable: 1 + template: + metadata: + labels: + app: cozystack-operator + spec: + serviceAccountName: cozystack + containers: + - name: cozystack-operator + image: "{{ .Values.cozystackOperator.image }}" + args: + - --leader-elect=true + - --install-flux=true + - --metrics-bind-address=0 + - --health-probe-bind-address= + {{- if .Values.cozystackOperator.disableTelemetry }} + - --disable-telemetry + {{- end }} + - --platform-source-name=cozystack-platform + - --platform-source-url={{ .Values.cozystackOperator.platformSourceUrl }} + {{- if .Values.cozystackOperator.platformSourceRef }} + - --platform-source-ref={{ .Values.cozystackOperator.platformSourceRef }} + {{- end }} + env: + # Generic Kubernetes: read from ConfigMap + # Create cozystack-operator-config ConfigMap before applying this manifest + - name: KUBERNETES_SERVICE_HOST + valueFrom: + configMapKeyRef: + name: cozystack-operator-config + key: KUBERNETES_SERVICE_HOST + optional: false + - name: KUBERNETES_SERVICE_PORT + valueFrom: + configMapKeyRef: + name: cozystack-operator-config + key: KUBERNETES_SERVICE_PORT + optional: false + hostNetwork: true + tolerations: + - key: "node.kubernetes.io/not-ready" + operator: "Exists" + - key: "node.kubernetes.io/unreachable" + operator: "Exists" + - key: "node.cilium.io/agent-not-ready" + operator: "Exists" + - key: "node.cloudprovider.kubernetes.io/uninitialized" + operator: "Exists" diff --git a/packages/core/installer/templates/cozystack-operator-hosted.yaml b/packages/core/installer/templates/cozystack-operator-hosted.yaml new file mode 100644 index 00000000..38ccdf75 --- /dev/null +++ b/packages/core/installer/templates/cozystack-operator-hosted.yaml @@ -0,0 +1,77 @@ +--- +apiVersion: v1 +kind: Namespace +metadata: + name: cozy-system + labels: + cozystack.io/system: "true" + pod-security.kubernetes.io/enforce: privileged +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: cozystack + namespace: cozy-system +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: cozystack +subjects: +- kind: ServiceAccount + name: cozystack + namespace: cozy-system +roleRef: + kind: ClusterRole + name: cluster-admin + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: cozystack-operator + namespace: cozy-system +spec: + replicas: 1 + selector: + matchLabels: + app: cozystack-operator + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 0 + maxUnavailable: 1 + template: + metadata: + labels: + app: cozystack-operator + spec: + serviceAccountName: cozystack + containers: + - name: cozystack-operator + image: "{{ .Values.cozystackOperator.image }}" + args: + - --leader-elect=true + - --install-flux=true + - --metrics-bind-address=0 + - --health-probe-bind-address= + {{- if .Values.cozystackOperator.disableTelemetry }} + - --disable-telemetry + {{- end }} + - --platform-source-name=cozystack-platform + - --platform-source-url={{ .Values.cozystackOperator.platformSourceUrl }} + {{- if .Values.cozystackOperator.platformSourceRef }} + - --platform-source-ref={{ .Values.cozystackOperator.platformSourceRef }} + {{- end }} + # Hosted: use in-cluster service account, no env override needed + env: [] + hostNetwork: true + tolerations: + - key: "node.kubernetes.io/not-ready" + operator: "Exists" + - key: "node.kubernetes.io/unreachable" + operator: "Exists" + - key: "node.cilium.io/agent-not-ready" + operator: "Exists" + - key: "node.cloudprovider.kubernetes.io/uninitialized" + operator: "Exists" diff --git a/packages/core/installer/templates/cozystack-operator.yaml b/packages/core/installer/templates/cozystack-operator.yaml index 31028757..c40dc663 100644 --- a/packages/core/installer/templates/cozystack-operator.yaml +++ b/packages/core/installer/templates/cozystack-operator.yaml @@ -64,10 +64,11 @@ spec: - --platform-source-ref={{ .Values.cozystackOperator.platformSourceRef }} {{- end }} env: + # Talos KubePrism endpoint - name: KUBERNETES_SERVICE_HOST - value: {{ .Values.cozystackOperator.kubernetesServiceHost | quote }} + value: "localhost" - name: KUBERNETES_SERVICE_PORT - value: {{ .Values.cozystackOperator.kubernetesServicePort | quote }} + value: "7445" hostNetwork: true tolerations: - key: "node.kubernetes.io/not-ready" From 116d9aeb50efeb87724520f68646a3a266e92a09 Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Fri, 30 Jan 2026 21:23:59 +0300 Subject: [PATCH 116/125] refactor(platform): deduplicate common packages in system bundle Extract common system packages shared between isp-full and isp-full-generic into a helper template `cozystack.platform.system.common-packages`. Packages moved to helper: - kubeovn-webhook, kubeovn-plunger, cozy-proxy - multus, metallb, reloader - linstor-scheduler, snapshot-controller Packages NOT in helper (differ between variants): - networking (variant differs: kubeovn-cilium vs kubeovn-cilium-generic) - linstor (talos.enabled differs) Co-Authored-By: Claude Signed-off-by: Aleksei Sviridkin --- packages/core/platform/templates/_helpers.tpl | 16 ++++++++++++++++ .../platform/templates/bundles/system.yaml | 18 ++---------------- 2 files changed, 18 insertions(+), 16 deletions(-) diff --git a/packages/core/platform/templates/_helpers.tpl b/packages/core/platform/templates/_helpers.tpl index 2c454177..684ee812 100644 --- a/packages/core/platform/templates/_helpers.tpl +++ b/packages/core/platform/templates/_helpers.tpl @@ -50,3 +50,19 @@ spec: {{- $root := index . 1 -}} {{- include "cozystack.platform.package.optional" (list $name "default" $root) }} {{ end }} + +{{/* +Common system packages shared between isp-full and isp-full-generic bundles. +Does NOT include: networking (variant differs), linstor (talos.enabled differs) +*/}} +{{- define "cozystack.platform.system.common-packages" -}} +{{- $root := . -}} +{{include "cozystack.platform.package.default" (list "cozystack.kubeovn-webhook" $root) }} +{{include "cozystack.platform.package.default" (list "cozystack.kubeovn-plunger" $root) }} +{{include "cozystack.platform.package.default" (list "cozystack.cozy-proxy" $root) }} +{{include "cozystack.platform.package.default" (list "cozystack.multus" $root) }} +{{include "cozystack.platform.package.default" (list "cozystack.metallb" $root) }} +{{include "cozystack.platform.package.default" (list "cozystack.reloader" $root) }} +{{include "cozystack.platform.package.default" (list "cozystack.linstor-scheduler" $root) }} +{{include "cozystack.platform.package.default" (list "cozystack.snapshot-controller" $root) }} +{{- end }} diff --git a/packages/core/platform/templates/bundles/system.yaml b/packages/core/platform/templates/bundles/system.yaml index d272962b..62f96521 100644 --- a/packages/core/platform/templates/bundles/system.yaml +++ b/packages/core/platform/templates/bundles/system.yaml @@ -23,15 +23,8 @@ {{- $_ := set $networkingComponents "cilium" (dict "values" $ciliumValues) -}} {{- end -}} {{include "cozystack.platform.package" (list "cozystack.networking" "kubeovn-cilium" $ $networkingComponents) }} -{{include "cozystack.platform.package.default" (list "cozystack.kubeovn-webhook" $) }} -{{include "cozystack.platform.package.default" (list "cozystack.kubeovn-plunger" $) }} -{{include "cozystack.platform.package.default" (list "cozystack.cozy-proxy" $) }} -{{include "cozystack.platform.package.default" (list "cozystack.multus" $) }} -{{include "cozystack.platform.package.default" (list "cozystack.metallb" $) }} -{{include "cozystack.platform.package.default" (list "cozystack.reloader" $) }} +{{include "cozystack.platform.system.common-packages" $ }} {{include "cozystack.platform.package.default" (list "cozystack.linstor" $) }} -{{include "cozystack.platform.package.default" (list "cozystack.linstor-scheduler" $) }} -{{include "cozystack.platform.package.default" (list "cozystack.snapshot-controller" $) }} {{- end }} {{- if eq .Values.bundles.system.variant "isp-hosted" }} @@ -98,17 +91,10 @@ {{- end -}} {{- /* Use kubeovn-cilium-generic variant (no values-talos.yaml) */ -}} {{include "cozystack.platform.package" (list "cozystack.networking" "kubeovn-cilium-generic" $ $networkingComponents) }} -{{include "cozystack.platform.package.default" (list "cozystack.kubeovn-webhook" $) }} -{{include "cozystack.platform.package.default" (list "cozystack.kubeovn-plunger" $) }} -{{include "cozystack.platform.package.default" (list "cozystack.cozy-proxy" $) }} -{{include "cozystack.platform.package.default" (list "cozystack.multus" $) }} -{{include "cozystack.platform.package.default" (list "cozystack.metallb" $) }} -{{include "cozystack.platform.package.default" (list "cozystack.reloader" $) }} +{{include "cozystack.platform.system.common-packages" $ }} {{- /* Pass talos.enabled: false to linstor for generic Linux */ -}} {{- $linstorComponents := dict "linstor" (dict "values" (dict "talos" (dict "enabled" false))) -}} {{include "cozystack.platform.package" (list "cozystack.linstor" "default" $ $linstorComponents) }} -{{include "cozystack.platform.package.default" (list "cozystack.linstor-scheduler" $) }} -{{include "cozystack.platform.package.default" (list "cozystack.snapshot-controller" $) }} {{- end }} # Cozystack Engine From 191661768608a643439bbce430efc046c83b306c Mon Sep 17 00:00:00 2001 From: Aleksei Sviridkin Date: Fri, 30 Jan 2026 21:29:21 +0300 Subject: [PATCH 117/125] fix(platform): remove apiServer fallback from values Removed reference to .Values.networking.apiServer which was removed from values.yaml. Use empty defaults and let ConfigMap or apiServerEndpoint parsing override them. Co-Authored-By: Claude Signed-off-by: Aleksei Sviridkin --- packages/core/platform/templates/bundles/system.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core/platform/templates/bundles/system.yaml b/packages/core/platform/templates/bundles/system.yaml index 62f96521..1ee7002e 100644 --- a/packages/core/platform/templates/bundles/system.yaml +++ b/packages/core/platform/templates/bundles/system.yaml @@ -41,8 +41,8 @@ {{- if and $cozystackCm $cozystackCm.data -}} {{- $apiServerEndpoint = default $apiServerEndpoint (index $cozystackCm.data "api-server-endpoint") -}} {{- end -}} -{{- $apiHost := .Values.networking.apiServer.host -}} -{{- $apiPort := .Values.networking.apiServer.port -}} +{{- $apiHost := "" -}} +{{- $apiPort := "6443" -}} {{- if $apiServerEndpoint -}} {{- $parsed := urlParse $apiServerEndpoint -}} {{- $hostPort := splitList ":" $parsed.host -}} From 281715b36575e0fe889cff9a9362ccdab7646435 Mon Sep 17 00:00:00 2001 From: IvanHunters Date: Sun, 1 Feb 2026 22:48:28 +0300 Subject: [PATCH 118/125] fix manifests for kubernetes deployment Signed-off-by: IvanHunters --- packages/apps/kubernetes/templates/cluster.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/apps/kubernetes/templates/cluster.yaml b/packages/apps/kubernetes/templates/cluster.yaml index 6acfb107..3d9c854a 100644 --- a/packages/apps/kubernetes/templates/cluster.yaml +++ b/packages/apps/kubernetes/templates/cluster.yaml @@ -292,6 +292,12 @@ metadata: {{- end }} spec: clusterName: {{ $.Release.Name }} + replicas: 2 + strategy: + rollingUpdate: + maxSurge: {{ $group.maxReplicas }} + maxUnavailable: 1 + type: RollingUpdate selector: matchLabels: cluster.x-k8s.io/cluster-name: {{ $.Release.Name }} @@ -326,6 +332,7 @@ metadata: namespace: {{ $.Release.Namespace }} spec: clusterName: {{ $.Release.Name }} + maxUnhealthy: 0 nodeStartupTimeout: 10m selector: matchLabels: From 8a034c58b189f9e18276d16feecaef7e0e6785c1 Mon Sep 17 00:00:00 2001 From: nbykov0 <166552198+nbykov0@users.noreply.github.com> Date: Mon, 2 Feb 2026 13:08:16 +0300 Subject: [PATCH 119/125] [branding] Separate values for keycloak Signed-off-by: nbykov0 <166552198+nbykov0@users.noreply.github.com> --- .../templates/configure-kk.yaml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/system/keycloak-configure/templates/configure-kk.yaml b/packages/system/keycloak-configure/templates/configure-kk.yaml index cc15b161..29afc6c0 100644 --- a/packages/system/keycloak-configure/templates/configure-kk.yaml +++ b/packages/system/keycloak-configure/templates/configure-kk.yaml @@ -5,12 +5,6 @@ {{- $existingKubeappsSecret := lookup "v1" "Secret" .Release.Namespace "kubeapps-client" }} {{- $existingAuthConfig := lookup "v1" "Secret" "cozy-dashboard" "kubeapps-auth-config" }} {{- $brandingConfig := .Values._cluster.branding | default dict }} - -{{ $branding := "" }} -{{- if $brandingConfig }} - {{- $branding = $brandingConfig.branding }} -{{- end }} - --- apiVersion: v1.edp.epam.com/v1alpha1 @@ -32,9 +26,15 @@ metadata: spec: realmName: cozy clusterKeycloakRef: keycloak-cozy - {{- if $branding }} - displayHtmlName: {{ $branding }} - displayName: {{ $branding }} + {{- if $brandingConfig }} + {{- if hasKey $brandingConfig "brandName" }} + displayName: {{ $brandingConfig.brandName }} + {{- end }} + {{- if hasKey $brandingConfig "brandHtmlName" }} + displayHtmlName: {{ $brandingConfig.brandHtmlName }} + {{- else if hasKey $brandingConfig "branding" }} + displayHtmlName: {{ $brandingConfig.branding }} + {{- end }} {{- end }} --- From c21d1e40895d1babc8e4b5fdf31cfcf2661ae8fd Mon Sep 17 00:00:00 2001 From: Kirill Ilin Date: Mon, 2 Feb 2026 15:26:15 +0500 Subject: [PATCH 120/125] [kubernetes] use NodePort service instead of hostNetwork for ingress-nginx Signed-off-by: Kirill Ilin --- .../kubernetes/templates/helmreleases/ingress-nginx.yaml | 7 +++++-- packages/apps/kubernetes/templates/ingress.yaml | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/apps/kubernetes/templates/helmreleases/ingress-nginx.yaml b/packages/apps/kubernetes/templates/helmreleases/ingress-nginx.yaml index 6f7b0759..5cafff90 100644 --- a/packages/apps/kubernetes/templates/helmreleases/ingress-nginx.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/ingress-nginx.yaml @@ -4,9 +4,12 @@ ingress-nginx: controller: kind: DaemonSet {{- if eq .Values.addons.ingressNginx.exposeMethod "Proxied" }} - hostNetwork: true service: - enabled: false + enabled: true + type: NodePort + nodePorts: + http: 30000 + https: 30001 {{- end }} {{- if not .Values.addons.certManager.enabled }} admissionWebhooks: diff --git a/packages/apps/kubernetes/templates/ingress.yaml b/packages/apps/kubernetes/templates/ingress.yaml index 2e7f9933..4adb7458 100644 --- a/packages/apps/kubernetes/templates/ingress.yaml +++ b/packages/apps/kubernetes/templates/ingress.yaml @@ -56,11 +56,11 @@ spec: - appProtocol: http name: http port: 80 - targetPort: 80 + targetPort: 30000 - appProtocol: https name: https port: 443 - targetPort: 443 + targetPort: 30001 selector: cluster.x-k8s.io/cluster-name: {{ .Release.Name }} node-role.kubernetes.io/ingress-nginx: "" From 1e8da1fca4f5faeb103ccdd53a8944dba2af1a57 Mon Sep 17 00:00:00 2001 From: Matthieu ROBIN Date: Mon, 2 Feb 2026 13:23:10 +0100 Subject: [PATCH 121/125] Add instance profile label to workload monitor Signed-off-by: Matthieu ROBIN --- internal/controller/workloadmonitor_controller.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/controller/workloadmonitor_controller.go b/internal/controller/workloadmonitor_controller.go index d4d86b97..677fda82 100644 --- a/internal/controller/workloadmonitor_controller.go +++ b/internal/controller/workloadmonitor_controller.go @@ -466,6 +466,8 @@ func (r *WorkloadMonitorReconciler) getWorkloadMetadata(obj client.Object) map[s annotations := obj.GetAnnotations() if instanceType, ok := annotations["kubevirt.io/cluster-instancetype-name"]; ok { labels["workloads.cozystack.io/kubevirt-vmi-instance-type"] = instanceType + if instanceProfile, ok := annotations["kubevirt.io/cluster-instanceprofile-name"]; ok { + labels["workloads.cozystack.io/kubevirt-vmi-instance-profile"] = instanceProfile } return labels } From 8f3d686492b5b8988f77de70eb67b672c3b52894 Mon Sep 17 00:00:00 2001 From: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com> Date: Mon, 2 Feb 2026 12:34:58 +0000 Subject: [PATCH 122/125] Prepare release v1.0.0-beta.2 Signed-off-by: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com> --- packages/apps/kubernetes/images/kubevirt-csi-driver.tag | 2 +- packages/apps/mysql/images/mariadb-backup.tag | 2 +- packages/core/installer/values.yaml | 4 ++-- packages/core/testing/values.yaml | 2 +- packages/extra/bootbox/images/matchbox.tag | 2 +- packages/extra/seaweedfs/images/objectstorage-sidecar.tag | 2 +- packages/system/backup-controller/values.yaml | 2 +- packages/system/backupstrategy-controller/values.yaml | 2 +- packages/system/bucket/images/s3manager.tag | 2 +- packages/system/cozystack-api/values.yaml | 2 +- packages/system/cozystack-controller/values.yaml | 2 +- packages/system/dashboard/templates/configmap.yaml | 2 +- packages/system/dashboard/values.yaml | 6 +++--- .../system/grafana-operator/images/grafana-dashboards.tag | 2 +- packages/system/kamaji/values.yaml | 4 ++-- packages/system/kilo/values.yaml | 2 +- packages/system/kubeovn-plunger/values.yaml | 2 +- packages/system/kubeovn-webhook/values.yaml | 2 +- packages/system/kubevirt-csi-node/values.yaml | 2 +- packages/system/lineage-controller-webhook/values.yaml | 2 +- packages/system/linstor/values.yaml | 4 +--- packages/system/objectstorage-controller/values.yaml | 2 +- packages/system/seaweedfs/values.yaml | 2 +- 23 files changed, 27 insertions(+), 29 deletions(-) diff --git a/packages/apps/kubernetes/images/kubevirt-csi-driver.tag b/packages/apps/kubernetes/images/kubevirt-csi-driver.tag index 420fbc66..02f8f103 100644 --- a/packages/apps/kubernetes/images/kubevirt-csi-driver.tag +++ b/packages/apps/kubernetes/images/kubevirt-csi-driver.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:31aee6f63c25a443974b56011d2907cedfeba245aa7caf732d7fc437b2b3ed50 +ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:726d9287e8caaea94eaf24c4f44734e3fbf4f8aa032b66b81848ebf95297cffe diff --git a/packages/apps/mysql/images/mariadb-backup.tag b/packages/apps/mysql/images/mariadb-backup.tag index 792c010b..1e381661 100644 --- a/packages/apps/mysql/images/mariadb-backup.tag +++ b/packages/apps/mysql/images/mariadb-backup.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/mariadb-backup:0.0.0@sha256:aca403030ff5d831415d72367866fdf291fab73ee2cfddbe4c93c2915a316ab1 +ghcr.io/cozystack/cozystack/mariadb-backup:0.0.0@sha256:0ddbbec0568dcb9fbc317cd9cc654e826dbe88ba3f184fa9b6b58aacb93b4570 diff --git a/packages/core/installer/values.yaml b/packages/core/installer/values.yaml index 6d244871..2325647e 100644 --- a/packages/core/installer/values.yaml +++ b/packages/core/installer/values.yaml @@ -1,4 +1,4 @@ cozystackOperator: - image: ghcr.io/cozystack/cozystack/cozystack-operator:v1.0.0-alpha.2@sha256:5bd4c8bb7bddb9e32555ef72ee3a04b5d8e0f488d7dce904ac2d7c9e0706debf + image: ghcr.io/cozystack/cozystack/cozystack-operator:v1.0.0-beta.2@sha256:aaea9d8430187f208e6464cb6f102dcbbcdb0584b6a7a8a690ad91fc1f5d45e6 platformSourceUrl: 'oci://ghcr.io/cozystack/cozystack/cozystack-packages' - platformSourceRef: 'digest=sha256:1b860c13b99da6fde37f2af3a5375aca234200836e2fa39a839ac30fa237962e' + platformSourceRef: 'digest=sha256:f59e562f2c91446117773ad457251d567706ea2964251a2b0acc65060fd1f3bc' diff --git a/packages/core/testing/values.yaml b/packages/core/testing/values.yaml index 9e577ed3..1e77f663 100644 --- a/packages/core/testing/values.yaml +++ b/packages/core/testing/values.yaml @@ -1,2 +1,2 @@ e2e: - image: ghcr.io/cozystack/cozystack/e2e-sandbox:v1.0.0-alpha.2@sha256:fde7616aacaf5939388bbe74eb6d946147ce855b9cceb47092f620b75ba2c98a + image: ghcr.io/cozystack/cozystack/e2e-sandbox:v1.0.0-beta.2@sha256:eac71ef0de3450fce96255629e77903630c63ade62b81e7055f1a689f92ee153 diff --git a/packages/extra/bootbox/images/matchbox.tag b/packages/extra/bootbox/images/matchbox.tag index ac9a2f76..05ee31c2 100644 --- a/packages/extra/bootbox/images/matchbox.tag +++ b/packages/extra/bootbox/images/matchbox.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/matchbox:v1.0.0-alpha.2@sha256:6a96746d43fde16db12bb2e0e6fc39823f4a26f41fc0be785bf6716c759009e1 +ghcr.io/cozystack/cozystack/matchbox:v1.0.0-beta.2@sha256:3d8c93822ca7b344b718a9ab0fb196d8fab92fcf852907975b39528d129811f0 diff --git a/packages/extra/seaweedfs/images/objectstorage-sidecar.tag b/packages/extra/seaweedfs/images/objectstorage-sidecar.tag index dd3c00a1..1db178ca 100644 --- a/packages/extra/seaweedfs/images/objectstorage-sidecar.tag +++ b/packages/extra/seaweedfs/images/objectstorage-sidecar.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/objectstorage-sidecar:v1.0.0-alpha.2@sha256:aebbe6efe34c0a0cc2e87dafbf9dbb021d7e44796ef9378a47ca81cf72a407bb +ghcr.io/cozystack/cozystack/objectstorage-sidecar:v1.0.0-beta.2@sha256:ea035d4eff4a05d9d83f487d00438504cc27a95c0ee78c534d6eed53f4b2f04e diff --git a/packages/system/backup-controller/values.yaml b/packages/system/backup-controller/values.yaml index 0c20d434..84038de8 100644 --- a/packages/system/backup-controller/values.yaml +++ b/packages/system/backup-controller/values.yaml @@ -1,5 +1,5 @@ backupController: - image: "ghcr.io/cozystack/cozystack/backup-controller:v1.0.0-alpha.2@sha256:3914624192b4d420b79f0d985d37a03acb13411c694e23c160de16b1d877c52f" + image: "ghcr.io/cozystack/cozystack/backup-controller:v1.0.0-beta.2@sha256:556cc41e4ec24c173e01c1679cf96915c7ca8c0b532b7945d461bf3797c8afbc" replicas: 2 debug: false metrics: diff --git a/packages/system/backupstrategy-controller/values.yaml b/packages/system/backupstrategy-controller/values.yaml index cd71b6e1..d4c9f253 100644 --- a/packages/system/backupstrategy-controller/values.yaml +++ b/packages/system/backupstrategy-controller/values.yaml @@ -1,5 +1,5 @@ backupStrategyController: - image: "ghcr.io/cozystack/cozystack/backupstrategy-controller:v1.0.0-alpha.2@sha256:a39b76c99f252916ca9b6606a68fbd5b0861988dd65426803855c2884117e49c" + image: "ghcr.io/cozystack/cozystack/backupstrategy-controller:v1.0.0-beta.2@sha256:5cbc85679790aa14fb55568439c669a704e29f02236df179abbb3a25c56a2aa7" replicas: 2 debug: false metrics: diff --git a/packages/system/bucket/images/s3manager.tag b/packages/system/bucket/images/s3manager.tag index 1e30a0e1..8ef294d1 100644 --- a/packages/system/bucket/images/s3manager.tag +++ b/packages/system/bucket/images/s3manager.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/s3manager:v0.5.0@sha256:db548ff13f27f98e888b8f3ffb0f859a8f44334f1502d567d1881b79b6e5eb09 +ghcr.io/cozystack/cozystack/s3manager:v0.5.0@sha256:3013e13ba967070948653cc5b913a920dea93a24370b10731fafcfd8fb6a21b0 diff --git a/packages/system/cozystack-api/values.yaml b/packages/system/cozystack-api/values.yaml index 7876981a..b5abfaec 100644 --- a/packages/system/cozystack-api/values.yaml +++ b/packages/system/cozystack-api/values.yaml @@ -1,5 +1,5 @@ cozystackAPI: - image: ghcr.io/cozystack/cozystack/cozystack-api:v1.0.0-alpha.2@sha256:5bde4dd3e8d3a880475c9e872925f841f6131f51f9a5017ec5c2a0f0bd288838 + image: ghcr.io/cozystack/cozystack/cozystack-api:v1.0.0-beta.2@sha256:2815ee65d13e3bde376ca3975a1106e1e32b8b911f2004d85a824a9fc30eebbd localK8sAPIEndpoint: enabled: true replicas: 2 diff --git a/packages/system/cozystack-controller/values.yaml b/packages/system/cozystack-controller/values.yaml index 633d0b28..7adbea35 100644 --- a/packages/system/cozystack-controller/values.yaml +++ b/packages/system/cozystack-controller/values.yaml @@ -1,5 +1,5 @@ cozystackController: - image: ghcr.io/cozystack/cozystack/cozystack-controller:v1.0.0-alpha.2@sha256:9cc3d4b05709d3e7495abcbe4c792467a9a439fd089a67df0e4a75fd3bb8ee81 + image: ghcr.io/cozystack/cozystack/cozystack-controller:v1.0.0-beta.2@sha256:3149c8341de741bce35de27591f72be82f22634ebc5dba8889b2bbae7892579a debug: false disableTelemetry: false cozystackAPIKind: "DaemonSet" diff --git a/packages/system/dashboard/templates/configmap.yaml b/packages/system/dashboard/templates/configmap.yaml index 675a396d..c3ee0595 100644 --- a/packages/system/dashboard/templates/configmap.yaml +++ b/packages/system/dashboard/templates/configmap.yaml @@ -1,6 +1,6 @@ {{- $brandingConfig := .Values._cluster.branding | default dict }} -{{- $tenantText := "v1.0.0-alpha.2" }} +{{- $tenantText := "v1.0.0-beta.2" }} {{- $footerText := "Cozystack" }} {{- $titleText := "Cozystack Dashboard" }} {{- $logoText := "" }} diff --git a/packages/system/dashboard/values.yaml b/packages/system/dashboard/values.yaml index fcbaeb8c..b1eae64c 100644 --- a/packages/system/dashboard/values.yaml +++ b/packages/system/dashboard/values.yaml @@ -1,6 +1,6 @@ openapiUI: - image: ghcr.io/cozystack/cozystack/openapi-ui:v1.0.0-alpha.2@sha256:5661e877919c8625c27b89ee5dfda4a93d43e660d033fc4dbeb27f336c05d908 + image: ghcr.io/cozystack/cozystack/openapi-ui:v1.0.0-beta.2@sha256:ed695cd89086df9bae6e3519bbcaeb2f26a36109f000dec4bc917bedcbd17d69 openapiUIK8sBff: - image: ghcr.io/cozystack/cozystack/openapi-ui-k8s-bff:v1.0.0-alpha.2@sha256:d33583995dc81a47c1dcbe45dbd866fa9097f88f4b6eb78b408dca432f15bd38 + image: ghcr.io/cozystack/cozystack/openapi-ui-k8s-bff:v1.0.0-beta.2@sha256:1f7827a1978bd9c81ac924dd0e78f6a3ce834a9a64af55047e220812bc15a944 tokenProxy: - image: ghcr.io/cozystack/cozystack/token-proxy:v1.0.0-alpha.2@sha256:73887f80d96e7e3c16f1cebab521b05b4308bf4662ccc6724e6a8a9745ed8254 + image: ghcr.io/cozystack/cozystack/token-proxy:v1.0.0-beta.2@sha256:063d34c25333e110dd7fad999279d4a5497e918723f1341991af48632b96ada1 diff --git a/packages/system/grafana-operator/images/grafana-dashboards.tag b/packages/system/grafana-operator/images/grafana-dashboards.tag index 892bb131..dd4b8a60 100644 --- a/packages/system/grafana-operator/images/grafana-dashboards.tag +++ b/packages/system/grafana-operator/images/grafana-dashboards.tag @@ -1 +1 @@ -ghcr.io/cozystack/cozystack/grafana-dashboards:v1.0.0-alpha.2@sha256:57051daa5686b7dcb0799b6a7c01e676b910a2ac224437ac0ad639a0e66566c2 +ghcr.io/cozystack/cozystack/grafana-dashboards:v1.0.0-beta.2@sha256:e866b5b3874b9d390b341183d2ee070e1387440c14cfe51af831695def6dc2ec diff --git a/packages/system/kamaji/values.yaml b/packages/system/kamaji/values.yaml index 0995afc7..f8f169f9 100644 --- a/packages/system/kamaji/values.yaml +++ b/packages/system/kamaji/values.yaml @@ -3,7 +3,7 @@ kamaji: deploy: false image: pullPolicy: IfNotPresent - tag: v1.0.0-alpha.2@sha256:d5f2fa2972ba33cd2ccb855256e4bda4734d7e250638811b77f2e0dc72ad6b19 + tag: v1.0.0-beta.2@sha256:d5f2fa2972ba33cd2ccb855256e4bda4734d7e250638811b77f2e0dc72ad6b19 repository: ghcr.io/cozystack/cozystack/kamaji resources: limits: @@ -13,4 +13,4 @@ kamaji: cpu: 100m memory: 100Mi extraArgs: - - --migrate-image=ghcr.io/cozystack/cozystack/kamaji:v1.0.0-alpha.2@sha256:d5f2fa2972ba33cd2ccb855256e4bda4734d7e250638811b77f2e0dc72ad6b19 + - --migrate-image=ghcr.io/cozystack/cozystack/kamaji:v1.0.0-beta.2@sha256:d5f2fa2972ba33cd2ccb855256e4bda4734d7e250638811b77f2e0dc72ad6b19 diff --git a/packages/system/kilo/values.yaml b/packages/system/kilo/values.yaml index 565191a9..7bcfc55c 100644 --- a/packages/system/kilo/values.yaml +++ b/packages/system/kilo/values.yaml @@ -1,7 +1,7 @@ kilo: image: pullPolicy: IfNotPresent - tag: v1.0.0-alpha.2@sha256:161fec69f38f536529139318babbd7d654bd47e7bc654828c26eb0ddb516eb79 + tag: v1.0.0-beta.2@sha256:45ad01a89ebb5311735660a0d1a1df36eda4b6f960be1b6319d2b94d2a7db701 repository: ghcr.io/cozystack/cozystack/kilo podCIDR: 10.244.0.0/16 serviceCIDR: 10.96.0.0/16 diff --git a/packages/system/kubeovn-plunger/values.yaml b/packages/system/kubeovn-plunger/values.yaml index d821bdac..ed7c84dd 100644 --- a/packages/system/kubeovn-plunger/values.yaml +++ b/packages/system/kubeovn-plunger/values.yaml @@ -1,4 +1,4 @@ portSecurity: true routes: "" -image: ghcr.io/cozystack/cozystack/kubeovn-plunger:v1.0.0-alpha.2@sha256:81d7e64b67b1e4fefa88ab0cf69c113a3fb44b04810074b616116102adcc3104 +image: ghcr.io/cozystack/cozystack/kubeovn-plunger:v1.0.0-beta.2@sha256:387fe9eca078edfb631511a091da9f2a7fcdc214867b4e2c269b55122a0f4ce7 ovnCentralName: ovn-central diff --git a/packages/system/kubeovn-webhook/values.yaml b/packages/system/kubeovn-webhook/values.yaml index df35d690..244879e7 100644 --- a/packages/system/kubeovn-webhook/values.yaml +++ b/packages/system/kubeovn-webhook/values.yaml @@ -1,3 +1,3 @@ portSecurity: true routes: "" -image: ghcr.io/cozystack/cozystack/kubeovn-webhook:v1.0.0-alpha.2@sha256:e18f9fd679e38f65362a8d0042f25468272f6d081136ad47027168d8e7e07a4a +image: ghcr.io/cozystack/cozystack/kubeovn-webhook:v1.0.0-beta.2@sha256:e18f9fd679e38f65362a8d0042f25468272f6d081136ad47027168d8e7e07a4a diff --git a/packages/system/kubevirt-csi-node/values.yaml b/packages/system/kubevirt-csi-node/values.yaml index 1fb152da..8457b6d1 100644 --- a/packages/system/kubevirt-csi-node/values.yaml +++ b/packages/system/kubevirt-csi-node/values.yaml @@ -1,3 +1,3 @@ storageClass: replicated csiDriver: - image: ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:31aee6f63c25a443974b56011d2907cedfeba245aa7caf732d7fc437b2b3ed50 + image: ghcr.io/cozystack/cozystack/kubevirt-csi-driver:0.0.0@sha256:726d9287e8caaea94eaf24c4f44734e3fbf4f8aa032b66b81848ebf95297cffe diff --git a/packages/system/lineage-controller-webhook/values.yaml b/packages/system/lineage-controller-webhook/values.yaml index 8225bd3c..6c19c22b 100644 --- a/packages/system/lineage-controller-webhook/values.yaml +++ b/packages/system/lineage-controller-webhook/values.yaml @@ -1,5 +1,5 @@ lineageControllerWebhook: - image: ghcr.io/cozystack/cozystack/lineage-controller-webhook:v1.0.0-alpha.2@sha256:e3ed98fce423359de7dd25094de959213342f5664d266c8d1041ecd2e9c82d59 + image: ghcr.io/cozystack/cozystack/lineage-controller-webhook:v1.0.0-beta.2@sha256:e2ffc29d244b9b5916ab048c338a8f284a47b0f4bd00e903dcf36df2a0299b72 debug: false localK8sAPIEndpoint: enabled: true diff --git a/packages/system/linstor/values.yaml b/packages/system/linstor/values.yaml index 8c19ba2a..8c22ac6b 100644 --- a/packages/system/linstor/values.yaml +++ b/packages/system/linstor/values.yaml @@ -2,11 +2,9 @@ piraeusServer: image: repository: ghcr.io/cozystack/cozystack/piraeus-server tag: 1.32.3@sha256:0e78fa31a3fe4ec2af43d1e59a9fc0f6d765780e32d473e18e1c495714051802 - # Talos-specific workarounds (disable for generic Linux like Ubuntu/Debian) talos: enabled: true - linstor: autoDiskful: enabled: true @@ -15,4 +13,4 @@ linstor: linstorCSI: image: repository: ghcr.io/cozystack/cozystack/linstor-csi - tag: v1.10.5@sha256:353a8bea0b41f832975132da3569da7d4fce85980474edce41a2a37097c7c3a9 + tag: v1.10.5@sha256:68465f120cfeec3d7ccbb389dd9bdbf7df1675da3ab9ba91c3feff21a799bc36 diff --git a/packages/system/objectstorage-controller/values.yaml b/packages/system/objectstorage-controller/values.yaml index 86ec9cd4..aa3756ad 100644 --- a/packages/system/objectstorage-controller/values.yaml +++ b/packages/system/objectstorage-controller/values.yaml @@ -1,3 +1,3 @@ objectstorage: controller: - image: "ghcr.io/cozystack/cozystack/objectstorage-controller:v1.0.0-alpha.2@sha256:e535bd383e2ec275cc403cd557996e26df3d206a5541c53037246641cf6a4f2e" + image: "ghcr.io/cozystack/cozystack/objectstorage-controller:v1.0.0-beta.2@sha256:1f35e09bae32cd11c6ce2268556cac76b8da68b448208aea3c13071306087534" diff --git a/packages/system/seaweedfs/values.yaml b/packages/system/seaweedfs/values.yaml index 88a05e9c..3a60c779 100644 --- a/packages/system/seaweedfs/values.yaml +++ b/packages/system/seaweedfs/values.yaml @@ -177,7 +177,7 @@ seaweedfs: bucketClassName: "seaweedfs" region: "" sidecar: - image: "ghcr.io/cozystack/cozystack/objectstorage-sidecar:v1.0.0-alpha.2@sha256:aebbe6efe34c0a0cc2e87dafbf9dbb021d7e44796ef9378a47ca81cf72a407bb" + image: "ghcr.io/cozystack/cozystack/objectstorage-sidecar:v1.0.0-beta.2@sha256:ea035d4eff4a05d9d83f487d00438504cc27a95c0ee78c534d6eed53f4b2f04e" certificates: commonName: "SeaweedFS CA" ipAddresses: [] From 3f59ce487632aa67f598e030163f5a23a08610df Mon Sep 17 00:00:00 2001 From: Matthieu ROBIN Date: Mon, 2 Feb 2026 13:47:56 +0100 Subject: [PATCH 123/125] Update internal/controller/workloadmonitor_controller.go Co-authored-by: Timofei Larkin Signed-off-by: Matthieu ROBIN --- internal/controller/workloadmonitor_controller.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/controller/workloadmonitor_controller.go b/internal/controller/workloadmonitor_controller.go index 677fda82..201fefee 100644 --- a/internal/controller/workloadmonitor_controller.go +++ b/internal/controller/workloadmonitor_controller.go @@ -466,7 +466,7 @@ func (r *WorkloadMonitorReconciler) getWorkloadMetadata(obj client.Object) map[s annotations := obj.GetAnnotations() if instanceType, ok := annotations["kubevirt.io/cluster-instancetype-name"]; ok { labels["workloads.cozystack.io/kubevirt-vmi-instance-type"] = instanceType - if instanceProfile, ok := annotations["kubevirt.io/cluster-instanceprofile-name"]; ok { + if instanceProfile, ok := annotations["kubevirt.io/cluster-preference-name"]; ok { labels["workloads.cozystack.io/kubevirt-vmi-instance-profile"] = instanceProfile } return labels From 09cd9e05c3d277d3c852e7317b7a2a80b0f77627 Mon Sep 17 00:00:00 2001 From: Timofei Larkin Date: Mon, 2 Feb 2026 15:52:25 +0300 Subject: [PATCH 124/125] Apply suggestion from @gemini-code-assist[bot] Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> Signed-off-by: Timofei Larkin --- internal/controller/workloadmonitor_controller.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/internal/controller/workloadmonitor_controller.go b/internal/controller/workloadmonitor_controller.go index 201fefee..a684df9b 100644 --- a/internal/controller/workloadmonitor_controller.go +++ b/internal/controller/workloadmonitor_controller.go @@ -466,8 +466,9 @@ func (r *WorkloadMonitorReconciler) getWorkloadMetadata(obj client.Object) map[s annotations := obj.GetAnnotations() if instanceType, ok := annotations["kubevirt.io/cluster-instancetype-name"]; ok { labels["workloads.cozystack.io/kubevirt-vmi-instance-type"] = instanceType - if instanceProfile, ok := annotations["kubevirt.io/cluster-preference-name"]; ok { - labels["workloads.cozystack.io/kubevirt-vmi-instance-profile"] = instanceProfile + } + if instanceProfile, ok := annotations["kubevirt.io/cluster-instanceprofile-name"]; ok { + labels["workloads.cozystack.io/kubevirt-vmi-instance-profile"] = instanceProfile } return labels } From 3a8e8fc290e82c29a7f5e0241ca23372c05d54de Mon Sep 17 00:00:00 2001 From: Kirill Ilin Date: Mon, 2 Feb 2026 17:51:39 +0500 Subject: [PATCH 125/125] [vm] allow changing field external after creation Service will be recreated Signed-off-by: Kirill Ilin --- .../templates/vm-update-hook.yaml | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/packages/apps/virtual-machine/templates/vm-update-hook.yaml b/packages/apps/virtual-machine/templates/vm-update-hook.yaml index 1197c027..3c7d7d34 100644 --- a/packages/apps/virtual-machine/templates/vm-update-hook.yaml +++ b/packages/apps/virtual-machine/templates/vm-update-hook.yaml @@ -3,14 +3,17 @@ {{- $existingVM := lookup "kubevirt.io/v1" "VirtualMachine" $namespace $vmName -}} {{- $existingPVC := lookup "v1" "PersistentVolumeClaim" $namespace $vmName -}} +{{- $existingService := lookup "v1" "Service" $namespace $vmName -}} {{- $instanceType := .Values.instanceType | default "" -}} {{- $instanceProfile := .Values.instanceProfile | default "" -}} {{- $desiredStorage := .Values.systemDisk.storage | default "" -}} +{{- $desiredServiceType := ternary "LoadBalancer" "ClusterIP" .Values.external -}} {{- $needUpdateType := false -}} {{- $needUpdateProfile := false -}} {{- $needResizePVC := false -}} +{{- $needRecreateService := false -}} {{- if and $existingVM $instanceType -}} {{- if not (eq $existingVM.spec.instancetype.name $instanceType) -}} @@ -35,7 +38,14 @@ {{- end -}} {{- end -}} -{{- if or $needUpdateType $needUpdateProfile $needResizePVC }} +{{- if $existingService -}} + {{- $currentServiceType := $existingService.spec.type -}} + {{- if ne $currentServiceType $desiredServiceType -}} + {{- $needRecreateService = true -}} + {{- end -}} +{{- end -}} + +{{- if or $needUpdateType $needUpdateProfile $needResizePVC $needRecreateService }} apiVersion: batch/v1 kind: Job metadata: @@ -86,6 +96,11 @@ spec: --type merge \ -p '{"spec":{"resources":{"requests":{"storage":"{{ $desiredStorage }}"}}}}' {{- end }} + + {{- if $needRecreateService }} + echo "Removing Service..." + kubectl delete service --cascade=orphan -n {{ $namespace }} {{ $vmName }} + {{- end }} --- apiVersion: v1 kind: ServiceAccount @@ -111,6 +126,10 @@ rules: - apiGroups: [""] resources: ["persistentvolumeclaims"] verbs: ["patch", "get", "list", "watch"] + - apiGroups: [""] + resources: ["services"] + resourceNames: ["{{ $vmName }}"] + verbs: ["delete"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding