From 25ff583f3407b76fbae6f32e7da01beb32b113f0 Mon Sep 17 00:00:00 2001 From: Matthieu Date: Mon, 2 Feb 2026 13:11:18 +0100 Subject: [PATCH 001/488] [apps] Add managed OpenSearch service Add OpenSearch application with operator and resource definition: - App chart with multi-version support (v1/v2/v3), TLS, auth, dashboards - OpenSearch operator wrapper (opster v2.8.0) with sysctl daemonset - ApplicationDefinition for Cozystack platform integration Co-Authored-By: Claude Opus 4.5 Signed-off-by: Matthieu --- packages/apps/opensearch/.helmignore | 23 + packages/apps/opensearch/Chart.yaml | 7 + packages/apps/opensearch/Makefile | 11 + packages/apps/opensearch/README.md | 60 + packages/apps/opensearch/charts/cozy-lib | 1 + packages/apps/opensearch/files/versions.yaml | 5 + .../apps/opensearch/hack/update-versions.sh | 53 + packages/apps/opensearch/logos/opensearch.svg | 11 + packages/apps/opensearch/templates/.gitkeep | 0 .../apps/opensearch/templates/_versions.tpl | 13 + .../templates/dashboard-resourcemap.yaml | 42 + .../opensearch/templates/external-svc.yaml | 39 + .../apps/opensearch/templates/opensearch.yaml | 99 + .../apps/opensearch/templates/security.yaml | 120 + packages/apps/opensearch/templates/users.yaml | 21 + .../opensearch/tests/opensearch_test.yaml | 516 ++ .../apps/opensearch/tests/security_test.yaml | 207 + .../apps/opensearch/tests/users_test.yaml | 176 + packages/apps/opensearch/values.schema.json | 239 + packages/apps/opensearch/values.yaml | 111 + .../system/opensearch-operator/.helmignore | 23 + .../system/opensearch-operator/Chart.yaml | 3 + packages/system/opensearch-operator/Makefile | 10 + .../charts/opensearch-operator/CHANGELOG.md | 61 + .../charts/opensearch-operator/Chart.yaml | 6 + .../charts/opensearch-operator/README.md | 29 + ...arch.opster.io_opensearchactiongroups.yaml | 94 + ...ensearch.opster.io_opensearchclusters.yaml | 6372 +++++++++++++++++ ...pster.io_opensearchcomponenttemplates.yaml | 136 + ...ch.opster.io_opensearchindextemplates.yaml | 163 + ...earch.opster.io_opensearchismpolicies.yaml | 461 ++ .../opensearch.opster.io_opensearchroles.yaml | 123 + ....opster.io_opensearchsnapshotpolicies.yaml | 203 + ...pensearch.opster.io_opensearchtenants.yaml | 85 + ....opster.io_opensearchuserrolebindings.yaml | 109 + .../opensearch.opster.io_opensearchusers.yaml | 117 + .../templates/_helpers.tpl | 62 + ...perator-controller-manager-deployment.yaml | 94 + ...ontroller-manager-metrics-service-svc.yaml | 13 + ...search-operator-controller-manager-sa.yaml | 6 + .../templates/opensearch-operator-crds.yaml | 5 + ...ch-operator-leader-election-role-role.yaml | 48 + ...erator-leader-election-rolebinding-rb.yaml | 11 + ...opensearch-operator-manager-config-cm.yaml | 17 + .../opensearch-operator-manager-role-cr.yaml | 414 ++ ...ensearch-operator-manager-rolebinding.yaml | 27 + ...opensearch-operator-metrics-reader-cr.yaml | 9 + .../opensearch-operator-proxy-role-cr.yaml | 17 + ...opensearch-operator-proxy-rolebinding.yaml | 27 + .../charts/opensearch-operator/values.yaml | 123 + .../templates/sysctl-daemonset.yaml | 64 + .../system/opensearch-operator/values.yaml | 4 + packages/system/opensearch-rd/Chart.yaml | 3 + packages/system/opensearch-rd/Makefile | 4 + .../opensearch-rd/cozyrds/opensearch.yaml | 42 + .../opensearch-rd/templates/cozyrd.yaml | 4 + packages/system/opensearch-rd/values.yaml | 1 + 57 files changed, 10744 insertions(+) create mode 100644 packages/apps/opensearch/.helmignore create mode 100644 packages/apps/opensearch/Chart.yaml create mode 100644 packages/apps/opensearch/Makefile create mode 100644 packages/apps/opensearch/README.md create mode 120000 packages/apps/opensearch/charts/cozy-lib create mode 100644 packages/apps/opensearch/files/versions.yaml create mode 100755 packages/apps/opensearch/hack/update-versions.sh create mode 100644 packages/apps/opensearch/logos/opensearch.svg create mode 100644 packages/apps/opensearch/templates/.gitkeep create mode 100644 packages/apps/opensearch/templates/_versions.tpl create mode 100644 packages/apps/opensearch/templates/dashboard-resourcemap.yaml create mode 100644 packages/apps/opensearch/templates/external-svc.yaml create mode 100644 packages/apps/opensearch/templates/opensearch.yaml create mode 100644 packages/apps/opensearch/templates/security.yaml create mode 100644 packages/apps/opensearch/templates/users.yaml create mode 100644 packages/apps/opensearch/tests/opensearch_test.yaml create mode 100644 packages/apps/opensearch/tests/security_test.yaml create mode 100644 packages/apps/opensearch/tests/users_test.yaml create mode 100644 packages/apps/opensearch/values.schema.json create mode 100644 packages/apps/opensearch/values.yaml create mode 100644 packages/system/opensearch-operator/.helmignore create mode 100644 packages/system/opensearch-operator/Chart.yaml create mode 100644 packages/system/opensearch-operator/Makefile create mode 100644 packages/system/opensearch-operator/charts/opensearch-operator/CHANGELOG.md create mode 100644 packages/system/opensearch-operator/charts/opensearch-operator/Chart.yaml create mode 100644 packages/system/opensearch-operator/charts/opensearch-operator/README.md create mode 100644 packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchactiongroups.yaml create mode 100644 packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchclusters.yaml create mode 100644 packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchcomponenttemplates.yaml create mode 100644 packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchindextemplates.yaml create mode 100644 packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchismpolicies.yaml create mode 100644 packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchroles.yaml create mode 100644 packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchsnapshotpolicies.yaml create mode 100644 packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchtenants.yaml create mode 100644 packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchuserrolebindings.yaml create mode 100644 packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchusers.yaml create mode 100644 packages/system/opensearch-operator/charts/opensearch-operator/templates/_helpers.tpl create mode 100644 packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-controller-manager-deployment.yaml create mode 100644 packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-controller-manager-metrics-service-svc.yaml create mode 100644 packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-controller-manager-sa.yaml create mode 100644 packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-crds.yaml create mode 100644 packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-leader-election-role-role.yaml create mode 100644 packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-leader-election-rolebinding-rb.yaml create mode 100644 packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-manager-config-cm.yaml create mode 100644 packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-manager-role-cr.yaml create mode 100644 packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-manager-rolebinding.yaml create mode 100644 packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-metrics-reader-cr.yaml create mode 100644 packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-proxy-role-cr.yaml create mode 100644 packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-proxy-rolebinding.yaml create mode 100644 packages/system/opensearch-operator/charts/opensearch-operator/values.yaml create mode 100644 packages/system/opensearch-operator/templates/sysctl-daemonset.yaml create mode 100644 packages/system/opensearch-operator/values.yaml create mode 100644 packages/system/opensearch-rd/Chart.yaml create mode 100644 packages/system/opensearch-rd/Makefile create mode 100644 packages/system/opensearch-rd/cozyrds/opensearch.yaml create mode 100644 packages/system/opensearch-rd/templates/cozyrd.yaml create mode 100644 packages/system/opensearch-rd/values.yaml diff --git a/packages/apps/opensearch/.helmignore b/packages/apps/opensearch/.helmignore new file mode 100644 index 00000000..0e8a0eb3 --- /dev/null +++ b/packages/apps/opensearch/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/packages/apps/opensearch/Chart.yaml b/packages/apps/opensearch/Chart.yaml new file mode 100644 index 00000000..8abefadf --- /dev/null +++ b/packages/apps/opensearch/Chart.yaml @@ -0,0 +1,7 @@ +apiVersion: v2 +name: opensearch +description: Managed OpenSearch service +icon: /logos/opensearch.svg +type: application +version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process +appVersion: "2.11.1" diff --git a/packages/apps/opensearch/Makefile b/packages/apps/opensearch/Makefile new file mode 100644 index 00000000..9440c3fd --- /dev/null +++ b/packages/apps/opensearch/Makefile @@ -0,0 +1,11 @@ +include ../../../hack/package.mk + +.PHONY: generate update + +generate: + cozyvalues-gen -v values.yaml -s values.schema.json -r README.md + ../../../hack/update-crd.sh + +update: + hack/update-versions.sh + make generate diff --git a/packages/apps/opensearch/README.md b/packages/apps/opensearch/README.md new file mode 100644 index 00000000..d968c665 --- /dev/null +++ b/packages/apps/opensearch/README.md @@ -0,0 +1,60 @@ +# Managed OpenSearch Service + +## Parameters + +### Common parameters + +| Name | Description | Type | Value | +| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ---------- | ------- | +| `replicas` | Number of OpenSearch nodes in the cluster. | `int` | `3` | +| `resources` | Explicit CPU and memory configuration for each OpenSearch node. When omitted, the preset defined in `resourcesPreset` is applied. | `object` | `{}` | +| `resources.cpu` | CPU available to each node. | `quantity` | `""` | +| `resources.memory` | Memory (RAM) available to each node. | `quantity` | `""` | +| `resourcesPreset` | Default sizing preset used when `resources` is omitted. OpenSearch requires minimum 2Gi memory. | `string` | `large` | +| `size` | Persistent Volume Claim size available for application data. | `quantity` | `10Gi` | +| `storageClass` | StorageClass used to store the data. | `string` | `""` | +| `external` | Enable external access from outside the cluster. | `bool` | `false` | +| `topologySpreadPolicy` | How strictly to enforce pod distribution across nodes and zones. | `string` | `soft` | +| `version` | OpenSearch major version to deploy. | `string` | `v2` | + + +### Image configuration + +| Name | Description | Type | Value | +| ------------------- | -------------------------------------- | -------- | ----- | +| `images` | Container images used by the operator. | `object` | `{}` | +| `images.opensearch` | OpenSearch image. | `string` | `""` | + + +### Node roles configuration + +| Name | Description | Type | Value | +| ------------------ | ----------------------------- | -------- | ------- | +| `nodeRoles` | Node roles configuration. | `object` | `{}` | +| `nodeRoles.master` | Enable cluster_manager role. | `bool` | `true` | +| `nodeRoles.data` | Enable data role. | `bool` | `true` | +| `nodeRoles.ingest` | Enable ingest role. | `bool` | `true` | +| `nodeRoles.ml` | Enable machine learning role. | `bool` | `false` | + + +### Users configuration + +| Name | Description | Type | Value | +| ---------------------- | -------------------------------------------------- | ------------------- | ----- | +| `users` | Custom OpenSearch users configuration map. | `map[string]object` | `{}` | +| `users[name].password` | Password for the user (auto-generated if omitted). | `string` | `""` | +| `users[name].roles` | List of OpenSearch roles. | `[]string` | `[]` | + + +### OpenSearch Dashboards configuration + +| Name | Description | Type | Value | +| ----------------------------- | ----------------------------------------------------- | ---------- | -------- | +| `dashboards` | OpenSearch Dashboards configuration. | `object` | `{}` | +| `dashboards.enabled` | Enable OpenSearch Dashboards deployment. | `bool` | `false` | +| `dashboards.replicas` | Number of Dashboards replicas. | `int` | `1` | +| `dashboards.resources` | Explicit CPU and memory configuration for Dashboards. | `object` | `{}` | +| `dashboards.resources.cpu` | CPU available to each node. | `quantity` | `""` | +| `dashboards.resources.memory` | Memory (RAM) available to each node. | `quantity` | `""` | +| `dashboards.resourcesPreset` | Default sizing preset for Dashboards. | `string` | `medium` | + diff --git a/packages/apps/opensearch/charts/cozy-lib b/packages/apps/opensearch/charts/cozy-lib new file mode 120000 index 00000000..e1813509 --- /dev/null +++ b/packages/apps/opensearch/charts/cozy-lib @@ -0,0 +1 @@ +../../../library/cozy-lib \ No newline at end of file diff --git a/packages/apps/opensearch/files/versions.yaml b/packages/apps/opensearch/files/versions.yaml new file mode 100644 index 00000000..5e88df94 --- /dev/null +++ b/packages/apps/opensearch/files/versions.yaml @@ -0,0 +1,5 @@ +# OpenSearch version mapping (major version -> image tag) +# Auto-generated by hack/update-versions.sh - do not edit manually +"v3": "3.0.0" +"v2": "2.11.1" +"v1": "1.3.20" diff --git a/packages/apps/opensearch/hack/update-versions.sh b/packages/apps/opensearch/hack/update-versions.sh new file mode 100755 index 00000000..19d9e7b2 --- /dev/null +++ b/packages/apps/opensearch/hack/update-versions.sh @@ -0,0 +1,53 @@ +#!/usr/bin/env bash + +set -o errexit +set -o nounset +set -o pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +OPENSEARCH_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" +VERSIONS_FILE="${OPENSEARCH_DIR}/files/versions.yaml" + +# Supported major versions (newest first) +SUPPORTED_MAJOR_VERSIONS="3 2 1" + +echo "Supported major versions: $SUPPORTED_MAJOR_VERSIONS" + +# Check if skopeo is installed +if ! command -v skopeo &> /dev/null; then + echo "Error: skopeo is not installed. Please install skopeo and try again." >&2 + exit 1 +fi + +# Check if jq is installed +if ! command -v jq &> /dev/null; then + echo "Error: jq is not installed. Please install jq and try again." >&2 + exit 1 +fi + +# Get available image tags from Docker Hub +IMAGE="docker.io/opensearchproject/opensearch" +echo "Fetching available image tags from registry..." +TAGS=$(skopeo list-tags "docker://${IMAGE}" | jq -r '.Tags[]') + +echo "# OpenSearch version mapping (major version -> image tag)" > "${VERSIONS_FILE}" +echo "# Auto-generated by hack/update-versions.sh - do not edit manually" >> "${VERSIONS_FILE}" + +for MAJOR in $SUPPORTED_MAJOR_VERSIONS; do + # Find the latest stable release for this major version + LATEST=$(echo "$TAGS" \ + | grep -E "^${MAJOR}\.[0-9]+\.[0-9]+$" \ + | sort -t. -k1,1n -k2,2n -k3,3n \ + | tail -1) + + if [ -n "$LATEST" ]; then + echo "v${MAJOR}: latest tag is ${LATEST}" + echo "\"v${MAJOR}\": \"${LATEST}\"" >> "${VERSIONS_FILE}" + else + echo "WARNING: No stable release found for major version ${MAJOR}" >&2 + fi +done + +echo "" +echo "Updated ${VERSIONS_FILE}:" +cat "${VERSIONS_FILE}" diff --git a/packages/apps/opensearch/logos/opensearch.svg b/packages/apps/opensearch/logos/opensearch.svg new file mode 100644 index 00000000..345fdd0a --- /dev/null +++ b/packages/apps/opensearch/logos/opensearch.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/packages/apps/opensearch/templates/.gitkeep b/packages/apps/opensearch/templates/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/packages/apps/opensearch/templates/_versions.tpl b/packages/apps/opensearch/templates/_versions.tpl new file mode 100644 index 00000000..4eae4163 --- /dev/null +++ b/packages/apps/opensearch/templates/_versions.tpl @@ -0,0 +1,13 @@ +{{/* +Version mapping helper +Loads version mapping from files/versions.yaml and returns the full version for a given major version +*/}} +{{- define "opensearch.versionMap" -}} +{{- $versions := .Files.Get "files/versions.yaml" | fromYaml -}} +{{- $version := .Values.version | default "v2" -}} +{{- if hasKey $versions $version -}} +{{- index $versions $version -}} +{{- else -}} +{{- fail (printf "Invalid version '%s'. Available versions: %s" $version (keys $versions | join ", ")) -}} +{{- end -}} +{{- end -}} diff --git a/packages/apps/opensearch/templates/dashboard-resourcemap.yaml b/packages/apps/opensearch/templates/dashboard-resourcemap.yaml new file mode 100644 index 00000000..91dbf8cb --- /dev/null +++ b/packages/apps/opensearch/templates/dashboard-resourcemap.yaml @@ -0,0 +1,42 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ .Release.Name }}-dashboard-resources +rules: +- apiGroups: + - "" + resources: + - services + resourceNames: + - {{ .Release.Name }} + - {{ .Release.Name }}-external + {{- if .Values.dashboards.enabled }} + - {{ .Release.Name }}-dashboards + - {{ .Release.Name }}-dashboards-external + {{- end }} + verbs: ["get", "list", "watch"] +- apiGroups: + - "" + resources: + - secrets + resourceNames: + - {{ .Release.Name }}-credentials + verbs: ["get", "list", "watch"] +- apiGroups: + - cozystack.io + resources: + - workloadmonitors + resourceNames: + - {{ .Release.Name }} + verbs: ["get", "list", "watch"] +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ .Release.Name }}-dashboard-resources +subjects: +{{ include "cozy-lib.rbac.subjectsForTenantAndAccessLevel" (list "use" .Release.Namespace) }} +roleRef: + kind: Role + name: {{ .Release.Name }}-dashboard-resources + apiGroup: rbac.authorization.k8s.io diff --git a/packages/apps/opensearch/templates/external-svc.yaml b/packages/apps/opensearch/templates/external-svc.yaml new file mode 100644 index 00000000..f28626ab --- /dev/null +++ b/packages/apps/opensearch/templates/external-svc.yaml @@ -0,0 +1,39 @@ +{{- if .Values.external }} +{{- $clusterDomain := (index .Values._cluster "cluster-domain") | default "cozy.local" }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }}-external + annotations: + external-dns.alpha.kubernetes.io/hostname: {{ .Release.Name }}.{{ .Release.Namespace }}.{{ $clusterDomain }} +spec: + type: LoadBalancer + selector: + opster.io/opensearch-cluster: {{ .Release.Name }} + opster.io/opensearch-nodepool: nodes + ports: + - name: https + port: 9200 + targetPort: 9200 + protocol: TCP +{{- if .Values.dashboards.enabled }} +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ .Release.Name }}-dashboards-external + annotations: + external-dns.alpha.kubernetes.io/hostname: {{ .Release.Name }}-dashboards.{{ .Release.Namespace }}.{{ $clusterDomain }} +spec: + type: LoadBalancer + selector: + opster.io/opensearch-cluster: {{ .Release.Name }} + app.kubernetes.io/component: dashboards + ports: + - name: https + port: 5601 + targetPort: 5601 + protocol: TCP +{{- end }} +{{- end }} diff --git a/packages/apps/opensearch/templates/opensearch.yaml b/packages/apps/opensearch/templates/opensearch.yaml new file mode 100644 index 00000000..9917cf5f --- /dev/null +++ b/packages/apps/opensearch/templates/opensearch.yaml @@ -0,0 +1,99 @@ +{{- $topologyMode := .Values.topologySpreadPolicy | default "soft" }} +{{- $whenUnsatisfiable := "ScheduleAnyway" }} +{{- if eq $topologyMode "hard" }} +{{- $whenUnsatisfiable = "DoNotSchedule" }} +{{- end }} +--- +apiVersion: opensearch.opster.io/v1 +kind: OpenSearchCluster +metadata: + name: {{ .Release.Name }} +spec: + general: + serviceName: {{ .Release.Name }} + version: {{ include "opensearch.versionMap" $ }} + httpPort: 9200 + drainDataNodes: true + {{- if gt (len .Values.images.opensearch) 0 }} + image: {{ .Values.images.opensearch }} + {{- end }} + bootstrap: + resources: {{- include "cozy-lib.resources.defaultingSanitize" (list .Values.resourcesPreset .Values.resources $) | nindent 6 }} + security: + tls: + transport: + generate: true + perNode: true + http: + generate: true + config: + securityConfigSecret: + name: {{ .Release.Name }}-security-config + adminCredentialsSecret: + name: {{ .Release.Name }}-admin-credentials + {{- if .Values.dashboards.enabled }} + dashboards: + enable: true + version: {{ include "opensearch.versionMap" $ }} + replicas: {{ .Values.dashboards.replicas | default 1 }} + tls: + enable: true + generate: true + resources: {{- include "cozy-lib.resources.defaultingSanitize" (list (.Values.dashboards.resourcesPreset | default "medium") .Values.dashboards.resources $) | nindent 6 }} + {{- end }} + nodePools: + - component: nodes + replicas: {{ .Values.replicas }} + diskSize: {{ .Values.size }} + persistence: + pvc: + accessModes: + - ReadWriteOnce + {{- if .Values.storageClass }} + storageClass: {{ .Values.storageClass }} + {{- else if eq (int .Values.replicas) 1 }} + storageClass: replicated + {{- else }} + storageClass: local + {{- end }} + resources: {{- include "cozy-lib.resources.defaultingSanitize" (list .Values.resourcesPreset .Values.resources $) | nindent 8 }} + roles: + {{- if .Values.nodeRoles.master }} + - cluster_manager + {{- end }} + {{- if .Values.nodeRoles.data }} + - data + {{- end }} + {{- if .Values.nodeRoles.ingest }} + - ingest + {{- end }} + {{- if .Values.nodeRoles.ml }} + - ml + {{- end }} + topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: {{ $whenUnsatisfiable }} + labelSelector: + matchLabels: + opster.io/opensearch-cluster: {{ .Release.Name }} + - maxSkew: 1 + topologyKey: topology.kubernetes.io/zone + whenUnsatisfiable: {{ $whenUnsatisfiable }} + labelSelector: + matchLabels: + opster.io/opensearch-cluster: {{ .Release.Name }} +--- +# WorkloadMonitor tracks OpenSearch pods +apiVersion: cozystack.io/v1alpha1 +kind: WorkloadMonitor +metadata: + name: {{ .Release.Name }} +spec: + replicas: {{ .Values.replicas }} + minReplicas: 1 + kind: opensearch + type: opensearch + selector: + opster.io/opensearch-cluster: {{ .Release.Name }} + version: {{ .Chart.Version }} diff --git a/packages/apps/opensearch/templates/security.yaml b/packages/apps/opensearch/templates/security.yaml new file mode 100644 index 00000000..d10d353a --- /dev/null +++ b/packages/apps/opensearch/templates/security.yaml @@ -0,0 +1,120 @@ +{{- $clusterDomain := (index .Values._cluster "cluster-domain") | default "cozy.local" }} +{{- $existingAdminCreds := lookup "v1" "Secret" .Release.Namespace (printf "%s-admin-credentials" .Release.Name) }} +{{- $existingSecurityConfig := lookup "v1" "Secret" .Release.Namespace (printf "%s-security-config" .Release.Name) }} +{{- $password := randAlphaNum 32 }} +{{- if and $existingAdminCreds (hasKey $existingAdminCreds.data "password") }} +{{- $password = index $existingAdminCreds.data "password" | b64dec }} +{{- end }} +--- +# Admin credentials for the OpenSearch operator (referenced by adminCredentialsSecret) +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Release.Name }}-admin-credentials + labels: + app.kubernetes.io/name: opensearch + app.kubernetes.io/instance: {{ .Release.Name }} +type: Opaque +stringData: + username: admin + password: {{ $password | quote }} +--- +# Security plugin configuration (referenced by securityConfigSecret) +# On upgrades, the existing secret is preserved to avoid bcrypt hash regeneration +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Release.Name }}-security-config + labels: + app.kubernetes.io/name: opensearch + app.kubernetes.io/instance: {{ .Release.Name }} +type: Opaque +{{- if and $existingSecurityConfig (hasKey $existingSecurityConfig.data "internal_users.yml") }} +data: + {{- range $key, $val := $existingSecurityConfig.data }} + {{ $key }}: {{ $val }} + {{- end }} +{{- else }} +stringData: + config.yml: | + --- + _meta: + type: "config" + config_version: 2 + config: + dynamic: + http: + anonymous_auth_enabled: false + authc: + basic_internal_auth_domain: + description: "Authenticate via HTTP Basic against internal users database" + http_enabled: true + transport_enabled: true + order: 0 + http_authenticator: + type: basic + challenge: true + authentication_backend: + type: internal + + internal_users.yml: | + --- + _meta: + type: "internalusers" + config_version: 2 + admin: + hash: {{ htpasswd "admin" $password | trimPrefix "admin:" | quote }} + reserved: true + backend_roles: + - "admin" + description: "Admin user" + + roles.yml: | + --- + _meta: + type: "roles" + config_version: 2 + + roles_mapping.yml: | + --- + _meta: + type: "rolesmapping" + config_version: 2 + all_access: + reserved: false + backend_roles: + - "admin" + + action_groups.yml: | + --- + _meta: + type: "actiongroups" + config_version: 2 + + tenants.yml: | + --- + _meta: + type: "tenants" + config_version: 2 +{{- end }} +--- +# User-facing credentials with connection URI +apiVersion: v1 +kind: Secret +metadata: + name: {{ .Release.Name }}-credentials + labels: + app.kubernetes.io/name: opensearch + app.kubernetes.io/instance: {{ .Release.Name }} +type: Opaque +stringData: + username: admin + password: {{ $password | quote }} + host: {{ .Release.Name }}.{{ .Release.Namespace }}.svc.{{ $clusterDomain }} + port: "9200" + uri: https://admin:{{ $password | urlquery }}@{{ .Release.Name }}.{{ .Release.Namespace }}.svc.{{ $clusterDomain }}:9200 + {{- if .Values.dashboards.enabled }} + dashboards-host: {{ .Release.Name }}-dashboards.{{ .Release.Namespace }}.svc.{{ $clusterDomain }} + dashboards-port: "5601" + dashboards-uri: https://admin:{{ $password | urlquery }}@{{ .Release.Name }}-dashboards.{{ .Release.Namespace }}.svc.{{ $clusterDomain }}:5601 + {{- end }} diff --git a/packages/apps/opensearch/templates/users.yaml b/packages/apps/opensearch/templates/users.yaml new file mode 100644 index 00000000..1d326206 --- /dev/null +++ b/packages/apps/opensearch/templates/users.yaml @@ -0,0 +1,21 @@ +{{- range $username, $user := .Values.users }} +{{- $existingSecret := lookup "v1" "Secret" $.Release.Namespace (printf "%s-user-%s" $.Release.Name $username) }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ $.Release.Name }}-user-{{ $username }} + labels: + opensearch.opster.io/credentials: "true" +type: kubernetes.io/basic-auth +stringData: + username: {{ $username | quote }} + {{- if $user.password }} + password: {{ $user.password | quote }} + {{- else if and $existingSecret (hasKey $existingSecret.data "password") }} + password: {{ index $existingSecret.data "password" | b64dec | quote }} + {{- else }} + password: {{ randAlphaNum 16 | quote }} + {{- end }} + roles: {{ $user.roles | default (list "readall") | join "," | quote }} +{{- end }} diff --git a/packages/apps/opensearch/tests/opensearch_test.yaml b/packages/apps/opensearch/tests/opensearch_test.yaml new file mode 100644 index 00000000..78850134 --- /dev/null +++ b/packages/apps/opensearch/tests/opensearch_test.yaml @@ -0,0 +1,516 @@ +suite: opensearch CR tests + +templates: + - templates/opensearch.yaml + +tests: + ################### + # Basic rendering # + ################### + + - it: renders OpenSearchCluster and WorkloadMonitor + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - hasDocuments: + count: 2 + - isKind: + of: OpenSearchCluster + documentIndex: 0 + - isKind: + of: WorkloadMonitor + documentIndex: 1 + + - it: sets correct CR name + release: + name: my-opensearch + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - equal: + path: metadata.name + value: my-opensearch + documentIndex: 0 + + ################## + # Version # + ################## + + - it: defaults to version 2.11.1 + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - equal: + path: spec.general.version + value: "2.11.1" + documentIndex: 0 + + - it: sets version 3.0.0 when v3 selected + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + version: v3 + asserts: + - equal: + path: spec.general.version + value: "3.0.0" + documentIndex: 0 + + - it: sets version 1.3.20 when v1 selected + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + version: v1 + asserts: + - equal: + path: spec.general.version + value: "1.3.20" + documentIndex: 0 + + ##################### + # General # + ##################### + + - it: sets drainDataNodes to true + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - equal: + path: spec.general.drainDataNodes + value: true + documentIndex: 0 + + - it: sets httpPort to 9200 + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - equal: + path: spec.general.httpPort + value: 9200 + documentIndex: 0 + + ##################### + # Security # + ##################### + + - it: always includes security section with TLS + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - equal: + path: spec.security.tls.transport.generate + value: true + documentIndex: 0 + - equal: + path: spec.security.tls.transport.perNode + value: true + documentIndex: 0 + - equal: + path: spec.security.tls.http.generate + value: true + documentIndex: 0 + + - it: references security config secret + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - equal: + path: spec.security.config.securityConfigSecret.name + value: test-os-security-config + documentIndex: 0 + + - it: references admin credentials secret + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - equal: + path: spec.security.config.adminCredentialsSecret.name + value: test-os-admin-credentials + documentIndex: 0 + + - it: does not disable security plugin + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - notExists: + path: spec.general.additionalConfig + documentIndex: 0 + + ##################### + # Node Pools # + ##################### + + - it: sets default replica count to 3 + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - equal: + path: spec.nodePools[0].replicas + value: 3 + documentIndex: 0 + + - it: sets replica count from values + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + replicas: 5 + asserts: + - equal: + path: spec.nodePools[0].replicas + value: 5 + documentIndex: 0 + + ##################### + # Node Roles # + ##################### + + - it: enables default node roles + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - contains: + path: spec.nodePools[0].roles + content: cluster_manager + documentIndex: 0 + - contains: + path: spec.nodePools[0].roles + content: data + documentIndex: 0 + - contains: + path: spec.nodePools[0].roles + content: ingest + documentIndex: 0 + + - it: disables master role when configured + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + nodeRoles: + master: false + data: true + ingest: true + ml: false + asserts: + - notContains: + path: spec.nodePools[0].roles + content: cluster_manager + documentIndex: 0 + + - it: enables ml role when configured + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + nodeRoles: + master: true + data: true + ingest: true + ml: true + asserts: + - contains: + path: spec.nodePools[0].roles + content: ml + documentIndex: 0 + + ########################### + # Topology Spread Policy # + ########################### + + - it: uses soft topology spread by default (ScheduleAnyway) + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - equal: + path: spec.nodePools[0].topologySpreadConstraints[0].whenUnsatisfiable + value: ScheduleAnyway + documentIndex: 0 + - equal: + path: spec.nodePools[0].topologySpreadConstraints[1].whenUnsatisfiable + value: ScheduleAnyway + documentIndex: 0 + + - it: uses hard topology spread when configured (DoNotSchedule) + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + topologySpreadPolicy: hard + asserts: + - equal: + path: spec.nodePools[0].topologySpreadConstraints[0].whenUnsatisfiable + value: DoNotSchedule + documentIndex: 0 + - equal: + path: spec.nodePools[0].topologySpreadConstraints[1].whenUnsatisfiable + value: DoNotSchedule + documentIndex: 0 + + - it: sets correct topology keys + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - equal: + path: spec.nodePools[0].topologySpreadConstraints[0].topologyKey + value: kubernetes.io/hostname + documentIndex: 0 + - equal: + path: spec.nodePools[0].topologySpreadConstraints[1].topologyKey + value: topology.kubernetes.io/zone + documentIndex: 0 + + ##################### + # Storage # + ##################### + + - it: sets accessModes to ReadWriteOnce + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - contains: + path: spec.nodePools[0].persistence.pvc.accessModes + content: ReadWriteOnce + documentIndex: 0 + + - it: sets storage size from values + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + size: 50Gi + asserts: + - equal: + path: spec.nodePools[0].diskSize + value: 50Gi + documentIndex: 0 + + - it: uses local storageClass when replicas > 1 and no storageClass + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + storageClass: "" + replicas: 3 + asserts: + - equal: + path: spec.nodePools[0].persistence.pvc.storageClass + value: local + documentIndex: 0 + + - it: uses replicated storageClass when single replica and no storageClass + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + storageClass: "" + replicas: 1 + asserts: + - equal: + path: spec.nodePools[0].persistence.pvc.storageClass + value: replicated + documentIndex: 0 + + - it: uses custom storageClass when provided + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + storageClass: fast-ssd + asserts: + - equal: + path: spec.nodePools[0].persistence.pvc.storageClass + value: fast-ssd + documentIndex: 0 + + ##################### + # Dashboards # + ##################### + + - it: does not render dashboards when disabled + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + dashboards: + enabled: false + replicas: 1 + resourcesPreset: "medium" + resources: {} + asserts: + - notExists: + path: spec.dashboards + documentIndex: 0 + + - it: renders dashboards when enabled + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + dashboards: + enabled: true + replicas: 1 + resourcesPreset: "medium" + resources: {} + asserts: + - equal: + path: spec.dashboards.enable + value: true + documentIndex: 0 + - equal: + path: spec.dashboards.replicas + value: 1 + documentIndex: 0 + - equal: + path: spec.dashboards.tls.enable + value: true + documentIndex: 0 + - equal: + path: spec.dashboards.tls.generate + value: true + documentIndex: 0 + + ########################### + # WorkloadMonitor # + ########################### + + - it: creates WorkloadMonitor with correct metadata + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - equal: + path: metadata.name + value: test-os + documentIndex: 1 + - equal: + path: spec.kind + value: opensearch + documentIndex: 1 + - equal: + path: spec.type + value: opensearch + documentIndex: 1 + + - it: sets replicas in WorkloadMonitor + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + replicas: 5 + asserts: + - equal: + path: spec.replicas + value: 5 + documentIndex: 1 + + - it: sets minReplicas to 1 + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - equal: + path: spec.minReplicas + value: 1 + documentIndex: 1 + + - it: sets correct selector labels + release: + name: mydb + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - equal: + path: spec.selector["opster.io/opensearch-cluster"] + value: mydb + documentIndex: 1 diff --git a/packages/apps/opensearch/tests/security_test.yaml b/packages/apps/opensearch/tests/security_test.yaml new file mode 100644 index 00000000..cd8740d9 --- /dev/null +++ b/packages/apps/opensearch/tests/security_test.yaml @@ -0,0 +1,207 @@ +suite: security secrets tests + +templates: + - templates/security.yaml + +tests: + ################### + # Basic rendering # + ################### + + - it: renders three secrets (admin-credentials, security-config, credentials) + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - hasDocuments: + count: 3 + + ########################### + # Admin credentials # + ########################### + + - it: sets admin-credentials secret name + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - equal: + path: metadata.name + value: test-os-admin-credentials + documentIndex: 0 + + - it: sets admin username + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - equal: + path: stringData.username + value: admin + documentIndex: 0 + + - it: generates a 32-char alphanumeric password + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - matchRegex: + path: stringData.password + pattern: "^[a-zA-Z0-9]{32}$" + documentIndex: 0 + + ########################### + # Security config # + ########################### + + - it: sets security-config secret name + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - equal: + path: metadata.name + value: test-os-security-config + documentIndex: 1 + + - it: includes config.yml with basic auth + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - exists: + path: stringData["config.yml"] + documentIndex: 1 + + - it: includes internal_users.yml with bcrypt hash + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - matchRegex: + path: stringData["internal_users.yml"] + pattern: "hash:.*\\$2a\\$" + documentIndex: 1 + + - it: includes roles_mapping.yml + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - exists: + path: stringData["roles_mapping.yml"] + documentIndex: 1 + + ########################### + # User-facing credentials # + ########################### + + - it: sets credentials secret name + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - equal: + path: metadata.name + value: test-os-credentials + documentIndex: 2 + + - it: sets correct host and port + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - equal: + path: stringData.host + value: test-os.tenant-test.svc.cozy.local + documentIndex: 2 + - equal: + path: stringData.port + value: "9200" + documentIndex: 2 + + - it: sets https URI with credentials + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + asserts: + - matchRegex: + path: stringData.uri + pattern: "^https://admin:[a-zA-Z0-9]{32}@test-os\\.tenant-test\\.svc\\.cozy\\.local:9200$" + documentIndex: 2 + + - it: does not include dashboards fields when dashboards disabled + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + dashboards: + enabled: false + replicas: 1 + resourcesPreset: "medium" + resources: {} + asserts: + - notExists: + path: stringData.dashboards-host + documentIndex: 2 + + - it: includes dashboards fields when dashboards enabled + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + dashboards: + enabled: true + replicas: 1 + resourcesPreset: "medium" + resources: {} + asserts: + - equal: + path: stringData.dashboards-host + value: test-os-dashboards.tenant-test.svc.cozy.local + documentIndex: 2 + - equal: + path: stringData.dashboards-port + value: "5601" + documentIndex: 2 + - matchRegex: + path: stringData.dashboards-uri + pattern: "^https://admin:[a-zA-Z0-9]{32}@test-os-dashboards\\.tenant-test\\.svc\\.cozy\\.local:5601$" + documentIndex: 2 diff --git a/packages/apps/opensearch/tests/users_test.yaml b/packages/apps/opensearch/tests/users_test.yaml new file mode 100644 index 00000000..f6b4990e --- /dev/null +++ b/packages/apps/opensearch/tests/users_test.yaml @@ -0,0 +1,176 @@ +suite: users secrets tests + +templates: + - templates/users.yaml + +tests: + ################### + # Basic rendering # + ################### + + - it: does not render secrets when no users defined + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + users: {} + asserts: + - hasDocuments: + count: 0 + + - it: renders one secret per user + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + users: + user1: + roles: + - readall + user2: + roles: + - all_access + asserts: + - hasDocuments: + count: 2 + + ##################### + # Secret metadata # + ##################### + + - it: sets correct secret name + release: + name: my-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + users: + myuser: + roles: + - readall + asserts: + - equal: + path: metadata.name + value: my-os-user-myuser + + - it: sets opensearch credentials label + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + users: + myuser: + roles: + - readall + asserts: + - equal: + path: metadata.labels["opensearch.opster.io/credentials"] + value: "true" + + - it: uses basic-auth secret type + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + users: + myuser: + roles: + - readall + asserts: + - equal: + path: type + value: kubernetes.io/basic-auth + + ##################### + # Secret data # + ##################### + + - it: sets username in secret + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + users: + testuser: + roles: + - readall + asserts: + - equal: + path: stringData.username + value: testuser + + - it: uses provided password + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + users: + myuser: + password: mysecretpassword + roles: + - readall + asserts: + - equal: + path: stringData.password + value: mysecretpassword + + - it: generates password when not provided + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + users: + myuser: + roles: + - readall + asserts: + - matchRegex: + path: stringData.password + pattern: "^[a-zA-Z0-9]{16}$" + + - it: sets roles as comma-separated string + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + users: + myuser: + roles: + - readall + - reporting_user + - monitoring_user + asserts: + - equal: + path: stringData.roles + value: "readall,reporting_user,monitoring_user" + + - it: defaults to readall role when no roles specified + release: + name: test-os + namespace: tenant-test + set: + _cluster: + cluster-domain: cozy.local + users: + myuser: {} + asserts: + - equal: + path: stringData.roles + value: "readall" diff --git a/packages/apps/opensearch/values.schema.json b/packages/apps/opensearch/values.schema.json new file mode 100644 index 00000000..54a6bb00 --- /dev/null +++ b/packages/apps/opensearch/values.schema.json @@ -0,0 +1,239 @@ +{ + "title": "Chart Values", + "type": "object", + "properties": { + "dashboards": { + "description": "OpenSearch Dashboards configuration.", + "type": "object", + "default": {}, + "required": [ + "enabled", + "replicas", + "resourcesPreset" + ], + "properties": { + "enabled": { + "description": "Enable OpenSearch Dashboards deployment.", + "type": "boolean", + "default": false + }, + "replicas": { + "description": "Number of Dashboards replicas.", + "type": "integer", + "default": 1 + }, + "resources": { + "description": "Explicit CPU and memory configuration for Dashboards.", + "type": "object", + "default": {}, + "properties": { + "cpu": { + "description": "CPU available to each node.", + "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 node.", + "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 for Dashboards.", + "type": "string", + "default": "medium", + "enum": [ + "nano", + "micro", + "small", + "medium", + "large", + "xlarge", + "2xlarge" + ] + } + } + }, + "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": [ + "opensearch" + ], + "properties": { + "opensearch": { + "description": "OpenSearch image.", + "type": "string", + "default": "" + } + } + }, + "nodeRoles": { + "description": "Node roles configuration.", + "type": "object", + "default": {}, + "required": [ + "data", + "ingest", + "master", + "ml" + ], + "properties": { + "data": { + "description": "Enable data role.", + "type": "boolean", + "default": true + }, + "ingest": { + "description": "Enable ingest role.", + "type": "boolean", + "default": true + }, + "master": { + "description": "Enable cluster_manager role.", + "type": "boolean", + "default": true + }, + "ml": { + "description": "Enable machine learning role.", + "type": "boolean", + "default": false + } + } + }, + "replicas": { + "description": "Number of OpenSearch nodes in the cluster.", + "type": "integer", + "default": 3 + }, + "resources": { + "description": "Explicit CPU and memory configuration for each OpenSearch node. When omitted, the preset defined in `resourcesPreset` is applied.", + "type": "object", + "default": {}, + "properties": { + "cpu": { + "description": "CPU available to each node.", + "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 node.", + "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. OpenSearch requires minimum 2Gi memory.", + "type": "string", + "default": "large", + "enum": [ + "nano", + "micro", + "small", + "medium", + "large", + "xlarge", + "2xlarge" + ] + }, + "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": "" + }, + "topologySpreadPolicy": { + "description": "How strictly to enforce pod distribution across nodes and zones.", + "type": "string", + "default": "soft", + "enum": [ + "soft", + "hard" + ] + }, + "users": { + "description": "Custom OpenSearch users configuration map.", + "type": "object", + "default": {}, + "additionalProperties": { + "type": "object", + "properties": { + "password": { + "description": "Password for the user (auto-generated if omitted).", + "type": "string" + }, + "roles": { + "description": "List of OpenSearch roles.", + "type": "array", + "items": { + "type": "string" + } + } + } + } + }, + "version": { + "description": "OpenSearch major version to deploy.", + "type": "string", + "default": "v2", + "enum": [ + "v3", + "v2", + "v1" + ] + } + } +} \ No newline at end of file diff --git a/packages/apps/opensearch/values.yaml b/packages/apps/opensearch/values.yaml new file mode 100644 index 00000000..8de805e3 --- /dev/null +++ b/packages/apps/opensearch/values.yaml @@ -0,0 +1,111 @@ +## +## @section Common parameters +## + +## @typedef {struct} Resources - Explicit CPU and memory configuration for each OpenSearch node. +## @field {quantity} [cpu] - CPU available to each node. +## @field {quantity} [memory] - Memory (RAM) available to each node. + +## @enum {string} ResourcesPreset - Default sizing preset. +## @value nano +## @value micro +## @value small +## @value medium +## @value large +## @value xlarge +## @value 2xlarge + +## @param {int} replicas - Number of OpenSearch nodes in the cluster. +replicas: 3 + +## @param {Resources} [resources] - Explicit CPU and memory configuration for each OpenSearch node. When omitted, the preset defined in `resourcesPreset` is applied. +resources: {} + +## @param {ResourcesPreset} resourcesPreset="large" - Default sizing preset used when `resources` is omitted. OpenSearch requires minimum 2Gi memory. +resourcesPreset: "large" + +## @param {quantity} size - Persistent Volume Claim size available for application data. +size: 10Gi + +## @param {string} storageClass - StorageClass used to store the data. +storageClass: "" + +## @param {bool} external - Enable external access from outside the cluster. +external: false + +## @enum {string} TopologySpreadPolicy - Pod distribution policy across nodes/zones. +## @value soft - Best-effort distribution (ScheduleAnyway) - pods may be scheduled on same node if needed +## @value hard - Strict distribution (DoNotSchedule) - pods will not schedule if spread cannot be achieved + +## @param {TopologySpreadPolicy} topologySpreadPolicy="soft" - How strictly to enforce pod distribution across nodes and zones. +topologySpreadPolicy: "soft" + +## +## @enum {string} Version +## @value v3 +## @value v2 +## @value v1 + +## @param {Version} version - OpenSearch major version to deploy. +version: v2 + +## +## @section Image configuration +## + +## @typedef {struct} Images - Container image configuration. +## @field {string} opensearch - OpenSearch image. + +## @param {Images} images - Container images used by the operator. +images: + opensearch: "" + +## +## @section Node roles configuration +## + +## @typedef {struct} NodeRoles - OpenSearch node roles. +## @field {bool} master - Enable cluster_manager role. +## @field {bool} data - Enable data role. +## @field {bool} ingest - Enable ingest role. +## @field {bool} ml - Enable machine learning role. + +## @param {NodeRoles} nodeRoles - Node roles configuration. +nodeRoles: + master: true + data: true + ingest: true + ml: false + +## +## @section Users configuration +## + +## @typedef {struct} User - User configuration. +## @field {string} [password] - Password for the user (auto-generated if omitted). +## @field {[]string} roles - List of OpenSearch roles. + +## @param {map[string]User} users - Custom OpenSearch users configuration map. +users: {} +## Example: +## users: +## myuser: +## roles: +## - all_access + +## +## @section OpenSearch Dashboards configuration +## + +## @typedef {struct} Dashboards - OpenSearch Dashboards deployment configuration. +## @field {bool} enabled - Enable OpenSearch Dashboards deployment. +## @field {int} replicas - Number of Dashboards replicas. +## @field {Resources} [resources] - Explicit CPU and memory configuration for Dashboards. +## @field {ResourcesPreset} resourcesPreset - Default sizing preset for Dashboards. + +## @param {Dashboards} dashboards - OpenSearch Dashboards configuration. +dashboards: + enabled: false + replicas: 1 + resources: {} + resourcesPreset: "medium" diff --git a/packages/system/opensearch-operator/.helmignore b/packages/system/opensearch-operator/.helmignore new file mode 100644 index 00000000..0e8a0eb3 --- /dev/null +++ b/packages/system/opensearch-operator/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/packages/system/opensearch-operator/Chart.yaml b/packages/system/opensearch-operator/Chart.yaml new file mode 100644 index 00000000..a991bdca --- /dev/null +++ b/packages/system/opensearch-operator/Chart.yaml @@ -0,0 +1,3 @@ +apiVersion: v2 +name: cozy-opensearch-operator +version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process diff --git a/packages/system/opensearch-operator/Makefile b/packages/system/opensearch-operator/Makefile new file mode 100644 index 00000000..71b94549 --- /dev/null +++ b/packages/system/opensearch-operator/Makefile @@ -0,0 +1,10 @@ +export NAME=opensearch-operator +export NAMESPACE=cozy-$(NAME) + +include ../../../hack/package.mk + +update: + rm -rf charts + helm repo add opensearch-operator https://opensearch-project.github.io/opensearch-k8s-operator/ + helm repo update opensearch-operator + helm pull opensearch-operator/opensearch-operator --untar --untardir charts diff --git a/packages/system/opensearch-operator/charts/opensearch-operator/CHANGELOG.md b/packages/system/opensearch-operator/charts/opensearch-operator/CHANGELOG.md new file mode 100644 index 00000000..1866ab55 --- /dev/null +++ b/packages/system/opensearch-operator/charts/opensearch-operator/CHANGELOG.md @@ -0,0 +1,61 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +--- +## [Unreleased] +### Added +- Added support for custom image used by `kubeRbacProxy`. +### Changed +### Deprecated +### Removed +### Fixed +### Security + +--- +## [2.0.0] +### Added +### Changed +- Modified `version` to `2.0.0` and `appVersion` to `v2.0`. +- Allow chart image tag to pick from `appVersion`, unless explicitly passed `tag` values in `values.yaml` file. +### Deprecated +### Removed +### Fixed +### Security + +--- +## [1.0.3] +### Added +### Changed +- Added missing spec `dashboards.additionalConfig` +### Deprecated +### Removed +### Fixed +### Security + +--- +## [1.0.2] +### Added +### Changed +- Added README.md file to charts/ folder. +### Deprecated +### Removed +### Fixed +### Security + +--- +## [1.0.1] +### Added +### Changed +- Updated version to 1.0.1 +### Deprecated +### Removed +### Fixed +### Security + +[Unreleased]: https://github.com/opensearch-project/opensearch-k8s-operator/compare/opensearch-operator-2.0.0...HEAD +[2.0.0]: https://github.com/opensearch-project/opensearch-k8s-operator/compare/opensearch-operator-1.0.3...opensearch-operator-2.0.0 +[1.0.3]: https://github.com/opensearch-project/opensearch-k8s-operator/compare/opensearch-operator-1.0.2...opensearch-operator-1.0.3 +[1.0.2]: https://github.com/opensearch-project/opensearch-k8s-operator/compare/opensearch-operator-1.0.1...opensearch-operator-1.0.2 diff --git a/packages/system/opensearch-operator/charts/opensearch-operator/Chart.yaml b/packages/system/opensearch-operator/charts/opensearch-operator/Chart.yaml new file mode 100644 index 00000000..331cf1e2 --- /dev/null +++ b/packages/system/opensearch-operator/charts/opensearch-operator/Chart.yaml @@ -0,0 +1,6 @@ +apiVersion: v2 +appVersion: 2.8.0 +description: The OpenSearch Operator Helm chart for Kubernetes +name: opensearch-operator +type: application +version: 2.8.0 diff --git a/packages/system/opensearch-operator/charts/opensearch-operator/README.md b/packages/system/opensearch-operator/charts/opensearch-operator/README.md new file mode 100644 index 00000000..dc63c250 --- /dev/null +++ b/packages/system/opensearch-operator/charts/opensearch-operator/README.md @@ -0,0 +1,29 @@ +# OpenSearch-k8s-operator + +The Kubernetes [OpenSearch Operator](https://github.com/opensearch-project/opensearch-k8s-operator) is used for automating the deployment, provisioning, management, and orchestration of OpenSearch clusters and OpenSearch dashboards. + +## Getting started + +The Operator can be easily installed using helm on any CNCF-certified Kubernetes cluster. Please refer to the [User Guide](https://github.com/opensearch-project/opensearch-k8s-operator/blob/main/docs/userguide/main.md) for more information. + +### Installation Using Helm + +#### Get Repo Info +``` +helm repo add opensearch-operator https://opensearch-project.github.io/opensearch-k8s-operator/ +helm repo update +``` +#### Install Chart +``` +helm install [RELEASE_NAME] opensearch-operator/opensearch-operator +``` +#### Uninstall Chart +``` +helm uninstall [RELEASE_NAME] +``` +#### Upgrade Chart +``` +helm repo update +helm upgrade [RELEASE_NAME] opensearch-operator/opensearch-operator +``` + diff --git a/packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchactiongroups.yaml b/packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchactiongroups.yaml new file mode 100644 index 00000000..8ba4c7b2 --- /dev/null +++ b/packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchactiongroups.yaml @@ -0,0 +1,94 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.0 + name: opensearchactiongroups.opensearch.opster.io +spec: + group: opensearch.opster.io + names: + kind: OpensearchActionGroup + listKind: OpensearchActionGroupList + plural: opensearchactiongroups + shortNames: + - opensearchactiongroup + singular: opensearchactiongroup + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + description: OpensearchActionGroup is the Schema for the opensearchactiongroups + 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: OpensearchActionGroupSpec defines the desired state of OpensearchActionGroup + properties: + allowedActions: + items: + type: string + type: array + description: + type: string + opensearchCluster: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + type: + type: string + required: + - allowedActions + - opensearchCluster + type: object + status: + description: OpensearchActionGroupStatus defines the observed state of + OpensearchActionGroup + properties: + existingActionGroup: + type: boolean + managedCluster: + description: |- + UID is a type that holds unique ID values, including UUIDs. Because we + don't ONLY use UUIDs, this is an alias to string. Being a type captures + intent and helps make sure that UIDs and names do not get conflated. + type: string + reason: + type: string + state: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchclusters.yaml b/packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchclusters.yaml new file mode 100644 index 00000000..063bbd00 --- /dev/null +++ b/packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchclusters.yaml @@ -0,0 +1,6372 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.0 + name: opensearchclusters.opensearch.opster.io +spec: + group: opensearch.opster.io + names: + kind: OpenSearchCluster + listKind: OpenSearchClusterList + plural: opensearchclusters + shortNames: + - os + - opensearch + singular: opensearchcluster + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.health + name: health + type: string + - description: Available nodes + jsonPath: .status.availableNodes + name: nodes + type: integer + - description: Opensearch version + jsonPath: .status.version + name: version + type: string + - jsonPath: .status.phase + name: phase + type: string + - jsonPath: .metadata.creationTimestamp + name: age + type: date + name: v1 + schema: + openAPIV3Schema: + description: Es is the Schema for the es 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: ClusterSpec defines the desired state of OpenSearchCluster + properties: + bootstrap: + properties: + additionalConfig: + additionalProperties: + type: string + description: Extra items to add to the opensearch.yml, defaults + to General.AdditionalConfig + type: object + affinity: + description: Affinity is a group of affinity scheduling rules. + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for + the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. + items: + description: |- + An empty preferred scheduling term matches all objects with implicit weight 0 + (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated with + the corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + 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. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + 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 + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + 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. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + 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 + type: object + x-kubernetes-map-type: atomic + weight: + description: Weight associated with matching the + corresponding nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. + The terms are ORed. + items: + description: |- + A null or empty node selector term matches no objects. The requirements of + them are ANDed. + The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + 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. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + 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 + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + 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. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + 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 + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. + co-locate this pod in the same node, zone, etc. as some + other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + 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 + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + 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 + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + 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 + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + 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 + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules + (e.g. avoid putting this pod in the same node, zone, etc. + as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the anti-affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + 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 + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + 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 + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the anti-affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the anti-affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + 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 + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + 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 + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + jvm: + type: string + keystore: + items: + properties: + keyMappings: + additionalProperties: + type: string + description: Key mappings from secret to keystore keys + type: object + secret: + description: Secret containing key value pairs + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + type: object + type: array + nodeSelector: + additionalProperties: + type: string + type: object + pluginsList: + items: + type: string + type: array + resources: + description: ResourceRequirements describes the compute resource + requirements. + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + tolerations: + 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 + 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 + 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 + type: object + confMgmt: + description: ConfMgmt defines which additional services will be deployed + properties: + VerUpdate: + type: boolean + autoScaler: + type: boolean + smartScaler: + type: boolean + type: object + dashboards: + properties: + additionalConfig: + additionalProperties: + type: string + description: Additional properties for opensearch_dashboards.yaml + type: object + additionalVolumes: + items: + properties: + configMap: + description: ConfigMap to use to populate the volume + properties: + defaultMode: + description: |- + defaultMode is optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + ConfigMap will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the ConfigMap, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + items: + description: Maps a string key to a path within a + volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: optional specify whether the ConfigMap + or its keys must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + description: CSI object to use to populate the volume + properties: + driver: + description: |- + driver is the name of the CSI driver that handles this volume. + Consult with your admin for the correct name as registered in the cluster. + type: string + fsType: + description: |- + fsType to mount. Ex. "ext4", "xfs", "ntfs". + If not provided, the empty value is passed to the associated CSI driver + which will determine the default filesystem to apply. + type: string + nodePublishSecretRef: + description: |- + nodePublishSecretRef is a reference to the secret object containing + sensitive information to pass to the CSI driver to complete the CSI + NodePublishVolume and NodeUnpublishVolume calls. + This field is optional, and may be empty if no secret is required. If the + secret object contains more than one secret, all secret references are passed. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + description: |- + readOnly specifies a read-only configuration for the volume. + Defaults to false (read/write). + type: boolean + volumeAttributes: + additionalProperties: + type: string + description: |- + volumeAttributes stores driver-specific properties that are passed to the CSI + driver. Consult your driver's documentation for supported values. + type: object + required: + - driver + type: object + emptyDir: + description: EmptyDir to use to populate the volume + properties: + medium: + description: |- + medium represents what type of storage medium should back this directory. + The default is "" which means to use the node's default medium. + Must be an empty string (default) or Memory. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + description: |- + sizeLimit is the total amount of local storage required for this EmptyDir volume. + The size limit is also applicable for memory medium. + The maximum usage on memory medium EmptyDir would be the minimum value between + the SizeLimit specified here and the sum of memory limits of all containers in a pod. + The default is nil which means that the limit is undefined. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + name: + description: Name to use for the volume. Required. + type: string + path: + description: Path in the container to mount the volume at. + Required. + type: string + projected: + description: Projected object to use to populate the volume + properties: + defaultMode: + description: |- + defaultMode are the mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + sources: + description: |- + sources is the list of volume projections. Each entry in this list + handles one source. + items: + description: |- + Projection that may be projected along with other supported volume types. + Exactly one of these fields must be set. + properties: + clusterTrustBundle: + description: |- + ClusterTrustBundle allows a pod to access the `.spec.trustBundle` field + of ClusterTrustBundle objects in an auto-updating file. + + Alpha, gated by the ClusterTrustBundleProjection feature gate. + + ClusterTrustBundle objects can either be selected by name, or by the + combination of signer name and a label selector. + + Kubelet performs aggressive normalization of the PEM contents written + into the pod filesystem. Esoteric PEM features such as inter-block + comments and block headers are stripped. Certificates are deduplicated. + The ordering of certificates within the file is arbitrary, and Kubelet + may change the order over time. + properties: + labelSelector: + description: |- + Select all ClusterTrustBundles that match this label selector. Only has + effect if signerName is set. Mutually-exclusive with name. If unset, + interpreted as "match nothing". If set but empty, interpreted as "match + everything". + 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: |- + Select a single ClusterTrustBundle by object name. Mutually-exclusive + with signerName and labelSelector. + type: string + optional: + description: |- + If true, don't block pod startup if the referenced ClusterTrustBundle(s) + aren't available. If using name, then the named ClusterTrustBundle is + allowed not to exist. If using signerName, then the combination of + signerName and labelSelector is allowed to match zero + ClusterTrustBundles. + type: boolean + path: + description: Relative path from the volume + root to write the bundle. + type: string + signerName: + description: |- + Select all ClusterTrustBundles that match this signer name. + Mutually-exclusive with name. The contents of all selected + ClusterTrustBundles will be unified and deduplicated. + type: string + required: + - path + type: object + configMap: + description: configMap information about the configMap + data to project + properties: + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + ConfigMap will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the ConfigMap, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + items: + description: Maps a string key to a path + within a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: optional specify whether the + ConfigMap or its keys must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + description: downwardAPI information about the + downwardAPI data to project + properties: + items: + description: Items is a list of DownwardAPIVolume + file + items: + description: DownwardAPIVolumeFile represents + information to create the file containing + the pod field + properties: + fieldRef: + description: 'Required: Selects a field + of the pod: only annotations, labels, + name, namespace and uid are supported.' + properties: + apiVersion: + description: Version of the schema + the FieldPath is written in terms + of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to + select in the specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + description: |- + Optional: mode bits used to set permissions on this file, must be an octal value + between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: 'Required: Path is the + relative path name of the file to + be created. Must not be absolute or + contain the ''..'' path. Must be utf-8 + encoded. The first item of the relative + path must not start with ''..''' + type: string + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. + properties: + containerName: + description: 'Container name: required + for volumes, optional for env + vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output + format of the exposed resources, + defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource + to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + secret: + description: secret information about the secret + data to project + properties: + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + Secret will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + items: + description: Maps a string key to a path + within a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: optional field specify whether + the Secret or its key must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + description: serviceAccountToken is information + about the serviceAccountToken data to project + properties: + audience: + description: |- + audience is the intended audience of the token. A recipient of a token + must identify itself with an identifier specified in the audience of the + token, and otherwise should reject the token. The audience defaults to the + identifier of the apiserver. + type: string + expirationSeconds: + description: |- + expirationSeconds is the requested duration of validity of the service + account token. As the token approaches expiration, the kubelet volume + plugin will proactively rotate the service account token. The kubelet will + start trying to rotate the token if the token is older than 80 percent of + its time to live or if the token is older than 24 hours.Defaults to 1 hour + and must be at least 10 minutes. + format: int64 + type: integer + path: + description: |- + path is the path relative to the mount point of the file to project the + token into. + type: string + required: + - path + type: object + type: object + type: array + x-kubernetes-list-type: atomic + type: object + restartPods: + description: Whether to restart the pods on content change + type: boolean + secret: + description: Secret to use populate the volume + properties: + defaultMode: + description: |- + defaultMode is Optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values + for mode bits. Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + items: + description: |- + items If unspecified, each key-value pair in the Data field of the referenced + Secret will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + items: + description: Maps a string key to a path within a + volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + optional: + description: optional field specify whether the Secret + or its keys must be defined + type: boolean + secretName: + description: |- + secretName is the name of the secret in the pod's namespace to use. + More info: https://kubernetes.io/docs/concepts/storage/volumes#secret + type: string + type: object + subPath: + description: SubPath of the referenced volume to mount. + type: string + required: + - name + - path + type: object + type: array + affinity: + description: Affinity is a group of affinity scheduling rules. + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for + the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. + items: + description: |- + An empty preferred scheduling term matches all objects with implicit weight 0 + (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated with + the corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + 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. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + 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 + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + 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. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + 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 + type: object + x-kubernetes-map-type: atomic + weight: + description: Weight associated with matching the + corresponding nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. + The terms are ORed. + items: + description: |- + A null or empty node selector term matches no objects. The requirements of + them are ANDed. + The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + 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. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + 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 + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + 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. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + 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 + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. + co-locate this pod in the same node, zone, etc. as some + other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + 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 + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + 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 + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + 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 + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + 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 + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules + (e.g. avoid putting this pod in the same node, zone, etc. + as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the anti-affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + 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 + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + 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 + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the anti-affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the anti-affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + 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 + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + 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 + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + annotations: + additionalProperties: + type: string + type: object + basePath: + description: Base Path for Opensearch Clusters running behind + a reverse proxy + type: string + enable: + type: boolean + env: + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be a + C_IDENTIFIER. + type: string + value: + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: |- + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key + must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + image: + type: string + imagePullPolicy: + description: PullPolicy describes a policy for if/when to pull + a container image + type: string + imagePullSecrets: + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + type: array + labels: + additionalProperties: + type: string + type: object + nodeSelector: + additionalProperties: + type: string + type: object + opensearchCredentialsSecret: + description: Secret that contains fields username and password + for dashboards to use to login to opensearch, must only be supplied + if a custom securityconfig is provided + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + pluginsList: + items: + type: string + type: array + podSecurityContext: + description: Set security context for the dashboards pods + properties: + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by the containers in this pod. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object + fsGroup: + description: |- + A special supplemental group that applies to all containers in a pod. + Some volume types allow the Kubelet to change the ownership of that volume + to be owned by the pod: + + 1. The owning GID will be the FSGroup + 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) + 3. The permission bits are OR'd with rw-rw---- + + If unset, the Kubelet will not modify the ownership and permissions of any volume. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + fsGroupChangePolicy: + description: |- + fsGroupChangePolicy defines behavior of changing ownership and permission of the volume + before being exposed inside Pod. This field will only apply to + volume types which support fsGroup based ownership(and permissions). + It will have no effect on ephemeral volume types such as: secret, configmaps + and emptydir. + Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used. + Note that this field cannot be set when spec.os.name is windows. + type: string + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence + for that container. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence + for that container. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxChangePolicy: + description: |- + seLinuxChangePolicy defines how the container's SELinux label is applied to all volumes used by the Pod. + It has no effect on nodes that do not support SELinux or to volumes does not support SELinux. + Valid values are "MountOption" and "Recursive". + + "Recursive" means relabeling of all files on all Pod volumes by the container runtime. + This may be slow for large volumes, but allows mixing privileged and unprivileged Pods sharing the same volume on the same node. + + "MountOption" mounts all eligible Pod volumes with `-o context` mount option. + This requires all Pods that share the same volume to use the same SELinux label. + It is not possible to share the same volume among privileged and unprivileged Pods. + Eligible volumes are in-tree FibreChannel and iSCSI volumes, and all CSI volumes + whose CSI driver announces SELinux support by setting spec.seLinuxMount: true in their + CSIDriver instance. Other volumes are always re-labelled recursively. + "MountOption" value is allowed only when SELinuxMount feature gate is enabled. + + If not specified and SELinuxMount feature gate is enabled, "MountOption" is used. + If not specified and SELinuxMount feature gate is disabled, "MountOption" is used for ReadWriteOncePod volumes + and "Recursive" for all other volumes. + + This field affects only Pods that have SELinux label set, either in PodSecurityContext or in SecurityContext of all containers. + + All Pods that use the same volume should use the same seLinuxChangePolicy, otherwise some pods can get stuck in ContainerCreating state. + Note that this field cannot be set when spec.os.name is windows. + type: string + seLinuxOptions: + description: |- + The SELinux context to be applied to all containers. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in SecurityContext. If set in + both SecurityContext and PodSecurityContext, the value specified in SecurityContext + takes precedence for that container. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by the containers in this pod. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + supplementalGroups: + description: |- + A list of groups applied to the first process run in each container, in + addition to the container's primary GID and fsGroup (if specified). If + the SupplementalGroupsPolicy feature is enabled, the + supplementalGroupsPolicy field determines whether these are in addition + to or instead of any group memberships defined in the container image. + If unspecified, no additional groups are added, though group memberships + defined in the container image may still be used, depending on the + supplementalGroupsPolicy field. + Note that this field cannot be set when spec.os.name is windows. + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + description: |- + Defines how supplemental groups of the first container processes are calculated. + Valid values are "Merge" and "Strict". If not specified, "Merge" is used. + (Alpha) Using the field requires the SupplementalGroupsPolicy feature gate to be enabled + and the container runtime must implement support for this feature. + Note that this field cannot be set when spec.os.name is windows. + type: string + sysctls: + description: |- + Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported + sysctls (by the container runtime) might fail to launch. + Note that this field cannot be set when spec.os.name is windows. + items: + description: Sysctl defines a kernel parameter to be set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options within a container's SecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + replicas: + format: int32 + type: integer + resources: + description: ResourceRequirements describes the compute resource + requirements. + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + securityContext: + description: Set security context for the dashboards pods' container + properties: + allowPrivilegeEscalation: + description: |- + AllowPrivilegeEscalation controls whether a process can gain more + privileges than its parent process. This bool directly controls if + the no_new_privs flag will be set on the container process. + AllowPrivilegeEscalation is true always when the container is: + 1) run as Privileged + 2) has CAP_SYS_ADMIN + Note that this field cannot be set when spec.os.name is windows. + type: boolean + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by this container. If set, this profile + overrides the pod's appArmorProfile. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object + capabilities: + description: |- + The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container runtime. + Note that this field cannot be set when spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + x-kubernetes-list-type: atomic + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + description: |- + Run container in privileged mode. + Processes in privileged containers are essentially equivalent to root on the host. + Defaults to false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: |- + procMount denotes the type of proc mount to use for the containers. + The default value is Default which uses the container runtime defaults for + readonly paths and masked paths. + This requires the ProcMountType feature flag to be enabled. + Note that this field cannot be set when spec.os.name is windows. + type: string + readOnlyRootFilesystem: + description: |- + Whether this container has a read-only root filesystem. + Default is false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: |- + The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by this container. If seccomp options are + provided at both the pod & container level, the container options + override the pod options. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + windowsOptions: + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options from the PodSecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + service: + properties: + labels: + additionalProperties: + type: string + type: object + loadBalancerSourceRanges: + items: + type: string + type: array + type: + default: ClusterIP + description: Service Type string describes ingress methods + for a service + enum: + - ClusterIP + - NodePort + - LoadBalancer + type: string + type: object + tls: + properties: + caSecret: + description: Optional, secret that contains the ca certificate + as ca.crt. If this and generate=true is set the existing + CA cert from that secret is used to generate the node certs. + In this case must contain ca.crt and ca.key fields + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + enable: + description: Enable HTTPS for Dashboards + type: boolean + generate: + description: Generate certificate, if false secret must be + provided + type: boolean + secret: + description: Optional, name of a TLS secret that contains + ca.crt, tls.key and tls.crt data. If ca.crt is in a different + secret provide it via the caSecret field + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + type: object + tolerations: + 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 + 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 + 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 + version: + type: string + required: + - replicas + - version + type: object + general: + description: |- + INSERT ADDITIONAL SPEC FIELDS - desired state of cluster + Important: Run "make" to regenerate code after modifying this file + properties: + additionalConfig: + additionalProperties: + type: string + description: Extra items to add to the opensearch.yml + type: object + additionalVolumes: + description: Additional volumes to mount to all pods in the cluster + items: + properties: + configMap: + description: ConfigMap to use to populate the volume + properties: + defaultMode: + description: |- + defaultMode is optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + ConfigMap will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the ConfigMap, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + items: + description: Maps a string key to a path within a + volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: optional specify whether the ConfigMap + or its keys must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + csi: + description: CSI object to use to populate the volume + properties: + driver: + description: |- + driver is the name of the CSI driver that handles this volume. + Consult with your admin for the correct name as registered in the cluster. + type: string + fsType: + description: |- + fsType to mount. Ex. "ext4", "xfs", "ntfs". + If not provided, the empty value is passed to the associated CSI driver + which will determine the default filesystem to apply. + type: string + nodePublishSecretRef: + description: |- + nodePublishSecretRef is a reference to the secret object containing + sensitive information to pass to the CSI driver to complete the CSI + NodePublishVolume and NodeUnpublishVolume calls. + This field is optional, and may be empty if no secret is required. If the + secret object contains more than one secret, all secret references are passed. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + readOnly: + description: |- + readOnly specifies a read-only configuration for the volume. + Defaults to false (read/write). + type: boolean + volumeAttributes: + additionalProperties: + type: string + description: |- + volumeAttributes stores driver-specific properties that are passed to the CSI + driver. Consult your driver's documentation for supported values. + type: object + required: + - driver + type: object + emptyDir: + description: EmptyDir to use to populate the volume + properties: + medium: + description: |- + medium represents what type of storage medium should back this directory. + The default is "" which means to use the node's default medium. + Must be an empty string (default) or Memory. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + description: |- + sizeLimit is the total amount of local storage required for this EmptyDir volume. + The size limit is also applicable for memory medium. + The maximum usage on memory medium EmptyDir would be the minimum value between + the SizeLimit specified here and the sum of memory limits of all containers in a pod. + The default is nil which means that the limit is undefined. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + name: + description: Name to use for the volume. Required. + type: string + path: + description: Path in the container to mount the volume at. + Required. + type: string + projected: + description: Projected object to use to populate the volume + properties: + defaultMode: + description: |- + defaultMode are the mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + sources: + description: |- + sources is the list of volume projections. Each entry in this list + handles one source. + items: + description: |- + Projection that may be projected along with other supported volume types. + Exactly one of these fields must be set. + properties: + clusterTrustBundle: + description: |- + ClusterTrustBundle allows a pod to access the `.spec.trustBundle` field + of ClusterTrustBundle objects in an auto-updating file. + + Alpha, gated by the ClusterTrustBundleProjection feature gate. + + ClusterTrustBundle objects can either be selected by name, or by the + combination of signer name and a label selector. + + Kubelet performs aggressive normalization of the PEM contents written + into the pod filesystem. Esoteric PEM features such as inter-block + comments and block headers are stripped. Certificates are deduplicated. + The ordering of certificates within the file is arbitrary, and Kubelet + may change the order over time. + properties: + labelSelector: + description: |- + Select all ClusterTrustBundles that match this label selector. Only has + effect if signerName is set. Mutually-exclusive with name. If unset, + interpreted as "match nothing". If set but empty, interpreted as "match + everything". + 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: |- + Select a single ClusterTrustBundle by object name. Mutually-exclusive + with signerName and labelSelector. + type: string + optional: + description: |- + If true, don't block pod startup if the referenced ClusterTrustBundle(s) + aren't available. If using name, then the named ClusterTrustBundle is + allowed not to exist. If using signerName, then the combination of + signerName and labelSelector is allowed to match zero + ClusterTrustBundles. + type: boolean + path: + description: Relative path from the volume + root to write the bundle. + type: string + signerName: + description: |- + Select all ClusterTrustBundles that match this signer name. + Mutually-exclusive with name. The contents of all selected + ClusterTrustBundles will be unified and deduplicated. + type: string + required: + - path + type: object + configMap: + description: configMap information about the configMap + data to project + properties: + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + ConfigMap will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the ConfigMap, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + items: + description: Maps a string key to a path + within a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: optional specify whether the + ConfigMap or its keys must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + downwardAPI: + description: downwardAPI information about the + downwardAPI data to project + properties: + items: + description: Items is a list of DownwardAPIVolume + file + items: + description: DownwardAPIVolumeFile represents + information to create the file containing + the pod field + properties: + fieldRef: + description: 'Required: Selects a field + of the pod: only annotations, labels, + name, namespace and uid are supported.' + properties: + apiVersion: + description: Version of the schema + the FieldPath is written in terms + of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to + select in the specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + mode: + description: |- + Optional: mode bits used to set permissions on this file, must be an octal value + between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: 'Required: Path is the + relative path name of the file to + be created. Must not be absolute or + contain the ''..'' path. Must be utf-8 + encoded. The first item of the relative + path must not start with ''..''' + type: string + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, requests.cpu and requests.memory) are currently supported. + properties: + containerName: + description: 'Container name: required + for volumes, optional for env + vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output + format of the exposed resources, + defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource + to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + required: + - path + type: object + type: array + x-kubernetes-list-type: atomic + type: object + secret: + description: secret information about the secret + data to project + properties: + items: + description: |- + items if unspecified, each key-value pair in the Data field of the referenced + Secret will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + items: + description: Maps a string key to a path + within a volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: optional field specify whether + the Secret or its key must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + serviceAccountToken: + description: serviceAccountToken is information + about the serviceAccountToken data to project + properties: + audience: + description: |- + audience is the intended audience of the token. A recipient of a token + must identify itself with an identifier specified in the audience of the + token, and otherwise should reject the token. The audience defaults to the + identifier of the apiserver. + type: string + expirationSeconds: + description: |- + expirationSeconds is the requested duration of validity of the service + account token. As the token approaches expiration, the kubelet volume + plugin will proactively rotate the service account token. The kubelet will + start trying to rotate the token if the token is older than 80 percent of + its time to live or if the token is older than 24 hours.Defaults to 1 hour + and must be at least 10 minutes. + format: int64 + type: integer + path: + description: |- + path is the path relative to the mount point of the file to project the + token into. + type: string + required: + - path + type: object + type: object + type: array + x-kubernetes-list-type: atomic + type: object + restartPods: + description: Whether to restart the pods on content change + type: boolean + secret: + description: Secret to use populate the volume + properties: + defaultMode: + description: |- + defaultMode is Optional: mode bits used to set permissions on created files by default. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values + for mode bits. Defaults to 0644. + Directories within the path are not affected by this setting. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + items: + description: |- + items If unspecified, each key-value pair in the Data field of the referenced + Secret will be projected into the volume as a file whose name is the + key and content is the value. If specified, the listed keys will be + projected into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in the Secret, + the volume setup will error unless it is marked optional. Paths must be + relative and may not contain the '..' path or start with '..'. + items: + description: Maps a string key to a path within a + volume. + properties: + key: + description: key is the key to project. + type: string + mode: + description: |- + mode is Optional: mode bits used to set permissions on this file. + Must be an octal value between 0000 and 0777 or a decimal value between 0 and 511. + YAML accepts both octal and decimal values, JSON requires decimal values for mode bits. + If not specified, the volume defaultMode will be used. + This might be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits set. + format: int32 + type: integer + path: + description: |- + path is the relative path of the file to map the key to. + May not be an absolute path. + May not contain the path element '..'. + May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + x-kubernetes-list-type: atomic + optional: + description: optional field specify whether the Secret + or its keys must be defined + type: boolean + secretName: + description: |- + secretName is the name of the secret in the pod's namespace to use. + More info: https://kubernetes.io/docs/concepts/storage/volumes#secret + type: string + type: object + subPath: + description: SubPath of the referenced volume to mount. + type: string + required: + - name + - path + type: object + type: array + annotations: + additionalProperties: + type: string + description: Adds support for annotations in services + type: object + command: + type: string + defaultRepo: + type: string + drainDataNodes: + description: Drain data nodes controls whether to drain data notes + on rolling restart operations + type: boolean + httpPort: + default: 9200 + format: int32 + type: integer + image: + type: string + imagePullPolicy: + description: PullPolicy describes a policy for if/when to pull + a container image + type: string + imagePullSecrets: + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + type: array + keystore: + description: Populate opensearch keystore before startup + items: + properties: + keyMappings: + additionalProperties: + type: string + description: Key mappings from secret to keystore keys + type: object + secret: + description: Secret containing key value pairs + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + type: object + type: array + monitoring: + properties: + enable: + type: boolean + labels: + additionalProperties: + type: string + type: object + monitoringUserSecret: + type: string + pluginUrl: + type: string + scrapeInterval: + type: string + tlsConfig: + properties: + insecureSkipVerify: + type: boolean + serverName: + type: string + type: object + type: object + pluginsList: + items: + type: string + type: array + podSecurityContext: + description: Set security context for the cluster pods + properties: + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by the containers in this pod. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object + fsGroup: + description: |- + A special supplemental group that applies to all containers in a pod. + Some volume types allow the Kubelet to change the ownership of that volume + to be owned by the pod: + + 1. The owning GID will be the FSGroup + 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) + 3. The permission bits are OR'd with rw-rw---- + + If unset, the Kubelet will not modify the ownership and permissions of any volume. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + fsGroupChangePolicy: + description: |- + fsGroupChangePolicy defines behavior of changing ownership and permission of the volume + before being exposed inside Pod. This field will only apply to + volume types which support fsGroup based ownership(and permissions). + It will have no effect on ephemeral volume types such as: secret, configmaps + and emptydir. + Valid values are "OnRootMismatch" and "Always". If not specified, "Always" is used. + Note that this field cannot be set when spec.os.name is windows. + type: string + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence + for that container. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence + for that container. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxChangePolicy: + description: |- + seLinuxChangePolicy defines how the container's SELinux label is applied to all volumes used by the Pod. + It has no effect on nodes that do not support SELinux or to volumes does not support SELinux. + Valid values are "MountOption" and "Recursive". + + "Recursive" means relabeling of all files on all Pod volumes by the container runtime. + This may be slow for large volumes, but allows mixing privileged and unprivileged Pods sharing the same volume on the same node. + + "MountOption" mounts all eligible Pod volumes with `-o context` mount option. + This requires all Pods that share the same volume to use the same SELinux label. + It is not possible to share the same volume among privileged and unprivileged Pods. + Eligible volumes are in-tree FibreChannel and iSCSI volumes, and all CSI volumes + whose CSI driver announces SELinux support by setting spec.seLinuxMount: true in their + CSIDriver instance. Other volumes are always re-labelled recursively. + "MountOption" value is allowed only when SELinuxMount feature gate is enabled. + + If not specified and SELinuxMount feature gate is enabled, "MountOption" is used. + If not specified and SELinuxMount feature gate is disabled, "MountOption" is used for ReadWriteOncePod volumes + and "Recursive" for all other volumes. + + This field affects only Pods that have SELinux label set, either in PodSecurityContext or in SecurityContext of all containers. + + All Pods that use the same volume should use the same seLinuxChangePolicy, otherwise some pods can get stuck in ContainerCreating state. + Note that this field cannot be set when spec.os.name is windows. + type: string + seLinuxOptions: + description: |- + The SELinux context to be applied to all containers. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in SecurityContext. If set in + both SecurityContext and PodSecurityContext, the value specified in SecurityContext + takes precedence for that container. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by the containers in this pod. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + supplementalGroups: + description: |- + A list of groups applied to the first process run in each container, in + addition to the container's primary GID and fsGroup (if specified). If + the SupplementalGroupsPolicy feature is enabled, the + supplementalGroupsPolicy field determines whether these are in addition + to or instead of any group memberships defined in the container image. + If unspecified, no additional groups are added, though group memberships + defined in the container image may still be used, depending on the + supplementalGroupsPolicy field. + Note that this field cannot be set when spec.os.name is windows. + items: + format: int64 + type: integer + type: array + x-kubernetes-list-type: atomic + supplementalGroupsPolicy: + description: |- + Defines how supplemental groups of the first container processes are calculated. + Valid values are "Merge" and "Strict". If not specified, "Merge" is used. + (Alpha) Using the field requires the SupplementalGroupsPolicy feature gate to be enabled + and the container runtime must implement support for this feature. + Note that this field cannot be set when spec.os.name is windows. + type: string + sysctls: + description: |- + Sysctls hold a list of namespaced sysctls used for the pod. Pods with unsupported + sysctls (by the container runtime) might fail to launch. + Note that this field cannot be set when spec.os.name is windows. + items: + description: Sysctl defines a kernel parameter to be set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + x-kubernetes-list-type: atomic + windowsOptions: + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options within a container's SecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + securityContext: + description: Set security context for the cluster pods' container + properties: + allowPrivilegeEscalation: + description: |- + AllowPrivilegeEscalation controls whether a process can gain more + privileges than its parent process. This bool directly controls if + the no_new_privs flag will be set on the container process. + AllowPrivilegeEscalation is true always when the container is: + 1) run as Privileged + 2) has CAP_SYS_ADMIN + Note that this field cannot be set when spec.os.name is windows. + type: boolean + appArmorProfile: + description: |- + appArmorProfile is the AppArmor options to use by this container. If set, this profile + overrides the pod's appArmorProfile. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile loaded on the node that should be used. + The profile must be preconfigured on the node to work. + Must match the loaded name of the profile. + Must be set if and only if type is "Localhost". + type: string + type: + description: |- + type indicates which kind of AppArmor profile will be applied. + Valid options are: + Localhost - a profile pre-loaded on the node. + RuntimeDefault - the container runtime's default profile. + Unconfined - no AppArmor enforcement. + type: string + required: + - type + type: object + capabilities: + description: |- + The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the container runtime. + Note that this field cannot be set when spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + x-kubernetes-list-type: atomic + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + x-kubernetes-list-type: atomic + type: object + privileged: + description: |- + Run container in privileged mode. + Processes in privileged containers are essentially equivalent to root on the host. + Defaults to false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: |- + procMount denotes the type of proc mount to use for the containers. + The default value is Default which uses the container runtime defaults for + readonly paths and masked paths. + This requires the ProcMountType feature flag to be enabled. + Note that this field cannot be set when spec.os.name is windows. + type: string + readOnlyRootFilesystem: + description: |- + Whether this container has a read-only root filesystem. + Default is false. + Note that this field cannot be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: |- + The GID to run the entrypoint of the container process. + Uses runtime default if unset. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: |- + Indicates that the container must run as a non-root user. + If true, the Kubelet will validate the image at runtime to ensure that it + does not run as UID 0 (root) and fail to start the container if it does. + If unset or false, no such validation will be performed. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: |- + The UID to run the entrypoint of the container process. + Defaults to user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: |- + The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random SELinux context for each + container. May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: |- + The seccomp options to use by this container. If seccomp options are + provided at both the pod & container level, the container options + override the pod options. + Note that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: |- + localhostProfile indicates a profile defined in a file on the node should be used. + The profile must be preconfigured on the node to work. + Must be a descending path, relative to the kubelet's configured seccomp profile location. + Must be set if type is "Localhost". Must NOT be set for any other type. + type: string + type: + description: |- + type indicates which kind of seccomp profile will be applied. + Valid options are: + + Localhost - a profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile should be used. + Unconfined - no profile should be applied. + type: string + required: + - type + type: object + windowsOptions: + description: |- + The Windows specific settings applied to all containers. + If unspecified, the options from the PodSecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: |- + GMSACredentialSpec is where the GMSA admission webhook + (https://github.com/kubernetes-sigs/windows-gmsa) inlines the contents of the + GMSA credential spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: |- + HostProcess determines if a container should be run as a 'Host Process' container. + All of a Pod's containers must have the same effective HostProcess value + (it is not allowed to have a mix of HostProcess containers and non-HostProcess containers). + In addition, if HostProcess is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: |- + The UserName in Windows to run the entrypoint of the container process. + Defaults to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + serviceAccount: + type: string + serviceName: + type: string + setVMMaxMapCount: + type: boolean + snapshotRepositories: + items: + properties: + name: + type: string + settings: + additionalProperties: + type: string + type: object + type: + type: string + required: + - name + - type + type: object + type: array + vendor: + enum: + - Opensearch + - Op + - OP + - os + - opensearch + type: string + version: + type: string + required: + - serviceName + type: object + initHelper: + properties: + image: + type: string + imagePullPolicy: + description: PullPolicy describes a policy for if/when to pull + a container image + type: string + imagePullSecrets: + items: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + type: array + resources: + description: ResourceRequirements describes the compute resource + requirements. + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + version: + type: string + type: object + nodePools: + items: + properties: + additionalConfig: + additionalProperties: + type: string + type: object + affinity: + description: Affinity is a group of affinity scheduling rules. + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for + the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node matches the corresponding matchExpressions; the + node(s) with the highest sum are the most preferred. + items: + description: |- + An empty preferred scheduling term matches all objects with implicit weight 0 + (i.e. it's a no-op). A null preferred scheduling term matches no objects (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated + with the corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the + selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + 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. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + 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 + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the + selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + 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. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + 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 + type: object + x-kubernetes-map-type: atomic + weight: + description: Weight associated with matching the + corresponding nodeSelectorTerm, in the range + 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. + The terms are ORed. + items: + description: |- + A null or empty node selector term matches no objects. The requirements of + them are ANDed. + The TopologySelectorTerm type implements a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the + selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + 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. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + 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 + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: |- + A node selector requirement is a selector that contains values, a key, and an operator + that relates the key and values. + properties: + key: + description: The label key that the + selector applies to. + type: string + operator: + description: |- + Represents a key's relationship to a set of values. + Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt. + type: string + values: + description: |- + 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. If the operator is Gt or Lt, the values + array must have a single element, which will be interpreted as an integer. + 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 + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + required: + - nodeSelectorTerms + type: object + x-kubernetes-map-type: atomic + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. + co-locate this pod in the same node, zone, etc. as some + other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + 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 + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + 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 + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + 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 + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + 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 + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules + (e.g. avoid putting this pod in the same node, zone, etc. + as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: |- + The scheduler will prefer to schedule pods to nodes that satisfy + the anti-affinity expressions specified by this field, but it may choose + a node that violates one or more of the expressions. The node that is + most preferred is the one with the greatest sum of weights, i.e. + for each node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity expressions, etc.), + compute a sum by iterating through the elements of this field and adding + "weight" to the sum if the node has pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + 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 + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + 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 + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: |- + weight associated with matching the corresponding podAffinityTerm, + in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + x-kubernetes-list-type: atomic + requiredDuringSchedulingIgnoredDuringExecution: + description: |- + If the anti-affinity requirements specified by this field are not met at + scheduling time, the pod will not be scheduled onto the node. + If the anti-affinity requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod label update), the + system may or may not try to eventually evict the pod from its node. + When there are multiple elements, the lists of nodes corresponding to each + podAffinityTerm are intersected, i.e. all terms must be satisfied. + items: + description: |- + Defines a set of pods (namely those matching the labelSelector + relative to the given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) with, + where co-located is defined as running on a node whose value of + the label with key matches that of any node on which + a pod of the set of pods is running + properties: + labelSelector: + description: |- + A label query over a set of resources, in this case pods. + If it's null, this PodAffinityTerm matches with no Pods. + 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 + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key in (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both matchLabelKeys and labelSelector. + Also, matchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + mismatchLabelKeys: + description: |- + MismatchLabelKeys is a set of pod label keys to select which pods will + be taken into consideration. The keys are used to lookup values from the + incoming pod labels, those key-value labels are merged with `labelSelector` as `key notin (value)` + to select the group of existing pods which pods will be taken into consideration + for the incoming pod's pod (anti) affinity. Keys that don't exist in the incoming + pod labels will be ignored. The default value is empty. + The same key is forbidden to exist in both mismatchLabelKeys and labelSelector. + Also, mismatchLabelKeys cannot be set when labelSelector isn't set. + items: + type: string + type: array + x-kubernetes-list-type: atomic + namespaceSelector: + description: |- + A label query over the set of namespaces that the term applies to. + The term is applied to the union of the namespaces selected by this field + and the ones listed in the namespaces field. + null selector and null or empty namespaces list means "this pod's namespace". + An empty selector ({}) matches all namespaces. + 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 + namespaces: + description: |- + namespaces specifies a static list of namespace names that the term applies to. + The term is applied to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. + null or empty namespaces list and null namespaceSelector means "this pod's namespace". + items: + type: string + type: array + x-kubernetes-list-type: atomic + topologyKey: + description: |- + This pod should be co-located (affinity) or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where co-located is defined as running on a node + whose value of the label with key topologyKey matches that of any node on which any of the + selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + x-kubernetes-list-type: atomic + type: object + type: object + annotations: + additionalProperties: + type: string + type: object + component: + type: string + diskSize: + type: string + env: + items: + description: EnvVar represents an environment variable present + in a Container. + properties: + name: + description: Name of the environment variable. Must be + a C_IDENTIFIER. + type: string + value: + description: |- + Variable references $(VAR_NAME) are expanded + using the previously defined environment variables in the container and + any service environment variables. If a variable cannot be resolved, + the reference in the input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) syntax: i.e. + "$$(VAR_NAME)" will produce the string literal "$(VAR_NAME)". + Escaped references will never be expanded, regardless of whether the variable + exists or not. + Defaults to "". + type: string + valueFrom: + description: Source for the environment variable's value. + Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap or + its key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + fieldRef: + description: |- + Selects a field of the pod: supports metadata.name, metadata.namespace, `metadata.labels['']`, `metadata.annotations['']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs. + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + x-kubernetes-map-type: atomic + resourceFieldRef: + description: |- + Selects a resource of the container: only resources limits and requests + (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu, requests.memory and requests.ephemeral-storage) are currently supported. + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + x-kubernetes-map-type: atomic + secretKeyRef: + description: Selects a key of a secret in the pod's + namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its + key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + type: object + required: + - name + type: object + type: array + jvm: + type: string + labels: + additionalProperties: + type: string + type: object + nodeSelector: + additionalProperties: + type: string + type: object + pdb: + properties: + enable: + type: boolean + maxUnavailable: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + minAvailable: + anyOf: + - type: integer + - type: string + x-kubernetes-int-or-string: true + type: object + persistence: + description: PersistencConfig defines options for data persistence + properties: + emptyDir: + description: |- + Represents an empty directory for a pod. + Empty directory volumes support ownership management and SELinux relabeling. + properties: + medium: + description: |- + medium represents what type of storage medium should back this directory. + The default is "" which means to use the node's default medium. + Must be an empty string (default) or Memory. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + description: |- + sizeLimit is the total amount of local storage required for this EmptyDir volume. + The size limit is also applicable for memory medium. + The maximum usage on memory medium EmptyDir would be the minimum value between + the SizeLimit specified here and the sum of memory limits of all containers in a pod. + The default is nil which means that the limit is undefined. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + hostPath: + description: |- + Represents a host path mapped into a pod. + Host path volumes do not support ownership management or SELinux relabeling. + properties: + path: + description: |- + path of the directory on the host. + If the path is a symlink, it will follow the link to the real path. + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + type: string + type: + description: |- + type for HostPath Volume + Defaults to "" + More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + type: string + required: + - path + type: object + pvc: + properties: + accessModes: + items: + type: string + type: array + storageClass: + type: string + type: object + type: object + priorityClassName: + type: string + probes: + properties: + liveness: + properties: + failureThreshold: + format: int32 + type: integer + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + readiness: + properties: + failureThreshold: + format: int32 + type: integer + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + startup: + properties: + failureThreshold: + format: int32 + type: integer + initialDelaySeconds: + format: int32 + type: integer + periodSeconds: + format: int32 + type: integer + successThreshold: + format: int32 + type: integer + timeoutSeconds: + format: int32 + type: integer + type: object + type: object + replicas: + format: int32 + type: integer + resources: + description: ResourceRequirements describes the compute resource + requirements. + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + roles: + items: + type: string + type: array + tolerations: + 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 + 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 + 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 + topologySpreadConstraints: + items: + description: TopologySpreadConstraint specifies how to spread + matching pods among the given topology. + properties: + labelSelector: + description: |- + LabelSelector is used to find matching pods. + Pods that match this label selector are counted to determine the number of pods + in their corresponding topology domain. + 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 + matchLabelKeys: + description: |- + MatchLabelKeys is a set of pod label keys to select the pods over which + spreading will be calculated. The keys are used to lookup values from the + incoming pod labels, those key-value labels are ANDed with labelSelector + to select the group of existing pods over which spreading will be calculated + for the incoming pod. The same key is forbidden to exist in both MatchLabelKeys and LabelSelector. + MatchLabelKeys cannot be set when LabelSelector isn't set. + Keys that don't exist in the incoming pod labels will + be ignored. A null or empty list means only match against labelSelector. + + This is a beta field and requires the MatchLabelKeysInPodTopologySpread feature gate to be enabled (enabled by default). + items: + type: string + type: array + x-kubernetes-list-type: atomic + maxSkew: + description: |- + MaxSkew describes the degree to which pods may be unevenly distributed. + When `whenUnsatisfiable=DoNotSchedule`, it is the maximum permitted difference + between the number of matching pods in the target topology and the global minimum. + The global minimum is the minimum number of matching pods in an eligible domain + or zero if the number of eligible domains is less than MinDomains. + For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same + labelSelector spread as 2/2/1: + In this case, the global minimum is 1. + | zone1 | zone2 | zone3 | + | P P | P P | P | + - if MaxSkew is 1, incoming pod can only be scheduled to zone3 to become 2/2/2; + scheduling it onto zone1(zone2) would make the ActualSkew(3-1) on zone1(zone2) + violate MaxSkew(1). + - if MaxSkew is 2, incoming pod can be scheduled onto any zone. + When `whenUnsatisfiable=ScheduleAnyway`, it is used to give higher precedence + to topologies that satisfy it. + It's a required field. Default value is 1 and 0 is not allowed. + format: int32 + type: integer + minDomains: + description: |- + MinDomains indicates a minimum number of eligible domains. + When the number of eligible domains with matching topology keys is less than minDomains, + Pod Topology Spread treats "global minimum" as 0, and then the calculation of Skew is performed. + And when the number of eligible domains with matching topology keys equals or greater than minDomains, + this value has no effect on scheduling. + As a result, when the number of eligible domains is less than minDomains, + scheduler won't schedule more than maxSkew Pods to those domains. + If value is nil, the constraint behaves as if MinDomains is equal to 1. + Valid values are integers greater than 0. + When value is not nil, WhenUnsatisfiable must be DoNotSchedule. + + For example, in a 3-zone cluster, MaxSkew is set to 2, MinDomains is set to 5 and pods with the same + labelSelector spread as 2/2/2: + | zone1 | zone2 | zone3 | + | P P | P P | P P | + The number of domains is less than 5(MinDomains), so "global minimum" is treated as 0. + In this situation, new pod with the same labelSelector cannot be scheduled, + because computed skew will be 3(3 - 0) if new Pod is scheduled to any of the three zones, + it will violate MaxSkew. + format: int32 + type: integer + nodeAffinityPolicy: + description: |- + NodeAffinityPolicy indicates how we will treat Pod's nodeAffinity/nodeSelector + when calculating pod topology spread skew. Options are: + - Honor: only nodes matching nodeAffinity/nodeSelector are included in the calculations. + - Ignore: nodeAffinity/nodeSelector are ignored. All nodes are included in the calculations. + + If this value is nil, the behavior is equivalent to the Honor policy. + type: string + nodeTaintsPolicy: + description: |- + NodeTaintsPolicy indicates how we will treat node taints when calculating + pod topology spread skew. Options are: + - Honor: nodes without taints, along with tainted nodes for which the incoming pod + has a toleration, are included. + - Ignore: node taints are ignored. All nodes are included. + + If this value is nil, the behavior is equivalent to the Ignore policy. + type: string + topologyKey: + description: |- + TopologyKey is the key of node labels. Nodes that have a label with this key + and identical values are considered to be in the same topology. + We consider each as a "bucket", and try to put balanced number + of pods into each bucket. + We define a domain as a particular instance of a topology. + Also, we define an eligible domain as a domain whose nodes meet the requirements of + nodeAffinityPolicy and nodeTaintsPolicy. + e.g. If TopologyKey is "kubernetes.io/hostname", each Node is a domain of that topology. + And, if TopologyKey is "topology.kubernetes.io/zone", each zone is a domain of that topology. + It's a required field. + type: string + whenUnsatisfiable: + description: |- + WhenUnsatisfiable indicates how to deal with a pod if it doesn't satisfy + the spread constraint. + - DoNotSchedule (default) tells the scheduler not to schedule it. + - ScheduleAnyway tells the scheduler to schedule the pod in any location, + but giving higher precedence to topologies that would help reduce the + skew. + A constraint is considered "Unsatisfiable" for an incoming pod + if and only if every possible node assignment for that pod would violate + "MaxSkew" on some topology. + For example, in a 3-zone cluster, MaxSkew is set to 1, and pods with the same + labelSelector spread as 3/1/1: + | zone1 | zone2 | zone3 | + | P P P | P | P | + If WhenUnsatisfiable is set to DoNotSchedule, incoming pod can only be scheduled + to zone2(zone3) to become 3/2/1(3/1/2) as ActualSkew(2-1) on zone2(zone3) satisfies + MaxSkew(1). In other words, the cluster can still be imbalanced, but scheduler + won't make it *more* imbalanced. + It's a required field. + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + required: + - component + - replicas + - roles + type: object + type: array + security: + description: Security defines options for managing the opensearch-security + plugin + properties: + config: + properties: + adminCredentialsSecret: + description: Secret that contains fields username and password + to be used by the operator to access the opensearch cluster + for node draining. Must be set if custom securityconfig + is provided. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + adminSecret: + description: TLS Secret that contains a client certificate + (tls.key, tls.crt, ca.crt) with admin rights in the opensearch + cluster. Must be set if transport certificates are provided + by user and not generated + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + securityConfigSecret: + description: Secret that contains the differnt yml files of + the opensearch-security config (config.yml, internal_users.yml, + ...) + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + updateJob: + description: Specific configs for the SecurityConfig update + job + properties: + resources: + description: ResourceRequirements describes the compute + resource requirements. + properties: + claims: + description: |- + Claims lists the names of resources, defined in spec.resourceClaims, + that are used by this container. + + This is an alpha field and requires enabling the + DynamicResourceAllocation feature gate. + + This field is immutable. It can only be set for containers. + items: + description: ResourceClaim references one entry + in PodSpec.ResourceClaims. + properties: + name: + description: |- + Name must match the name of one entry in pod.spec.resourceClaims of + the Pod where this field is used. It makes that resource available + inside a container. + type: string + request: + description: |- + Request is the name chosen for a request in the referenced claim. + If empty, everything from the claim is made available, otherwise + only the result of this request. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Limits describes the maximum amount of compute resources allowed. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: |- + Requests describes the minimum amount of compute resources required. + If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, + otherwise to an implementation-defined value. Requests cannot exceed Limits. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + type: object + type: object + type: object + type: object + tls: + description: Configure tls usage for transport and http interface + properties: + http: + properties: + caSecret: + description: Optional, secret that contains the ca certificate + as ca.crt. If this and generate=true is set the existing + CA cert from that secret is used to generate the node + certs. In this case must contain ca.crt and ca.key fields + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + generate: + description: If set to true the operator will generate + a CA and certificates for the cluster to use, if false + secrets with existing certificates must be supplied + type: boolean + secret: + description: Optional, name of a TLS secret that contains + ca.crt, tls.key and tls.crt data. If ca.crt is in a + different secret provide it via the caSecret field + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + type: object + transport: + properties: + adminDn: + description: DNs of certificates that should have admin + access, mainly used for securityconfig updates via securityadmin.sh, + only used when existing certificates are provided + items: + type: string + type: array + caSecret: + description: Optional, secret that contains the ca certificate + as ca.crt. If this and generate=true is set the existing + CA cert from that secret is used to generate the node + certs. In this case must contain ca.crt and ca.key fields + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + generate: + description: If set to true the operator will generate + a CA and certificates for the cluster to use, if false + secrets with existing certificates must be supplied + type: boolean + nodesDn: + description: Allowed Certificate DNs for nodes, only used + when existing certificates are provided + items: + type: string + type: array + perNode: + description: Configure transport node certificate + type: boolean + secret: + description: Optional, name of a TLS secret that contains + ca.crt, tls.key and tls.crt data. If ca.crt is in a + different secret provide it via the caSecret field + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + type: object + type: object + type: object + required: + - nodePools + type: object + status: + description: ClusterStatus defines the observed state of Es + properties: + availableNodes: + description: AvailableNodes is the number of available instances. + format: int32 + type: integer + componentsStatus: + items: + properties: + component: + type: string + conditions: + items: + type: string + type: array + description: + type: string + status: + type: string + type: object + type: array + health: + description: OpenSearchHealth is the health of the cluster as returned + by the health API. + type: string + initialized: + type: boolean + phase: + description: |- + INSERT ADDITIONAL STATUS FIELD - define observed state of cluster + Important: Run "make" to regenerate code after modifying this file + type: string + version: + type: string + required: + - componentsStatus + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchcomponenttemplates.yaml b/packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchcomponenttemplates.yaml new file mode 100644 index 00000000..7d4fd549 --- /dev/null +++ b/packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchcomponenttemplates.yaml @@ -0,0 +1,136 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.0 + name: opensearchcomponenttemplates.opensearch.opster.io +spec: + group: opensearch.opster.io + names: + kind: OpensearchComponentTemplate + listKind: OpensearchComponentTemplateList + plural: opensearchcomponenttemplates + shortNames: + - opensearchcomponenttemplate + singular: opensearchcomponenttemplate + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + description: OpensearchComponentTemplate is the schema for the OpenSearch + component templates 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: + properties: + _meta: + description: Optional user metadata about the component template + x-kubernetes-preserve-unknown-fields: true + allowAutoCreate: + description: If true, then indices can be automatically created using + this template + type: boolean + name: + description: The name of the component template. Defaults to metadata.name + type: string + opensearchCluster: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + template: + description: The template that should be applied + properties: + aliases: + additionalProperties: + description: Describes the specs of an index alias + properties: + alias: + description: The name of the alias. + type: string + filter: + description: Query used to limit documents the alias can + access. + x-kubernetes-preserve-unknown-fields: true + index: + description: The name of the index that the alias points + to. + type: string + isWriteIndex: + description: If true, the index is the write index for the + alias + type: boolean + routing: + description: Value used to route indexing and search operations + to a specific shard. + type: string + type: object + description: Aliases to add + type: object + mappings: + description: Mapping for fields in the index + x-kubernetes-preserve-unknown-fields: true + settings: + description: Configuration options for the index + x-kubernetes-preserve-unknown-fields: true + type: object + version: + description: Version number used to manage the component template + externally + type: integer + required: + - opensearchCluster + - template + type: object + status: + properties: + componentTemplateName: + description: Name of the currently managed component template + type: string + existingComponentTemplate: + type: boolean + managedCluster: + description: |- + UID is a type that holds unique ID values, including UUIDs. Because we + don't ONLY use UUIDs, this is an alias to string. Being a type captures + intent and helps make sure that UIDs and names do not get conflated. + type: string + reason: + type: string + state: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchindextemplates.yaml b/packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchindextemplates.yaml new file mode 100644 index 00000000..37e517ee --- /dev/null +++ b/packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchindextemplates.yaml @@ -0,0 +1,163 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.0 + name: opensearchindextemplates.opensearch.opster.io +spec: + group: opensearch.opster.io + names: + kind: OpensearchIndexTemplate + listKind: OpensearchIndexTemplateList + plural: opensearchindextemplates + shortNames: + - opensearchindextemplate + singular: opensearchindextemplate + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + description: OpensearchIndexTemplate is the schema for the OpenSearch index + templates 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: + properties: + _meta: + description: Optional user metadata about the index template + x-kubernetes-preserve-unknown-fields: true + composedOf: + description: |- + An ordered list of component template names. Component templates are merged in the order specified, + meaning that the last component template specified has the highest precedence + items: + type: string + type: array + dataStream: + description: The dataStream config that should be applied + properties: + timestamp_field: + description: TimestampField for dataStream + properties: + name: + description: Name of the field that are used for the DataStream + type: string + required: + - name + type: object + type: object + indexPatterns: + description: Array of wildcard expressions used to match the names + of indices during creation + items: + type: string + type: array + name: + description: The name of the index template. Defaults to metadata.name + type: string + opensearchCluster: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + priority: + description: |- + Priority to determine index template precedence when a new data stream or index is created. + The index template with the highest priority is chosen + type: integer + template: + description: The template that should be applied + properties: + aliases: + additionalProperties: + description: Describes the specs of an index alias + properties: + alias: + description: The name of the alias. + type: string + filter: + description: Query used to limit documents the alias can + access. + x-kubernetes-preserve-unknown-fields: true + index: + description: The name of the index that the alias points + to. + type: string + isWriteIndex: + description: If true, the index is the write index for the + alias + type: boolean + routing: + description: Value used to route indexing and search operations + to a specific shard. + type: string + type: object + description: Aliases to add + type: object + mappings: + description: Mapping for fields in the index + x-kubernetes-preserve-unknown-fields: true + settings: + description: Configuration options for the index + x-kubernetes-preserve-unknown-fields: true + type: object + version: + description: Version number used to manage the component template + externally + type: integer + required: + - indexPatterns + - opensearchCluster + type: object + status: + properties: + existingIndexTemplate: + type: boolean + indexTemplateName: + description: Name of the currently managed index template + type: string + managedCluster: + description: |- + UID is a type that holds unique ID values, including UUIDs. Because we + don't ONLY use UUIDs, this is an alias to string. Being a type captures + intent and helps make sure that UIDs and names do not get conflated. + type: string + reason: + type: string + state: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchismpolicies.yaml b/packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchismpolicies.yaml new file mode 100644 index 00000000..2f4b261d --- /dev/null +++ b/packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchismpolicies.yaml @@ -0,0 +1,461 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.0 + name: opensearchismpolicies.opensearch.opster.io +spec: + group: opensearch.opster.io + names: + kind: OpenSearchISMPolicy + listKind: OpenSearchISMPolicyList + plural: opensearchismpolicies + shortNames: + - ismp + - ismpolicy + singular: opensearchismpolicy + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + 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: ISMPolicySpec is the specification for the ISM policy for + OS. + properties: + applyToExistingIndices: + description: If true, apply the policy to existing indices that match + the index patterns in the ISM template. + type: boolean + defaultState: + description: The default starting state for each index that uses this + policy. + type: string + description: + description: A human-readable description of the policy. + type: string + errorNotification: + properties: + channel: + type: string + destination: + description: The destination URL. + properties: + amazon: + properties: + url: + type: string + type: object + chime: + properties: + url: + type: string + type: object + customWebhook: + properties: + url: + type: string + type: object + slack: + properties: + url: + type: string + type: object + type: object + messageTemplate: + description: The text of the message + properties: + source: + type: string + type: object + type: object + ismTemplate: + description: Specify an ISM template pattern that matches the index + to apply the policy. + properties: + indexPatterns: + description: Index patterns on which this policy has to be applied + items: + type: string + type: array + priority: + description: Priority of the template, defaults to 0 + type: integer + required: + - indexPatterns + type: object + opensearchCluster: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + policyId: + type: string + states: + description: The states that you define in the policy. + items: + properties: + actions: + description: The actions to execute after entering a state. + items: + description: Actions are the steps that the policy sequentially + executes on entering a specific state. + properties: + alias: + properties: + actions: + description: Allocate the index to a node with a specified + attribute. + items: + properties: + add: + properties: + aliases: + description: The name of the alias. + items: + type: string + type: array + index: + description: The name of the index that + the alias points to. + type: string + isWriteIndex: + description: Specify the index that accepts + any write operations to the alias. + type: boolean + routing: + description: Limit search to an associated + shard value + type: string + type: object + remove: + properties: + aliases: + description: The name of the alias. + items: + type: string + type: array + index: + description: The name of the index that + the alias points to. + type: string + isWriteIndex: + description: Specify the index that accepts + any write operations to the alias. + type: boolean + routing: + description: Limit search to an associated + shard value + type: string + type: object + type: object + type: array + required: + - actions + type: object + allocation: + description: Allocate the index to a node with a specific + attribute set + properties: + exclude: + description: Allocate the index to a node with a specified + attribute. + type: string + include: + description: Allocate the index to a node with any + of the specified attributes. + type: string + require: + description: Don’t allocate the index to a node with + any of the specified attributes. + type: string + waitFor: + description: Wait for the policy to execute before + allocating the index to a node with a specified + attribute. + type: string + required: + - exclude + - include + - require + - waitFor + type: object + close: + description: Closes the managed index. + type: object + delete: + description: Deletes a managed index. + type: object + forceMerge: + description: Reduces the number of Lucene segments by + merging the segments of individual shards. + properties: + maxNumSegments: + description: The number of segments to reduce the + shard to. + format: int64 + type: integer + required: + - maxNumSegments + type: object + indexPriority: + description: Set the priority for the index in a specific + state. + properties: + priority: + description: The priority for the index as soon as + it enters a state. + format: int64 + type: integer + required: + - priority + type: object + notification: + description: Name string `json:"name,omitempty"` + properties: + destination: + type: string + messageTemplate: + properties: + source: + type: string + type: object + required: + - destination + - messageTemplate + type: object + open: + description: Opens a managed index. + type: object + readOnly: + description: Sets a managed index to be read only. + type: object + readWrite: + description: Sets a managed index to be writeable. + type: object + replicaCount: + description: Sets the number of replicas to assign to + an index. + properties: + numberOfReplicas: + format: int64 + type: integer + required: + - numberOfReplicas + type: object + retry: + description: The retry configuration for the action. + properties: + backoff: + description: The backoff policy type to use when retrying. + type: string + count: + description: The number of retry counts. + format: int64 + type: integer + delay: + description: The time to wait between retries. + type: string + required: + - count + type: object + rollover: + description: Rolls an alias over to a new index when the + managed index meets one of the rollover conditions. + properties: + minDocCount: + description: The minimum number of documents required + to roll over the index. + format: int64 + type: integer + minIndexAge: + description: The minimum age required to roll over + the index. + type: string + minPrimaryShardSize: + description: The minimum storage size of a single + primary shard required to roll over the index. + type: string + minSize: + description: The minimum size of the total primary + shard storage (not counting replicas) required to + roll over the index. + type: string + type: object + rollup: + description: Periodically reduce data granularity by rolling + up old data into summarized indexes. + type: object + shrink: + description: Allows you to reduce the number of primary + shards in your indexes + properties: + forceUnsafe: + description: If true, executes the shrink action even + if there are no replicas. + type: boolean + maxShardSize: + description: The maximum size in bytes of a shard + for the target index. + type: string + numNewShards: + description: The maximum number of primary shards + in the shrunken index. + type: integer + percentageOfSourceShards: + description: Percentage of the number of original + primary shards to shrink. + format: int64 + type: integer + targetIndexNameTemplate: + description: The name of the shrunken index. + type: string + type: object + snapshot: + description: Back up your cluster’s indexes and state + properties: + repository: + description: The repository name that you register + through the native snapshot API operations. + type: string + snapshot: + description: The name of the snapshot. + type: string + required: + - repository + - snapshot + type: object + timeout: + description: The timeout period for the action. Accepts + time units for minutes, hours, and days. + type: string + type: object + type: array + name: + description: The name of the state. + type: string + transitions: + description: The next states and the conditions required to + transition to those states. If no transitions exist, the policy + assumes that it’s complete and can now stop managing the index + items: + properties: + conditions: + description: conditions for the transition. + properties: + cron: + description: The cron job that triggers the transition + if no other transition happens first. + properties: + cron: + description: A wrapper for the cron job that triggers + the transition if no other transition happens + first. This wrapper is here to adhere to the + OpenSearch API. + properties: + expression: + description: The cron expression that triggers + the transition. + type: string + timezone: + description: The timezone that triggers the + transition. + type: string + required: + - expression + - timezone + type: object + required: + - cron + type: object + minDocCount: + description: The minimum document count of the index + required to transition. + format: int64 + type: integer + minIndexAge: + description: The minimum age of the index required + to transition. + type: string + minRolloverAge: + description: The minimum age required after a rollover + has occurred to transition to the next state. + type: string + minSize: + description: The minimum size of the total primary + shard storage (not counting replicas) required to + transition. + type: string + type: object + stateName: + description: The name of the state to transition to if + the conditions are met. + type: string + required: + - conditions + - stateName + type: object + type: array + required: + - actions + - name + type: object + type: array + required: + - defaultState + - description + - states + type: object + status: + description: OpensearchISMPolicyStatus defines the observed state of OpensearchISMPolicy + properties: + existingISMPolicy: + type: boolean + managedCluster: + description: |- + UID is a type that holds unique ID values, including UUIDs. Because we + don't ONLY use UUIDs, this is an alias to string. Being a type captures + intent and helps make sure that UIDs and names do not get conflated. + type: string + policyId: + type: string + reason: + type: string + state: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchroles.yaml b/packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchroles.yaml new file mode 100644 index 00000000..36ae514a --- /dev/null +++ b/packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchroles.yaml @@ -0,0 +1,123 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.0 + name: opensearchroles.opensearch.opster.io +spec: + group: opensearch.opster.io + names: + kind: OpensearchRole + listKind: OpensearchRoleList + plural: opensearchroles + shortNames: + - opensearchrole + singular: opensearchrole + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + description: OpensearchRole is the Schema for the opensearchroles 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: OpensearchRoleSpec defines the desired state of OpensearchRole + properties: + clusterPermissions: + items: + type: string + type: array + indexPermissions: + items: + properties: + allowedActions: + items: + type: string + type: array + dls: + type: string + fls: + items: + type: string + type: array + indexPatterns: + items: + type: string + type: array + maskedFields: + items: + type: string + type: array + type: object + type: array + opensearchCluster: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + tenantPermissions: + items: + properties: + allowedActions: + items: + type: string + type: array + tenantPatterns: + items: + type: string + type: array + type: object + type: array + required: + - opensearchCluster + type: object + status: + description: OpensearchRoleStatus defines the observed state of OpensearchRole + properties: + existingRole: + type: boolean + managedCluster: + description: |- + UID is a type that holds unique ID values, including UUIDs. Because we + don't ONLY use UUIDs, this is an alias to string. Being a type captures + intent and helps make sure that UIDs and names do not get conflated. + type: string + reason: + type: string + state: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchsnapshotpolicies.yaml b/packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchsnapshotpolicies.yaml new file mode 100644 index 00000000..2c32048d --- /dev/null +++ b/packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchsnapshotpolicies.yaml @@ -0,0 +1,203 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.0 + name: opensearchsnapshotpolicies.opensearch.opster.io +spec: + group: opensearch.opster.io + names: + kind: OpensearchSnapshotPolicy + listKind: OpensearchSnapshotPolicyList + plural: opensearchsnapshotpolicies + singular: opensearchsnapshotpolicy + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: Existing policy state + jsonPath: .status.existingSnapshotPolicy + name: existingpolicy + type: boolean + - description: Snapshot policy name + jsonPath: .status.snapshotPolicyName + name: policyName + type: string + - jsonPath: .status.state + name: state + type: string + - jsonPath: .metadata.creationTimestamp + name: age + type: date + name: v1 + schema: + openAPIV3Schema: + description: OpensearchSnapshotPolicy is the Schema for the opensearchsnapshotpolicies + 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: + properties: + creation: + properties: + schedule: + properties: + cron: + properties: + expression: + type: string + timezone: + type: string + required: + - expression + - timezone + type: object + required: + - cron + type: object + timeLimit: + type: string + required: + - schedule + type: object + deletion: + properties: + deleteCondition: + properties: + maxAge: + type: string + maxCount: + type: integer + minCount: + type: integer + type: object + schedule: + properties: + cron: + properties: + expression: + type: string + timezone: + type: string + required: + - expression + - timezone + type: object + required: + - cron + type: object + timeLimit: + type: string + type: object + description: + type: string + enabled: + type: boolean + notification: + properties: + channel: + properties: + id: + type: string + required: + - id + type: object + conditions: + properties: + creation: + type: boolean + deletion: + type: boolean + failure: + type: boolean + type: object + required: + - channel + type: object + opensearchCluster: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + policyName: + type: string + snapshotConfig: + properties: + dateFormat: + type: string + dateFormatTimezone: + type: string + ignoreUnavailable: + type: boolean + includeGlobalState: + type: boolean + indices: + type: string + metadata: + additionalProperties: + type: string + type: object + partial: + type: boolean + repository: + type: string + required: + - repository + type: object + required: + - creation + - opensearchCluster + - policyName + - snapshotConfig + type: object + status: + description: OpensearchSnapshotPolicyStatus defines the observed state + of OpensearchSnapshotPolicy + properties: + existingSnapshotPolicy: + type: boolean + managedCluster: + description: |- + UID is a type that holds unique ID values, including UUIDs. Because we + don't ONLY use UUIDs, this is an alias to string. Being a type captures + intent and helps make sure that UIDs and names do not get conflated. + type: string + reason: + type: string + snapshotPolicyName: + type: string + state: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchtenants.yaml b/packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchtenants.yaml new file mode 100644 index 00000000..d085f3ca --- /dev/null +++ b/packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchtenants.yaml @@ -0,0 +1,85 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.0 + name: opensearchtenants.opensearch.opster.io +spec: + group: opensearch.opster.io + names: + kind: OpensearchTenant + listKind: OpensearchTenantList + plural: opensearchtenants + shortNames: + - opensearchtenant + singular: opensearchtenant + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + description: OpensearchTenant is the Schema for the opensearchtenants 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: OpensearchTenantSpec defines the desired state of OpensearchTenant + properties: + description: + type: string + opensearchCluster: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + required: + - opensearchCluster + type: object + status: + description: OpensearchTenantStatus defines the observed state of OpensearchTenant + properties: + existingTenant: + type: boolean + managedCluster: + description: |- + UID is a type that holds unique ID values, including UUIDs. Because we + don't ONLY use UUIDs, this is an alias to string. Being a type captures + intent and helps make sure that UIDs and names do not get conflated. + type: string + reason: + type: string + state: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchuserrolebindings.yaml b/packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchuserrolebindings.yaml new file mode 100644 index 00000000..2e9453c6 --- /dev/null +++ b/packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchuserrolebindings.yaml @@ -0,0 +1,109 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.0 + name: opensearchuserrolebindings.opensearch.opster.io +spec: + group: opensearch.opster.io + names: + kind: OpensearchUserRoleBinding + listKind: OpensearchUserRoleBindingList + plural: opensearchuserrolebindings + shortNames: + - opensearchuserrolebinding + singular: opensearchuserrolebinding + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + description: OpensearchUserRoleBinding is the Schema for the opensearchuserrolebindings + 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: OpensearchUserRoleBindingSpec defines the desired state of + OpensearchUserRoleBinding + properties: + backendRoles: + items: + type: string + type: array + opensearchCluster: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + roles: + items: + type: string + type: array + users: + items: + type: string + type: array + required: + - opensearchCluster + - roles + type: object + status: + description: OpensearchUserRoleBindingStatus defines the observed state + of OpensearchUserRoleBinding + properties: + managedCluster: + description: |- + UID is a type that holds unique ID values, including UUIDs. Because we + don't ONLY use UUIDs, this is an alias to string. Being a type captures + intent and helps make sure that UIDs and names do not get conflated. + type: string + provisionedBackendRoles: + items: + type: string + type: array + provisionedRoles: + items: + type: string + type: array + provisionedUsers: + items: + type: string + type: array + reason: + type: string + state: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchusers.yaml b/packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchusers.yaml new file mode 100644 index 00000000..8d573ee7 --- /dev/null +++ b/packages/system/opensearch-operator/charts/opensearch-operator/files/opensearch.opster.io_opensearchusers.yaml @@ -0,0 +1,117 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.16.0 + name: opensearchusers.opensearch.opster.io +spec: + group: opensearch.opster.io + names: + kind: OpensearchUser + listKind: OpensearchUserList + plural: opensearchusers + shortNames: + - opensearchuser + singular: opensearchuser + scope: Namespaced + versions: + - name: v1 + schema: + openAPIV3Schema: + description: OpensearchUser is the Schema for the opensearchusers 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: OpensearchUserSpec defines the desired state of OpensearchUser + properties: + attributes: + additionalProperties: + type: string + type: object + backendRoles: + items: + type: string + type: array + opendistroSecurityRoles: + items: + type: string + type: array + opensearchCluster: + description: |- + LocalObjectReference contains enough information to let you locate the + referenced object inside the same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + passwordFrom: + description: SecretKeySelector selects a key of a Secret. + properties: + key: + description: The key of the secret to select from. Must be a + valid secret key. + type: string + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret or its key must be defined + type: boolean + required: + - key + type: object + x-kubernetes-map-type: atomic + required: + - opensearchCluster + - passwordFrom + type: object + status: + description: OpensearchUserStatus defines the observed state of OpensearchUser + properties: + managedCluster: + description: |- + UID is a type that holds unique ID values, including UUIDs. Because we + don't ONLY use UUIDs, this is an alias to string. Being a type captures + intent and helps make sure that UIDs and names do not get conflated. + type: string + reason: + type: string + state: + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/packages/system/opensearch-operator/charts/opensearch-operator/templates/_helpers.tpl b/packages/system/opensearch-operator/charts/opensearch-operator/templates/_helpers.tpl new file mode 100644 index 00000000..32f313e3 --- /dev/null +++ b/packages/system/opensearch-operator/charts/opensearch-operator/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "opensearch-operator.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "opensearch-operator.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "opensearch-operator.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "opensearch-operator.labels" -}} +helm.sh/chart: {{ include "opensearch-operator.chart" . }} +{{ include "opensearch-operator.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "opensearch-operator.selectorLabels" -}} +app.kubernetes.io/name: {{ include "opensearch-operator.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "opensearch-operator.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (printf "%s-%s" (include "opensearch-operator.fullname" .) "controller-manager") .Values.serviceAccount.name }} +{{- else }} +{{- default "opensearch-operator-controller-manager" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-controller-manager-deployment.yaml b/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-controller-manager-deployment.yaml new file mode 100644 index 00000000..4b5cb194 --- /dev/null +++ b/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-controller-manager-deployment.yaml @@ -0,0 +1,94 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + control-plane: controller-manager + name: {{ include "opensearch-operator.fullname" . }}-controller-manager +spec: + replicas: 1 + selector: + matchLabels: + control-plane: controller-manager + template: + metadata: + labels: + control-plane: controller-manager + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + containers: + {{- if or (.Values.kubeRbacProxy.enable) (eq (.Values.kubeRbacProxy.enable | toString) "") }} + - args: + - --secure-listen-address=0.0.0.0:8443 + - --upstream=http://127.0.0.1:8080/ + - --proxy-endpoints-port=10443 + - --logtostderr=true + - --v=10 + image: "{{ .Values.kubeRbacProxy.image.repository }}:{{ .Values.kubeRbacProxy.image.tag}}" + name: kube-rbac-proxy + resources: +{{- toYaml .Values.kubeRbacProxy.resources | nindent 10 }} + readinessProbe: +{{- toYaml .Values.kubeRbacProxy.readinessProbe | nindent 10 }} + livenessProbe: +{{- toYaml .Values.kubeRbacProxy.livenessProbe | nindent 10 }} + securityContext: +{{- toYaml .Values.kubeRbacProxy.securityContext | nindent 10 }} + ports: + - containerPort: 8443 + name: https + - containerPort: 10443 + name: https-proxy + protocol: TCP + {{- end}} + - args: + - --health-probe-bind-address=:8081 + - --metrics-bind-address=127.0.0.1:8080 + - --leader-elect + {{- if .Values.manager.watchNamespace }} + - --watch-namespace={{ .Values.manager.watchNamespace }} + {{- end }} + - --loglevel={{ .Values.manager.loglevel }} + command: + - /manager + image: "{{ .Values.manager.image.repository }}:{{ .Values.manager.image.tag | default .Chart.AppVersion }}" + name: operator-controller-manager + imagePullPolicy: "{{ .Values.manager.image.pullPolicy }}" + resources: +{{- toYaml .Values.manager.resources | nindent 10 }} + readinessProbe: +{{- toYaml .Values.manager.readinessProbe | nindent 10 }} + livenessProbe: +{{- toYaml .Values.manager.livenessProbe | nindent 10 }} + env: + - name: DNS_BASE + value: {{ .Values.manager.dnsBase }} + - name: PARALLEL_RECOVERY_ENABLED + value: "{{ .Values.manager.parallelRecoveryEnabled }}" + - name: PPROF_ENDPOINTS_ENABLED + value: "{{ .Values.manager.pprofEndpointsEnabled }}" + {{- if .Values.manager.extraEnv }} + {{- toYaml .Values.manager.extraEnv | nindent 8 }} + {{- end }} + securityContext: +{{- toYaml .Values.manager.securityContext | nindent 10 }} + nodeSelector: +{{- toYaml .Values.nodeSelector | nindent 8 }} + tolerations: +{{- toYaml .Values.tolerations | nindent 8 }} + securityContext: +{{- toYaml .Values.securityContext | nindent 8}} + {{- if .Values.manager.imagePullSecrets }} + imagePullSecrets: + {{- toYaml .Values.manager.imagePullSecrets | nindent 6 }} + {{- end }} + serviceAccountName: {{ include "opensearch-operator.serviceAccountName" . }} + terminationGracePeriodSeconds: 10 + {{- if .Values.priorityClassName }} + priorityClassName: {{ .Values.priorityClassName }} + {{- end }} diff --git a/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-controller-manager-metrics-service-svc.yaml b/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-controller-manager-metrics-service-svc.yaml new file mode 100644 index 00000000..e4c6e820 --- /dev/null +++ b/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-controller-manager-metrics-service-svc.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + control-plane: controller-manager + name: {{ include "opensearch-operator.fullname" . }}-controller-manager-metrics-service +spec: + ports: + - name: https + port: 8443 + targetPort: https + selector: + control-plane: controller-manager diff --git a/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-controller-manager-sa.yaml b/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-controller-manager-sa.yaml new file mode 100644 index 00000000..ee5a1ca2 --- /dev/null +++ b/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-controller-manager-sa.yaml @@ -0,0 +1,6 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "opensearch-operator.serviceAccountName" . }} +{{- end -}} diff --git a/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-crds.yaml b/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-crds.yaml new file mode 100644 index 00000000..21fdeedb --- /dev/null +++ b/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-crds.yaml @@ -0,0 +1,5 @@ +{{- if .Values.installCRDs -}} +{{- range $path, $bytes := .Files.Glob "files/*.yaml" }} +{{ $.Files.Get $path }} +{{- end }} +{{- end }} diff --git a/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-leader-election-role-role.yaml b/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-leader-election-role-role.yaml new file mode 100644 index 00000000..aa8853e0 --- /dev/null +++ b/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-leader-election-role-role.yaml @@ -0,0 +1,48 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "opensearch-operator.fullname" . }}-leader-election-role +rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch +- apiGroups: + - monitoring.coreos.com + resources: + - servicemonitors + verbs: + - create + - delete + - get + - list + - patch + - update + - watch diff --git a/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-leader-election-rolebinding-rb.yaml b/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-leader-election-rolebinding-rb.yaml new file mode 100644 index 00000000..654a0a7d --- /dev/null +++ b/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-leader-election-rolebinding-rb.yaml @@ -0,0 +1,11 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "opensearch-operator.fullname" . }}-leader-election-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "opensearch-operator.fullname" . }}-leader-election-role +subjects: +- kind: ServiceAccount + name: {{ include "opensearch-operator.serviceAccountName" . }} diff --git a/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-manager-config-cm.yaml b/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-manager-config-cm.yaml new file mode 100644 index 00000000..c2e9a13f --- /dev/null +++ b/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-manager-config-cm.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +data: + controller_manager_config.yaml: | + apiVersion: controller-runtime.sigs.k8s.io/v1alpha1 + kind: ControllerManagerConfig + health: + healthProbeBindAddress: :8081 + metrics: + bindAddress: 127.0.0.1:8080 + webhook: + port: 9443 + leaderElection: + leaderElect: true + resourceName: a867c7dc.opensearch.opster.io +kind: ConfigMap +metadata: + name: {{ include "opensearch-operator.fullname" . }}-manager-config diff --git a/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-manager-role-cr.yaml b/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-manager-role-cr.yaml new file mode 100644 index 00000000..3daf70d4 --- /dev/null +++ b/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-manager-role-cr.yaml @@ -0,0 +1,414 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + name: {{ include "opensearch-operator.fullname" . }}-{{ .Release.Namespace }}-manager-role +rules: +- apiGroups: + - apps + resources: + - deployments + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - apps + resources: + - statefulsets + - statefulsets/status + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - batch + resources: + - jobs + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - configmaps + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - events + verbs: + - create + - patch + - update +- apiGroups: + - "" + resources: + - namespaces + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - pods + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - secrets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "" + resources: + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - "policy" + resources: + - poddisruptionbudgets + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - opensearch.opster.io + resources: + - events + verbs: + - create + - patch +- apiGroups: + - opensearch.opster.io + resources: + - opensearchactiongroups + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - opensearch.opster.io + resources: + - opensearchactiongroups/finalizers + verbs: + - update +- apiGroups: + - opensearch.opster.io + resources: + - opensearchactiongroups/status + verbs: + - get + - patch + - update +- apiGroups: + - opensearch.opster.io + resources: + - opensearchclusters + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - opensearch.opster.io + resources: + - opensearchclusters/finalizers + verbs: + - update +- apiGroups: + - opensearch.opster.io + resources: + - opensearchclusters/status + verbs: + - get + - patch + - update +- apiGroups: + - opensearch.opster.io + resources: + - opensearchcomponenttemplates + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - opensearch.opster.io + resources: + - opensearchcomponenttemplates/finalizers + verbs: + - update +- apiGroups: + - opensearch.opster.io + resources: + - opensearchcomponenttemplates/status + verbs: + - get + - patch + - update +- apiGroups: + - opensearch.opster.io + resources: + - opensearchindextemplates + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - opensearch.opster.io + resources: + - opensearchindextemplates/finalizers + verbs: + - update +- apiGroups: + - opensearch.opster.io + resources: + - opensearchindextemplates/status + verbs: + - get + - patch + - update +- apiGroups: + - opensearch.opster.io + resources: + - opensearchroles + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - opensearch.opster.io + resources: + - opensearchroles/finalizers + verbs: + - update +- apiGroups: + - opensearch.opster.io + resources: + - opensearchroles/status + verbs: + - get + - patch + - update +- apiGroups: + - opensearch.opster.io + resources: + - opensearchtenants + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - opensearch.opster.io + resources: + - opensearchtenants/finalizers + verbs: + - update +- apiGroups: + - opensearch.opster.io + resources: + - opensearchtenants/status + verbs: + - get + - patch + - update +- apiGroups: + - opensearch.opster.io + resources: + - opensearchuserrolebindings + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - opensearch.opster.io + resources: + - opensearchuserrolebindings/finalizers + verbs: + - update +- apiGroups: + - opensearch.opster.io + resources: + - opensearchuserrolebindings/status + verbs: + - get + - patch + - update +- apiGroups: + - opensearch.opster.io + resources: + - opensearchismpolicies/status + verbs: + - get + - patch + - update +- apiGroups: + - opensearch.opster.io + resources: + - opensearchismpolicies/finalizers + verbs: + - update +- apiGroups: + - opensearch.opster.io + resources: + - opensearchusers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - opensearch.opster.io + resources: + - opensearchismpolicies + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - opensearch.opster.io + resources: + - opensearchusers/finalizers + verbs: + - update +- apiGroups: + - opensearch.opster.io + resources: + - opensearchusers/status + verbs: + - get + - patch + - update +- apiGroups: + - "" + resources: + - persistentvolumeclaims + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - monitoring.coreos.com + resources: + - servicemonitors + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - opensearch.opster.io + resources: + - opensearchsnapshotpolicies + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - opensearch.opster.io + resources: + - opensearchsnapshotpolicies/status + verbs: + - get + - patch + - update +- apiGroups: + - opensearch.opster.io + resources: + - opensearchsnapshotpolicies/finalizers + verbs: + - update \ No newline at end of file diff --git a/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-manager-rolebinding.yaml b/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-manager-rolebinding.yaml new file mode 100644 index 00000000..a528ffdf --- /dev/null +++ b/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-manager-rolebinding.yaml @@ -0,0 +1,27 @@ +{{- if .Values.useRoleBindings }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "opensearch-operator.fullname" . }}-{{ .Release.Namespace }}-manager-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "opensearch-operator.fullname" . }}-{{ .Release.Namespace }}-manager-role +subjects: +- kind: ServiceAccount + name: {{ include "opensearch-operator.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +{{- else }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "opensearch-operator.fullname" . }}-{{ .Release.Namespace }}-manager-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "opensearch-operator.fullname" . }}-{{ .Release.Namespace }}-manager-role +subjects: +- kind: ServiceAccount + name: {{ include "opensearch-operator.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-metrics-reader-cr.yaml b/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-metrics-reader-cr.yaml new file mode 100644 index 00000000..c8f9d89c --- /dev/null +++ b/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-metrics-reader-cr.yaml @@ -0,0 +1,9 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "opensearch-operator.fullname" . }}-{{ .Release.Namespace }}-metrics-reader +rules: +- nonResourceURLs: + - /metrics + verbs: + - get diff --git a/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-proxy-role-cr.yaml b/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-proxy-role-cr.yaml new file mode 100644 index 00000000..cbd6cb77 --- /dev/null +++ b/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-proxy-role-cr.yaml @@ -0,0 +1,17 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "opensearch-operator.fullname" . }}-{{ .Release.Namespace }}-proxy-role +rules: +- apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create diff --git a/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-proxy-rolebinding.yaml b/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-proxy-rolebinding.yaml new file mode 100644 index 00000000..d9a5d339 --- /dev/null +++ b/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-proxy-rolebinding.yaml @@ -0,0 +1,27 @@ +{{- if .Values.useRoleBindings }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "opensearch-operator.fullname" . }}-{{ .Release.Namespace }}-proxy-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "opensearch-operator.fullname" . }}-{{ .Release.Namespace }}-proxy-role +subjects: +- kind: ServiceAccount + name: {{ include "opensearch-operator.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +{{- else }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "opensearch-operator.fullname" . }}-{{ .Release.Namespace }}-proxy-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "opensearch-operator.fullname" . }}-{{ .Release.Namespace }}-proxy-role +subjects: +- kind: ServiceAccount + name: {{ include "opensearch-operator.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/packages/system/opensearch-operator/charts/opensearch-operator/values.yaml b/packages/system/opensearch-operator/charts/opensearch-operator/values.yaml new file mode 100644 index 00000000..1ee839bb --- /dev/null +++ b/packages/system/opensearch-operator/charts/opensearch-operator/values.yaml @@ -0,0 +1,123 @@ +nameOverride: "" +fullnameOverride: "" + +podAnnotations: {} +podLabels: {} +nodeSelector: {} +tolerations: [] +securityContext: + runAsNonRoot: true +priorityClassName: "" +manager: + securityContext: + allowPrivilegeEscalation: false + extraEnv: [] + resources: + limits: + cpu: 200m + memory: 500Mi + requests: + cpu: 100m + memory: 350Mi + + livenessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 8081 + periodSeconds: 15 + successThreshold: 1 + timeoutSeconds: 3 + initialDelaySeconds: 10 + + readinessProbe: + failureThreshold: 3 + httpGet: + path: /readyz + port: 8081 + periodSeconds: 15 + successThreshold: 1 + timeoutSeconds: 3 + initialDelaySeconds: 10 + + # Set this to false to disable the experimental parallel recovery in case you are experiencing problems + parallelRecoveryEnabled: true + # Set this to true to enable the standard go pprof endpoints on port 6060 (https://pkg.go.dev/net/http/pprof) + # Should only be used for debugging purposes + pprofEndpointsEnabled: false + + image: + repository: opensearchproject/opensearch-operator + ## tag default uses appVersion from Chart.yaml, to override specify tag tag: "v1.1" + tag: "" + pullPolicy: "Always" + + ## Optional array of imagePullSecrets containing private registry credentials + imagePullSecrets: [] + # - name: secretName + + dnsBase: cluster.local + + # Log level of the operator. Possible values: debug, info, warn, error + loglevel: info + + # If a watchNamespace is specified, the manager's cache will be restricted to + # watch objects in the desired namespace. Defaults is to watch all namespaces. + watchNamespace: + +# Install the Custom Resource Definitions with Helm +installCRDs: true + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Override the service account name. Defaults to opensearch-operator-controller-manager + name: "" + +kubeRbacProxy: + enable: true + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: + - ALL + resources: + limits: + cpu: 50m + memory: 50Mi + requests: + cpu: 25m + memory: 25Mi + + livenessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 10443 + scheme: HTTPS + periodSeconds: 15 + successThreshold: 1 + timeoutSeconds: 3 + initialDelaySeconds: 10 + + readinessProbe: + failureThreshold: 3 + httpGet: + path: /healthz + port: 10443 + scheme: HTTPS + periodSeconds: 15 + successThreshold: 1 + timeoutSeconds: 3 + initialDelaySeconds: 10 + + image: + repository: "gcr.io/kubebuilder/kube-rbac-proxy" + tag: "v0.15.0" + +## If this is set to true, RoleBindings will be used instead of ClusterRoleBindings, inorder to restrict ClusterRoles +## to the namespace where the operator and OpenSearch cluster are in. In that case, specify the namespace where they +## are in in manager.watchNamespace field. +## If false, ClusterRoleBindings will be used +useRoleBindings: false diff --git a/packages/system/opensearch-operator/templates/sysctl-daemonset.yaml b/packages/system/opensearch-operator/templates/sysctl-daemonset.yaml new file mode 100644 index 00000000..76c3569e --- /dev/null +++ b/packages/system/opensearch-operator/templates/sysctl-daemonset.yaml @@ -0,0 +1,64 @@ +--- +# DaemonSet to configure vm.max_map_count on all nodes for OpenSearch +# This runs once on each node to ensure the kernel parameter is set +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: opensearch-sysctl-setter + labels: + app.kubernetes.io/name: opensearch-sysctl-setter + app.kubernetes.io/component: sysctl +spec: + selector: + matchLabels: + app.kubernetes.io/name: opensearch-sysctl-setter + template: + metadata: + labels: + app.kubernetes.io/name: opensearch-sysctl-setter + spec: + initContainers: + - name: sysctl + image: busybox:1.36 + securityContext: + privileged: true + resources: + requests: + cpu: 1m + memory: 1Mi + limits: + cpu: 50m + memory: 16Mi + command: + - sh + - -c + - | + sysctl -w vm.max_map_count=262144 + # Keep the value persistent by writing to sysctl.conf if writable + if [ -w /host-etc/sysctl.conf ]; then + grep -q "vm.max_map_count" /host-etc/sysctl.conf || echo "vm.max_map_count=262144" >> /host-etc/sysctl.conf + fi + volumeMounts: + - name: host-etc + mountPath: /host-etc + readOnly: false + containers: + - name: pause + image: registry.k8s.io/pause:3.9 + resources: + requests: + cpu: 1m + memory: 1Mi + limits: + cpu: 10m + memory: 10Mi + volumes: + - name: host-etc + hostPath: + path: /etc + type: Directory + tolerations: + - operator: Exists + effect: NoSchedule + - operator: Exists + effect: NoExecute diff --git a/packages/system/opensearch-operator/values.yaml b/packages/system/opensearch-operator/values.yaml new file mode 100644 index 00000000..619aa99b --- /dev/null +++ b/packages/system/opensearch-operator/values.yaml @@ -0,0 +1,4 @@ +opensearch-operator: + manager: + # Enable cluster-wide mode to watch all namespaces + watchNamespace: "" diff --git a/packages/system/opensearch-rd/Chart.yaml b/packages/system/opensearch-rd/Chart.yaml new file mode 100644 index 00000000..c0ca3b86 --- /dev/null +++ b/packages/system/opensearch-rd/Chart.yaml @@ -0,0 +1,3 @@ +apiVersion: v2 +name: opensearch-rd +version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process diff --git a/packages/system/opensearch-rd/Makefile b/packages/system/opensearch-rd/Makefile new file mode 100644 index 00000000..7d3c0311 --- /dev/null +++ b/packages/system/opensearch-rd/Makefile @@ -0,0 +1,4 @@ +export NAME=opensearch-rd +export NAMESPACE=cozy-system + +include ../../../hack/package.mk diff --git a/packages/system/opensearch-rd/cozyrds/opensearch.yaml b/packages/system/opensearch-rd/cozyrds/opensearch.yaml new file mode 100644 index 00000000..be15d8a1 --- /dev/null +++ b/packages/system/opensearch-rd/cozyrds/opensearch.yaml @@ -0,0 +1,42 @@ +apiVersion: cozystack.io/v1alpha1 +kind: ApplicationDefinition +metadata: + name: opensearch +spec: + application: + kind: OpenSearch + singular: opensearch + plural: opensearches + openAPISchema: |- + {"title":"Chart Values","type":"object","properties":{"dashboards":{"description":"OpenSearch Dashboards configuration.","type":"object","default":{},"required":["enabled","replicas","resourcesPreset"],"properties":{"enabled":{"description":"Enable OpenSearch Dashboards deployment.","type":"boolean","default":false},"replicas":{"description":"Number of Dashboards replicas.","type":"integer","default":1},"resources":{"description":"Explicit CPU and memory configuration for Dashboards.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available to each node.","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 node.","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 for Dashboards.","type":"string","default":"medium","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]}}},"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":["opensearch"],"properties":{"opensearch":{"description":"OpenSearch image.","type":"string","default":""}}},"nodeRoles":{"description":"Node roles configuration.","type":"object","default":{},"required":["data","ingest","master","ml"],"properties":{"data":{"description":"Enable data role.","type":"boolean","default":true},"ingest":{"description":"Enable ingest role.","type":"boolean","default":true},"master":{"description":"Enable cluster_manager role.","type":"boolean","default":true},"ml":{"description":"Enable machine learning role.","type":"boolean","default":false}}},"replicas":{"description":"Number of OpenSearch nodes in the cluster.","type":"integer","default":3},"resources":{"description":"Explicit CPU and memory configuration for each OpenSearch node. When omitted, the preset defined in `resourcesPreset` is applied.","type":"object","default":{},"properties":{"cpu":{"description":"CPU available to each node.","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 node.","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. OpenSearch requires minimum 2Gi memory.","type":"string","default":"large","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]},"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":""},"topologySpreadPolicy":{"description":"How strictly to enforce pod distribution across nodes and zones.","type":"string","default":"soft","enum":["soft","hard"]},"users":{"description":"Custom OpenSearch users configuration map.","type":"object","default":{},"additionalProperties":{"type":"object","properties":{"password":{"description":"Password for the user (auto-generated if omitted).","type":"string"},"roles":{"description":"List of OpenSearch roles.","type":"array","items":{"type":"string"}}}}},"version":{"description":"OpenSearch major version to deploy.","type":"string","default":"v2","enum":["v3","v2","v1"]}}} + release: + prefix: opensearch- + labels: + sharding.fluxcd.io/key: tenants + chartRef: + kind: ExternalArtifact + name: cozystack-opensearch-application-default-opensearch + namespace: cozy-system + dashboard: + category: PaaS + singular: OpenSearch + plural: OpenSearch Clusters + description: Managed OpenSearch service + tags: + - database + - search + icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxNDQiIGhlaWdodD0iMTQ0IiByeD0iMjQiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl9vcGVuc2VhcmNoKSIvPgo8cGF0aCBkPSJNNzIgMzZDNDQgMzYgMjggNTIgMjggNzJDMjggOTIgNDQgMTA4IDcyIDEwOEMxMDAgMTA4IDExNiA5MiAxMTYgNzIiIHN0cm9rZT0iIzAwNUVCOCIgc3Ryb2tlLXdpZHRoPSI4IiBzdHJva2UtbGluZWNhcD0icm91bmQiIGZpbGw9Im5vbmUiLz4KPGNpcmNsZSBjeD0iMTE2IiBjeT0iNzIiIHI9IjgiIGZpbGw9IiMwMDVFQjgiLz4KPGRlZnM+CjxsaW5lYXJHcmFkaWVudCBpZD0icGFpbnQwX2xpbmVhcl9vcGVuc2VhcmNoIiB4MT0iMTQwIiB5MT0iMTMwLjUiIHgyPSI0IiB5Mj0iOS40OTk5OSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgo8c3RvcCBzdG9wLWNvbG9yPSIjMDAzQjVDIi8+CjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iIzAwNUVCOCIvPgo8L2xpbmVhckdyYWRpZW50Pgo8L2RlZnM+Cjwvc3ZnPgo= + keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "replicas"], ["spec", "resources"], ["spec", "resourcesPreset"], ["spec", "size"], ["spec", "storageClass"], ["spec", "external"], ["spec", "topologySpreadPolicy"], ["spec", "version"], ["spec", "images"], ["spec", "images", "opensearch"], ["spec", "nodeRoles"], ["spec", "nodeRoles", "master"], ["spec", "nodeRoles", "data"], ["spec", "nodeRoles", "ingest"], ["spec", "nodeRoles", "ml"], ["spec", "users"], ["spec", "dashboards"], ["spec", "dashboards", "enabled"], ["spec", "dashboards", "replicas"], ["spec", "dashboards", "resources"], ["spec", "dashboards", "resourcesPreset"]] + secrets: + exclude: [] + include: + - resourceNames: + - opensearch-{{ .name }}-credentials + services: + exclude: [] + include: + - resourceNames: + - opensearch-{{ .name }} + - opensearch-{{ .name }}-external + - opensearch-{{ .name }}-dashboards + - opensearch-{{ .name }}-dashboards-external diff --git a/packages/system/opensearch-rd/templates/cozyrd.yaml b/packages/system/opensearch-rd/templates/cozyrd.yaml new file mode 100644 index 00000000..e079ddcf --- /dev/null +++ b/packages/system/opensearch-rd/templates/cozyrd.yaml @@ -0,0 +1,4 @@ +{{- range $path, $_ := .Files.Glob "cozyrds/*" }} +--- +{{ $.Files.Get $path }} +{{- end }} diff --git a/packages/system/opensearch-rd/values.yaml b/packages/system/opensearch-rd/values.yaml new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/packages/system/opensearch-rd/values.yaml @@ -0,0 +1 @@ +{} From 4e59e5e656e59ca9b9055ae04cc1ea836d132756 Mon Sep 17 00:00:00 2001 From: Matthieu Date: Mon, 16 Feb 2026 22:28:35 +0100 Subject: [PATCH 002/488] Add PackageSource definitions for OpenSearch Add operator and application PackageSource CRs to expose OpenSearch in the Cozystack platform: - opensearch-operator: operator deployment in cozy-opensearch-operator namespace - opensearch-application: app + resource definition with cozy-lib integration This enables OpenSearch to appear in the platform dashboard and be deployed by tenants. Co-Authored-By: Claude Sonnet 4.5 Signed-off-by: Matthieu --- .../sources/opensearch-application.yaml | 28 +++++++++++++++++++ .../platform/sources/opensearch-operator.yaml | 22 +++++++++++++++ 2 files changed, 50 insertions(+) create mode 100644 packages/core/platform/sources/opensearch-application.yaml create mode 100644 packages/core/platform/sources/opensearch-operator.yaml diff --git a/packages/core/platform/sources/opensearch-application.yaml b/packages/core/platform/sources/opensearch-application.yaml new file mode 100644 index 00000000..2b499cd0 --- /dev/null +++ b/packages/core/platform/sources/opensearch-application.yaml @@ -0,0 +1,28 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.opensearch-application +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + - cozystack.opensearch-operator + libraries: + - name: cozy-lib + path: library/cozy-lib + components: + - name: opensearch + path: apps/opensearch + libraries: ["cozy-lib"] + - name: opensearch-rd + path: system/opensearch-rd + install: + namespace: cozy-system + releaseName: opensearch-rd diff --git a/packages/core/platform/sources/opensearch-operator.yaml b/packages/core/platform/sources/opensearch-operator.yaml new file mode 100644 index 00000000..21dd7a43 --- /dev/null +++ b/packages/core/platform/sources/opensearch-operator.yaml @@ -0,0 +1,22 @@ +--- +apiVersion: cozystack.io/v1alpha1 +kind: PackageSource +metadata: + name: cozystack.opensearch-operator +spec: + sourceRef: + kind: OCIRepository + name: cozystack-packages + namespace: cozy-system + path: / + variants: + - name: default + dependsOn: + - cozystack.networking + - cozystack.prometheus-operator-crds + components: + - name: opensearch-operator + path: system/opensearch-operator + install: + namespace: cozy-opensearch-operator + releaseName: opensearch-operator From 7181fd1c937a05e94073519c21135cc2d3dbbd8b Mon Sep 17 00:00:00 2001 From: Matthieu Date: Mon, 16 Feb 2026 22:40:01 +0100 Subject: [PATCH 003/488] Fix critical issues in OpenSearch operator templates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address CodeRabbit review feedback: - Fix extraEnv indentation (nindent 8 → 10) in controller-manager deployment - Fix imagePullSecrets indentation (nindent 6 → 8) for proper YAML alignment - Change proxy-rolebinding from conditional RoleBinding to always ClusterRoleBinding (required for cluster-scoped tokenreviews/subjectaccessreviews permissions) - Pin operator chart version to 2.8.0 in Makefile for reproducible builds Co-Authored-By: Claude Sonnet 4.5 Signed-off-by: Matthieu --- packages/system/opensearch-operator/Makefile | 2 +- ...ch-operator-controller-manager-deployment.yaml | 4 ++-- .../opensearch-operator-proxy-rolebinding.yaml | 15 --------------- 3 files changed, 3 insertions(+), 18 deletions(-) diff --git a/packages/system/opensearch-operator/Makefile b/packages/system/opensearch-operator/Makefile index 71b94549..36522495 100644 --- a/packages/system/opensearch-operator/Makefile +++ b/packages/system/opensearch-operator/Makefile @@ -7,4 +7,4 @@ update: rm -rf charts helm repo add opensearch-operator https://opensearch-project.github.io/opensearch-k8s-operator/ helm repo update opensearch-operator - helm pull opensearch-operator/opensearch-operator --untar --untardir charts + helm pull opensearch-operator/opensearch-operator --version 2.8.0 --untar --untardir charts diff --git a/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-controller-manager-deployment.yaml b/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-controller-manager-deployment.yaml index 4b5cb194..43a70d65 100644 --- a/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-controller-manager-deployment.yaml +++ b/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-controller-manager-deployment.yaml @@ -73,7 +73,7 @@ spec: - name: PPROF_ENDPOINTS_ENABLED value: "{{ .Values.manager.pprofEndpointsEnabled }}" {{- if .Values.manager.extraEnv }} - {{- toYaml .Values.manager.extraEnv | nindent 8 }} + {{- toYaml .Values.manager.extraEnv | nindent 10 }} {{- end }} securityContext: {{- toYaml .Values.manager.securityContext | nindent 10 }} @@ -85,7 +85,7 @@ spec: {{- toYaml .Values.securityContext | nindent 8}} {{- if .Values.manager.imagePullSecrets }} imagePullSecrets: - {{- toYaml .Values.manager.imagePullSecrets | nindent 6 }} + {{- toYaml .Values.manager.imagePullSecrets | nindent 8 }} {{- end }} serviceAccountName: {{ include "opensearch-operator.serviceAccountName" . }} terminationGracePeriodSeconds: 10 diff --git a/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-proxy-rolebinding.yaml b/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-proxy-rolebinding.yaml index d9a5d339..5cba0693 100644 --- a/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-proxy-rolebinding.yaml +++ b/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-proxy-rolebinding.yaml @@ -1,17 +1,3 @@ -{{- if .Values.useRoleBindings }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: {{ include "opensearch-operator.fullname" . }}-{{ .Release.Namespace }}-proxy-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: {{ include "opensearch-operator.fullname" . }}-{{ .Release.Namespace }}-proxy-role -subjects: -- kind: ServiceAccount - name: {{ include "opensearch-operator.serviceAccountName" . }} - namespace: {{ .Release.Namespace }} -{{- else }} apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: @@ -24,4 +10,3 @@ subjects: - kind: ServiceAccount name: {{ include "opensearch-operator.serviceAccountName" . }} namespace: {{ .Release.Namespace }} -{{- end }} From f7767d1ee695d9ef25e8a79d0e46617a1c018ab6 Mon Sep 17 00:00:00 2001 From: Matthieu Date: Mon, 16 Feb 2026 22:48:28 +0100 Subject: [PATCH 004/488] Fix YAML style issues in OpenSearch packages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address CodeRabbit nitpick feedback: - Fix inconsistent indentation in opensearch-application.yaml (4 spaces → 2 spaces) to match opensearch-operator.yaml style - Add missing space in controller-manager-deployment.yaml template (nindent 8}} → nindent 8 }}) for consistency Co-Authored-By: Claude Sonnet 4.5 Signed-off-by: Matthieu --- .../sources/opensearch-application.yaml | 32 +++++++++---------- ...perator-controller-manager-deployment.yaml | 2 +- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/packages/core/platform/sources/opensearch-application.yaml b/packages/core/platform/sources/opensearch-application.yaml index 2b499cd0..19358c21 100644 --- a/packages/core/platform/sources/opensearch-application.yaml +++ b/packages/core/platform/sources/opensearch-application.yaml @@ -10,19 +10,19 @@ spec: namespace: cozy-system path: / variants: - - name: default - dependsOn: - - cozystack.networking - - cozystack.opensearch-operator - libraries: - - name: cozy-lib - path: library/cozy-lib - components: - - name: opensearch - path: apps/opensearch - libraries: ["cozy-lib"] - - name: opensearch-rd - path: system/opensearch-rd - install: - namespace: cozy-system - releaseName: opensearch-rd + - name: default + dependsOn: + - cozystack.networking + - cozystack.opensearch-operator + libraries: + - name: cozy-lib + path: library/cozy-lib + components: + - name: opensearch + path: apps/opensearch + libraries: ["cozy-lib"] + - name: opensearch-rd + path: system/opensearch-rd + install: + namespace: cozy-system + releaseName: opensearch-rd diff --git a/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-controller-manager-deployment.yaml b/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-controller-manager-deployment.yaml index 43a70d65..114e6b20 100644 --- a/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-controller-manager-deployment.yaml +++ b/packages/system/opensearch-operator/charts/opensearch-operator/templates/opensearch-operator-controller-manager-deployment.yaml @@ -82,7 +82,7 @@ spec: tolerations: {{- toYaml .Values.tolerations | nindent 8 }} securityContext: -{{- toYaml .Values.securityContext | nindent 8}} +{{- toYaml .Values.securityContext | nindent 8 }} {{- if .Values.manager.imagePullSecrets }} imagePullSecrets: {{- toYaml .Values.manager.imagePullSecrets | nindent 8 }} From 47d81f70d7f9fcf012741e46072bf2fb8796e60e Mon Sep 17 00:00:00 2001 From: Myasnikov Daniil Date: Fri, 27 Feb 2026 14:02:42 +0500 Subject: [PATCH 005/488] Disabled private key rotation in CA certs Signed-off-by: Myasnikov Daniil --- packages/extra/etcd/templates/etcd-cluster.yaml | 2 ++ packages/system/cozystack-api/templates/certmanager.yaml | 2 ++ .../templates/admission-webhooks/cert-manager.yaml | 2 ++ packages/system/kubeovn-webhook/templates/certmanager.yaml | 2 ++ .../lineage-controller-webhook/templates/certmanager.yaml | 2 ++ .../charts/linstor-scheduler/templates/certmanager.yaml | 2 ++ packages/system/linstor/templates/linstor-api-tls.yaml | 2 ++ packages/system/linstor/templates/linstor-internal-tls.yaml | 2 ++ .../seaweedfs/charts/seaweedfs/templates/cert/ca-cert.yaml | 2 ++ .../charts/victoria-metrics-operator/templates/webhook.yaml | 2 ++ 10 files changed, 20 insertions(+) diff --git a/packages/extra/etcd/templates/etcd-cluster.yaml b/packages/extra/etcd/templates/etcd-cluster.yaml index 44851cc6..a604b448 100644 --- a/packages/extra/etcd/templates/etcd-cluster.yaml +++ b/packages/extra/etcd/templates/etcd-cluster.yaml @@ -104,6 +104,7 @@ spec: - {{ .Release.Name }} secretName: etcd-peer-ca-tls privateKey: + rotationPolicy: Never algorithm: RSA size: 4096 issuerRef: @@ -130,6 +131,7 @@ spec: - {{ .Release.Name }} secretName: etcd-ca-tls privateKey: + rotationPolicy: Never algorithm: RSA size: 4096 issuerRef: diff --git a/packages/system/cozystack-api/templates/certmanager.yaml b/packages/system/cozystack-api/templates/certmanager.yaml index def27bd1..4f73768a 100644 --- a/packages/system/cozystack-api/templates/certmanager.yaml +++ b/packages/system/cozystack-api/templates/certmanager.yaml @@ -18,6 +18,8 @@ spec: issuerRef: name: cozystack-api-selfsigned isCA: true + privateKey: + rotationPolicy: Never --- apiVersion: cert-manager.io/v1 kind: Issuer diff --git a/packages/system/ingress-nginx/charts/ingress-nginx/templates/admission-webhooks/cert-manager.yaml b/packages/system/ingress-nginx/charts/ingress-nginx/templates/admission-webhooks/cert-manager.yaml index db2946c3..fab1e02e 100644 --- a/packages/system/ingress-nginx/charts/ingress-nginx/templates/admission-webhooks/cert-manager.yaml +++ b/packages/system/ingress-nginx/charts/ingress-nginx/templates/admission-webhooks/cert-manager.yaml @@ -23,6 +23,8 @@ spec: name: {{ include "ingress-nginx.fullname" . }}-self-signed-issuer commonName: "ca.webhook.ingress-nginx" isCA: true + privateKey: + rotationPolicy: Never subject: organizations: - ingress-nginx diff --git a/packages/system/kubeovn-webhook/templates/certmanager.yaml b/packages/system/kubeovn-webhook/templates/certmanager.yaml index be0f5af1..f8eee740 100644 --- a/packages/system/kubeovn-webhook/templates/certmanager.yaml +++ b/packages/system/kubeovn-webhook/templates/certmanager.yaml @@ -18,6 +18,8 @@ spec: issuerRef: name: {{ include "namespace-annotation-webhook.fullname" . }}-selfsigned-issuer isCA: true + privateKey: + rotationPolicy: Never --- apiVersion: cert-manager.io/v1 kind: Issuer diff --git a/packages/system/lineage-controller-webhook/templates/certmanager.yaml b/packages/system/lineage-controller-webhook/templates/certmanager.yaml index c2dc18d2..11b7f21a 100644 --- a/packages/system/lineage-controller-webhook/templates/certmanager.yaml +++ b/packages/system/lineage-controller-webhook/templates/certmanager.yaml @@ -18,6 +18,8 @@ spec: issuerRef: name: lineage-controller-webhook-selfsigned isCA: true + privateKey: + rotationPolicy: Never --- apiVersion: cert-manager.io/v1 kind: Issuer diff --git a/packages/system/linstor-scheduler/charts/linstor-scheduler/templates/certmanager.yaml b/packages/system/linstor-scheduler/charts/linstor-scheduler/templates/certmanager.yaml index 3942555b..760a9d5d 100644 --- a/packages/system/linstor-scheduler/charts/linstor-scheduler/templates/certmanager.yaml +++ b/packages/system/linstor-scheduler/charts/linstor-scheduler/templates/certmanager.yaml @@ -24,6 +24,8 @@ spec: issuerRef: name: {{ include "linstor-scheduler.fullname" . }}-admission-selfsigned isCA: true + privateKey: + rotationPolicy: Never --- apiVersion: cert-manager.io/v1 kind: Issuer diff --git a/packages/system/linstor/templates/linstor-api-tls.yaml b/packages/system/linstor/templates/linstor-api-tls.yaml index eeda9731..055e6d08 100644 --- a/packages/system/linstor/templates/linstor-api-tls.yaml +++ b/packages/system/linstor/templates/linstor-api-tls.yaml @@ -9,6 +9,8 @@ spec: secretName: linstor-api-ca duration: 87600h # 10 years isCA: true + privateKey: + rotationPolicy: Never usages: - signing - key encipherment diff --git a/packages/system/linstor/templates/linstor-internal-tls.yaml b/packages/system/linstor/templates/linstor-internal-tls.yaml index 5d536625..fe5ff4b1 100644 --- a/packages/system/linstor/templates/linstor-internal-tls.yaml +++ b/packages/system/linstor/templates/linstor-internal-tls.yaml @@ -9,6 +9,8 @@ spec: secretName: linstor-internal-ca duration: 87600h # 10 years isCA: true + privateKey: + rotationPolicy: Never usages: - signing - key encipherment diff --git a/packages/system/seaweedfs/charts/seaweedfs/templates/cert/ca-cert.yaml b/packages/system/seaweedfs/charts/seaweedfs/templates/cert/ca-cert.yaml index b01a8dcc..a38287de 100644 --- a/packages/system/seaweedfs/charts/seaweedfs/templates/cert/ca-cert.yaml +++ b/packages/system/seaweedfs/charts/seaweedfs/templates/cert/ca-cert.yaml @@ -13,6 +13,8 @@ spec: secretName: {{ template "seaweedfs.name" . }}-ca-cert commonName: "{{ template "seaweedfs.name" . }}-root-ca" isCA: true + privateKey: + rotationPolicy: Never {{- if .Values.certificates.ca.duration }} duration: {{ .Values.certificates.ca.duration }} {{- end }} diff --git a/packages/system/victoria-metrics-operator/charts/victoria-metrics-operator/templates/webhook.yaml b/packages/system/victoria-metrics-operator/charts/victoria-metrics-operator/templates/webhook.yaml index 2e027ab4..1bead84d 100644 --- a/packages/system/victoria-metrics-operator/charts/victoria-metrics-operator/templates/webhook.yaml +++ b/packages/system/victoria-metrics-operator/charts/victoria-metrics-operator/templates/webhook.yaml @@ -78,6 +78,8 @@ spec: name: {{ $fullname }}-root commonName: {{ $certManager.ca.commonName }} isCA: true + privateKey: + rotationPolicy: Never --- apiVersion: cert-manager.io/v1 kind: Issuer From 013b5b0873ae88e09f96750bc0a65372bd4769b5 Mon Sep 17 00:00:00 2001 From: Myasnikov Daniil Date: Mon, 2 Mar 2026 12:35:14 +0500 Subject: [PATCH 006/488] Replaced direct chart edits with patch files Signed-off-by: Myasnikov Daniil --- packages/system/ingress-nginx/Makefile | 1 + .../patches/disable-ca-key-rotation.patch | 13 +++++++++++++ packages/system/linstor-scheduler/Makefile | 1 + .../patches/disable-ca-key-rotation.patch | 13 +++++++++++++ packages/system/seaweedfs/Makefile | 1 + .../seaweedfs/patches/disable-ca-key-rotation.patch | 13 +++++++++++++ packages/system/victoria-metrics-operator/Makefile | 1 + .../patches/disable-ca-key-rotation.patch | 13 +++++++++++++ 8 files changed, 56 insertions(+) create mode 100644 packages/system/ingress-nginx/patches/disable-ca-key-rotation.patch create mode 100644 packages/system/linstor-scheduler/patches/disable-ca-key-rotation.patch create mode 100644 packages/system/seaweedfs/patches/disable-ca-key-rotation.patch create mode 100644 packages/system/victoria-metrics-operator/patches/disable-ca-key-rotation.patch diff --git a/packages/system/ingress-nginx/Makefile b/packages/system/ingress-nginx/Makefile index 573c253b..81d78744 100644 --- a/packages/system/ingress-nginx/Makefile +++ b/packages/system/ingress-nginx/Makefile @@ -8,5 +8,6 @@ update: helm repo update ingress-nginx helm pull ingress-nginx/ingress-nginx --untar --untardir charts patch --no-backup-if-mismatch -p 3 < patches/add-metrics2.patch + patch --no-backup-if-mismatch -p4 < patches/disable-ca-key-rotation.patch rm -f charts/ingress-nginx/templates/controller-deployment.yaml.orig rm -rf charts/ingress-nginx/changelog/ diff --git a/packages/system/ingress-nginx/patches/disable-ca-key-rotation.patch b/packages/system/ingress-nginx/patches/disable-ca-key-rotation.patch new file mode 100644 index 00000000..721795e4 --- /dev/null +++ b/packages/system/ingress-nginx/patches/disable-ca-key-rotation.patch @@ -0,0 +1,13 @@ +diff --git a/packages/system/ingress-nginx/charts/ingress-nginx/templates/admission-webhooks/cert-manager.yaml b/packages/system/ingress-nginx/charts/ingress-nginx/templates/admission-webhooks/cert-manager.yaml +index db2946c3..fab1e02e 100644 +--- a/packages/system/ingress-nginx/charts/ingress-nginx/templates/admission-webhooks/cert-manager.yaml ++++ b/packages/system/ingress-nginx/charts/ingress-nginx/templates/admission-webhooks/cert-manager.yaml +@@ -23,6 +23,8 @@ spec: + name: {{ include "ingress-nginx.fullname" . }}-self-signed-issuer + commonName: "ca.webhook.ingress-nginx" + isCA: true ++ privateKey: ++ rotationPolicy: Never + subject: + organizations: + - ingress-nginx diff --git a/packages/system/linstor-scheduler/Makefile b/packages/system/linstor-scheduler/Makefile index 79b50c28..f9986c9a 100644 --- a/packages/system/linstor-scheduler/Makefile +++ b/packages/system/linstor-scheduler/Makefile @@ -8,3 +8,4 @@ update: helm repo add piraeus-charts https://piraeus.io/helm-charts/ helm repo update piraeus-charts helm pull piraeus-charts/linstor-scheduler --untar --untardir charts + patch --no-backup-if-mismatch -p4 < patches/disable-ca-key-rotation.patch diff --git a/packages/system/linstor-scheduler/patches/disable-ca-key-rotation.patch b/packages/system/linstor-scheduler/patches/disable-ca-key-rotation.patch new file mode 100644 index 00000000..45715d6b --- /dev/null +++ b/packages/system/linstor-scheduler/patches/disable-ca-key-rotation.patch @@ -0,0 +1,13 @@ +diff --git a/packages/system/linstor-scheduler/charts/linstor-scheduler/templates/certmanager.yaml b/packages/system/linstor-scheduler/charts/linstor-scheduler/templates/certmanager.yaml +index 3942555b..760a9d5d 100644 +--- a/packages/system/linstor-scheduler/charts/linstor-scheduler/templates/certmanager.yaml ++++ b/packages/system/linstor-scheduler/charts/linstor-scheduler/templates/certmanager.yaml +@@ -24,6 +24,8 @@ spec: + issuerRef: + name: {{ include "linstor-scheduler.fullname" . }}-admission-selfsigned + isCA: true ++ privateKey: ++ rotationPolicy: Never + --- + apiVersion: cert-manager.io/v1 + kind: Issuer diff --git a/packages/system/seaweedfs/Makefile b/packages/system/seaweedfs/Makefile index c3a2f777..cd938a4a 100644 --- a/packages/system/seaweedfs/Makefile +++ b/packages/system/seaweedfs/Makefile @@ -11,5 +11,6 @@ update: tar xzvf - --strip 3 -C charts seaweedfs-$${version}/k8s/charts/seaweedfs patch --no-backup-if-mismatch -p4 < patches/resize-api-server-annotation.diff patch --no-backup-if-mismatch -p4 < patches/s3-traffic-distribution.patch + patch --no-backup-if-mismatch -p4 < patches/disable-ca-key-rotation.patch #patch --no-backup-if-mismatch -p4 < patches/retention-policy-delete.yaml diff --git a/packages/system/seaweedfs/patches/disable-ca-key-rotation.patch b/packages/system/seaweedfs/patches/disable-ca-key-rotation.patch new file mode 100644 index 00000000..8e2fe0f3 --- /dev/null +++ b/packages/system/seaweedfs/patches/disable-ca-key-rotation.patch @@ -0,0 +1,13 @@ +diff --git a/packages/system/seaweedfs/charts/seaweedfs/templates/cert/ca-cert.yaml b/packages/system/seaweedfs/charts/seaweedfs/templates/cert/ca-cert.yaml +index b01a8dcc..a38287de 100644 +--- a/packages/system/seaweedfs/charts/seaweedfs/templates/cert/ca-cert.yaml ++++ b/packages/system/seaweedfs/charts/seaweedfs/templates/cert/ca-cert.yaml +@@ -13,6 +13,8 @@ spec: + secretName: {{ template "seaweedfs.name" . }}-ca-cert + commonName: "{{ template "seaweedfs.name" . }}-root-ca" + isCA: true ++ privateKey: ++ rotationPolicy: Never + {{- if .Values.certificates.ca.duration }} + duration: {{ .Values.certificates.ca.duration }} + {{- end }} diff --git a/packages/system/victoria-metrics-operator/Makefile b/packages/system/victoria-metrics-operator/Makefile index 981a1dbc..bb57f807 100644 --- a/packages/system/victoria-metrics-operator/Makefile +++ b/packages/system/victoria-metrics-operator/Makefile @@ -9,3 +9,4 @@ update: helm repo add vm https://victoriametrics.github.io/helm-charts/ helm repo update vm helm pull vm/victoria-metrics-operator --untar --untardir charts + patch --no-backup-if-mismatch -p4 < patches/disable-ca-key-rotation.patch diff --git a/packages/system/victoria-metrics-operator/patches/disable-ca-key-rotation.patch b/packages/system/victoria-metrics-operator/patches/disable-ca-key-rotation.patch new file mode 100644 index 00000000..c8a0bfbc --- /dev/null +++ b/packages/system/victoria-metrics-operator/patches/disable-ca-key-rotation.patch @@ -0,0 +1,13 @@ +diff --git a/packages/system/victoria-metrics-operator/charts/victoria-metrics-operator/templates/webhook.yaml b/packages/system/victoria-metrics-operator/charts/victoria-metrics-operator/templates/webhook.yaml +index 2e027ab4..1bead84d 100644 +--- a/packages/system/victoria-metrics-operator/charts/victoria-metrics-operator/templates/webhook.yaml ++++ b/packages/system/victoria-metrics-operator/charts/victoria-metrics-operator/templates/webhook.yaml +@@ -78,6 +78,8 @@ spec: + name: {{ $fullname }}-root + commonName: {{ $certManager.ca.commonName }} + isCA: true ++ privateKey: ++ rotationPolicy: Never + --- + apiVersion: cert-manager.io/v1 + kind: Issuer From 14228aa0d760ea54633a13dff777b22d6f8aec6d Mon Sep 17 00:00:00 2001 From: Kirill Ilin Date: Mon, 2 Mar 2026 14:35:04 +0500 Subject: [PATCH 007/488] fix(keycloak): replace deprecated KC_PROXY with KC_PROXY_HEADERS KC_PROXY=edge was deprecated and removed in Keycloak 26.x, causing "Non-secure context detected" warnings and broken cookie handling behind reverse proxy. Replace with KC_PROXY_HEADERS=xforwarded and KC_HTTP_ENABLED=true. Co-Authored-By: Claude Signed-off-by: Kirill Ilin --- packages/system/keycloak/templates/sts.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/system/keycloak/templates/sts.yaml b/packages/system/keycloak/templates/sts.yaml index 1cdbec62..c7506f8f 100644 --- a/packages/system/keycloak/templates/sts.yaml +++ b/packages/system/keycloak/templates/sts.yaml @@ -82,8 +82,10 @@ spec: value: "ispn" - name: KC_CACHE_STACK value: "kubernetes" - - name: KC_PROXY - value: "edge" + - name: KC_PROXY_HEADERS + value: "xforwarded" + - name: KC_HTTP_ENABLED + value: "true" - name: KEYCLOAK_ADMIN value: admin - name: KEYCLOAK_ADMIN_PASSWORD From 79c57874bbd1cda73102cd364b2e5b34d676293d Mon Sep 17 00:00:00 2001 From: Myasnikov Daniil Date: Mon, 2 Mar 2026 15:34:13 +0500 Subject: [PATCH 008/488] [platform] Fixed run-migrations script Signed-off-by: Myasnikov Daniil --- packages/core/platform/images/migrations/run-migrations.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/platform/images/migrations/run-migrations.sh b/packages/core/platform/images/migrations/run-migrations.sh index c35ade21..a8224ef7 100755 --- a/packages/core/platform/images/migrations/run-migrations.sh +++ b/packages/core/platform/images/migrations/run-migrations.sh @@ -24,7 +24,7 @@ if [ "$CURRENT_VERSION" -ge "$TARGET_VERSION" ]; then fi # Run migrations sequentially from current version to target version -for i in $(seq $((CURRENT_VERSION + 1)) $TARGET_VERSION); do +for i in $(seq $CURRENT_VERSION $((TARGET_VERSION - 1))); do if [ -f "/migrations/$i" ]; then echo "Running migration $i" chmod +x /migrations/$i From dc5c3dc9bc11e2f160094432dd709c8a912999d1 Mon Sep 17 00:00:00 2001 From: Myasnikov Daniil Date: Mon, 2 Mar 2026 15:36:20 +0500 Subject: [PATCH 009/488] [rabbitmq] Added app version selection Signed-off-by: Myasnikov Daniil --- packages/apps/rabbitmq/Chart.yaml | 2 +- packages/apps/rabbitmq/Makefile | 4 + packages/apps/rabbitmq/README.md | 1 + packages/apps/rabbitmq/files/versions.yaml | 4 + .../apps/rabbitmq/hack/update-versions.sh | 129 ++++++++++++++++++ .../apps/rabbitmq/templates/_versions.tpl | 8 ++ .../apps/rabbitmq/templates/rabbitmq.yaml | 1 + packages/apps/rabbitmq/values.schema.json | 11 ++ packages/apps/rabbitmq/values.yaml | 9 ++ .../platform/images/migrations/migrations/34 | 37 +++++ packages/core/platform/values.yaml | 2 +- .../system/rabbitmq-rd/cozyrds/rabbitmq.yaml | 4 +- 12 files changed, 208 insertions(+), 4 deletions(-) create mode 100644 packages/apps/rabbitmq/files/versions.yaml create mode 100755 packages/apps/rabbitmq/hack/update-versions.sh create mode 100644 packages/apps/rabbitmq/templates/_versions.tpl create mode 100755 packages/core/platform/images/migrations/migrations/34 diff --git a/packages/apps/rabbitmq/Chart.yaml b/packages/apps/rabbitmq/Chart.yaml index 13500669..e764e7dd 100644 --- a/packages/apps/rabbitmq/Chart.yaml +++ b/packages/apps/rabbitmq/Chart.yaml @@ -4,4 +4,4 @@ description: Managed RabbitMQ service icon: /logos/rabbitmq.svg type: application version: 0.0.0 # Placeholder, the actual version will be automatically set during the build process -appVersion: "3.13.2" +appVersion: "4.2.4" diff --git a/packages/apps/rabbitmq/Makefile b/packages/apps/rabbitmq/Makefile index d1cfda8e..aa9b0ccc 100644 --- a/packages/apps/rabbitmq/Makefile +++ b/packages/apps/rabbitmq/Makefile @@ -3,3 +3,7 @@ include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md ../../../hack/update-crd.sh + +update: + hack/update-versions.sh + make generate diff --git a/packages/apps/rabbitmq/README.md b/packages/apps/rabbitmq/README.md index 4b19c7b3..b1f01f21 100644 --- a/packages/apps/rabbitmq/README.md +++ b/packages/apps/rabbitmq/README.md @@ -23,6 +23,7 @@ The service utilizes official RabbitMQ operator. This ensures the reliability an | `size` | Persistent Volume Claim size available for application data. | `quantity` | `10Gi` | | `storageClass` | StorageClass used to store the data. | `string` | `""` | | `external` | Enable external access from outside the cluster. | `bool` | `false` | +| `version` | RabbitMQ major.minor version to deploy | `string` | `v4.2` | ### Application-specific parameters diff --git a/packages/apps/rabbitmq/files/versions.yaml b/packages/apps/rabbitmq/files/versions.yaml new file mode 100644 index 00000000..0cf87dd0 --- /dev/null +++ b/packages/apps/rabbitmq/files/versions.yaml @@ -0,0 +1,4 @@ +"v4.2": "4.2.4" +"v4.1": "4.1.8" +"v4.0": "4.0.9" +"v3.13": "3.13.7" diff --git a/packages/apps/rabbitmq/hack/update-versions.sh b/packages/apps/rabbitmq/hack/update-versions.sh new file mode 100755 index 00000000..7dec5a84 --- /dev/null +++ b/packages/apps/rabbitmq/hack/update-versions.sh @@ -0,0 +1,129 @@ +#!/usr/bin/env bash + +set -o errexit +set -o nounset +set -o pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +RABBITMQ_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" +VALUES_FILE="${RABBITMQ_DIR}/values.yaml" +VERSIONS_FILE="${RABBITMQ_DIR}/files/versions.yaml" +GITHUB_API_URL="https://api.github.com/repos/rabbitmq/rabbitmq-server/releases" + +# Check if jq is installed +if ! command -v jq &> /dev/null; then + echo "Error: jq is not installed. Please install jq and try again." >&2 + exit 1 +fi + +# Fetch releases from GitHub API +echo "Fetching releases from GitHub API..." +RELEASES_JSON=$(curl -sSL "${GITHUB_API_URL}?per_page=100") + +if [ -z "$RELEASES_JSON" ]; then + echo "Error: Could not fetch releases from GitHub API" >&2 + exit 1 +fi + +# Extract stable release tags (format: v3.13.7, v4.0.3, etc.) +# Filter out pre-releases and draft releases +RELEASE_TAGS=$(echo "$RELEASES_JSON" | jq -r '.[] | select(.prerelease == false) | select(.draft == false) | .tag_name' | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | sort -V) + +if [ -z "$RELEASE_TAGS" ]; then + echo "Error: Could not find any stable release tags" >&2 + exit 1 +fi + +echo "Found release tags: $(echo "$RELEASE_TAGS" | tr '\n' ' ')" + +# Supported major.minor versions (newest first) +# We support the last few minor releases of each active major +SUPPORTED_MAJORS=("4.2" "4.1" "4.0" "3.13") + +# Build versions map: major.minor -> latest patch version +declare -A VERSION_MAP +MAJOR_VERSIONS=() + +for major_minor in "${SUPPORTED_MAJORS[@]}"; do + # Find the latest patch version for this major.minor + MATCHING=$(echo "$RELEASE_TAGS" | grep -E "^v${major_minor//./\\.}\.[0-9]+$" | tail -n1) + + if [ -n "$MATCHING" ]; then + # Strip the 'v' prefix for the value (Docker tag format is e.g. 3.13.7) + TAG_VERSION="${MATCHING#v}" + VERSION_MAP["v${major_minor}"]="${TAG_VERSION}" + MAJOR_VERSIONS+=("v${major_minor}") + echo "Found version: v${major_minor} -> ${TAG_VERSION}" + else + echo "Warning: No stable releases found for ${major_minor}, skipping..." >&2 + fi +done + +if [ ${#MAJOR_VERSIONS[@]} -eq 0 ]; then + echo "Error: No matching versions found" >&2 + exit 1 +fi + +echo "Major versions to add: ${MAJOR_VERSIONS[*]}" + +# Create/update versions.yaml file +echo "Updating $VERSIONS_FILE..." +{ + for major_ver in "${MAJOR_VERSIONS[@]}"; do + echo "\"${major_ver}\": \"${VERSION_MAP[$major_ver]}\"" + done +} > "$VERSIONS_FILE" + +echo "Successfully updated $VERSIONS_FILE" + +# Update values.yaml - enum with major.minor versions only +TEMP_FILE=$(mktemp) +trap "rm -f $TEMP_FILE" EXIT + +# Build new version section +NEW_VERSION_SECTION="## @enum {string} Version" +for major_ver in "${MAJOR_VERSIONS[@]}"; do + NEW_VERSION_SECTION="${NEW_VERSION_SECTION} +## @value $major_ver" +done +NEW_VERSION_SECTION="${NEW_VERSION_SECTION} + +## @param {Version} version - RabbitMQ major.minor version to deploy +version: ${MAJOR_VERSIONS[0]}" + +# Check if version section already exists +if grep -q "^## @enum {string} Version" "$VALUES_FILE"; then + # Version section exists, update it using awk + echo "Updating existing version section in $VALUES_FILE..." + + awk -v new_section="$NEW_VERSION_SECTION" ' + /^## @enum {string} Version/ { + in_section = 1 + print new_section + next + } + in_section && /^version: / { + in_section = 0 + next + } + in_section { + next + } + { print } + ' "$VALUES_FILE" > "$TEMP_FILE.tmp" + mv "$TEMP_FILE.tmp" "$VALUES_FILE" +else + # Version section doesn't exist, insert it before Application-specific parameters section + echo "Inserting new version section in $VALUES_FILE..." + + awk -v new_section="$NEW_VERSION_SECTION" ' + /^## @section Application-specific parameters/ { + print new_section + print "" + } + { print } + ' "$VALUES_FILE" > "$TEMP_FILE.tmp" + mv "$TEMP_FILE.tmp" "$VALUES_FILE" +fi + +echo "Successfully updated $VALUES_FILE with major.minor versions: ${MAJOR_VERSIONS[*]}" diff --git a/packages/apps/rabbitmq/templates/_versions.tpl b/packages/apps/rabbitmq/templates/_versions.tpl new file mode 100644 index 00000000..76955b33 --- /dev/null +++ b/packages/apps/rabbitmq/templates/_versions.tpl @@ -0,0 +1,8 @@ +{{- define "rabbitmq.versionMap" }} +{{- $versionMap := .Files.Get "files/versions.yaml" | fromYaml }} +{{- if not (hasKey $versionMap .Values.version) }} + {{- printf `RabbitMQ version %s is not supported, allowed versions are %s` $.Values.version (keys $versionMap) | fail }} +{{- end }} +{{- index $versionMap .Values.version }} +{{- end }} + diff --git a/packages/apps/rabbitmq/templates/rabbitmq.yaml b/packages/apps/rabbitmq/templates/rabbitmq.yaml index b111285e..bbf2efbe 100644 --- a/packages/apps/rabbitmq/templates/rabbitmq.yaml +++ b/packages/apps/rabbitmq/templates/rabbitmq.yaml @@ -7,6 +7,7 @@ metadata: app.kubernetes.io/managed-by: {{ .Release.Service }} spec: replicas: {{ .Values.replicas }} + image: 'rabbitmq:{{ include "rabbitmq.versionMap" $ }}-management' {{- if .Values.external }} service: type: LoadBalancer diff --git a/packages/apps/rabbitmq/values.schema.json b/packages/apps/rabbitmq/values.schema.json index 04b8fa11..87b1a6da 100644 --- a/packages/apps/rabbitmq/values.schema.json +++ b/packages/apps/rabbitmq/values.schema.json @@ -92,6 +92,17 @@ } } }, + "version": { + "description": "RabbitMQ major.minor version to deploy", + "type": "string", + "default": "v4.2", + "enum": [ + "v4.2", + "v4.1", + "v4.0", + "v3.13" + ] + }, "vhosts": { "description": "Virtual hosts configuration map.", "type": "object", diff --git a/packages/apps/rabbitmq/values.yaml b/packages/apps/rabbitmq/values.yaml index 65d5de44..08b6f5e6 100644 --- a/packages/apps/rabbitmq/values.yaml +++ b/packages/apps/rabbitmq/values.yaml @@ -34,6 +34,15 @@ storageClass: "" external: false ## +## @enum {string} Version +## @value v4.2 +## @value v4.1 +## @value v4.0 +## @value v3.13 + +## @param {Version} version - RabbitMQ major.minor version to deploy +version: v4.2 + ## @section Application-specific parameters ## diff --git a/packages/core/platform/images/migrations/migrations/34 b/packages/core/platform/images/migrations/migrations/34 new file mode 100755 index 00000000..57204ff0 --- /dev/null +++ b/packages/core/platform/images/migrations/migrations/34 @@ -0,0 +1,37 @@ +#!/bin/sh +# Migration 34 --> 35 +# Backfill spec.version on rabbitmq.apps.cozystack.io resources. +# +# Before this migration RabbitMQ had no user-selectable version; the +# operator always used its built-in default image (v3.x). A version field +# was added in this release. Without this migration every existing cluster +# would be upgraded to the new default (v4.2) on the next reconcile. +# +# Set spec.version to "v3.13" for any rabbitmq app resource that does not +# already have it set. + +set -euo pipefail + +DEFAULT_VERSION="v3.13" +RABBITMQS=$(kubectl get rabbitmqs.apps.cozystack.io -A -o jsonpath='{range .items[*]}{.metadata.namespace}/{.metadata.name}{"\n"}{end}') +for resource in $RABBITMQS; do + NS="${resource%%/*}" + APP_NAME="${resource##*/}" + + # Skip if spec.version is already set + CURRENT_VER=$(kubectl get rabbitmqs.apps.cozystack.io -n "$NS" "$APP_NAME" \ + -o jsonpath='{.spec.version}') + if [ -n "$CURRENT_VER" ]; then + echo "SKIP $NS/$APP_NAME: spec.version already set to '$CURRENT_VER'" + continue + fi + + echo "Patching rabbitmq/$APP_NAME in $NS: setting version=$DEFAULT_VERSION" + + kubectl patch rabbitmqs.apps.cozystack.io -n "$NS" "$APP_NAME" --type=merge \ + --patch "{\"spec\":{\"version\":\"${DEFAULT_VERSION}\"}}" +done + +# Stamp version +kubectl create configmap -n cozy-system cozystack-version \ + --from-literal=version=35 --dry-run=client -o yaml | kubectl apply -f- diff --git a/packages/core/platform/values.yaml b/packages/core/platform/values.yaml index 155cc78a..80ebb936 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:v1.0.0@sha256:68dabdebc38ac439228ae07031cc70e0fa184a24bd4e5b3b22c17466b2a55201 - targetVersion: 34 + targetVersion: 35 # Bundle deployment configuration bundles: system: diff --git a/packages/system/rabbitmq-rd/cozyrds/rabbitmq.yaml b/packages/system/rabbitmq-rd/cozyrds/rabbitmq.yaml index 1bbed02d..6ca2b79f 100644 --- a/packages/system/rabbitmq-rd/cozyrds/rabbitmq.yaml +++ b/packages/system/rabbitmq-rd/cozyrds/rabbitmq.yaml @@ -8,7 +8,7 @@ spec: plural: rabbitmqs singular: rabbitmq openAPISchema: |- - {"title":"Chart Values","type":"object","properties":{"external":{"description":"Enable external access from outside the cluster.","type":"boolean","default":false},"replicas":{"description":"Number of RabbitMQ replicas.","type":"integer","default":3},"resources":{"description":"Explicit CPU and memory configuration for each RabbitMQ 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":"nano","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]},"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.","type":"string"}}}},"vhosts":{"description":"Virtual hosts configuration map.","type":"object","default":{},"additionalProperties":{"type":"object","required":["roles"],"properties":{"roles":{"description":"Virtual host roles list.","type":"object","properties":{"admin":{"description":"List of admin users.","type":"array","items":{"type":"string"}},"readonly":{"description":"List of readonly users.","type":"array","items":{"type":"string"}}}}}}}}} + {"title":"Chart Values","type":"object","properties":{"external":{"description":"Enable external access from outside the cluster.","type":"boolean","default":false},"replicas":{"description":"Number of RabbitMQ replicas.","type":"integer","default":3},"resources":{"description":"Explicit CPU and memory configuration for each RabbitMQ 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":"nano","enum":["nano","micro","small","medium","large","xlarge","2xlarge"]},"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.","type":"string"}}}},"version":{"description":"RabbitMQ major.minor version to deploy","type":"string","default":"v4.2","enum":["v4.2","v4.1","v4.0","v3.13"]},"vhosts":{"description":"Virtual hosts configuration map.","type":"object","default":{},"additionalProperties":{"type":"object","required":["roles"],"properties":{"roles":{"description":"Virtual host roles list.","type":"object","properties":{"admin":{"description":"List of admin users.","type":"array","items":{"type":"string"}},"readonly":{"description":"List of readonly users.","type":"array","items":{"type":"string"}}}}}}}}} release: prefix: rabbitmq- labels: @@ -25,7 +25,7 @@ spec: tags: - messaging icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHg9Ii0wLjAwMTk1MzEyIiB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgcng9IjI0IiBmaWxsPSJ1cmwoI3BhaW50MF9saW5lYXJfNjgzXzI5NzIpIi8+CjxwYXRoIGQ9Ik0xMTEuNDExIDYyLjhIODIuNDkzOUM4MS43OTY5IDYyLjc5OTcgODEuMTI4NSA2Mi41MjI4IDgwLjYzNTYgNjIuMDMwMUM4MC4xNDI3IDYxLjUzNzMgNzkuODY1NiA2MC44NjkxIDc5Ljg2NTMgNjAuMTcyMlYzMC4wNDEyQzc5Ljg2NTMgMjcuODEgNzguMDU1NiAyNiA3NS44MjQ5IDI2SDY1LjUwMjFDNjMuMjcgMjYgNjEuNDYxNCAyNy44MSA2MS40NjE0IDMwLjA0MTJWNTkuOTg5OEM2MS40NjE0IDYxLjU0MzUgNjAuMjA1IDYyLjgwNjUgNTguNjUwOCA2Mi44MTMzTDQ5LjE3NDMgNjIuODU4NEM0Ny42MDY5IDYyLjg2NjkgNDYuMzMzNiA2MS41OTU1IDQ2LjMzNjYgNjAuMDI5OEw0Ni4zOTU0IDMwLjA0OEM0Ni40MDA1IDI3LjgxMzQgNDQuNTkwMiAyNiA0Mi4zNTUgMjZIMzIuMDQwN0MyOS44MDgzIDI2IDI4IDI3LjgxIDI4IDMwLjA0MTJWMTE0LjQxMkMyOCAxMTYuMzk0IDI5LjYwNjEgMTE4IDMxLjU4NzEgMTE4SDExMS40MTFDMTEzLjM5NCAxMTggMTE1IDExNi4zOTQgMTE1IDExNC40MTJWNjYuMzg4QzExNSA2NC40MDU4IDExMy4zOTQgNjIuOCAxMTEuNDExIDYyLjhaTTk3Ljg1MDggOTQuNDc3OUM5Ny44NTA4IDk3LjA3NTUgOTUuNzQ0NSA5OS4xODE3IDkzLjE0NjQgOTkuMTgxN0g4NC45ODg0QzgyLjM5IDk5LjE4MTcgODAuMjgzNiA5Ny4wNzU1IDgwLjI4MzYgOTQuNDc3OVY4Ni4zMjE3QzgwLjI4MzYgODMuNzIzOCA4Mi4zOSA4MS42MTc5IDg0Ljk4ODQgODEuNjE3OUg5My4xNDY0Qzk1Ljc0NDUgODEuNjE3OSA5Ny44NTA4IDgzLjcyMzggOTcuODUwOCA4Ni4zMjE3Vjk0LjQ3NzlaIiBmaWxsPSJ3aGl0ZSIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyXzY4M18yOTcyIiB4MT0iNSIgeTE9Ii03LjUiIHgyPSIxNDEiIHkyPSIxMjQuNSIgZ3JhZGllbnRVbml0cz0idXNlclNwYWNlT25Vc2UiPgo8c3RvcCBzdG9wLWNvbG9yPSIjRkY4MjJGIi8+CjxzdG9wIG9mZnNldD0iMSIgc3RvcC1jb2xvcj0iI0ZGNjYwMCIvPgo8L2xpbmVhckdyYWRpZW50Pgo8L2RlZnM+Cjwvc3ZnPgo= - keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "replicas"], ["spec", "resources"], ["spec", "resourcesPreset"], ["spec", "size"], ["spec", "storageClass"], ["spec", "external"], ["spec", "users"], ["spec", "vhosts"]] + keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "replicas"], ["spec", "resources"], ["spec", "resourcesPreset"], ["spec", "size"], ["spec", "storageClass"], ["spec", "external"], ["spec", "version"], ["spec", "users"], ["spec", "vhosts"]] secrets: exclude: [] include: From 14a9017932871b781b4006a0b459c7a085b579f4 Mon Sep 17 00:00:00 2001 From: Andrei Kvapil Date: Mon, 2 Mar 2026 12:56:44 +0100 Subject: [PATCH 010/488] fix(migration): suspend cozy-proxy if it conflicts with installer release In v0.41.x, cozy-proxy HelmRelease was configured with releaseName: cozystack, which collides with the installer helm release. If not suspended before upgrade, the cozy-proxy HR reconciles and overwrites the installer release, deleting cozystack-operator. Add a check in the migration script that detects this conflict and suspends the cozy-proxy HelmRelease before proceeding. Co-Authored-By: Claude Signed-off-by: Andrei Kvapil --- hack/migrate-to-version-1.0.sh | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/hack/migrate-to-version-1.0.sh b/hack/migrate-to-version-1.0.sh index bf648926..471a417d 100755 --- a/hack/migrate-to-version-1.0.sh +++ b/hack/migrate-to-version-1.0.sh @@ -56,6 +56,30 @@ else fi echo "" +# Step 1: Check for cozy-proxy HelmRelease with conflicting releaseName +# In v0.41.x, cozy-proxy was incorrectly configured with releaseName "cozystack", +# which conflicts with the installer helm release name. If not suspended, cozy-proxy +# HelmRelease will overwrite the installer release and delete cozystack-operator. +COZY_PROXY_RELEASE_NAME=$(kubectl get hr -n "$NAMESPACE" cozy-proxy -o jsonpath='{.spec.releaseName}' 2>/dev/null || true) +if [ "$COZY_PROXY_RELEASE_NAME" = "cozystack" ]; then + echo "WARNING: HelmRelease cozy-proxy has releaseName 'cozystack', which conflicts" + echo "with the installer release. It must be suspended before proceeding, otherwise" + echo "it will overwrite the installer and delete cozystack-operator." + echo "" + read -p "Suspend HelmRelease cozy-proxy? (y/N) " -n 1 -r + echo "" + if [[ $REPLY =~ ^[Yy]$ ]]; then + kubectl -n "$NAMESPACE" patch hr cozy-proxy --type=merge --field-manager=flux-client-side-apply -p '{"spec":{"suspend":true}}' + echo "HelmRelease cozy-proxy suspended." + else + echo "ERROR: Cannot proceed with conflicting cozy-proxy HelmRelease active." + echo "Please suspend it manually:" + echo " kubectl -n $NAMESPACE patch hr cozy-proxy --type=merge -p '{\"spec\":{\"suspend\":true}}'" + exit 1 + fi + echo "" +fi + # Read ConfigMap cozystack echo "Reading ConfigMap cozystack..." COZYSTACK_CM=$(kubectl get configmap -n "$NAMESPACE" cozystack -o json 2>/dev/null || echo "{}") From 41f2c64241a46ee40f8abf5a4bf488dbcb392e13 Mon Sep 17 00:00:00 2001 From: IvanHunters Date: Mon, 2 Mar 2026 16:22:20 +0300 Subject: [PATCH 011/488] fix(migration): protect cozy-keycloak namespace during v1.0 migration The migration script only annotated cozy-system namespace with helm.sh/resource-policy=keep, leaving cozy-keycloak unprotected. This could cause Helm to delete the namespace and all its secrets (including keycloak credentials and realm data) during the migration to Package-based configuration. Signed-off-by: IvanHunters --- hack/migrate-to-version-1.0.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hack/migrate-to-version-1.0.sh b/hack/migrate-to-version-1.0.sh index bf648926..ff727276 100755 --- a/hack/migrate-to-version-1.0.sh +++ b/hack/migrate-to-version-1.0.sh @@ -39,6 +39,7 @@ echo "The following resources will be annotated with helm.sh/resource-policy=kee echo "to prevent Helm from deleting them when the installer release is removed:" echo " - Namespace: $NAMESPACE" echo " - ConfigMap: $NAMESPACE/cozystack-version" +echo " - Namespace: cozy-keycloak" echo "" read -p "Do you want to annotate these resources? (y/N) " -n 1 -r echo "" @@ -48,6 +49,10 @@ if [[ $REPLY =~ ^[Yy]$ ]]; then kubectl annotate namespace "$NAMESPACE" helm.sh/resource-policy=keep --overwrite echo "Annotating ConfigMap cozystack-version..." kubectl annotate configmap -n "$NAMESPACE" cozystack-version helm.sh/resource-policy=keep --overwrite 2>/dev/null || echo " ConfigMap cozystack-version not found, skipping." + + echo "Annotating namespace cozy-keycloak..." + kubectl annotate namespace cozy-keycloak helm.sh/resource-policy=keep --overwrite 2>/dev/null || echo " Namespace cozy-keycloak not found, skipping." + echo "" echo "Resources annotated successfully." else From c2bf8cf56fb6e33a5df5aff1ee22b228cbd3393f Mon Sep 17 00:00:00 2001 From: Kirill Ilin Date: Mon, 2 Mar 2026 21:26:51 +0500 Subject: [PATCH 012/488] feat(dashboard): add storageClass dropdown for all stateful apps Replace plain text input with an API-backed listInput dropdown for storageClass fields across all applications that expose them. The dropdown fetches available StorageClasses from the cluster via /api/clusters/{cluster}/k8s/apis/storage.k8s.io/v1/storageclasses, following the same pattern as the instanceType dropdown for VMInstance. Top-level spec.storageClass: ClickHouse, Harbor, HTTPCache, Kubernetes, MariaDB, MongoDB, NATS, OpenBAO, Postgres, Qdrant, RabbitMQ, Redis, VMDisk. Nested paths: FoundationDB (spec.storage.storageClass), Kafka (spec.kafka.storageClass and spec.zookeeper.storageClass). Co-Authored-By: Claude Signed-off-by: Kirill Ilin --- .../dashboard/customformsoverride.go | 28 ++++++++ .../dashboard/customformsoverride_test.go | 66 +++++++++++++++++++ 2 files changed, 94 insertions(+) diff --git a/internal/controller/dashboard/customformsoverride.go b/internal/controller/dashboard/customformsoverride.go index 67b131cf..97912fea 100644 --- a/internal/controller/dashboard/customformsoverride.go +++ b/internal/controller/dashboard/customformsoverride.go @@ -214,6 +214,34 @@ func applyListInputOverrides(schema map[string]any, kind string, openAPIProps ma "keysToLabel": []any{"metadata", "name"}, }, } + + case "ClickHouse", "Harbor", "HTTPCache", "Kubernetes", "MariaDB", "MongoDB", + "NATS", "OpenBAO", "Postgres", "Qdrant", "RabbitMQ", "Redis", "VMDisk": + specProps := ensureSchemaPath(schema, "spec") + specProps["storageClass"] = storageClassListInput() + + case "FoundationDB": + storageProps := ensureSchemaPath(schema, "spec", "storage") + storageProps["storageClass"] = storageClassListInput() + + case "Kafka": + kafkaProps := ensureSchemaPath(schema, "spec", "kafka") + kafkaProps["storageClass"] = storageClassListInput() + zkProps := ensureSchemaPath(schema, "spec", "zookeeper") + zkProps["storageClass"] = storageClassListInput() + } +} + +// storageClassListInput returns a listInput field config for a storageClass dropdown +// backed by the cluster's available StorageClasses. +func storageClassListInput() map[string]any { + return map[string]any{ + "type": "listInput", + "customProps": map[string]any{ + "valueUri": "/api/clusters/{cluster}/k8s/apis/storage.k8s.io/v1/storageclasses", + "keysToValue": []any{"metadata", "name"}, + "keysToLabel": []any{"metadata", "name"}, + }, } } diff --git a/internal/controller/dashboard/customformsoverride_test.go b/internal/controller/dashboard/customformsoverride_test.go index 37da1244..2677cfa8 100644 --- a/internal/controller/dashboard/customformsoverride_test.go +++ b/internal/controller/dashboard/customformsoverride_test.go @@ -232,6 +232,72 @@ func TestApplyListInputOverrides_VMInstance(t *testing.T) { } } +func TestApplyListInputOverrides_StorageClassSimple(t *testing.T) { + for _, kind := range []string{ + "ClickHouse", "Harbor", "HTTPCache", "Kubernetes", "MariaDB", "MongoDB", + "NATS", "OpenBAO", "Postgres", "Qdrant", "RabbitMQ", "Redis", "VMDisk", + } { + t.Run(kind, func(t *testing.T) { + schema := map[string]any{} + applyListInputOverrides(schema, kind, map[string]any{}) + + specProps := schema["properties"].(map[string]any)["spec"].(map[string]any)["properties"].(map[string]any) + sc, ok := specProps["storageClass"].(map[string]any) + if !ok { + t.Fatalf("storageClass not found in spec.properties for kind %s", kind) + } + assertStorageClassListInput(t, sc) + }) + } +} + +func TestApplyListInputOverrides_StorageClassFoundationDB(t *testing.T) { + schema := map[string]any{} + applyListInputOverrides(schema, "FoundationDB", map[string]any{}) + + storageProps := schema["properties"].(map[string]any)["spec"].(map[string]any)["properties"].(map[string]any)["storage"].(map[string]any)["properties"].(map[string]any) + sc, ok := storageProps["storageClass"].(map[string]any) + if !ok { + t.Fatal("storageClass not found in spec.storage.properties") + } + assertStorageClassListInput(t, sc) +} + +func TestApplyListInputOverrides_StorageClassKafka(t *testing.T) { + schema := map[string]any{} + applyListInputOverrides(schema, "Kafka", map[string]any{}) + + specProps := schema["properties"].(map[string]any)["spec"].(map[string]any)["properties"].(map[string]any) + + kafkaSC, ok := specProps["kafka"].(map[string]any)["properties"].(map[string]any)["storageClass"].(map[string]any) + if !ok { + t.Fatal("storageClass not found in spec.kafka.properties") + } + assertStorageClassListInput(t, kafkaSC) + + zkSC, ok := specProps["zookeeper"].(map[string]any)["properties"].(map[string]any)["storageClass"].(map[string]any) + if !ok { + t.Fatal("storageClass not found in spec.zookeeper.properties") + } + assertStorageClassListInput(t, zkSC) +} + +// assertStorageClassListInput verifies that a field is a correctly configured storageClass listInput. +func assertStorageClassListInput(t *testing.T, field map[string]any) { + t.Helper() + if field["type"] != "listInput" { + t.Errorf("expected type listInput, got %v", field["type"]) + } + customProps, ok := field["customProps"].(map[string]any) + if !ok { + t.Fatal("customProps not found") + } + expectedURI := "/api/clusters/{cluster}/k8s/apis/storage.k8s.io/v1/storageclasses" + if customProps["valueUri"] != expectedURI { + t.Errorf("expected valueUri %s, got %v", expectedURI, customProps["valueUri"]) + } +} + func TestApplyListInputOverrides_UnknownKind(t *testing.T) { schema := map[string]any{} applyListInputOverrides(schema, "SomeOtherKind", map[string]any{}) From 87fca4dfe280fd35b4f594b4f75fad7b20a90085 Mon Sep 17 00:00:00 2001 From: IvanHunters Date: Mon, 2 Mar 2026 20:07:01 +0300 Subject: [PATCH 013/488] fix(migration): protect keycloak HelmReleases during v1.0 upgrade In v0.41.x keycloak HelmReleases were created directly by the platform chart (helmreleases.yaml). In v1.0 this template was removed and keycloak is managed via Package API. Without protection, Helm deletes these HelmReleases during upgrade, triggering FluxCD to run helm uninstall and destroying all keycloak data (credentials, realm config, PostgreSQL DB). Add migration 34 that annotates keycloak, keycloak-operator, and keycloak-configure HelmReleases with helm.sh/resource-policy=keep before the platform chart upgrade. This allows the Package operator to adopt existing HelmReleases and FluxCD to perform helm upgrade instead of a fresh install, preserving all data. Signed-off-by: IvanHunters --- .../platform/images/migrations/migrations/34 | 34 +++++++++++++++++++ packages/core/platform/values.yaml | 2 +- 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100755 packages/core/platform/images/migrations/migrations/34 diff --git a/packages/core/platform/images/migrations/migrations/34 b/packages/core/platform/images/migrations/migrations/34 new file mode 100755 index 00000000..13d3be5f --- /dev/null +++ b/packages/core/platform/images/migrations/migrations/34 @@ -0,0 +1,34 @@ +#!/bin/sh +# Migration 34 --> 35 +# Protect keycloak HelmReleases from deletion during platform chart upgrade. +# +# In v0.41.x keycloak, keycloak-operator, and keycloak-configure HelmReleases +# were created directly by the platform chart (helmreleases.yaml). In v1.0 +# this template was removed and keycloak is managed via Package API instead. +# Without the keep annotation, Helm deletes these HelmReleases during upgrade, +# which triggers FluxCD to run helm uninstall, destroying all keycloak data +# (credentials, realm configuration, PostgreSQL database). +# +# The keep annotation ensures the HelmReleases survive the chart upgrade so +# the Package operator can adopt them and FluxCD performs a helm upgrade +# instead of a fresh install. + +set -euo pipefail + +echo "=== Protecting keycloak HelmReleases from deletion ===" + +for hr_name in keycloak keycloak-operator keycloak-configure; do + if kubectl get helmrelease "$hr_name" -n cozy-keycloak --no-headers 2>/dev/null | grep -q .; then + echo " [ANNOTATE] hr/${hr_name} in cozy-keycloak" + kubectl annotate helmrelease "$hr_name" -n cozy-keycloak \ + helm.sh/resource-policy=keep --overwrite + else + echo " [SKIP] hr/${hr_name} not found in cozy-keycloak" + fi +done + +echo "=== Done ===" + +# Stamp version +kubectl create configmap -n cozy-system cozystack-version \ + --from-literal=version=35 --dry-run=client -o yaml | kubectl apply -f- diff --git a/packages/core/platform/values.yaml b/packages/core/platform/values.yaml index 155cc78a..80ebb936 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:v1.0.0@sha256:68dabdebc38ac439228ae07031cc70e0fa184a24bd4e5b3b22c17466b2a55201 - targetVersion: 34 + targetVersion: 35 # Bundle deployment configuration bundles: system: From a3ccb4f87d9b89bbbf791183720f7507ee72e276 Mon Sep 17 00:00:00 2001 From: Kirill Ilin Date: Mon, 2 Mar 2026 22:18:53 +0500 Subject: [PATCH 014/488] feat(dashboard): allow clearing instanceType field in VMInstance form Update openapi-k8s-toolkit to release/1.4.0 (d6b9e4ad). The previous value-binding layout refactor is already included upstream, so drop the formlistinput-value-binding.diff patch. Add formlistinput-allow-empty.diff patch which introduces two props to the listInput component: - allowEmpty: when set, auto-persists the field so BFF sends an empty value instead of falling back to the schema default - persistType: controls the type of empty value ('str' | 'number' | 'arr' | 'obj'), allowing the feature to work correctly for any field type Set allowEmpty: true on the VMInstance instanceType field so users can explicitly clear the selection and override the default instance type. Co-Authored-By: Claude Signed-off-by: Kirill Ilin --- .../patches/formlistinput-allow-empty.diff | 37 ++++++++++++++ .../patches/formlistinput-value-binding.diff | 49 ------------------- 2 files changed, 37 insertions(+), 49 deletions(-) create mode 100644 packages/system/dashboard/images/openapi-ui/openapi-k8s-toolkit/patches/formlistinput-allow-empty.diff delete mode 100644 packages/system/dashboard/images/openapi-ui/openapi-k8s-toolkit/patches/formlistinput-value-binding.diff diff --git a/packages/system/dashboard/images/openapi-ui/openapi-k8s-toolkit/patches/formlistinput-allow-empty.diff b/packages/system/dashboard/images/openapi-ui/openapi-k8s-toolkit/patches/formlistinput-allow-empty.diff new file mode 100644 index 00000000..1c83df7c --- /dev/null +++ b/packages/system/dashboard/images/openapi-ui/openapi-k8s-toolkit/patches/formlistinput-allow-empty.diff @@ -0,0 +1,37 @@ +diff --git a/src/localTypes/formExtensions.ts b/src/localTypes/formExtensions.ts +--- a/src/localTypes/formExtensions.ts ++++ b/src/localTypes/formExtensions.ts +@@ -59,2 +59,4 @@ + relatedValuePath?: string ++ allowEmpty?: boolean ++ persistType?: 'str' | 'number' | 'arr' | 'obj' + } +diff --git a/src/components/molecules/BlackholeForm/molecules/FormListInput/FormListInput.tsx b/src/components/molecules/BlackholeForm/molecules/FormListInput/FormListInput.tsx +--- a/src/components/molecules/BlackholeForm/molecules/FormListInput/FormListInput.tsx ++++ b/src/components/molecules/BlackholeForm/molecules/FormListInput/FormListInput.tsx +@@ -149,3 +149,10 @@ + }, [relatedPath, form, arrName, fixedName, relatedFieldValue, onValuesChangeCallBack, isTouchedPeristed]) + ++ // When allowEmpty is set, auto-persist the field so the BFF preserves empty values ++ useEffect(() => { ++ if (customProps.allowEmpty) { ++ persistedControls.onPersistMark(persistName || name, customProps.persistType ?? 'str') ++ } ++ }, [customProps.allowEmpty, customProps.persistType, persistedControls, persistName, name]) ++ + const uri = prepareTemplate({ +@@ -267,5 +274,14 @@ + validateTrigger="onBlur" + hasFeedback={designNewLayout ? { icons: feedbackIcons } : true} + style={{ flex: 1 }} ++ normalize={(value: unknown) => { ++ if (customProps.allowEmpty && (value === undefined || value === null)) { ++ if (customProps.persistType === 'number') return 0 ++ if (customProps.persistType === 'arr') return [] ++ if (customProps.persistType === 'obj') return {} ++ return '' ++ } ++ return value ++ }} + > + = ({ - showSearch - style={{ width: '100%' }} - /> -- {relatedValueTooltip && ( -- -- -- -- )} -- -- -+ -+ {relatedValueTooltip && ( -+ -+ -+ -+ )} -+ - - ) - } From 6e8ce65e495fadd7dd260fa076acc4727672d0e1 Mon Sep 17 00:00:00 2001 From: Kirill Ilin Date: Mon, 2 Mar 2026 22:19:54 +0500 Subject: [PATCH 015/488] feat(dashboard): set allowEmpty on instanceType and update openapi-ui toolkit Update openapi-k8s-toolkit commit to d6b9e4ad (release/1.4.0) which includes the FormListInput layout refactor, making formlistinput-value-binding.diff obsolete. Set allowEmpty: true on the VMInstance instanceType field so users can explicitly clear the selection and override the default instance type. Co-Authored-By: Claude Signed-off-by: Kirill Ilin --- internal/controller/dashboard/customformsoverride.go | 1 + internal/controller/dashboard/customformsoverride_test.go | 4 ++++ packages/system/dashboard/images/openapi-ui/Dockerfile | 2 +- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/internal/controller/dashboard/customformsoverride.go b/internal/controller/dashboard/customformsoverride.go index 67b131cf..cfacb2fa 100644 --- a/internal/controller/dashboard/customformsoverride.go +++ b/internal/controller/dashboard/customformsoverride.go @@ -195,6 +195,7 @@ func applyListInputOverrides(schema map[string]any, kind string, openAPIProps ma "valueUri": "/api/clusters/{cluster}/k8s/apis/instancetype.kubevirt.io/v1beta1/virtualmachineclusterinstancetypes", "keysToValue": []any{"metadata", "name"}, "keysToLabel": []any{"metadata", "name"}, + "allowEmpty": true, }, } if prop, _ := openAPIProps["instanceType"].(map[string]any); prop != nil { diff --git a/internal/controller/dashboard/customformsoverride_test.go b/internal/controller/dashboard/customformsoverride_test.go index 37da1244..76fc4f83 100644 --- a/internal/controller/dashboard/customformsoverride_test.go +++ b/internal/controller/dashboard/customformsoverride_test.go @@ -202,6 +202,10 @@ func TestApplyListInputOverrides_VMInstance(t *testing.T) { t.Errorf("expected valueUri %s, got %v", expectedURI, customProps["valueUri"]) } + if customProps["allowEmpty"] != true { + t.Errorf("expected allowEmpty true, got %v", customProps["allowEmpty"]) + } + // Check disks[].name is a listInput disks, ok := specProps["disks"].(map[string]any) if !ok { diff --git a/packages/system/dashboard/images/openapi-ui/Dockerfile b/packages/system/dashboard/images/openapi-ui/Dockerfile index 4b80c960..82cc6db0 100644 --- a/packages/system/dashboard/images/openapi-ui/Dockerfile +++ b/packages/system/dashboard/images/openapi-ui/Dockerfile @@ -6,7 +6,7 @@ FROM node:${NODE_VERSION}-alpine AS openapi-k8s-toolkit-builder RUN apk add git WORKDIR /src # release/1.4.0 -ARG COMMIT=c67029cc7b7495c65ee0406033576e773a73bb01 +ARG COMMIT=d6b9e4ad0d1eb9d3730f7f0c664792c8dda3214d RUN wget -O- https://github.com/PRO-Robotech/openapi-k8s-toolkit/archive/${COMMIT}.tar.gz | tar -xzvf- --strip-components=1 COPY openapi-k8s-toolkit/patches /patches From 99ee0e34bf71024e041d2f96e8bf0d0b6223508e Mon Sep 17 00:00:00 2001 From: Kirill Ilin Date: Mon, 2 Mar 2026 22:36:01 +0500 Subject: [PATCH 016/488] fix(dashboard): preserve newlines when copying secrets with CMD+C Add onCopy handler to SecretBase64Plain inputs to intercept native browser copy events and explicitly write the full decoded text (including newlines) to the clipboard. Without this, input[type=text] strips newlines on copy. Upstream PR: https://github.com/PRO-Robotech/openapi-k8s-toolkit/pull/339 Co-Authored-By: Claude Signed-off-by: Kirill Ilin --- .../secret-copy-preserve-newlines.diff | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 packages/system/dashboard/images/openapi-ui/openapi-k8s-toolkit/patches/secret-copy-preserve-newlines.diff diff --git a/packages/system/dashboard/images/openapi-ui/openapi-k8s-toolkit/patches/secret-copy-preserve-newlines.diff b/packages/system/dashboard/images/openapi-ui/openapi-k8s-toolkit/patches/secret-copy-preserve-newlines.diff new file mode 100644 index 00000000..7bce6c25 --- /dev/null +++ b/packages/system/dashboard/images/openapi-ui/openapi-k8s-toolkit/patches/secret-copy-preserve-newlines.diff @@ -0,0 +1,29 @@ +diff --git a/src/components/organisms/DynamicComponents/molecules/SecretBase64Plain/SecretBase64Plain.tsx b/src/components/organisms/DynamicComponents/molecules/SecretBase64Plain/SecretBase64Plain.tsx +--- a/src/components/organisms/DynamicComponents/molecules/SecretBase64Plain/SecretBase64Plain.tsx ++++ b/src/components/organisms/DynamicComponents/molecules/SecretBase64Plain/SecretBase64Plain.tsx +@@ -145,6 +145,12 @@ + handleInputClick(e, effectiveHidden, value)} ++ onCopy={e => { ++ if (!effectiveHidden) { ++ e.preventDefault() ++ e.clipboardData?.setData('text/plain', value) ++ } ++ }} + value={shownValue} + readOnly + /> +@@ -161,6 +167,12 @@ + handleInputClick(e, effectiveHidden, value)} ++ onCopy={e => { ++ if (!effectiveHidden) { ++ e.preventDefault() ++ e.clipboardData?.setData('text/plain', value) ++ } ++ }} + value={shownValue} + readOnly + /> From d21373e78047907fe3e57e09c54b0137fa6b5b70 Mon Sep 17 00:00:00 2001 From: IvanHunters Date: Mon, 2 Mar 2026 21:00:21 +0300 Subject: [PATCH 017/488] fix(migration): delete helm release secrets to protect keycloak data Replace HelmRelease annotation approach with helm secret deletion. Annotating HelmReleases with resource-policy=keep does not prevent FluxCD from running helm uninstall when spec.chart changes to spec.chartRef. Deleting helm release secrets ensures FluxCD has nothing to uninstall and performs helm install instead, adopting existing resources. Update manual migration script with the same protection and fix error handling for cozy-keycloak namespace check. Signed-off-by: IvanHunters --- hack/migrate-to-version-1.0.sh | 46 +++++++++- .../platform/images/migrations/migrations/34 | 90 +++++++++++++++---- 2 files changed, 118 insertions(+), 18 deletions(-) diff --git a/hack/migrate-to-version-1.0.sh b/hack/migrate-to-version-1.0.sh index ff727276..99b79dbd 100755 --- a/hack/migrate-to-version-1.0.sh +++ b/hack/migrate-to-version-1.0.sh @@ -51,7 +51,11 @@ if [[ $REPLY =~ ^[Yy]$ ]]; then kubectl annotate configmap -n "$NAMESPACE" cozystack-version helm.sh/resource-policy=keep --overwrite 2>/dev/null || echo " ConfigMap cozystack-version not found, skipping." echo "Annotating namespace cozy-keycloak..." - kubectl annotate namespace cozy-keycloak helm.sh/resource-policy=keep --overwrite 2>/dev/null || echo " Namespace cozy-keycloak not found, skipping." + if kubectl get namespace cozy-keycloak >/dev/null 2>&1; then + kubectl annotate namespace cozy-keycloak helm.sh/resource-policy=keep --overwrite + else + echo " Namespace cozy-keycloak not found, skipping." + fi echo "" echo "Resources annotated successfully." @@ -61,6 +65,46 @@ else fi echo "" +# Step 0.5: Delete keycloak helm release secrets to prevent FluxCD from running helm uninstall +# When the Package operator recreates keycloak HelmReleases with spec.chartRef +# (replacing spec.chart), FluxCD sees an incompatible change and runs helm uninstall, +# destroying all keycloak data. Without helm secrets, FluxCD has nothing to uninstall +# and performs helm install instead, adopting existing resources. +echo "Step 0.5: Protect keycloak data from destruction" +echo "" +echo "This will delete Helm release secrets for keycloak releases in cozy-keycloak" +echo "namespace. Without these secrets, FluxCD cannot run helm uninstall and will" +echo "adopt existing resources instead of recreating them." +echo "" + +if kubectl get namespace cozy-keycloak >/dev/null 2>&1; then + read -p "Do you want to delete keycloak helm release secrets? (y/N) " -n 1 -r + echo "" + + if [[ $REPLY =~ ^[Yy]$ ]]; then + for release in keycloak keycloak-operator keycloak-configure; do + echo " Deleting helm release secrets for ${release}..." + kubectl delete secrets -n cozy-keycloak -l "name=${release},owner=helm" --ignore-not-found + # Fallback: delete by name pattern + remaining=$(kubectl get secrets -n cozy-keycloak -o name | { grep "^secret/sh\.helm\.release\.v1\.${release}\." || true; }) + if [ -n "$remaining" ]; then + echo "$remaining" | while IFS= read -r secret; do + echo " Deleting $secret" + kubectl delete -n cozy-keycloak "$secret" --ignore-not-found + done + fi + done + echo "" + echo "Keycloak helm release secrets deleted." + else + echo "WARNING: Skipping. Keycloak data may be lost during upgrade if FluxCD" + echo "runs helm uninstall due to chart source change." + fi +else + echo " Namespace cozy-keycloak does not exist, keycloak not deployed — skipping." +fi +echo "" + # Read ConfigMap cozystack echo "Reading ConfigMap cozystack..." COZYSTACK_CM=$(kubectl get configmap -n "$NAMESPACE" cozystack -o json 2>/dev/null || echo "{}") diff --git a/packages/core/platform/images/migrations/migrations/34 b/packages/core/platform/images/migrations/migrations/34 index 13d3be5f..39979f4c 100755 --- a/packages/core/platform/images/migrations/migrations/34 +++ b/packages/core/platform/images/migrations/migrations/34 @@ -1,29 +1,85 @@ #!/bin/sh # Migration 34 --> 35 -# Protect keycloak HelmReleases from deletion during platform chart upgrade. +# Protect keycloak data from destruction during v1.0 upgrade. # -# In v0.41.x keycloak, keycloak-operator, and keycloak-configure HelmReleases -# were created directly by the platform chart (helmreleases.yaml). In v1.0 -# this template was removed and keycloak is managed via Package API instead. -# Without the keep annotation, Helm deletes these HelmReleases during upgrade, -# which triggers FluxCD to run helm uninstall, destroying all keycloak data -# (credentials, realm configuration, PostgreSQL database). +# Problem: +# In v0.41.x keycloak HelmReleases (keycloak, keycloak-operator, keycloak-configure) +# were created via platform chart template (helmreleases.yaml) with spec.chart +# pointing to a HelmRepository. In v1.0 the Package operator recreates these +# HelmReleases with spec.chartRef pointing to an ExternalArtifact. +# FluxCD sees an incompatible chart source change and runs helm uninstall, +# destroying all keycloak resources (PVCs, PostgreSQL databases, credentials, +# realm configuration) before doing a fresh helm install. # -# The keep annotation ensures the HelmReleases survive the chart upgrade so -# the Package operator can adopt them and FluxCD performs a helm upgrade -# instead of a fresh install. +# Solution: +# Delete Helm release secrets (sh.helm.release.v1..*) BEFORE the chart +# source change happens. Without these secrets FluxCD has no release to uninstall, +# so it performs helm install directly. Existing resources with correct +# meta.helm.sh/release-name annotations are adopted by the new release. +# +# Pattern from migration 26 (monitoring migration). set -euo pipefail -echo "=== Protecting keycloak HelmReleases from deletion ===" +NAMESPACE="cozy-keycloak" -for hr_name in keycloak keycloak-operator keycloak-configure; do - if kubectl get helmrelease "$hr_name" -n cozy-keycloak --no-headers 2>/dev/null | grep -q .; then - echo " [ANNOTATE] hr/${hr_name} in cozy-keycloak" - kubectl annotate helmrelease "$hr_name" -n cozy-keycloak \ - helm.sh/resource-policy=keep --overwrite +# Delete all helm release secrets for a given release name in a namespace. +# Uses both label selector and name-pattern matching to ensure complete cleanup. +delete_helm_secrets() { + local ns="$1" + local release="$2" + + # Primary: delete by label selector + kubectl delete secrets -n "$ns" -l "name=${release},owner=helm" --ignore-not-found + + # Fallback: find and delete by name pattern (in case labels were modified) + local remaining + remaining=$(kubectl get secrets -n "$ns" -o name | { grep "^secret/sh\.helm\.release\.v1\.${release}\." || true; }) + if [ -n "$remaining" ]; then + echo " Found secrets not matched by label selector, deleting by name..." + echo "$remaining" | while IFS= read -r secret; do + echo " Deleting $secret" + kubectl delete -n "$ns" "$secret" --ignore-not-found + done + fi + + # Verify all secrets are gone + remaining=$(kubectl get secrets -n "$ns" -o name | { grep "^secret/sh\.helm\.release\.v1\.${release}\." || true; }) + if [ -n "$remaining" ]; then + echo " ERROR: Failed to delete helm release secrets:" + echo "$remaining" + return 1 + fi +} + +echo "=== Protecting keycloak data from destruction during upgrade ===" + +# Check if namespace exists; if not, keycloak was never deployed — skip +if ! kubectl get namespace "$NAMESPACE" >/dev/null 2>&1; then + echo " Namespace $NAMESPACE does not exist, keycloak not deployed — skipping" + kubectl create configmap -n cozy-system cozystack-version \ + --from-literal=version=35 --dry-run=client -o yaml | kubectl apply -f- + exit 0 +fi + +for release in keycloak keycloak-operator keycloak-configure; do + # Check if HelmRelease exists (handle missing CRD gracefully) + out=$(kubectl get helmrelease "$release" -n "$NAMESPACE" -o name 2>&1) && found=true || found=false + + if [ "$found" = "true" ]; then + echo " [DELETE SECRETS] Removing helm release secrets for ${release} in ${NAMESPACE}" + delete_helm_secrets "$NAMESPACE" "$release" else - echo " [SKIP] hr/${hr_name} not found in cozy-keycloak" + # Distinguish "not found" from real errors + case "$out" in + *"NotFound"*|*"not found"*|*"the server doesn't have a resource type"*|*"no matches for kind"*) + echo " [SKIP] hr/${release} not found in ${NAMESPACE}" + ;; + *) + echo " [ERROR] Failed to query hr/${release}: $out" >&2 + exit 1 + ;; + esac fi done From 45b61f812de45882dcfcb7966d79155f44c4936e Mon Sep 17 00:00:00 2001 From: Kirill Ilin Date: Mon, 2 Mar 2026 23:13:48 +0500 Subject: [PATCH 018/488] fix(dashboard): restore stock-instance sidebars for namespace-level pages PR #2106 removed stock-instance-* sidebar resources to fix broken URLs on the main page before namespace selection. However, these sidebars are required for rendering namespace-level pages (api-table, api-form, etc.) such as the Backup Plans page. Without stock-instance-api-table, the frontend cannot find the sidebar for namespace-scoped api-table pages and renders an empty page instead. The original bug (broken URLs with empty namespace placeholder) is already fixed by CUSTOMIZATION_SIDEBAR_FALLBACK_ID="" in web.yaml, so re-adding stock-instance-* sidebars does not reintroduce it. Co-Authored-By: Claude Signed-off-by: Kirill Ilin --- internal/controller/dashboard/manager.go | 4 ++++ internal/controller/dashboard/sidebar.go | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/internal/controller/dashboard/manager.go b/internal/controller/dashboard/manager.go index 339c9b9e..42c641ad 100644 --- a/internal/controller/dashboard/manager.go +++ b/internal/controller/dashboard/manager.go @@ -307,6 +307,10 @@ func (m *Manager) buildExpectedResourceSet(crds []cozyv1alpha1.ApplicationDefini "stock-project-builtin-table", "stock-project-crd-form", "stock-project-crd-table", + "stock-instance-api-form", + "stock-instance-api-table", + "stock-instance-builtin-form", + "stock-instance-builtin-table", } for _, sidebarID := range stockSidebars { expected["Sidebar"][sidebarID] = true diff --git a/internal/controller/dashboard/sidebar.go b/internal/controller/dashboard/sidebar.go index 0ea41f0d..1f1c1670 100644 --- a/internal/controller/dashboard/sidebar.go +++ b/internal/controller/dashboard/sidebar.go @@ -243,6 +243,11 @@ func (m *Manager) ensureSidebar(ctx context.Context, crd *cozyv1alpha1.Applicati "stock-project-builtin-table", "stock-project-crd-form", "stock-project-crd-table", + // stock-instance sidebars (namespace-level pages after namespace is selected) + "stock-instance-api-form", + "stock-instance-api-table", + "stock-instance-builtin-form", + "stock-instance-builtin-table", } // Add details sidebars for all CRDs with dashboard config From aed46a88192cad70f690138c675e1e7360a24951 Mon Sep 17 00:00:00 2001 From: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com> Date: Mon, 2 Mar 2026 18:45:19 +0000 Subject: [PATCH 019/488] docs: add changelog for v1.0.2 Signed-off-by: cozystack-bot <217169706+cozystack-bot@users.noreply.github.com> --- docs/changelogs/v1.0.2.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 docs/changelogs/v1.0.2.md diff --git a/docs/changelogs/v1.0.2.md b/docs/changelogs/v1.0.2.md new file mode 100644 index 00000000..fad9275e --- /dev/null +++ b/docs/changelogs/v1.0.2.md @@ -0,0 +1,19 @@ + + +## Fixes + +* **[platform] Suspend cozy-proxy if it conflicts with installer release during migration**: Added a check in the v0.41→v1.0 migration script to detect and automatically suspend the `cozy-proxy` HelmRelease when its `releaseName` is set to `cozystack`, which conflicts with the installer release and would cause `cozystack-operator` deletion during the upgrade ([**@kvaps**](https://github.com/kvaps) in #2128, #2130). + +* **[platform] Fix off-by-one error in run-migrations script**: Fixed a bug in the migration runner where the first required migration was always skipped due to an off-by-one error in the migration range calculation, ensuring all upgrade steps execute correctly ([**@myasnikovdaniil**](https://github.com/myasnikovdaniil) in #2126, #2132). + +* **[system] Fix Keycloak proxy configuration for v26.x**: Replaced the deprecated `KC_PROXY=edge` environment variable with `KC_PROXY_HEADERS=xforwarded` and `KC_HTTP_ENABLED=true` in the Keycloak StatefulSet template. `KC_PROXY` was removed in Keycloak 26.x, previously causing "Non-secure context detected" warnings and broken cookie handling when running behind a reverse proxy with TLS termination ([**@sircthulhu**](https://github.com/sircthulhu) in #2125, #2134). + +* **[dashboard] Allow clearing instanceType field and preserve newlines in secret copy**: Added `allowEmpty: true` to the `instanceType` field in the VMInstance form so users can explicitly clear it to use custom KubeVirt resources without a named instance type. Also fixed newline preservation when copying secrets with CMD+C ([**@sircthulhu**](https://github.com/sircthulhu) in #2135, #2137). + +* **[dashboard] Restore stock-instance sidebars for namespace-level pages**: Restored `stock-instance-api-form`, `stock-instance-api-table`, `stock-instance-builtin-form`, and `stock-instance-builtin-table` sidebar resources that were inadvertently removed in #2106. Without these sidebars, namespace-level pages such as Backup Plans rendered as empty pages with no interactive content ([**@sircthulhu**](https://github.com/sircthulhu) in #2136, #2138). + +--- + +**Full Changelog**: https://github.com/cozystack/cozystack/compare/v1.0.1...v1.0.2 From 780af33ee167b1bfad0a36496bee23202034c969 Mon Sep 17 00:00:00 2001 From: Myasnikov Daniil Date: Tue, 3 Mar 2026 19:10:39 +0500 Subject: [PATCH 020/488] Fixed packages name conversion in migration script Signed-off-by: Myasnikov Daniil --- hack/migrate-to-version-1.0.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hack/migrate-to-version-1.0.sh b/hack/migrate-to-version-1.0.sh index 471a417d..a7c3f9b4 100755 --- a/hack/migrate-to-version-1.0.sh +++ b/hack/migrate-to-version-1.0.sh @@ -155,13 +155,13 @@ fi if [ -z "$BUNDLE_DISABLE" ]; then DISABLED_PACKAGES="[]" else - DISABLED_PACKAGES=$(echo "$BUNDLE_DISABLE" | sed 's/,/\n/g' | awk 'BEGIN{print}{print " - "$0}') + DISABLED_PACKAGES=$(echo "$BUNDLE_DISABLE" | sed 's/,/\n/g' | awk 'BEGIN{print}{print " - cozystack."$0}') fi if [ -z "$BUNDLE_ENABLE" ]; then ENABLED_PACKAGES="[]" else - ENABLED_PACKAGES=$(echo "$BUNDLE_ENABLE" | sed 's/,/\n/g' | awk 'BEGIN{print}{print " - "$0}') + ENABLED_PACKAGES=$(echo "$BUNDLE_ENABLE" | sed 's/,/\n/g' | awk 'BEGIN{print}{print " - cozystack."$0}') fi if [ -z "$EXPOSE_SERVICES" ]; then @@ -175,7 +175,7 @@ BUNDLE_NAME=$(echo "$BUNDLE_NAME" | sed 's/paas/isp/') # Extract branding if available BRANDING=$(echo "$BRANDING_CM" | jq -r '.data // {} | to_entries[] | "\(.key): \"\(.value)\""') -if [ -z "$BRANDING" ]; then +if [ -z "$BRANDING" ]; then BRANDING="{}" else BRANDING=$(echo "$BRANDING" | awk 'BEGIN{print}{print " " $0}') From 9ac8b2d2919c2ee4399f609625d376a89573de3e Mon Sep 17 00:00:00 2001 From: Andrey Kolkov Date: Tue, 3 Mar 2026 18:16:28 +0400 Subject: [PATCH 021/488] fix(backups): rbac actualized Signed-off-by: Andrey Kolkov --- .../backup-controller/templates/rbac.yaml | 24 +++--------------- .../templates/rbac.yaml | 25 +++++++++++++++++++ 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/packages/system/backup-controller/templates/rbac.yaml b/packages/system/backup-controller/templates/rbac.yaml index 255398e8..37ab7243 100644 --- a/packages/system/backup-controller/templates/rbac.yaml +++ b/packages/system/backup-controller/templates/rbac.yaml @@ -3,30 +3,14 @@ apiVersion: rbac.authorization.k8s.io/v1 metadata: name: backups.cozystack.io:core-controller rules: +# Plan: reconcile schedule and update status - apiGroups: ["backups.cozystack.io"] resources: ["plans"] verbs: ["get", "list", "watch"] - apiGroups: ["backups.cozystack.io"] - resources: ["backupjobs"] - verbs: ["create", "get", "list", "watch", "update", "patch"] -- apiGroups: ["backups.cozystack.io"] - resources: ["backupjobs/status"] + resources: ["plans/status"] verbs: ["get", "update", "patch"] +# BackupJob: create when schedule fires (status is updated by backupstrategy-controller) - apiGroups: ["backups.cozystack.io"] - resources: ["backups"] + resources: ["backupjobs"] verbs: ["create", "get", "list", "watch"] -- apiGroups: ["apps.cozystack.io"] - resources: ["buckets", "bucketaccesses", "virtualmachines"] - verbs: ["get", "list", "watch"] -- apiGroups: ["objectstorage.k8s.io"] - resources: ["buckets", "bucketaccesses"] - verbs: ["get", "list", "watch"] -- apiGroups: [""] - resources: ["secrets"] - verbs: ["create", "get", "list", "watch", "update", "patch"] -- apiGroups: ["kubevirt.io"] - resources: ["virtualmachines"] - verbs: ["get", "list", "watch"] -- apiGroups: ["velero.io"] - resources: ["backups", "backupstoragelocations", "volumesnapshotlocations", "restores"] - verbs: ["create", "get", "list", "watch", "update", "patch"] diff --git a/packages/system/backupstrategy-controller/templates/rbac.yaml b/packages/system/backupstrategy-controller/templates/rbac.yaml index 85431a4b..b69a2744 100644 --- a/packages/system/backupstrategy-controller/templates/rbac.yaml +++ b/packages/system/backupstrategy-controller/templates/rbac.yaml @@ -3,9 +3,34 @@ apiVersion: rbac.authorization.k8s.io/v1 metadata: name: backups.cozystack.io:strategy-controller rules: +# Strategy types (Velero, Job) - apiGroups: ["strategy.backups.cozystack.io"] resources: ["*"] verbs: ["get", "list", "watch"] +# BackupClass: resolve strategy per application +- apiGroups: ["backups.cozystack.io"] + resources: ["backupclasses"] + verbs: ["get", "list", "watch"] +# BackupJob / RestoreJob: reconcile and update status - apiGroups: ["backups.cozystack.io"] resources: ["backupjobs", "restorejobs"] verbs: ["get", "list", "watch"] +- apiGroups: ["backups.cozystack.io"] + resources: ["backupjobs/status", "restorejobs/status"] + verbs: ["get", "update", "patch"] +# Backup: create after Velero backup completes +- apiGroups: ["backups.cozystack.io"] + resources: ["backups"] + verbs: ["create", "get", "list", "watch"] +# Application refs (e.g. VMInstance, VirtualMachine) for backup/restore scope +- apiGroups: ["apps.cozystack.io"] + resources: ["*"] + verbs: ["get", "list", "watch"] +# Velero Backup/Restore in cozy-velero namespace +- apiGroups: ["velero.io"] + resources: ["backups", "restores"] + verbs: ["create", "get", "list", "watch", "update", "patch"] +# Leader election (--leader-elect) +- apiGroups: ["coordination.k8s.io"] + resources: ["leases"] + verbs: ["get", "list", "watch", "create", "update", "patch"] From e822768fcfb56ea75aa7fdb9c2595be4645e1227 Mon Sep 17 00:00:00 2001 From: IvanHunters Date: Fri, 27 Feb 2026 23:04:38 +0300 Subject: [PATCH 022/488] feat(seaweedfs): rename -worm to -lock BucketClass and update pool COSI parameters Rename storage pool COSI resources to use consistent naming: - BucketClass suffix: -worm -> -lock - Parameter name: disk -> diskType - Retention days: 36500 -> 365 - Validation suffix check updated accordingly Signed-off-by: IvanHunters --- packages/extra/seaweedfs/templates/seaweedfs.yaml | 8 ++++---- .../templates/storage-pool-bucket-classes.yaml | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/extra/seaweedfs/templates/seaweedfs.yaml b/packages/extra/seaweedfs/templates/seaweedfs.yaml index f345b00d..42441b26 100644 --- a/packages/extra/seaweedfs/templates/seaweedfs.yaml +++ b/packages/extra/seaweedfs/templates/seaweedfs.yaml @@ -32,8 +32,8 @@ {{- if not (regexMatch "^[a-z0-9]([a-z0-9-]*[a-z0-9])?$" $poolName) }} {{- fail (printf "volume.pools key '%s' must be a valid DNS label (lowercase alphanumeric and hyphens, no dots)." $poolName) }} {{- end }} -{{- if or (hasSuffix "-worm" $poolName) (hasSuffix "-readonly" $poolName) }} -{{- fail (printf "volume.pools key '%s' must not end with '-worm' or '-readonly' (reserved suffixes for COSI resources)." $poolName) }} +{{- if or (hasSuffix "-lock" $poolName) (hasSuffix "-readonly" $poolName) }} +{{- fail (printf "volume.pools key '%s' must not end with '-lock' or '-readonly' (reserved suffixes for COSI resources)." $poolName) }} {{- end }} {{- if not $pool.diskType }} {{- fail (printf "volume.pools.%s.diskType is required." $poolName) }} @@ -52,8 +52,8 @@ {{- if not (regexMatch "^[a-z0-9]([a-z0-9-]*[a-z0-9])?$" $poolName) }} {{- fail (printf "volume.zones.%s.pools key '%s' must be a valid DNS label." $zoneName $poolName) }} {{- end }} -{{- if or (hasSuffix "-worm" $poolName) (hasSuffix "-readonly" $poolName) }} -{{- fail (printf "volume.zones.%s.pools key '%s' must not end with '-worm' or '-readonly' (reserved suffixes for COSI resources)." $zoneName $poolName) }} +{{- if or (hasSuffix "-lock" $poolName) (hasSuffix "-readonly" $poolName) }} +{{- fail (printf "volume.zones.%s.pools key '%s' must not end with '-lock' or '-readonly' (reserved suffixes for COSI resources)." $zoneName $poolName) }} {{- end }} {{- if not $pool.diskType }} {{- fail (printf "volume.zones.%s.pools.%s.diskType is required." $zoneName $poolName) }} diff --git a/packages/extra/seaweedfs/templates/storage-pool-bucket-classes.yaml b/packages/extra/seaweedfs/templates/storage-pool-bucket-classes.yaml index a3966a68..ea490db6 100644 --- a/packages/extra/seaweedfs/templates/storage-pool-bucket-classes.yaml +++ b/packages/extra/seaweedfs/templates/storage-pool-bucket-classes.yaml @@ -20,19 +20,19 @@ metadata: driverName: {{ $.Release.Namespace }}.seaweedfs.objectstorage.k8s.io deletionPolicy: Delete parameters: - disk: {{ $diskType }} + diskType: {{ $diskType }} --- kind: BucketClass apiVersion: objectstorage.k8s.io/v1alpha1 metadata: - name: {{ $.Release.Namespace }}-{{ $poolName }}-worm + name: {{ $.Release.Namespace }}-{{ $poolName }}-lock driverName: {{ $.Release.Namespace }}.seaweedfs.objectstorage.k8s.io deletionPolicy: Retain parameters: - disk: {{ $diskType }} + diskType: {{ $diskType }} objectLockEnabled: "true" - objectLockRetentionMode: COMPLIANCE - objectLockRetentionDays: "36500" + objectLockRetentionMode: "COMPLIANCE" + objectLockRetentionDays: "365" --- kind: BucketAccessClass apiVersion: objectstorage.k8s.io/v1alpha1 From 96e27cbcab5fe4c90293d000ea4996afdf4d831d Mon Sep 17 00:00:00 2001 From: IvanHunters Date: Fri, 27 Feb 2026 23:18:56 +0300 Subject: [PATCH 023/488] feat(seaweedfs): add -lock BucketClass and -readonly BucketAccessClass Add COSI resources for object locking and read-only access to both client topology and system chart: - BucketClass with -lock suffix (COMPLIANCE mode, 365 days retention) - BucketAccessClass with -readonly suffix - Explicit accessPolicy: readwrite on default BucketAccessClass Signed-off-by: IvanHunters --- .../templates/client/cosi-bucket-class.yaml | 22 +++++++++++++++++++ .../templates/cosi/cosi-bucket-class.yaml | 15 ++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/packages/extra/seaweedfs/templates/client/cosi-bucket-class.yaml b/packages/extra/seaweedfs/templates/client/cosi-bucket-class.yaml index 76eda0dc..ce049b31 100644 --- a/packages/extra/seaweedfs/templates/client/cosi-bucket-class.yaml +++ b/packages/extra/seaweedfs/templates/client/cosi-bucket-class.yaml @@ -7,10 +7,32 @@ metadata: driverName: {{ .Release.Namespace }}.seaweedfs.objectstorage.k8s.io deletionPolicy: Delete --- +kind: BucketClass +apiVersion: objectstorage.k8s.io/v1alpha1 +metadata: + name: {{ .Release.Namespace }}-lock +driverName: {{ .Release.Namespace }}.seaweedfs.objectstorage.k8s.io +deletionPolicy: Retain +parameters: + objectLockEnabled: "true" + objectLockRetentionMode: "COMPLIANCE" + objectLockRetentionDays: "365" +--- kind: BucketAccessClass apiVersion: objectstorage.k8s.io/v1alpha1 metadata: name: {{ .Release.Namespace }} driverName: {{ .Release.Namespace }}.seaweedfs.objectstorage.k8s.io authenticationType: KEY +parameters: + accessPolicy: readwrite +--- +kind: BucketAccessClass +apiVersion: objectstorage.k8s.io/v1alpha1 +metadata: + name: {{ .Release.Namespace }}-readonly +driverName: {{ .Release.Namespace }}.seaweedfs.objectstorage.k8s.io +authenticationType: KEY +parameters: + accessPolicy: readonly {{- end }} diff --git a/packages/system/seaweedfs/charts/seaweedfs/templates/cosi/cosi-bucket-class.yaml b/packages/system/seaweedfs/charts/seaweedfs/templates/cosi/cosi-bucket-class.yaml index 5184a042..25e00252 100644 --- a/packages/system/seaweedfs/charts/seaweedfs/templates/cosi/cosi-bucket-class.yaml +++ b/packages/system/seaweedfs/charts/seaweedfs/templates/cosi/cosi-bucket-class.yaml @@ -7,12 +7,25 @@ metadata: driverName: {{ .Values.cosi.driverName }} deletionPolicy: Delete --- +kind: BucketClass +apiVersion: objectstorage.k8s.io/v1alpha1 +metadata: + name: {{ .Values.cosi.bucketClassName }}-lock +driverName: {{ .Values.cosi.driverName }} +deletionPolicy: Retain +parameters: + objectLockEnabled: "true" + objectLockRetentionMode: "COMPLIANCE" + objectLockRetentionDays: "365" +--- kind: BucketAccessClass apiVersion: objectstorage.k8s.io/v1alpha1 metadata: name: {{ .Values.cosi.bucketClassName }} driverName: {{ .Values.cosi.driverName }} authenticationType: KEY +parameters: + accessPolicy: readwrite --- kind: BucketAccessClass apiVersion: objectstorage.k8s.io/v1alpha1 @@ -21,5 +34,5 @@ metadata: driverName: {{ .Values.cosi.driverName }} authenticationType: KEY parameters: - accessPolicy: "readonly" + accessPolicy: readonly {{- end }} From 2dbc27075a1eb1a39debd06aa2a9d7eb336214d6 Mon Sep 17 00:00:00 2001 From: IvanHunters Date: Fri, 27 Feb 2026 23:21:24 +0300 Subject: [PATCH 024/488] feat(bucket): add locking, storagePool, and users configuration Replace hardcoded BucketAccess with user-driven model: - locking: provisions from -lock BucketClass (object lock enabled) - storagePool: selects pool-specific BucketClass - users: map of named users, each creating a BucketAccess with optional readonly flag Empty users map produces zero BucketAccess resources (breaking change). Update ApplicationDefinition schema and dashboard RBAC accordingly. Signed-off-by: IvanHunters --- .../apps/bucket/templates/bucketclaim.yaml | 23 ++++++--------- .../templates/dashboard-resourcemap.yaml | 5 ++-- packages/apps/bucket/values.schema.json | 28 ++++++++++++++++++- packages/apps/bucket/values.yaml | 12 +++++++- packages/system/bucket-rd/cozyrds/bucket.yaml | 4 +-- 5 files changed, 50 insertions(+), 22 deletions(-) diff --git a/packages/apps/bucket/templates/bucketclaim.yaml b/packages/apps/bucket/templates/bucketclaim.yaml index e27dd0ed..2f57fd70 100644 --- a/packages/apps/bucket/templates/bucketclaim.yaml +++ b/packages/apps/bucket/templates/bucketclaim.yaml @@ -1,29 +1,22 @@ {{- $seaweedfs := .Values._namespace.seaweedfs }} +{{- $pool := .Values.storagePool }} apiVersion: objectstorage.k8s.io/v1alpha1 kind: BucketClaim metadata: name: {{ .Release.Name }} spec: - bucketClassName: {{ $seaweedfs }} + bucketClassName: {{ $seaweedfs }}{{- if $pool }}-{{ $pool }}{{- end }}{{- if .Values.locking }}-lock{{- end }} protocols: - s3 +{{- range $name, $user := .Values.users }} --- apiVersion: objectstorage.k8s.io/v1alpha1 kind: BucketAccess metadata: - name: {{ .Release.Name }} + name: {{ $.Release.Name }}-{{ $name }} spec: - bucketAccessClassName: {{ $seaweedfs }} - bucketClaimName: {{ .Release.Name }} - credentialsSecretName: {{ .Release.Name }} - protocol: s3 ---- -apiVersion: objectstorage.k8s.io/v1alpha1 -kind: BucketAccess -metadata: - name: {{ .Release.Name }}-readonly -spec: - bucketAccessClassName: {{ $seaweedfs }}-readonly - bucketClaimName: {{ .Release.Name }} - credentialsSecretName: {{ .Release.Name }}-readonly + bucketAccessClassName: {{ $seaweedfs }}{{- if $pool }}-{{ $pool }}{{- end }}{{- if $user.readonly }}-readonly{{- end }} + bucketClaimName: {{ $.Release.Name }} + credentialsSecretName: {{ $.Release.Name }}-{{ $name }} protocol: s3 +{{- end }} diff --git a/packages/apps/bucket/templates/dashboard-resourcemap.yaml b/packages/apps/bucket/templates/dashboard-resourcemap.yaml index f10b5d32..ec2fb568 100644 --- a/packages/apps/bucket/templates/dashboard-resourcemap.yaml +++ b/packages/apps/bucket/templates/dashboard-resourcemap.yaml @@ -8,9 +8,10 @@ rules: resources: - secrets resourceNames: - - {{ .Release.Name }} - {{ .Release.Name }}-credentials - - {{ .Release.Name }}-readonly + {{- range $name, $user := .Values.users }} + - {{ $.Release.Name }}-{{ $name }} + {{- end }} verbs: ["get", "list", "watch"] - apiGroups: - networking.k8s.io diff --git a/packages/apps/bucket/values.schema.json b/packages/apps/bucket/values.schema.json index 167bc53e..f661a852 100644 --- a/packages/apps/bucket/values.schema.json +++ b/packages/apps/bucket/values.schema.json @@ -1,5 +1,31 @@ { "title": "Chart Values", "type": "object", - "properties": {} + "properties": { + "locking": { + "description": "Provisions bucket from the -lock BucketClass (with object lock enabled).", + "type": "boolean", + "default": false + }, + "storagePool": { + "description": "Selects a specific BucketClass by storage pool name.", + "type": "string", + "default": "" + }, + "users": { + "description": "Users configuration map. Each entry creates a BucketAccess.", + "type": "object", + "default": {}, + "additionalProperties": { + "type": "object", + "properties": { + "readonly": { + "description": "Whether the user has read-only access.", + "type": "boolean", + "default": false + } + } + } + } + } } diff --git a/packages/apps/bucket/values.yaml b/packages/apps/bucket/values.yaml index 0967ef42..df284df7 100644 --- a/packages/apps/bucket/values.yaml +++ b/packages/apps/bucket/values.yaml @@ -1 +1,11 @@ -{} +## @param {bool} locking=false - Provisions bucket from the `-lock` BucketClass (with object lock enabled). +locking: false + +## @param {string} [storagePool] - Selects a specific BucketClass by storage pool name. +storagePool: "" + +## @typedef {struct} User - Bucket user configuration. +## @field {bool} [readonly] - Whether the user has read-only access. + +## @param {map[string]User} users - Users configuration map. +users: {} diff --git a/packages/system/bucket-rd/cozyrds/bucket.yaml b/packages/system/bucket-rd/cozyrds/bucket.yaml index 5974b03d..b0194505 100644 --- a/packages/system/bucket-rd/cozyrds/bucket.yaml +++ b/packages/system/bucket-rd/cozyrds/bucket.yaml @@ -8,7 +8,7 @@ spec: plural: buckets singular: bucket openAPISchema: |- - {"title":"Chart Values","type":"object","properties":{}} + {"title":"Chart Values","type":"object","properties":{"locking":{"description":"Provisions bucket from the -lock BucketClass (with object lock enabled).","type":"boolean","default":false},"storagePool":{"description":"Selects a specific BucketClass by storage pool name.","type":"string","default":""},"users":{"description":"Users configuration map. Each entry creates a BucketAccess.","type":"object","default":{},"additionalProperties":{"type":"object","properties":{"readonly":{"description":"Whether the user has read-only access.","type":"boolean","default":false}}}}}} release: prefix: bucket- labels: @@ -31,9 +31,7 @@ spec: exclude: [] include: - resourceNames: - - bucket-{{ .name }} - bucket-{{ .name }}-credentials - - bucket-{{ .name }}-readonly ingresses: exclude: [] include: From d4043bd71cf3fa2a621ae4a9a97b70a3ee54f44b Mon Sep 17 00:00:00 2001 From: IvanHunters Date: Mon, 2 Mar 2026 20:09:02 +0300 Subject: [PATCH 025/488] fix(seaweedfs): use correct COSI driver parameter name 'disk' The seaweedfs-cosi-driver v0.3.0 expects the parameter key 'disk', not 'diskType'. Restore the correct key to match the driver's paramDisk constant. Co-Authored-By: Claude Signed-off-by: IvanHunters --- .../seaweedfs/templates/storage-pool-bucket-classes.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/extra/seaweedfs/templates/storage-pool-bucket-classes.yaml b/packages/extra/seaweedfs/templates/storage-pool-bucket-classes.yaml index ea490db6..bf58340a 100644 --- a/packages/extra/seaweedfs/templates/storage-pool-bucket-classes.yaml +++ b/packages/extra/seaweedfs/templates/storage-pool-bucket-classes.yaml @@ -20,7 +20,7 @@ metadata: driverName: {{ $.Release.Namespace }}.seaweedfs.objectstorage.k8s.io deletionPolicy: Delete parameters: - diskType: {{ $diskType }} + disk: {{ $diskType }} --- kind: BucketClass apiVersion: objectstorage.k8s.io/v1alpha1 @@ -29,7 +29,7 @@ metadata: driverName: {{ $.Release.Namespace }}.seaweedfs.objectstorage.k8s.io deletionPolicy: Retain parameters: - diskType: {{ $diskType }} + disk: {{ $diskType }} objectLockEnabled: "true" objectLockRetentionMode: "COMPLIANCE" objectLockRetentionDays: "365" From 70c6dfd704a926e062314746bc3f4460dfc3ba5d Mon Sep 17 00:00:00 2001 From: IvanHunters Date: Mon, 2 Mar 2026 21:46:08 +0300 Subject: [PATCH 026/488] fix(bucket): include COSI user credential secrets in dashboard Add a catch-all include selector so that COSI-created user credential secrets (dynamically named per user) are visible in the dashboard. The lineage webhook already verifies ownership via the graph walk (Secret -> BucketAccess -> HelmRelease -> Bucket), so an empty selector safely matches only secrets belonging to this application. This is needed because COSI sidecar creates secrets without custom labels, making the matchLabels pattern (used by rabbitmq) inapplicable. Co-Authored-By: Claude Signed-off-by: IvanHunters --- packages/system/bucket-rd/cozyrds/bucket.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/system/bucket-rd/cozyrds/bucket.yaml b/packages/system/bucket-rd/cozyrds/bucket.yaml index b0194505..b2519075 100644 --- a/packages/system/bucket-rd/cozyrds/bucket.yaml +++ b/packages/system/bucket-rd/cozyrds/bucket.yaml @@ -32,6 +32,7 @@ spec: include: - resourceNames: - bucket-{{ .name }}-credentials + - {} ingresses: exclude: [] include: From 757f8b5699963b9feb62fdb56f351c7b9695ae4c Mon Sep 17 00:00:00 2001 From: IvanHunters Date: Tue, 3 Mar 2026 18:48:35 +0300 Subject: [PATCH 027/488] Revert "fix(bucket): include COSI user credential secrets in dashboard" This reverts commit 87b89d90e61283b60abe49cd004d49ce94e57cf5. Signed-off-by: IvanHunters --- packages/system/bucket-rd/cozyrds/bucket.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/system/bucket-rd/cozyrds/bucket.yaml b/packages/system/bucket-rd/cozyrds/bucket.yaml index b2519075..b0194505 100644 --- a/packages/system/bucket-rd/cozyrds/bucket.yaml +++ b/packages/system/bucket-rd/cozyrds/bucket.yaml @@ -32,7 +32,6 @@ spec: include: - resourceNames: - bucket-{{ .name }}-credentials - - {} ingresses: exclude: [] include: From b4f1e96b986605537eaf2a8d46f4ef71b92ebe87 Mon Sep 17 00:00:00 2001 From: IvanHunters Date: Tue, 3 Mar 2026 19:12:27 +0300 Subject: [PATCH 028/488] feat(bucket): include COSI user credential secrets in dashboard Create labeled secrets in the -system chart using lookup to copy credentials from COSI-created secrets. The ApplicationDefinition matchLabels selector exposes them in the dashboard. Co-Authored-By: Claude Signed-off-by: IvanHunters --- .../apps/bucket/templates/helmrelease.yaml | 1 + packages/system/bucket-rd/cozyrds/bucket.yaml | 2 ++ .../bucket/templates/user-credentials.yaml | 20 +++++++++++++++++++ packages/system/bucket/values.yaml | 1 + 4 files changed, 24 insertions(+) create mode 100644 packages/system/bucket/templates/user-credentials.yaml diff --git a/packages/apps/bucket/templates/helmrelease.yaml b/packages/apps/bucket/templates/helmrelease.yaml index 704470a8..d3290512 100644 --- a/packages/apps/bucket/templates/helmrelease.yaml +++ b/packages/apps/bucket/templates/helmrelease.yaml @@ -23,3 +23,4 @@ spec: name: cozystack-values values: bucketName: {{ .Release.Name }} + users: {{ .Values.users | toJson }} diff --git a/packages/system/bucket-rd/cozyrds/bucket.yaml b/packages/system/bucket-rd/cozyrds/bucket.yaml index b0194505..d546f689 100644 --- a/packages/system/bucket-rd/cozyrds/bucket.yaml +++ b/packages/system/bucket-rd/cozyrds/bucket.yaml @@ -32,6 +32,8 @@ spec: include: - resourceNames: - bucket-{{ .name }}-credentials + - matchLabels: + apps.cozystack.io/user-secret: "true" ingresses: exclude: [] include: diff --git a/packages/system/bucket/templates/user-credentials.yaml b/packages/system/bucket/templates/user-credentials.yaml new file mode 100644 index 00000000..7ccdd731 --- /dev/null +++ b/packages/system/bucket/templates/user-credentials.yaml @@ -0,0 +1,20 @@ +{{- range $name, $user := .Values.users }} +{{- $secretName := printf "%s-%s" $.Values.bucketName $name }} +{{- $existingSecret := lookup "v1" "Secret" $.Release.Namespace $secretName }} +{{- if $existingSecret }} +{{- $bucketInfo := fromJson (b64dec (index $existingSecret.data "BucketInfo")) }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ $secretName }}-credentials + labels: + apps.cozystack.io/user-secret: "true" +type: Opaque +stringData: + accessKey: {{ index $bucketInfo.spec.secretS3 "accessKeyID" | quote }} + secretKey: {{ index $bucketInfo.spec.secretS3 "accessSecretKey" | quote }} + endpoint: {{ trimPrefix "https://" (index $bucketInfo.spec.secretS3 "endpoint") }} + bucketName: {{ index $bucketInfo.spec "bucketName" | quote }} +{{- end }} +{{- end }} diff --git a/packages/system/bucket/values.yaml b/packages/system/bucket/values.yaml index e1499c6e..739c4977 100644 --- a/packages/system/bucket/values.yaml +++ b/packages/system/bucket/values.yaml @@ -1 +1,2 @@ bucketName: "cozystack" +users: {} From 1d41e270b99b95f9b3ccfe4504e32d184bd55ca4 Mon Sep 17 00:00:00 2001 From: IvanHunters Date: Tue, 3 Mar 2026 19:17:51 +0300 Subject: [PATCH 029/488] fix(bucket): regenerate schema and docs from values.yaml annotations Remove the yq strip of properties from Makefile that was clearing the schema, and run make generate to sync all generated files. Co-Authored-By: Claude Signed-off-by: IvanHunters --- packages/apps/bucket/Makefile | 1 - packages/apps/bucket/README.md | 10 ++++++++++ packages/apps/bucket/values.schema.json | 9 ++++----- packages/system/bucket-rd/cozyrds/bucket.yaml | 4 ++-- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/packages/apps/bucket/Makefile b/packages/apps/bucket/Makefile index 27cd199b..d1cfda8e 100644 --- a/packages/apps/bucket/Makefile +++ b/packages/apps/bucket/Makefile @@ -2,5 +2,4 @@ include ../../../hack/package.mk generate: cozyvalues-gen -v values.yaml -s values.schema.json -r README.md - yq -o json -i '.properties = {}' values.schema.json ../../../hack/update-crd.sh diff --git a/packages/apps/bucket/README.md b/packages/apps/bucket/README.md index 89749b1d..982225ce 100644 --- a/packages/apps/bucket/README.md +++ b/packages/apps/bucket/README.md @@ -1,3 +1,13 @@ # S3 bucket ## Parameters + +### Parameters + +| Name | Description | Type | Value | +| ---------------------- | -------------------------------------------------------------------------- | ------------------- | ------- | +| `locking` | Provisions bucket from the `-lock` BucketClass (with object lock enabled). | `bool` | `false` | +| `storagePool` | Selects a specific BucketClass by storage pool name. | `string` | `""` | +| `users` | Users configuration map. | `map[string]object` | `{}` | +| `users[name].readonly` | Whether the user has read-only access. | `bool` | `false` | + diff --git a/packages/apps/bucket/values.schema.json b/packages/apps/bucket/values.schema.json index f661a852..ef213a18 100644 --- a/packages/apps/bucket/values.schema.json +++ b/packages/apps/bucket/values.schema.json @@ -3,7 +3,7 @@ "type": "object", "properties": { "locking": { - "description": "Provisions bucket from the -lock BucketClass (with object lock enabled).", + "description": "Provisions bucket from the `-lock` BucketClass (with object lock enabled).", "type": "boolean", "default": false }, @@ -13,7 +13,7 @@ "default": "" }, "users": { - "description": "Users configuration map. Each entry creates a BucketAccess.", + "description": "Users configuration map.", "type": "object", "default": {}, "additionalProperties": { @@ -21,11 +21,10 @@ "properties": { "readonly": { "description": "Whether the user has read-only access.", - "type": "boolean", - "default": false + "type": "boolean" } } } } } -} +} \ No newline at end of file diff --git a/packages/system/bucket-rd/cozyrds/bucket.yaml b/packages/system/bucket-rd/cozyrds/bucket.yaml index d546f689..7eab56b6 100644 --- a/packages/system/bucket-rd/cozyrds/bucket.yaml +++ b/packages/system/bucket-rd/cozyrds/bucket.yaml @@ -8,7 +8,7 @@ spec: plural: buckets singular: bucket openAPISchema: |- - {"title":"Chart Values","type":"object","properties":{"locking":{"description":"Provisions bucket from the -lock BucketClass (with object lock enabled).","type":"boolean","default":false},"storagePool":{"description":"Selects a specific BucketClass by storage pool name.","type":"string","default":""},"users":{"description":"Users configuration map. Each entry creates a BucketAccess.","type":"object","default":{},"additionalProperties":{"type":"object","properties":{"readonly":{"description":"Whether the user has read-only access.","type":"boolean","default":false}}}}}} + {"title":"Chart Values","type":"object","properties":{"locking":{"description":"Provisions bucket from the `-lock` BucketClass (with object lock enabled).","type":"boolean","default":false},"storagePool":{"description":"Selects a specific BucketClass by storage pool name.","type":"string","default":""},"users":{"description":"Users configuration map.","type":"object","default":{},"additionalProperties":{"type":"object","properties":{"readonly":{"description":"Whether the user has read-only access.","type":"boolean"}}}}}} release: prefix: bucket- labels: @@ -26,7 +26,7 @@ spec: tags: - storage icon: PHN2ZyB3aWR0aD0iMTQ0IiBoZWlnaHQ9IjE0NCIgdmlld0JveD0iMCAwIDE0NCAxNDQiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxNDQiIGhlaWdodD0iMTQ0IiByeD0iMjQiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl82ODNfMzA5MSkiLz4KPHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik03MiAzMC4xNjQxTDExNy45ODMgMzYuNzc4OVY0MC42NzM5QzExNy45ODMgNDYuNDY1MyA5Ny4zODYyIDUxLjEzMzIgNzEuOTgyNyA1MS4xMzMyQzQ2LjU3OTIgNTEuMTMzMiAyNiA0Ni40NjUzIDI2IDQwLjY3MzlWMzYuNDQzMUw3MiAzMC4xNjQxWk03MiA1OC4yNjc4QzkxLjIwODQgNTguMjY3OCAxMDcuNjU4IDU1LjU5ODYgMTE0LjU0NyA1MS44MDQ4TDExNi44MDMgNDguMTExTDExNy43MjMgNDQuNzUzVjQ4LjkxNzFMMTAyLjY3OSAxMTEuMDMzQzEwMi42NzkgMTE0Ljg5NSA4OC45NTMzIDExOCA3Mi4wMTcyIDExOEM1NS4wODEyIDExOCA0MS4zNzQzIDExNC44OTUgNDEuMzc0MyAxMTEuMDMzTDI2LjMzIDQ4LjkxNzFWNDQuODM2OUwyOS44MDA3IDUxLjkzODJDMzYuNzA2NSA1NS42NjUzIDUyLjk5OTcgNTguMjY3OCA3MiA1OC4yNjc4WiIgZmlsbD0iIzhDMzEyMyIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTcyLjAwMDMgMjZDOTcuNDAzOCAyNiAxMTggMzAuNjgzOSAxMTggMzYuNDQyQzExOCA0Mi4yIDk3LjM4NjYgNDYuODUwNyA3Mi4wMDAzIDQ2Ljg1MDdDNDYuNjE0MSA0Ni44NTA3IDI2LjAxNzYgNDIuMjM0NSAyNi4wMTc2IDM2LjQ0MkMyNi4wMTc2IDMwLjY0OTQgNDYuNTk2OCAyNiA3Mi4wMDAzIDI2Wk03Mi4wMDAzIDU0LjEwMzdDOTUuNjg1NyA1NC4xMDM3IDExNS4xNzIgNTAuMDU4IDExNy43MDYgNDQuODE5N0wxMDIuNjYyIDEwNi45MzdDMTAyLjY2MiAxMTAuNzk5IDg4LjkzNjQgMTEzLjkwNSA3Mi4wMDAzIDExMy45MDVDNTUuMDY0MyAxMTMuOTA1IDQxLjMzOSAxMTAuODE2IDQxLjMzOSAxMDYuOTU0TDI2LjI5NTkgNDQuODM3QzI4Ljg0NjYgNTAuMDU4IDQ4LjMzMzMgNTQuMTAzNyA3Mi4wMDAzIDU0LjEwMzdaIiBmaWxsPSIjRTA1MjQzIi8+CjxwYXRoIGZpbGwtcnVsZT0iZXZlbm9kZCIgY2xpcC1ydWxlPSJldmVub2RkIiBkPSJNNjEuMTcyNSA2MC4wMjkzSDgxLjA5MjhWNzkuMTY3Nkg2MS4xNzI1VjYwLjAyOTNaTTQ1LjMzMDEgOTUuMzY4OEM0NS4zMzAxIDkwLjE0MiA0OS43MTA0IDg1LjkzNDIgNTUuMTUxMSA4NS45MzQyQzYwLjU5MTcgODUuOTM0MiA2NC45NzIxIDkwLjE0MiA2NC45NzIxIDk1LjM2ODhDNjQuOTcyMSAxMDAuNTk2IDYwLjU5MTcgMTA0LjgwMyA1NS4xNTExIDEwNC44MDNDNDkuNzEwNCAxMDQuODAzIDQ1LjMzMDEgMTAwLjU5NiA0NS4zMzAxIDk1LjM2ODhaTTk2LjQ0ODcgMTA0LjM2OEg3Ni43NzIyTDg2LjYxMDUgODYuNzczN0w5Ni40NDg3IDEwNC4zNjhaIiBmaWxsPSJ3aGl0ZSIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyXzY4M18zMDkxIiB4MT0iMCIgeTE9IjAiIHgyPSIxNTEiIHkyPSIxODAiIGdyYWRpZW50VW5pdHM9InVzZXJTcGFjZU9uVXNlIj4KPHN0b3Agc3RvcC1jb2xvcj0iI0ZGRjBFRSIvPgo8c3RvcCBvZmZzZXQ9IjEiIHN0b3AtY29sb3I9IiNFQzg4N0QiLz4KPC9saW5lYXJHcmFkaWVudD4KPC9kZWZzPgo8L3N2Zz4K - keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"]] + keysOrder: [["apiVersion"], ["appVersion"], ["kind"], ["metadata"], ["metadata", "name"], ["spec", "locking"], ["spec", "storagePool"], ["spec", "users"]] secrets: exclude: [] include: From 79b2546e676012dcea691e19b1aabe4a78ec51ac Mon Sep 17 00:00:00 2001 From: IvanHunters Date: Tue, 3 Mar 2026 21:34:37 +0300 Subject: [PATCH 030/488] fix(kubernetes): set explicit MTU for Cilium in tenant clusters Cilium's MTU auto-detection does not account for VXLAN overhead when running inside KubeVirt VMs. The VM interface inherits MTU 1400 from the parent OVN/Geneve overlay, and Cilium sets all interfaces (cilium_vxlan, lxc*, cilium_host/net) to 1400 without subtracting the 50-byte VXLAN encapsulation overhead. This causes intermittent packet drops for large packets (TLS handshakes, HTTP responses with data), resulting in timeouts and 499 errors for services running in tenant clusters. Set MTU to 1350 (1400 - 50 VXLAN overhead) explicitly in the default Cilium values for tenant Kubernetes clusters. Signed-off-by: IvanHunters --- packages/apps/kubernetes/templates/helmreleases/cilium.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/apps/kubernetes/templates/helmreleases/cilium.yaml b/packages/apps/kubernetes/templates/helmreleases/cilium.yaml index 64027e94..d032f5b6 100644 --- a/packages/apps/kubernetes/templates/helmreleases/cilium.yaml +++ b/packages/apps/kubernetes/templates/helmreleases/cilium.yaml @@ -3,6 +3,7 @@ cilium: k8sServiceHost: {{ .Release.Name }}.{{ .Release.Namespace }}.svc k8sServicePort: 6443 routingMode: tunnel + MTU: 1350 enableIPv4Masquerade: true ipv4NativeRoutingCIDR: "" {{- if $.Values.addons.gatewayAPI.enabled }} From 9e6c240d6e9ae3bf4b77fa8ea5ba4384628b9562 Mon Sep 17 00:00:00 2001 From: IvanHunters Date: Wed, 4 Mar 2026 00:34:33 +0300 Subject: [PATCH 031/488] fix(bucket): use per-user COSI secret for UI credentials The default BucketAccess was removed in favor of per-user access. Update secret.yaml to look up the first user's COSI secret instead of the non-existent default one, with nil-check for race conditions. Co-Authored-By: Claude Signed-off-by: IvanHunters --- packages/system/bucket/templates/secret.yaml | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/packages/system/bucket/templates/secret.yaml b/packages/system/bucket/templates/secret.yaml index 15474569..07450ec7 100644 --- a/packages/system/bucket/templates/secret.yaml +++ b/packages/system/bucket/templates/secret.yaml @@ -1,14 +1,18 @@ -{{- $existingSecret := lookup "v1" "Secret" .Release.Namespace .Values.bucketName }} +{{- $users := keys .Values.users | sortAlpha }} +{{- if $users }} +{{- $firstUser := index $users 0 }} +{{- $secretName := printf "%s-%s" .Values.bucketName $firstUser }} +{{- $existingSecret := lookup "v1" "Secret" .Release.Namespace $secretName }} +{{- if $existingSecret }} {{- $bucketInfo := fromJson (b64dec (index $existingSecret.data "BucketInfo")) }} {{- $accessKeyID := index $bucketInfo.spec.secretS3 "accessKeyID" }} {{- $accessSecretKey := index $bucketInfo.spec.secretS3 "accessSecretKey" }} {{- $endpoint := index $bucketInfo.spec.secretS3 "endpoint" }} {{- $bucketName := index $bucketInfo.spec "bucketName" }} - apiVersion: v1 kind: Secret metadata: - name: {{ .Values.bucketName }}-credentials + name: {{ $.Values.bucketName }}-credentials type: Opaque stringData: accessKey: {{ $accessKeyID | quote }} @@ -19,6 +23,8 @@ stringData: apiVersion: v1 kind: Secret metadata: - name: {{ .Values.bucketName }}-ui-auth + name: {{ $.Values.bucketName }}-ui-auth data: auth: {{ htpasswd $accessKeyID $accessSecretKey | b64enc | quote }} +{{- end }} +{{- end }} From 69e0320e3adae2c1f5bba6b6e574374c0745fcbb Mon Sep 17 00:00:00 2001 From: IvanHunters Date: Wed, 4 Mar 2026 17:02:36 +0300 Subject: [PATCH 032/488] fix(bucket): remove duplicate credentials from dashboard Show only per-user credential secrets in the dashboard instead of both the internal UI secret and per-user secrets. Signed-off-by: IvanHunters --- packages/apps/bucket/templates/dashboard-resourcemap.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/apps/bucket/templates/dashboard-resourcemap.yaml b/packages/apps/bucket/templates/dashboard-resourcemap.yaml index ec2fb568..82a51e16 100644 --- a/packages/apps/bucket/templates/dashboard-resourcemap.yaml +++ b/packages/apps/bucket/templates/dashboard-resourcemap.yaml @@ -8,9 +8,8 @@ rules: resources: - secrets resourceNames: - - {{ .Release.Name }}-credentials {{- range $name, $user := .Values.users }} - - {{ $.Release.Name }}-{{ $name }} + - {{ $.Release.Name }}-{{ $name }}-credentials {{- end }} verbs: ["get", "list", "watch"] - apiGroups: From f777f659d5a1393b07a3489abd215c7ad36caa30 Mon Sep 17 00:00:00 2001 From: IvanHunters Date: Wed, 4 Mar 2026 18:30:40 +0300 Subject: [PATCH 033/488] feat(bucket): add login screen to s3manager for credentialless mode When a bucket has no users configured, s3manager previously crashed due to missing ACCESS_KEY_ID/SECRET_ACCESS_KEY env vars. This adds a login mode where users enter their S3 credentials via a web form. New Go code (via cozystack.patch): - auth.go: session-based auth middleware, login/logout handlers, per-request S3 client from encrypted cookie session - login.html.tmpl: Materialize CSS login form - main.go: LoginMode toggle, conditional route setup - Dependency: gorilla/sessions for AES-256 encrypted cookies Dockerfile: add go mod tidy step for new dependency resolution. Co-Authored-By: Claude Signed-off-by: IvanHunters --- .../system/bucket/images/s3manager/Dockerfile | 1 + .../bucket/images/s3manager/cozystack.patch | 473 ++++++++++++++++++ 2 files changed, 474 insertions(+) diff --git a/packages/system/bucket/images/s3manager/Dockerfile b/packages/system/bucket/images/s3manager/Dockerfile index 179acead..e97adf07 100644 --- a/packages/system/bucket/images/s3manager/Dockerfile +++ b/packages/system/bucket/images/s3manager/Dockerfile @@ -9,6 +9,7 @@ WORKDIR /usr/src/app RUN wget -O- https://github.com/cloudlena/s3manager/archive/9a7c8e446b422f8973b8c461990f39fdafee9c27.tar.gz | tar -xzf- --strip 1 ADD cozystack.patch / RUN git apply /cozystack.patch +RUN go mod tidy RUN GOOS=$TARGETOS GOARCH=$TARGETARCH CGO_ENABLED=0 go build -ldflags="-s -w" -a -installsuffix cgo -o bin/s3manager FROM docker.io/library/alpine:latest diff --git a/packages/system/bucket/images/s3manager/cozystack.patch b/packages/system/bucket/images/s3manager/cozystack.patch index f631b144..9e98edde 100644 --- a/packages/system/bucket/images/s3manager/cozystack.patch +++ b/packages/system/bucket/images/s3manager/cozystack.patch @@ -1,3 +1,401 @@ +diff --git a/go.mod b/go.mod +index b5d8540..6ede8e8 100644 +--- a/go.mod ++++ b/go.mod +@@ -1,10 +1,11 @@ + module github.com/cloudlena/s3manager + +-go 1.22.5 ++go 1.23 + + require ( + github.com/cloudlena/adapters v0.0.0-20240708203353-a39be02cc801 + github.com/gorilla/mux v1.8.1 ++ github.com/gorilla/sessions v1.4.0 + github.com/matryer/is v1.4.1 + github.com/minio/minio-go/v7 v7.0.74 + github.com/spf13/viper v1.19.0 +@@ -16,6 +17,7 @@ require ( + github.com/go-ini/ini v1.67.0 // indirect + github.com/goccy/go-json v0.10.3 // indirect + github.com/google/uuid v1.6.0 // indirect ++ github.com/gorilla/securecookie v1.1.2 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/klauspost/compress v1.17.9 // indirect + github.com/klauspost/cpuid/v2 v2.2.8 // indirect +diff --git a/internal/app/s3manager/auth.go b/internal/app/s3manager/auth.go +new file mode 100644 +index 0000000..a315cde +--- /dev/null ++++ b/internal/app/s3manager/auth.go +@@ -0,0 +1,173 @@ ++package s3manager ++ ++import ( ++ "context" ++ "crypto/rand" ++ "crypto/tls" ++ "fmt" ++ "html/template" ++ "io/fs" ++ "net/http" ++ ++ "github.com/gorilla/sessions" ++ "github.com/minio/minio-go/v7" ++ "github.com/minio/minio-go/v7/pkg/credentials" ++) ++ ++type contextKey string ++ ++const s3ContextKey contextKey = "s3client" ++ ++// S3FromContext retrieves the S3 client from the request context. ++func S3FromContext(ctx context.Context) S3 { ++ s3, _ := ctx.Value(s3ContextKey).(S3) ++ return s3 ++} ++ ++func contextWithS3(ctx context.Context, s3 S3) context.Context { ++ return context.WithValue(ctx, s3ContextKey, s3) ++} ++ ++// NewS3Client creates a new minio S3 client with the given credentials. ++func NewS3Client(endpoint, accessKey, secretKey string, useSSL, skipVerify bool) (*minio.Client, error) { ++ opts := &minio.Options{ ++ Creds: credentials.NewStaticV4(accessKey, secretKey, ""), ++ Secure: useSSL, ++ } ++ if useSSL && skipVerify { ++ opts.Transport = &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} //nolint:gosec ++ } ++ return minio.New(endpoint, opts) ++} ++ ++// NewSessionStore creates a new encrypted cookie store with a random key. ++func NewSessionStore() *sessions.CookieStore { ++ key := make([]byte, 32) ++ if _, err := rand.Read(key); err != nil { ++ panic(fmt.Sprintf("failed to generate session key: %v", err)) ++ } ++ store := sessions.NewCookieStore(key) ++ store.Options = &sessions.Options{ ++ Path: "/", ++ MaxAge: 86400, // 24 hours ++ HttpOnly: true, ++ Secure: true, ++ SameSite: http.SameSiteLaxMode, ++ } ++ return store ++} ++ ++// RequireAuth is middleware that checks for valid S3 credentials in the session. ++func RequireAuth(store *sessions.CookieStore, endpoint string, useSSL, skipVerify bool) func(http.Handler) http.Handler { ++ return func(next http.Handler) http.Handler { ++ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ++ session, err := store.Get(r, "s3session") ++ if err != nil { ++ http.Redirect(w, r, "/login", http.StatusFound) ++ return ++ } ++ ++ accessKey, ok1 := session.Values["accessKey"].(string) ++ secretKey, ok2 := session.Values["secretKey"].(string) ++ if !ok1 || !ok2 || accessKey == "" || secretKey == "" { ++ http.Redirect(w, r, "/login", http.StatusFound) ++ return ++ } ++ ++ s3Client, err := NewS3Client(endpoint, accessKey, secretKey, useSSL, skipVerify) ++ if err != nil { ++ http.Redirect(w, r, "/login?error=invalid_credentials", http.StatusFound) ++ return ++ } ++ ++ ctx := contextWithS3(r.Context(), s3Client) ++ next.ServeHTTP(w, r.WithContext(ctx)) ++ }) ++ } ++} ++ ++// HandleLoginView renders the login page. ++func HandleLoginView(templates fs.FS) http.HandlerFunc { ++ return func(w http.ResponseWriter, r *http.Request) { ++ data := struct { ++ Error string ++ }{ ++ Error: r.URL.Query().Get("error"), ++ } ++ t, err := template.ParseFS(templates, "layout.html.tmpl", "login.html.tmpl") ++ if err != nil { ++ handleHTTPError(w, fmt.Errorf("error parsing login template: %w", err)) ++ return ++ } ++ err = t.ExecuteTemplate(w, "layout", data) ++ if err != nil { ++ handleHTTPError(w, fmt.Errorf("error executing login template: %w", err)) ++ return ++ } ++ } ++} ++ ++// HandleLogin processes login form submission. ++func HandleLogin(store *sessions.CookieStore, endpoint string, useSSL, skipVerify bool) http.HandlerFunc { ++ return func(w http.ResponseWriter, r *http.Request) { ++ accessKey := r.FormValue("accessKey") ++ secretKey := r.FormValue("secretKey") ++ ++ if accessKey == "" || secretKey == "" { ++ http.Redirect(w, r, "/login?error=credentials_required", http.StatusFound) ++ return ++ } ++ ++ // Validate credentials by attempting to list buckets ++ s3Client, err := NewS3Client(endpoint, accessKey, secretKey, useSSL, skipVerify) ++ if err != nil { ++ http.Redirect(w, r, "/login?error=invalid_credentials", http.StatusFound) ++ return ++ } ++ ++ _, err = s3Client.ListBuckets(r.Context()) ++ if err != nil { ++ http.Redirect(w, r, "/login?error=invalid_credentials", http.StatusFound) ++ return ++ } ++ ++ session, err := store.Get(r, "s3session") ++ if err != nil { ++ http.Redirect(w, r, "/login?error=session_error", http.StatusFound) ++ return ++ } ++ ++ session.Values["accessKey"] = accessKey ++ session.Values["secretKey"] = secretKey ++ if err := session.Save(r, w); err != nil { ++ http.Redirect(w, r, "/login?error=session_error", http.StatusFound) ++ return ++ } ++ ++ http.Redirect(w, r, "/buckets", http.StatusFound) ++ } ++} ++ ++// HandleLogout destroys the session and redirects to login. ++func HandleLogout(store *sessions.CookieStore) http.HandlerFunc { ++ return func(w http.ResponseWriter, r *http.Request) { ++ session, err := store.Get(r, "s3session") ++ if err == nil { ++ session.Options.MaxAge = -1 ++ _ = session.Save(r, w) ++ } ++ http.Redirect(w, r, "/login", http.StatusFound) ++ } ++} ++ ++// DynamicS3Handler wraps a handler factory that takes S3 as its only parameter. ++func DynamicS3Handler(factory func(S3) http.HandlerFunc) http.HandlerFunc { ++ return func(w http.ResponseWriter, r *http.Request) { ++ s3 := S3FromContext(r.Context()) ++ if s3 == nil { ++ http.Redirect(w, r, "/login", http.StatusFound) ++ return ++ } ++ factory(s3).ServeHTTP(w, r) ++ } ++} +diff --git a/main.go b/main.go +index 2ffe8ab..5fdc4cd 100644 +--- a/main.go ++++ b/main.go +@@ -41,10 +41,12 @@ type configuration struct { + Timeout int32 + SseType string + SseKey string ++ LoginMode bool + } + + func parseConfiguration() configuration { + var accessKeyID, secretAccessKey, iamEndpoint string ++ var loginMode bool + + viper.AutomaticEnv() + +@@ -57,13 +59,11 @@ func parseConfiguration() configuration { + iamEndpoint = viper.GetString("IAM_ENDPOINT") + } else { + accessKeyID = viper.GetString("ACCESS_KEY_ID") +- if len(accessKeyID) == 0 { +- log.Fatal("please provide ACCESS_KEY_ID") +- } +- + secretAccessKey = viper.GetString("SECRET_ACCESS_KEY") +- if len(secretAccessKey) == 0 { +- log.Fatal("please provide SECRET_ACCESS_KEY") ++ ++ if len(accessKeyID) == 0 || len(secretAccessKey) == 0 { ++ log.Println("ACCESS_KEY_ID or SECRET_ACCESS_KEY not set, starting in login mode") ++ loginMode = true + } + } + +@@ -115,6 +115,7 @@ func parseConfiguration() configuration { + Timeout: timeout, + SseType: sseType, + SseKey: sseKey, ++ LoginMode: loginMode, + } + } + +@@ -135,57 +136,103 @@ func main() { + log.Fatal(err) + } + +- // Set up S3 client +- opts := &minio.Options{ +- Secure: configuration.UseSSL, +- } +- if configuration.UseIam { +- opts.Creds = credentials.NewIAM(configuration.IamEndpoint) ++ // Set up router ++ r := mux.NewRouter() ++ r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.FS(statics)))).Methods(http.MethodGet) ++ ++ if configuration.LoginMode { ++ // Login mode: no pre-configured S3 client, use per-session credentials ++ store := s3manager.NewSessionStore() ++ authMiddleware := s3manager.RequireAuth(store, configuration.Endpoint, configuration.UseSSL, configuration.SkipSSLVerification) ++ ++ // Public routes ++ r.Handle("/login", s3manager.HandleLoginView(templates)).Methods(http.MethodGet) ++ r.Handle("/login", s3manager.HandleLogin(store, configuration.Endpoint, configuration.UseSSL, configuration.SkipSSLVerification)).Methods(http.MethodPost) ++ r.Handle("/logout", s3manager.HandleLogout(store)).Methods(http.MethodPost) ++ ++ // Protected routes - use dynamic S3 client from session ++ protected := r.PathPrefix("/").Subrouter() ++ protected.Use(authMiddleware) ++ ++ protected.Handle("/", http.RedirectHandler("/buckets", http.StatusPermanentRedirect)).Methods(http.MethodGet) ++ protected.Handle("/buckets", s3manager.DynamicS3Handler(func(s3 s3manager.S3) http.HandlerFunc { ++ return s3manager.HandleBucketsView(s3, templates, configuration.AllowDelete) ++ })).Methods(http.MethodGet) ++ protected.PathPrefix("/buckets/").Handler(s3manager.DynamicS3Handler(func(s3 s3manager.S3) http.HandlerFunc { ++ return s3manager.HandleBucketView(s3, templates, configuration.AllowDelete, configuration.ListRecursive) ++ })).Methods(http.MethodGet) ++ protected.Handle("/api/buckets", s3manager.DynamicS3Handler(func(s3 s3manager.S3) http.HandlerFunc { ++ return s3manager.HandleCreateBucket(s3) ++ })).Methods(http.MethodPost) ++ if configuration.AllowDelete { ++ protected.Handle("/api/buckets/{bucketName}", s3manager.DynamicS3Handler(func(s3 s3manager.S3) http.HandlerFunc { ++ return s3manager.HandleDeleteBucket(s3) ++ })).Methods(http.MethodDelete) ++ } ++ protected.Handle("/api/buckets/{bucketName}/objects", s3manager.DynamicS3Handler(func(s3 s3manager.S3) http.HandlerFunc { ++ return s3manager.HandleCreateObject(s3, sseType) ++ })).Methods(http.MethodPost) ++ protected.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}/url", s3manager.DynamicS3Handler(func(s3 s3manager.S3) http.HandlerFunc { ++ return s3manager.HandleGenerateUrl(s3) ++ })).Methods(http.MethodGet) ++ protected.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}", s3manager.DynamicS3Handler(func(s3 s3manager.S3) http.HandlerFunc { ++ return s3manager.HandleGetObject(s3, configuration.ForceDownload) ++ })).Methods(http.MethodGet) ++ if configuration.AllowDelete { ++ protected.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}", s3manager.DynamicS3Handler(func(s3 s3manager.S3) http.HandlerFunc { ++ return s3manager.HandleDeleteObject(s3) ++ })).Methods(http.MethodDelete) ++ } + } else { +- var signatureType credentials.SignatureType +- +- switch configuration.SignatureType { +- case "V2": +- signatureType = credentials.SignatureV2 +- case "V4": +- signatureType = credentials.SignatureV4 +- case "V4Streaming": +- signatureType = credentials.SignatureV4Streaming +- case "Anonymous": +- signatureType = credentials.SignatureAnonymous +- default: +- log.Fatalf("Invalid SIGNATURE_TYPE: %s", configuration.SignatureType) ++ // Pre-configured mode: existing behavior with static S3 client ++ opts := &minio.Options{ ++ Secure: configuration.UseSSL, ++ } ++ if configuration.UseIam { ++ opts.Creds = credentials.NewIAM(configuration.IamEndpoint) ++ } else { ++ var signatureType credentials.SignatureType ++ ++ switch configuration.SignatureType { ++ case "V2": ++ signatureType = credentials.SignatureV2 ++ case "V4": ++ signatureType = credentials.SignatureV4 ++ case "V4Streaming": ++ signatureType = credentials.SignatureV4Streaming ++ case "Anonymous": ++ signatureType = credentials.SignatureAnonymous ++ default: ++ log.Fatalf("Invalid SIGNATURE_TYPE: %s", configuration.SignatureType) ++ } ++ ++ opts.Creds = credentials.NewStatic(configuration.AccessKeyID, configuration.SecretAccessKey, "", signatureType) + } + +- opts.Creds = credentials.NewStatic(configuration.AccessKeyID, configuration.SecretAccessKey, "", signatureType) +- } +- +- if configuration.Region != "" { +- opts.Region = configuration.Region +- } +- if configuration.UseSSL && configuration.SkipSSLVerification { +- opts.Transport = &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} //nolint:gosec +- } +- s3, err := minio.New(configuration.Endpoint, opts) +- if err != nil { +- log.Fatalln(fmt.Errorf("error creating s3 client: %w", err)) +- } ++ if configuration.Region != "" { ++ opts.Region = configuration.Region ++ } ++ if configuration.UseSSL && configuration.SkipSSLVerification { ++ opts.Transport = &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} //nolint:gosec ++ } ++ s3, err := minio.New(configuration.Endpoint, opts) ++ if err != nil { ++ log.Fatalln(fmt.Errorf("error creating s3 client: %w", err)) ++ } + +- // Set up router +- r := mux.NewRouter() +- r.Handle("/", http.RedirectHandler("/buckets", http.StatusPermanentRedirect)).Methods(http.MethodGet) +- r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.FS(statics)))).Methods(http.MethodGet) +- r.Handle("/buckets", s3manager.HandleBucketsView(s3, templates, configuration.AllowDelete)).Methods(http.MethodGet) +- r.PathPrefix("/buckets/").Handler(s3manager.HandleBucketView(s3, templates, configuration.AllowDelete, configuration.ListRecursive)).Methods(http.MethodGet) +- r.Handle("/api/buckets", s3manager.HandleCreateBucket(s3)).Methods(http.MethodPost) +- if configuration.AllowDelete { +- r.Handle("/api/buckets/{bucketName}", s3manager.HandleDeleteBucket(s3)).Methods(http.MethodDelete) +- } +- r.Handle("/api/buckets/{bucketName}/objects", s3manager.HandleCreateObject(s3, sseType)).Methods(http.MethodPost) +- r.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}/url", s3manager.HandleGenerateUrl(s3)).Methods(http.MethodGet) +- r.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}", s3manager.HandleGetObject(s3, configuration.ForceDownload)).Methods(http.MethodGet) +- if configuration.AllowDelete { +- r.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}", s3manager.HandleDeleteObject(s3)).Methods(http.MethodDelete) ++ r.Handle("/", http.RedirectHandler("/buckets", http.StatusPermanentRedirect)).Methods(http.MethodGet) ++ r.Handle("/buckets", s3manager.HandleBucketsView(s3, templates, configuration.AllowDelete)).Methods(http.MethodGet) ++ r.PathPrefix("/buckets/").Handler(s3manager.HandleBucketView(s3, templates, configuration.AllowDelete, configuration.ListRecursive)).Methods(http.MethodGet) ++ r.Handle("/api/buckets", s3manager.HandleCreateBucket(s3)).Methods(http.MethodPost) ++ if configuration.AllowDelete { ++ r.Handle("/api/buckets/{bucketName}", s3manager.HandleDeleteBucket(s3)).Methods(http.MethodDelete) ++ } ++ r.Handle("/api/buckets/{bucketName}/objects", s3manager.HandleCreateObject(s3, sseType)).Methods(http.MethodPost) ++ r.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}/url", s3manager.HandleGenerateUrl(s3)).Methods(http.MethodGet) ++ r.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}", s3manager.HandleGetObject(s3, configuration.ForceDownload)).Methods(http.MethodGet) ++ if configuration.AllowDelete { ++ r.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}", s3manager.HandleDeleteObject(s3)).Methods(http.MethodDelete) ++ } + } + + lr := logging.Handler(os.Stdout)(r) diff --git a/web/template/bucket.html.tmpl b/web/template/bucket.html.tmpl index e2f8d28..87add13 100644 --- a/web/template/bucket.html.tmpl @@ -24,3 +422,78 @@ index c7ea184..fb1dce7 100644 +diff --git a/web/template/login.html.tmpl b/web/template/login.html.tmpl +new file mode 100644 +index 0000000..7a1730a +--- /dev/null ++++ b/web/template/login.html.tmpl +@@ -0,0 +1,69 @@ ++{{ define "content" }} ++ ++ ++
++
++
++
++
++
++ Login ++

Enter your S3 credentials to access buckets.

++
++ ++ {{ if eq .Error "invalid_credentials" }} ++
++ error ++ Invalid credentials. Please check your Access Key and Secret Key. ++
++ {{ end }} ++ ++ {{ if eq .Error "credentials_required" }} ++
++ error ++ Both Access Key ID and Secret Access Key are required. ++
++ {{ end }} ++ ++ {{ if eq .Error "session_error" }} ++
++ error ++ Session error. Please try again. ++
++ {{ end }} ++ ++
++
++
++ vpn_key ++ ++ ++
++
++
++
++ lock ++ ++ ++
++
++
++
++ ++
++
++
++
++
++
++
++
++
++{{ end }} From 175b3badd2642a21d249fcac590f807b66a6fa2c Mon Sep 17 00:00:00 2001 From: IvanHunters Date: Wed, 4 Mar 2026 18:30:49 +0300 Subject: [PATCH 034/488] feat(bucket): make credentials and basic auth conditional on users deployment.yaml: use s3._namespace.host for ENDPOINT instead of secret ref, inject ACCESS_KEY_ID/SECRET_ACCESS_KEY only when users exist. Without users, s3manager starts in login mode. ingress.yaml: nginx basic auth annotations only when users exist. Without users, s3manager handles authentication via its login form. Co-Authored-By: Claude Signed-off-by: IvanHunters --- packages/system/bucket/templates/deployment.yaml | 8 ++++---- packages/system/bucket/templates/ingress.yaml | 3 +++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/system/bucket/templates/deployment.yaml b/packages/system/bucket/templates/deployment.yaml index 7e115de0..086a6934 100644 --- a/packages/system/bucket/templates/deployment.yaml +++ b/packages/system/bucket/templates/deployment.yaml @@ -1,3 +1,4 @@ +{{- $users := keys .Values.users | sortAlpha }} apiVersion: apps/v1 kind: Deployment metadata: @@ -17,12 +18,10 @@ spec: image: "{{ $.Files.Get "images/s3manager.tag" | trim }}" env: - name: ENDPOINT - valueFrom: - secretKeyRef: - name: {{ .Values.bucketName }}-credentials - key: endpoint + value: "s3.{{ .Values._namespace.host }}" - name: SKIP_SSL_VERIFICATION value: "true" + {{- if $users }} - name: ACCESS_KEY_ID valueFrom: secretKeyRef: @@ -33,3 +32,4 @@ spec: secretKeyRef: name: {{ .Values.bucketName }}-credentials key: secretKey + {{- end }} diff --git a/packages/system/bucket/templates/ingress.yaml b/packages/system/bucket/templates/ingress.yaml index 9ad9f46d..e54fede3 100644 --- a/packages/system/bucket/templates/ingress.yaml +++ b/packages/system/bucket/templates/ingress.yaml @@ -2,15 +2,18 @@ {{- $ingress := .Values._namespace.ingress }} {{- $solver := (index .Values._cluster "solver") | default "http01" }} {{- $clusterIssuer := (index .Values._cluster "issuer-name") | default "letsencrypt-prod" }} +{{- $users := keys .Values.users | sortAlpha }} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: {{ .Values.bucketName }}-ui annotations: + {{- if $users }} nginx.ingress.kubernetes.io/auth-type: "basic" nginx.ingress.kubernetes.io/auth-secret: "{{ .Values.bucketName }}-ui-auth" nginx.ingress.kubernetes.io/auth-realm: "Authentication Required" + {{- end }} nginx.ingress.kubernetes.io/proxy-body-size: "0" nginx.ingress.kubernetes.io/proxy-read-timeout: "99999" nginx.ingress.kubernetes.io/proxy-send-timeout: "99999" From 84da47a2ce5494cab5ddefe6c3922a53201635a1 Mon Sep 17 00:00:00 2001 From: IvanHunters Date: Wed, 4 Mar 2026 18:47:51 +0300 Subject: [PATCH 035/488] refactor(bucket): replace basic auth with s3manager login page Remove nginx basic auth and credential secret injection from the bucket Helm chart. s3manager now always starts in login mode and handles authentication via its own login page with encrypted session cookies. This eliminates the dependency on the -credentials and -ui-auth secrets for the UI layer. Co-Authored-By: Claude Signed-off-by: IvanHunters --- .../bucket/images/s3manager/cozystack.patch | 648 ++++++++++-------- .../system/bucket/templates/deployment.yaml | 13 - packages/system/bucket/templates/ingress.yaml | 6 - packages/system/bucket/templates/secret.yaml | 32 +- 4 files changed, 353 insertions(+), 346 deletions(-) diff --git a/packages/system/bucket/images/s3manager/cozystack.patch b/packages/system/bucket/images/s3manager/cozystack.patch index 9e98edde..2f569042 100644 --- a/packages/system/bucket/images/s3manager/cozystack.patch +++ b/packages/system/bucket/images/s3manager/cozystack.patch @@ -23,187 +23,29 @@ index b5d8540..6ede8e8 100644 github.com/hashicorp/hcl v1.0.0 // indirect github.com/klauspost/compress v1.17.9 // indirect github.com/klauspost/cpuid/v2 v2.2.8 // indirect -diff --git a/internal/app/s3manager/auth.go b/internal/app/s3manager/auth.go -new file mode 100644 -index 0000000..a315cde ---- /dev/null -+++ b/internal/app/s3manager/auth.go -@@ -0,0 +1,173 @@ -+package s3manager -+ -+import ( -+ "context" -+ "crypto/rand" -+ "crypto/tls" -+ "fmt" -+ "html/template" -+ "io/fs" -+ "net/http" -+ -+ "github.com/gorilla/sessions" -+ "github.com/minio/minio-go/v7" -+ "github.com/minio/minio-go/v7/pkg/credentials" -+) -+ -+type contextKey string -+ -+const s3ContextKey contextKey = "s3client" -+ -+// S3FromContext retrieves the S3 client from the request context. -+func S3FromContext(ctx context.Context) S3 { -+ s3, _ := ctx.Value(s3ContextKey).(S3) -+ return s3 -+} -+ -+func contextWithS3(ctx context.Context, s3 S3) context.Context { -+ return context.WithValue(ctx, s3ContextKey, s3) -+} -+ -+// NewS3Client creates a new minio S3 client with the given credentials. -+func NewS3Client(endpoint, accessKey, secretKey string, useSSL, skipVerify bool) (*minio.Client, error) { -+ opts := &minio.Options{ -+ Creds: credentials.NewStaticV4(accessKey, secretKey, ""), -+ Secure: useSSL, -+ } -+ if useSSL && skipVerify { -+ opts.Transport = &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} //nolint:gosec -+ } -+ return minio.New(endpoint, opts) -+} -+ -+// NewSessionStore creates a new encrypted cookie store with a random key. -+func NewSessionStore() *sessions.CookieStore { -+ key := make([]byte, 32) -+ if _, err := rand.Read(key); err != nil { -+ panic(fmt.Sprintf("failed to generate session key: %v", err)) -+ } -+ store := sessions.NewCookieStore(key) -+ store.Options = &sessions.Options{ -+ Path: "/", -+ MaxAge: 86400, // 24 hours -+ HttpOnly: true, -+ Secure: true, -+ SameSite: http.SameSiteLaxMode, -+ } -+ return store -+} -+ -+// RequireAuth is middleware that checks for valid S3 credentials in the session. -+func RequireAuth(store *sessions.CookieStore, endpoint string, useSSL, skipVerify bool) func(http.Handler) http.Handler { -+ return func(next http.Handler) http.Handler { -+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { -+ session, err := store.Get(r, "s3session") -+ if err != nil { -+ http.Redirect(w, r, "/login", http.StatusFound) -+ return -+ } -+ -+ accessKey, ok1 := session.Values["accessKey"].(string) -+ secretKey, ok2 := session.Values["secretKey"].(string) -+ if !ok1 || !ok2 || accessKey == "" || secretKey == "" { -+ http.Redirect(w, r, "/login", http.StatusFound) -+ return -+ } -+ -+ s3Client, err := NewS3Client(endpoint, accessKey, secretKey, useSSL, skipVerify) -+ if err != nil { -+ http.Redirect(w, r, "/login?error=invalid_credentials", http.StatusFound) -+ return -+ } -+ -+ ctx := contextWithS3(r.Context(), s3Client) -+ next.ServeHTTP(w, r.WithContext(ctx)) -+ }) -+ } -+} -+ -+// HandleLoginView renders the login page. -+func HandleLoginView(templates fs.FS) http.HandlerFunc { -+ return func(w http.ResponseWriter, r *http.Request) { -+ data := struct { -+ Error string -+ }{ -+ Error: r.URL.Query().Get("error"), -+ } -+ t, err := template.ParseFS(templates, "layout.html.tmpl", "login.html.tmpl") -+ if err != nil { -+ handleHTTPError(w, fmt.Errorf("error parsing login template: %w", err)) -+ return -+ } -+ err = t.ExecuteTemplate(w, "layout", data) -+ if err != nil { -+ handleHTTPError(w, fmt.Errorf("error executing login template: %w", err)) -+ return -+ } -+ } -+} -+ -+// HandleLogin processes login form submission. -+func HandleLogin(store *sessions.CookieStore, endpoint string, useSSL, skipVerify bool) http.HandlerFunc { -+ return func(w http.ResponseWriter, r *http.Request) { -+ accessKey := r.FormValue("accessKey") -+ secretKey := r.FormValue("secretKey") -+ -+ if accessKey == "" || secretKey == "" { -+ http.Redirect(w, r, "/login?error=credentials_required", http.StatusFound) -+ return -+ } -+ -+ // Validate credentials by attempting to list buckets -+ s3Client, err := NewS3Client(endpoint, accessKey, secretKey, useSSL, skipVerify) -+ if err != nil { -+ http.Redirect(w, r, "/login?error=invalid_credentials", http.StatusFound) -+ return -+ } -+ -+ _, err = s3Client.ListBuckets(r.Context()) -+ if err != nil { -+ http.Redirect(w, r, "/login?error=invalid_credentials", http.StatusFound) -+ return -+ } -+ -+ session, err := store.Get(r, "s3session") -+ if err != nil { -+ http.Redirect(w, r, "/login?error=session_error", http.StatusFound) -+ return -+ } -+ -+ session.Values["accessKey"] = accessKey -+ session.Values["secretKey"] = secretKey -+ if err := session.Save(r, w); err != nil { -+ http.Redirect(w, r, "/login?error=session_error", http.StatusFound) -+ return -+ } -+ -+ http.Redirect(w, r, "/buckets", http.StatusFound) -+ } -+} -+ -+// HandleLogout destroys the session and redirects to login. -+func HandleLogout(store *sessions.CookieStore) http.HandlerFunc { -+ return func(w http.ResponseWriter, r *http.Request) { -+ session, err := store.Get(r, "s3session") -+ if err == nil { -+ session.Options.MaxAge = -1 -+ _ = session.Save(r, w) -+ } -+ http.Redirect(w, r, "/login", http.StatusFound) -+ } -+} -+ -+// DynamicS3Handler wraps a handler factory that takes S3 as its only parameter. -+func DynamicS3Handler(factory func(S3) http.HandlerFunc) http.HandlerFunc { -+ return func(w http.ResponseWriter, r *http.Request) { -+ s3 := S3FromContext(r.Context()) -+ if s3 == nil { -+ http.Redirect(w, r, "/login", http.StatusFound) -+ return -+ } -+ factory(s3).ServeHTTP(w, r) -+ } -+} +diff --git a/go.sum b/go.sum +index 1ea1b16..d7866ce 100644 +--- a/go.sum ++++ b/go.sum +@@ -16,10 +16,16 @@ github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= + github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= + github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= + github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= ++github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= ++github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= + github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= + github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= + github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= + github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= ++github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= ++github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= ++github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ= ++github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik= + github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= + github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= + github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= diff --git a/main.go b/main.go -index 2ffe8ab..5fdc4cd 100644 +index 2ffe8ab..723a1b8 100644 --- a/main.go +++ b/main.go @@ -41,10 +41,12 @@ type configuration struct { @@ -219,7 +61,7 @@ index 2ffe8ab..5fdc4cd 100644 viper.AutomaticEnv() -@@ -57,13 +59,11 @@ func parseConfiguration() configuration { +@@ -57,13 +59,10 @@ func parseConfiguration() configuration { iamEndpoint = viper.GetString("IAM_ENDPOINT") } else { accessKeyID = viper.GetString("ACCESS_KEY_ID") @@ -230,14 +72,13 @@ index 2ffe8ab..5fdc4cd 100644 secretAccessKey = viper.GetString("SECRET_ACCESS_KEY") - if len(secretAccessKey) == 0 { - log.Fatal("please provide SECRET_ACCESS_KEY") -+ + if len(accessKeyID) == 0 || len(secretAccessKey) == 0 { + log.Println("ACCESS_KEY_ID or SECRET_ACCESS_KEY not set, starting in login mode") + loginMode = true } } -@@ -115,6 +115,7 @@ func parseConfiguration() configuration { +@@ -115,6 +114,7 @@ func parseConfiguration() configuration { Timeout: timeout, SseType: sseType, SseKey: sseKey, @@ -245,7 +86,7 @@ index 2ffe8ab..5fdc4cd 100644 } } -@@ -135,57 +136,103 @@ func main() { +@@ -135,57 +135,96 @@ func main() { log.Fatal(err) } @@ -255,54 +96,7 @@ index 2ffe8ab..5fdc4cd 100644 - } - if configuration.UseIam { - opts.Creds = credentials.NewIAM(configuration.IamEndpoint) -+ // Set up router -+ r := mux.NewRouter() -+ r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.FS(statics)))).Methods(http.MethodGet) -+ -+ if configuration.LoginMode { -+ // Login mode: no pre-configured S3 client, use per-session credentials -+ store := s3manager.NewSessionStore() -+ authMiddleware := s3manager.RequireAuth(store, configuration.Endpoint, configuration.UseSSL, configuration.SkipSSLVerification) -+ -+ // Public routes -+ r.Handle("/login", s3manager.HandleLoginView(templates)).Methods(http.MethodGet) -+ r.Handle("/login", s3manager.HandleLogin(store, configuration.Endpoint, configuration.UseSSL, configuration.SkipSSLVerification)).Methods(http.MethodPost) -+ r.Handle("/logout", s3manager.HandleLogout(store)).Methods(http.MethodPost) -+ -+ // Protected routes - use dynamic S3 client from session -+ protected := r.PathPrefix("/").Subrouter() -+ protected.Use(authMiddleware) -+ -+ protected.Handle("/", http.RedirectHandler("/buckets", http.StatusPermanentRedirect)).Methods(http.MethodGet) -+ protected.Handle("/buckets", s3manager.DynamicS3Handler(func(s3 s3manager.S3) http.HandlerFunc { -+ return s3manager.HandleBucketsView(s3, templates, configuration.AllowDelete) -+ })).Methods(http.MethodGet) -+ protected.PathPrefix("/buckets/").Handler(s3manager.DynamicS3Handler(func(s3 s3manager.S3) http.HandlerFunc { -+ return s3manager.HandleBucketView(s3, templates, configuration.AllowDelete, configuration.ListRecursive) -+ })).Methods(http.MethodGet) -+ protected.Handle("/api/buckets", s3manager.DynamicS3Handler(func(s3 s3manager.S3) http.HandlerFunc { -+ return s3manager.HandleCreateBucket(s3) -+ })).Methods(http.MethodPost) -+ if configuration.AllowDelete { -+ protected.Handle("/api/buckets/{bucketName}", s3manager.DynamicS3Handler(func(s3 s3manager.S3) http.HandlerFunc { -+ return s3manager.HandleDeleteBucket(s3) -+ })).Methods(http.MethodDelete) -+ } -+ protected.Handle("/api/buckets/{bucketName}/objects", s3manager.DynamicS3Handler(func(s3 s3manager.S3) http.HandlerFunc { -+ return s3manager.HandleCreateObject(s3, sseType) -+ })).Methods(http.MethodPost) -+ protected.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}/url", s3manager.DynamicS3Handler(func(s3 s3manager.S3) http.HandlerFunc { -+ return s3manager.HandleGenerateUrl(s3) -+ })).Methods(http.MethodGet) -+ protected.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}", s3manager.DynamicS3Handler(func(s3 s3manager.S3) http.HandlerFunc { -+ return s3manager.HandleGetObject(s3, configuration.ForceDownload) -+ })).Methods(http.MethodGet) -+ if configuration.AllowDelete { -+ protected.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}", s3manager.DynamicS3Handler(func(s3 s3manager.S3) http.HandlerFunc { -+ return s3manager.HandleDeleteObject(s3) -+ })).Methods(http.MethodDelete) -+ } - } else { +- } else { - var signatureType credentials.SignatureType - - switch configuration.SignatureType { @@ -316,6 +110,59 @@ index 2ffe8ab..5fdc4cd 100644 - signatureType = credentials.SignatureAnonymous - default: - log.Fatalf("Invalid SIGNATURE_TYPE: %s", configuration.SignatureType) ++ // Set up router ++ r := mux.NewRouter() ++ r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.FS(statics)))).Methods(http.MethodGet) ++ ++ if configuration.LoginMode { ++ // Login mode: no pre-configured S3 client, per-session credentials ++ sessionCfg := &s3manager.SessionConfig{ ++ Store: s3manager.NewSessionStore(), ++ Endpoint: configuration.Endpoint, ++ UseSSL: configuration.UseSSL, ++ SkipSSLVerify: configuration.SkipSSLVerification, ++ AllowDelete: configuration.AllowDelete, ++ ForceDownload: configuration.ForceDownload, ++ ListRecursive: configuration.ListRecursive, ++ SseInfo: sseType, ++ Templates: templates, + } + +- opts.Creds = credentials.NewStatic(configuration.AccessKeyID, configuration.SecretAccessKey, "", signatureType) +- } ++ // Public routes (no auth required) ++ r.Handle("/login", s3manager.HandleLoginView(templates)).Methods(http.MethodGet) ++ r.Handle("/login", s3manager.HandleLogin(sessionCfg)).Methods(http.MethodPost) ++ r.Handle("/logout", s3manager.HandleLogout(sessionCfg)).Methods(http.MethodPost) ++ ++ // Protected routes (auth required via middleware) ++ protected := mux.NewRouter() ++ protected.Handle("/", http.RedirectHandler("/buckets", http.StatusPermanentRedirect)).Methods(http.MethodGet) ++ protected.Handle("/buckets", s3manager.HandleBucketsViewDynamic(templates, configuration.AllowDelete)).Methods(http.MethodGet) ++ protected.PathPrefix("/buckets/").Handler(s3manager.HandleBucketViewDynamic(templates, configuration.AllowDelete, configuration.ListRecursive)).Methods(http.MethodGet) ++ protected.Handle("/api/buckets", s3manager.HandleCreateBucketDynamic()).Methods(http.MethodPost) ++ if configuration.AllowDelete { ++ protected.Handle("/api/buckets/{bucketName}", s3manager.HandleDeleteBucketDynamic()).Methods(http.MethodDelete) ++ } ++ protected.Handle("/api/buckets/{bucketName}/objects", s3manager.HandleCreateObjectDynamic(sseType)).Methods(http.MethodPost) ++ protected.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}/url", s3manager.HandleGenerateUrlDynamic()).Methods(http.MethodGet) ++ protected.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}", s3manager.HandleGetObjectDynamic(configuration.ForceDownload)).Methods(http.MethodGet) ++ if configuration.AllowDelete { ++ protected.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}", s3manager.HandleDeleteObjectDynamic()).Methods(http.MethodDelete) ++ } + +- if configuration.Region != "" { +- opts.Region = configuration.Region +- } +- if configuration.UseSSL && configuration.SkipSSLVerification { +- opts.Transport = &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} //nolint:gosec +- } +- s3, err := minio.New(configuration.Endpoint, opts) +- if err != nil { +- log.Fatalln(fmt.Errorf("error creating s3 client: %w", err)) +- } ++ r.PathPrefix("/").Handler(s3manager.RequireAuth(sessionCfg, protected)) ++ } else { + // Pre-configured mode: existing behavior with static S3 client + opts := &minio.Options{ + Secure: configuration.UseSSL, @@ -339,30 +186,6 @@ index 2ffe8ab..5fdc4cd 100644 + } + + opts.Creds = credentials.NewStatic(configuration.AccessKeyID, configuration.SecretAccessKey, "", signatureType) - } - -- opts.Creds = credentials.NewStatic(configuration.AccessKeyID, configuration.SecretAccessKey, "", signatureType) -- } -- -- if configuration.Region != "" { -- opts.Region = configuration.Region -- } -- if configuration.UseSSL && configuration.SkipSSLVerification { -- opts.Transport = &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} //nolint:gosec -- } -- s3, err := minio.New(configuration.Endpoint, opts) -- if err != nil { -- log.Fatalln(fmt.Errorf("error creating s3 client: %w", err)) -- } -+ if configuration.Region != "" { -+ opts.Region = configuration.Region -+ } -+ if configuration.UseSSL && configuration.SkipSSLVerification { -+ opts.Transport = &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} //nolint:gosec -+ } -+ s3, err := minio.New(configuration.Endpoint, opts) -+ if err != nil { -+ log.Fatalln(fmt.Errorf("error creating s3 client: %w", err)) + } - // Set up router @@ -380,6 +203,17 @@ index 2ffe8ab..5fdc4cd 100644 - r.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}", s3manager.HandleGetObject(s3, configuration.ForceDownload)).Methods(http.MethodGet) - if configuration.AllowDelete { - r.Handle("/api/buckets/{bucketName}/objects/{objectName:.*}", s3manager.HandleDeleteObject(s3)).Methods(http.MethodDelete) ++ if configuration.Region != "" { ++ opts.Region = configuration.Region ++ } ++ if configuration.UseSSL && configuration.SkipSSLVerification { ++ opts.Transport = &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} //nolint:gosec ++ } ++ s3, err := minio.New(configuration.Endpoint, opts) ++ if err != nil { ++ log.Fatalln(fmt.Errorf("error creating s3 client: %w", err)) ++ } ++ + r.Handle("/", http.RedirectHandler("/buckets", http.StatusPermanentRedirect)).Methods(http.MethodGet) + r.Handle("/buckets", s3manager.HandleBucketsView(s3, templates, configuration.AllowDelete)).Methods(http.MethodGet) + r.PathPrefix("/buckets/").Handler(s3manager.HandleBucketView(s3, templates, configuration.AllowDelete, configuration.ListRecursive)).Methods(http.MethodGet) @@ -422,12 +256,255 @@ index c7ea184..fb1dce7 100644 +diff --git a/internal/app/s3manager/auth.go b/internal/app/s3manager/auth.go +new file mode 100644 +index 0000000..58589e2 +--- /dev/null ++++ b/internal/app/s3manager/auth.go +@@ -0,0 +1,237 @@ ++package s3manager ++ ++import ( ++ "context" ++ "crypto/rand" ++ "crypto/tls" ++ "fmt" ++ "html/template" ++ "io/fs" ++ "log" ++ "net/http" ++ ++ "github.com/gorilla/sessions" ++ "github.com/minio/minio-go/v7" ++ "github.com/minio/minio-go/v7/pkg/credentials" ++) ++ ++type contextKey string ++ ++const s3ContextKey contextKey = "s3client" ++ ++// SessionConfig holds session store and S3 connection settings for login mode. ++type SessionConfig struct { ++ Store *sessions.CookieStore ++ Endpoint string ++ UseSSL bool ++ SkipSSLVerify bool ++ AllowDelete bool ++ ForceDownload bool ++ ListRecursive bool ++ SseInfo SSEType ++ Templates fs.FS ++} ++ ++// NewSessionStore creates a CookieStore with a random encryption key. ++func NewSessionStore() *sessions.CookieStore { ++ key := make([]byte, 32) ++ if _, err := rand.Read(key); err != nil { ++ log.Fatal("failed to generate session key:", err) ++ } ++ store := sessions.NewCookieStore(key) ++ store.Options = &sessions.Options{ ++ Path: "/", ++ MaxAge: 86400, ++ HttpOnly: true, ++ Secure: true, ++ SameSite: http.SameSiteLaxMode, ++ } ++ return store ++} ++ ++// NewS3Client creates a minio client from user-provided credentials. ++func NewS3Client(endpoint, accessKey, secretKey string, useSSL, skipSSLVerify bool) (*minio.Client, error) { ++ opts := &minio.Options{ ++ Creds: credentials.NewStaticV4(accessKey, secretKey, ""), ++ Secure: useSSL, ++ } ++ if useSSL && skipSSLVerify { ++ opts.Transport = &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}} //nolint:gosec ++ } ++ return minio.New(endpoint, opts) ++} ++ ++// S3FromContext retrieves the S3 client stored in request context. ++func S3FromContext(ctx context.Context) S3 { ++ if s3, ok := ctx.Value(s3ContextKey).(S3); ok { ++ return s3 ++ } ++ return nil ++} ++ ++func contextWithS3(ctx context.Context, s3 S3) context.Context { ++ return context.WithValue(ctx, s3ContextKey, s3) ++} ++ ++// RequireAuth is middleware that validates session credentials and injects ++// an S3 client into the request context. Redirects to /login if no session. ++func RequireAuth(cfg *SessionConfig, next http.Handler) http.Handler { ++ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ++ session, _ := cfg.Store.Get(r, "s3session") ++ accessKey, ok1 := session.Values["accessKey"].(string) ++ secretKey, ok2 := session.Values["secretKey"].(string) ++ if !ok1 || !ok2 || accessKey == "" || secretKey == "" { ++ http.Redirect(w, r, "/login", http.StatusFound) ++ return ++ } ++ ++ s3, err := NewS3Client(cfg.Endpoint, accessKey, secretKey, cfg.UseSSL, cfg.SkipSSLVerify) ++ if err != nil { ++ // Session has bad credentials — clear and redirect to login ++ session.Options.MaxAge = -1 ++ _ = session.Save(r, w) ++ http.Redirect(w, r, "/login", http.StatusFound) ++ return ++ } ++ ++ ctx := contextWithS3(r.Context(), s3) ++ next.ServeHTTP(w, r.WithContext(ctx)) ++ }) ++} ++ ++// HandleLoginView renders the login page. ++func HandleLoginView(templates fs.FS) http.HandlerFunc { ++ return func(w http.ResponseWriter, r *http.Request) { ++ errorMsg := r.URL.Query().Get("error") ++ ++ data := struct { ++ Error string ++ }{ ++ Error: errorMsg, ++ } ++ ++ t, err := template.ParseFS(templates, "layout.html.tmpl", "login.html.tmpl") ++ if err != nil { ++ handleHTTPError(w, fmt.Errorf("error parsing login template: %w", err)) ++ return ++ } ++ err = t.ExecuteTemplate(w, "layout", data) ++ if err != nil { ++ handleHTTPError(w, fmt.Errorf("error executing login template: %w", err)) ++ return ++ } ++ } ++} ++ ++// HandleLogin processes the login form POST. ++func HandleLogin(cfg *SessionConfig) http.HandlerFunc { ++ return func(w http.ResponseWriter, r *http.Request) { ++ accessKey := r.FormValue("accessKey") ++ secretKey := r.FormValue("secretKey") ++ ++ if accessKey == "" || secretKey == "" { ++ http.Redirect(w, r, "/login?error=credentials+required", http.StatusFound) ++ return ++ } ++ ++ // Validate credentials by attempting ListBuckets ++ s3, err := NewS3Client(cfg.Endpoint, accessKey, secretKey, cfg.UseSSL, cfg.SkipSSLVerify) ++ if err != nil { ++ http.Redirect(w, r, "/login?error=connection+failed", http.StatusFound) ++ return ++ } ++ _, err = s3.ListBuckets(r.Context()) ++ if err != nil { ++ http.Redirect(w, r, "/login?error=invalid+credentials", http.StatusFound) ++ return ++ } ++ ++ // Save credentials to session ++ session, _ := cfg.Store.Get(r, "s3session") ++ session.Values["accessKey"] = accessKey ++ session.Values["secretKey"] = secretKey ++ err = session.Save(r, w) ++ if err != nil { ++ handleHTTPError(w, fmt.Errorf("error saving session: %w", err)) ++ return ++ } ++ ++ http.Redirect(w, r, "/buckets", http.StatusFound) ++ } ++} ++ ++// HandleLogout destroys the session and redirects to login. ++func HandleLogout(cfg *SessionConfig) http.HandlerFunc { ++ return func(w http.ResponseWriter, r *http.Request) { ++ session, _ := cfg.Store.Get(r, "s3session") ++ session.Options.MaxAge = -1 ++ _ = session.Save(r, w) ++ http.Redirect(w, r, "/login", http.StatusFound) ++ } ++} ++ ++// Dynamic handler wrappers — extract S3 from context, delegate to original handlers. ++ ++// HandleBucketsViewDynamic wraps HandleBucketsView for login mode. ++func HandleBucketsViewDynamic(templates fs.FS, allowDelete bool) http.HandlerFunc { ++ return func(w http.ResponseWriter, r *http.Request) { ++ s3 := S3FromContext(r.Context()) ++ HandleBucketsView(s3, templates, allowDelete).ServeHTTP(w, r) ++ } ++} ++ ++// HandleBucketViewDynamic wraps HandleBucketView for login mode. ++func HandleBucketViewDynamic(templates fs.FS, allowDelete bool, listRecursive bool) http.HandlerFunc { ++ return func(w http.ResponseWriter, r *http.Request) { ++ s3 := S3FromContext(r.Context()) ++ HandleBucketView(s3, templates, allowDelete, listRecursive).ServeHTTP(w, r) ++ } ++} ++ ++// HandleCreateBucketDynamic wraps HandleCreateBucket for login mode. ++func HandleCreateBucketDynamic() http.HandlerFunc { ++ return func(w http.ResponseWriter, r *http.Request) { ++ s3 := S3FromContext(r.Context()) ++ HandleCreateBucket(s3).ServeHTTP(w, r) ++ } ++} ++ ++// HandleDeleteBucketDynamic wraps HandleDeleteBucket for login mode. ++func HandleDeleteBucketDynamic() http.HandlerFunc { ++ return func(w http.ResponseWriter, r *http.Request) { ++ s3 := S3FromContext(r.Context()) ++ HandleDeleteBucket(s3).ServeHTTP(w, r) ++ } ++} ++ ++// HandleCreateObjectDynamic wraps HandleCreateObject for login mode. ++func HandleCreateObjectDynamic(sseInfo SSEType) http.HandlerFunc { ++ return func(w http.ResponseWriter, r *http.Request) { ++ s3 := S3FromContext(r.Context()) ++ HandleCreateObject(s3, sseInfo).ServeHTTP(w, r) ++ } ++} ++ ++// HandleGenerateUrlDynamic wraps HandleGenerateUrl for login mode. ++func HandleGenerateUrlDynamic() http.HandlerFunc { ++ return func(w http.ResponseWriter, r *http.Request) { ++ s3 := S3FromContext(r.Context()) ++ HandleGenerateUrl(s3).ServeHTTP(w, r) ++ } ++} ++ ++// HandleGetObjectDynamic wraps HandleGetObject for login mode. ++func HandleGetObjectDynamic(forceDownload bool) http.HandlerFunc { ++ return func(w http.ResponseWriter, r *http.Request) { ++ s3 := S3FromContext(r.Context()) ++ HandleGetObject(s3, forceDownload).ServeHTTP(w, r) ++ } ++} ++ ++// HandleDeleteObjectDynamic wraps HandleDeleteObject for login mode. ++func HandleDeleteObjectDynamic() http.HandlerFunc { ++ return func(w http.ResponseWriter, r *http.Request) { ++ s3 := S3FromContext(r.Context()) ++ HandleDeleteObject(s3).ServeHTTP(w, r) ++ } ++} diff --git a/web/template/login.html.tmpl b/web/template/login.html.tmpl new file mode 100644 -index 0000000..7a1730a +index 0000000..f153018 --- /dev/null +++ b/web/template/login.html.tmpl -@@ -0,0 +1,69 @@ +@@ -0,0 +1,46 @@ +{{ define "content" }} +