diff --git a/.gitignore b/.gitignore index d7ff67e9..64cb1073 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,8 @@ .LSOverride .idea +*.tar.gz + # helm chart dependencies charts/*/charts/ **/charts/*.tgz diff --git a/charts/must-gather/.helmignore b/charts/must-gather/.helmignore new file mode 100644 index 00000000..0e8a0eb3 --- /dev/null +++ b/charts/must-gather/.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/charts/must-gather/Chart.yaml b/charts/must-gather/Chart.yaml new file mode 100644 index 00000000..eeee1300 --- /dev/null +++ b/charts/must-gather/Chart.yaml @@ -0,0 +1,38 @@ +annotations: + charts.openshift.io/name: Red Hat Developer Hub Must Gather + charts.openshift.io/provider: Red Hat + charts.openshift.io/archs: x86_64 + charts.openshift.io/supportURL: https://access.redhat.com/support +apiVersion: v2 +name: rhdh-must-gather +description: | + A Helm chart for deploying the RHDH Must-Gather diagnostic tool on Kubernetes + +type: application + +# Application version - matches the must-gather tool version +appVersion: "0.2.0" + +keywords: + - rhdh + - red-hat-developer-hub + - backstage + - diagnostics + - must-gather + - troubleshooting + +home: https://github.com/redhat-developer/rhdh-must-gather +sources: + - https://github.com/redhat-developer/rhdh-must-gather + +kubeVersion: ">= 1.27.0-0" +maintainers: + - name: Red Hat + url: https://redhat.com + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +# Note that when this chart is published to https://github.com/openshift-helm-charts/charts +# it will follow the RHDH versioning 1.y.z +version: 0.1.0 diff --git a/charts/must-gather/README.md b/charts/must-gather/README.md new file mode 100644 index 00000000..766d6600 --- /dev/null +++ b/charts/must-gather/README.md @@ -0,0 +1,151 @@ + +# Must Gather Chart for Red Hat Developer Hub (RHDH) + +![Version: 0.1.0](https://img.shields.io/badge/Version-0.1.0-informational?style=flat-square) +![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) + +A Helm chart for deploying the RHDH Must-Gather diagnostic tool on Kubernetes + +**Homepage:** + +## Maintainers + +| Name | Email | Url | +| ---- | ------ | --- | +| Red Hat | | | + +## Source Code + +* + +## Requirements + +Kubernetes: `>= 1.27.0-0` + +## TL;DR + +```console +helm upgrade --install my-rhdh-must-gather rhdh-must-gather \ + --repo https://redhat-developer.github.io/rhdh-chart \ + --version 0.1.0 +``` + +Running the command again will automatically replace the previous pod and start a new gather. + +Then follow the instructions that will be printed to retrieve the gathered data. + +## Running on OpenShift + +This chart works on both Kubernetes and OpenShift. + +But for OpenShift, we recommend using the `oc adm must-gather` command, like so: + +```sh +oc adm must-gather --image=quay.io/rhdh-community/rhdh-must-gather +``` + +See the [must-gather tool README](https://github.com/redhat-developer/rhdh-must-gather#for-openshift-clusters) for more details. + +## Testing a Release + +Once a Helm Release has been deployed, you can test it using the [`helm test`](https://helm.sh/docs/helm/helm_test/) command: + +```sh +helm test +``` + +This will run a simple Pod in the cluster to check that the required resources have been created. + +You can control whether to disable this test pod or you can also customize the image it leverages. +See the `test.enabled` and `test.image` parameters in the [`values.yaml`](./values.yaml) file. + +> **Tip**: Disabling the test pod will not prevent the `helm test` command from passing later on. It will simply report that no test suite is available. + +Below are a few examples: + +
+ +Disabling the test pod + +```sh +helm install \ + --set test.enabled=false +``` + +
+ +
+ +Customizing the test pod image + +```sh +helm install \ + --set test.image.registry=docker.io \ + --set test.image.repository=bitnami/kubectl \ + --set test.image.tag=1.30.0 +``` + +
+ +## Uninstalling the Chart + +To uninstall/delete a Helm release named `my-rhdh-must-gather`: + +```console +helm uninstall my-rhdh-must-gather +``` + +The command removes all the Kubernetes resources associated with the chart and deletes the release. + +## Values + +| Key | Description | Type | Default | +|-----|-------------|------|---------| +| affinity | Affinity rules for pod scheduling | object | `{}` | +| dataHolder | Runs alongside the gather container and stays alive so you can exec in and retrieve the output. | object | `{"resources":{"limits":{"cpu":"100m","ephemeral-storage":"64Mi","memory":"128Mi"},"requests":{"cpu":"50m","ephemeral-storage":"32Mi","memory":"64Mi"}}}` | +| dataHolder.resources | Resource requests and limits for the data-holder container | object | `{"limits":{"cpu":"100m","ephemeral-storage":"64Mi","memory":"128Mi"},"requests":{"cpu":"50m","ephemeral-storage":"32Mi","memory":"64Mi"}}` | +| fullnameOverride | | string | `""` | +| gather | Gather script configuration | object | `{"clusterInfo":false,"cmdTimeout":"30","extraArgs":[],"logLevel":"info","namespaces":[],"since":"","sinceTime":"","withHeapDumps":false,"withHelm":true,"withIngress":true,"withNamespaceInspect":true,"withOperator":true,"withOrchestrator":true,"withPlatform":true,"withRoute":true,"withSecrets":false}` | +| gather.cmdTimeout | Command timeout for individual kubectl/helm commands (seconds) | string | `"30"` | +| gather.extraArgs | Additional custom arguments to pass to the gather script | list | `[]` | +| gather.logLevel | Log level: info, INFO, debug, DEBUG, trace, TRACE | string | `"info"` | +| gather.namespaces | Example: ["rhdh-prod", "rhdh-staging"] | list | `[]` | +| gather.since | Relative time for log collection (e.g., "2h", "30m") | string | `""` | +| gather.sinceTime | Absolute timestamp for log collection (RFC3339 format) | string | `""` | +| gather.withOperator | Collection features (enabled by default; set to false to skip) | bool | `true` | +| gather.withSecrets | Optional collection features (disabled by default) | bool | `false` | +| image | Container image configuration | object | `{"digest":"","pullPolicy":"","registry":"quay.io","repository":"rhdh-community/rhdh-must-gather","tag":"latest"}` | +| image.digest | Image digest (e.g., sha256:abc123...). Can be used with or without tag. | string | `""` | +| image.tag | Overrides the image tag whose default is the chart appVersion. | string | `"latest"` | +| imagePullSecrets | Secrets for pulling images from a private registry | list | `[]` | +| nameOverride | Override the chart name | string | `""` | +| nodeSelector | Node selector for pod scheduling | object | `{}` | +| persistence | Persistent volume configuration for storing gathered data | object | `{"accessMode":"ReadWriteOnce","size":"1Gi","storageClass":""}` | +| persistence.accessMode | Access mode | string | `"ReadWriteOnce"` | +| persistence.size | Storage size | string | `"1Gi"` | +| persistence.storageClass | Storage class (empty = use cluster default) | string | `""` | +| podAnnotations | Pod annotations | object | `{}` | +| podLabels | Pod labels | object | `{}` | +| podSecurityContext | Pod security context | object | `{"runAsNonRoot":true,"seccompProfile":{"type":"RuntimeDefault"}}` | +| rbac | RBAC configuration | object | `{"create":true,"rules":{"backstages":true,"ingresses":true,"knative":true,"olm":true,"platform":true,"routes":true,"sonataflow":true},"scope":"cluster"}` | +| rbac.create | Create RBAC resources (Role/ClusterRole and bindings) | bool | `true` | +| rbac.rules | a rule here does not require disabling the corresponding gather.with* flag. | object | `{"backstages":true,"ingresses":true,"knative":true,"olm":true,"platform":true,"routes":true,"sonataflow":true}` | +| rbac.rules.backstages | rhdh.redhat.com — Backstage custom resources | bool | `true` | +| rbac.rules.ingresses | networking.k8s.io — Ingresses, NetworkPolicies | bool | `true` | +| rbac.rules.knative | operator.knative.dev, operator.serverless.openshift.io — Knative/Serverless | bool | `true` | +| rbac.rules.olm | operators.coreos.com — OLM resources (subscriptions, CSVs, etc.) | bool | `true` | +| rbac.rules.platform | config.openshift.io — ClusterVersions, Infrastructures (cluster scope only) | bool | `true` | +| rbac.rules.routes | route.openshift.io — OpenShift Routes | bool | `true` | +| rbac.rules.sonataflow | sonataflow.org — SonataFlow custom resources | bool | `true` | +| resources | Resource requests and limits for the gather container | object | `{"limits":{"cpu":"500m","ephemeral-storage":"128Mi","memory":"512Mi"},"requests":{"cpu":"100m","ephemeral-storage":"64Mi","memory":"128Mi"}}` | +| securityContext | Container security context | object | `{"allowPrivilegeEscalation":false,"capabilities":{"drop":["ALL"]}}` | +| serviceAccount | Service account configuration | object | `{"annotations":{},"name":"","tokenExpirationSeconds":3600}` | +| serviceAccount.annotations | Annotations to add to the service account | object | `{}` | +| serviceAccount.name | If not set, a name is generated using the fullname template. | string | `""` | +| serviceAccount.tokenExpirationSeconds | into the gather init container (minimum 600). | int | `3600` | +| strategy | Deployment strategy | object | `{"type":"Recreate"}` | +| test | Helm test configuration | object | `{"enabled":true,"image":{"digest":"","pullPolicy":"","registry":"docker.io","repository":"bitnami/kubectl","tag":"latest"}}` | +| test.enabled | Enable the Helm test | bool | `true` | +| test.image | Image for the test pod | object | `{"digest":"","pullPolicy":"","registry":"docker.io","repository":"bitnami/kubectl","tag":"latest"}` | +| tolerations | Tolerations for pod scheduling | list | `[]` | + diff --git a/charts/must-gather/README.md.gotmpl b/charts/must-gather/README.md.gotmpl new file mode 100644 index 00000000..571a7db7 --- /dev/null +++ b/charts/must-gather/README.md.gotmpl @@ -0,0 +1,93 @@ +# Must Gather Chart for Red Hat Developer Hub (RHDH) + +{{ template "chart.deprecationWarning" . }} + +{{ template "chart.versionBadge" . }} +{{ template "chart.typeBadge" . }} + +{{ template "chart.description" . }} + +{{ template "chart.homepageLine" . }} + +{{ template "chart.maintainersSection" . }} + +{{ template "chart.sourcesSection" . }} + +{{ template "chart.requirementsSection" . }} + +## TL;DR + +```console +helm upgrade --install my-rhdh-must-gather rhdh-must-gather \ + --repo https://redhat-developer.github.io/rhdh-chart \ + --version {{ template "chart.version" . }} +``` + +Running the command again will automatically replace the previous pod and start a new gather. + +Then follow the instructions that will be printed to retrieve the gathered data. + +## Running on OpenShift + +This chart works on both Kubernetes and OpenShift. + +But for OpenShift, we recommend using the `oc adm must-gather` command, like so: + +```sh +oc adm must-gather --image=quay.io/rhdh-community/rhdh-must-gather +``` + +See the [must-gather tool README](https://github.com/redhat-developer/rhdh-must-gather#for-openshift-clusters) for more details. + +## Testing a Release + +Once a Helm Release has been deployed, you can test it using the [`helm test`](https://helm.sh/docs/helm/helm_test/) command: + +```sh +helm test +``` + +This will run a simple Pod in the cluster to check that the required resources have been created. + +You can control whether to disable this test pod or you can also customize the image it leverages. +See the `test.enabled` and `test.image` parameters in the [`values.yaml`](./values.yaml) file. + +> **Tip**: Disabling the test pod will not prevent the `helm test` command from passing later on. It will simply report that no test suite is available. + +Below are a few examples: + +
+ +Disabling the test pod + +```sh +helm install \ + --set test.enabled=false +``` + +
+ +
+ +Customizing the test pod image + +```sh +helm install \ + --set test.image.registry=docker.io \ + --set test.image.repository=bitnami/kubectl \ + --set test.image.tag=1.30.0 +``` + +
+ +## Uninstalling the Chart + +To uninstall/delete a Helm release named `my-rhdh-must-gather`: + +```console +helm uninstall my-rhdh-must-gather +``` + +The command removes all the Kubernetes resources associated with the chart and deletes the release. + +{{ template "chart.valuesSection" . }} diff --git a/charts/must-gather/ci/default-values.yaml b/charts/must-gather/ci/default-values.yaml new file mode 100644 index 00000000..f819fdc3 --- /dev/null +++ b/charts/must-gather/ci/default-values.yaml @@ -0,0 +1 @@ +# Default values — tests the chart with all defaults diff --git a/charts/must-gather/ci/with-minimal-collection-values.yaml b/charts/must-gather/ci/with-minimal-collection-values.yaml new file mode 100644 index 00000000..19d25d0e --- /dev/null +++ b/charts/must-gather/ci/with-minimal-collection-values.yaml @@ -0,0 +1,8 @@ +# Minimal collection — skip optional components to speed up gathering +gather: + withOperator: false + withOrchestrator: false + withHelm: false + withRoute: false + withIngress: false + logLevel: "TRACE" diff --git a/charts/must-gather/ci/with-namespace-rbac-values.yaml b/charts/must-gather/ci/with-namespace-rbac-values.yaml new file mode 100644 index 00000000..3c109a97 --- /dev/null +++ b/charts/must-gather/ci/with-namespace-rbac-values.yaml @@ -0,0 +1,9 @@ +# Namespace-scoped RBAC — creates a Role instead of a ClusterRole. +# Collection is restricted to the release namespace only; +# gather.namespaces is ignored in this mode. +rbac: + scope: namespace +gather: + # NOTE: this field will be ignored when rbac.scope is "namespace". + namespaces: + - "kube-system" diff --git a/charts/must-gather/ci/with-namespace-scoped-values.yaml b/charts/must-gather/ci/with-namespace-scoped-values.yaml new file mode 100644 index 00000000..781f5fe5 --- /dev/null +++ b/charts/must-gather/ci/with-namespace-scoped-values.yaml @@ -0,0 +1,6 @@ +# Scope collection to specific namespaces +gather: + namespaces: + - "rhdh-prod" + - "rhdh-staging" + since: "2h" diff --git a/charts/must-gather/ci/with-secrets-and-cluster-info-values.yaml b/charts/must-gather/ci/with-secrets-and-cluster-info-values.yaml new file mode 100644 index 00000000..f52888cd --- /dev/null +++ b/charts/must-gather/ci/with-secrets-and-cluster-info-values.yaml @@ -0,0 +1,5 @@ +# Collect secrets (sanitized) and cluster-level info +gather: + withSecrets: true + clusterInfo: true + logLevel: "DEBUG" diff --git a/charts/must-gather/ci/with-test-disabled-values.yaml b/charts/must-gather/ci/with-test-disabled-values.yaml new file mode 100644 index 00000000..695bbdeb --- /dev/null +++ b/charts/must-gather/ci/with-test-disabled-values.yaml @@ -0,0 +1,3 @@ +# Helm test disabled +test: + enabled: false diff --git a/charts/must-gather/templates/NOTES.txt b/charts/must-gather/templates/NOTES.txt new file mode 100644 index 00000000..d6245cd9 --- /dev/null +++ b/charts/must-gather/templates/NOTES.txt @@ -0,0 +1,32 @@ +------------------------------------------------------------------- +RHDH Must-Gather is now running. This might take a few minutes. + +To check the gather logs: + kubectl -n {{ .Release.Namespace }} logs -l app.kubernetes.io/instance={{ .Release.Name }},app.kubernetes.io/component=gather -c gather -f + +To retrieve the gathered diagnostic data once the gather completes: + +1. Wait for the pod to be ready (gather init container completed): + kubectl -n {{ .Release.Namespace }} wait --for=condition=ready pod -l app.kubernetes.io/instance={{ .Release.Name }},app.kubernetes.io/component=gather --timeout=3600s + +2. Download the gathered data from the data-holder container: + kubectl -n {{ .Release.Namespace }} exec deploy/{{ include "rhdh-must-gather.fullname" . }} -c data-holder -- tar czf - -C /must-gather . > rhdh-must-gather-output.tar.gz + +To clean up the must-gather resources: + helm uninstall {{ .Release.Name }} -n {{ .Release.Namespace }} + +Configuration used: + - Log level: {{ .Values.gather.logLevel | lower }} + - Storage size: {{ .Values.persistence.size }} +{{- if .Values.gather.namespaces }} + - Target namespaces: {{ .Values.gather.namespaces | join ", " }} +{{- end }} +{{- if .Values.gather.withSecrets }} + - Secrets collection: enabled (sanitized) +{{- end }} +{{- if .Values.gather.withHeapDumps }} + - Heap dump collection: enabled +{{- end }} + +For more information, visit: https://github.com/redhat-developer/rhdh-must-gather +------------------------------------------------------------------- diff --git a/charts/must-gather/templates/_helpers.tpl b/charts/must-gather/templates/_helpers.tpl new file mode 100644 index 00000000..e45085ab --- /dev/null +++ b/charts/must-gather/templates/_helpers.tpl @@ -0,0 +1,84 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "rhdh-must-gather.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 "rhdh-must-gather.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 "rhdh-must-gather.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "rhdh-must-gather.labels" -}} +helm.sh/chart: {{ include "rhdh-must-gather.chart" . }} +{{ include "rhdh-must-gather.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "rhdh-must-gather.selectorLabels" -}} +app.kubernetes.io/name: {{ include "rhdh-must-gather.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "rhdh-must-gather.serviceAccountName" -}} +{{- default (include "rhdh-must-gather.fullname" .) .Values.serviceAccount.name }} +{{- end }} + + +{{/* +Build a full image reference from registry, repository, and tag. +Usage: {{ include "rhdh-must-gather.image" (dict "image" .Values.image "defaultTag" .Chart.AppVersion) }} +*/}} +{{- define "rhdh-must-gather.image" -}} +{{- $registry := .image.registry -}} +{{- $repository := .image.repository -}} +{{- $tag := .image.tag | default .defaultTag | default "" -}} +{{- $digest := .image.digest -}} +{{- $ref := "" -}} +{{- if and $tag $digest -}} +{{- $ref = printf ":%s@%s" $tag $digest -}} +{{- else if $digest -}} +{{- $ref = printf "@%s" $digest -}} +{{- else if $tag -}} +{{- $ref = printf ":%s" $tag -}} +{{- end -}} +{{- if $registry -}} +{{- printf "%s/%s%s" $registry $repository $ref -}} +{{- else -}} +{{- printf "%s%s" $repository $ref -}} +{{- end -}} +{{- end -}} + diff --git a/charts/must-gather/templates/clusterrbac.yaml b/charts/must-gather/templates/clusterrbac.yaml new file mode 100644 index 00000000..7021000a --- /dev/null +++ b/charts/must-gather/templates/clusterrbac.yaml @@ -0,0 +1,96 @@ +{{- if and .Values.rbac.create (eq .Values.rbac.scope "cluster") -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ include "rhdh-must-gather.fullname" . }} + labels: + {{- include "rhdh-must-gather.labels" . | nindent 4 }} +rules: + - apiGroups: [""] + resources: ["pods"] + verbs: ["get", "list", "watch"] + - apiGroups: [""] + resources: ["pods/log", "services", "endpoints", "configmaps", "events", "persistentvolumeclaims", "serviceaccounts", "replicationcontrollers", "resourcequotas", "limitranges"] + verbs: ["get", "list"] + {{- if or .Values.gather.withSecrets .Values.gather.withHelm }} + - apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "list"] + {{- end }} + - apiGroups: [""] + resources: ["namespaces", "nodes"] + verbs: ["get", "list"] + - apiGroups: ["apps"] + resources: ["deployments", "replicasets", "statefulsets", "daemonsets"] + verbs: ["get", "list"] + {{- if .Values.rbac.rules.platform }} + - apiGroups: ["config.openshift.io"] + resources: ["clusterversions", "infrastructures"] + verbs: ["get", "list"] + {{- end }} + {{- if .Values.rbac.rules.ingresses }} + - apiGroups: ["networking.k8s.io"] + resources: ["ingresses", "networkpolicies", "ingressclasses"] + verbs: ["get", "list"] + {{- end }} + {{- if .Values.rbac.rules.routes }} + - apiGroups: ["route.openshift.io"] + resources: ["routes"] + verbs: ["get", "list"] + {{- end }} + - apiGroups: ["rbac.authorization.k8s.io"] + resources: ["roles", "rolebindings", "clusterroles", "clusterrolebindings"] + verbs: ["get", "list"] + - apiGroups: ["storage.k8s.io"] + resources: ["storageclasses", "volumeattachments"] + verbs: ["get", "list"] + - apiGroups: ["apiextensions.k8s.io"] + resources: ["customresourcedefinitions"] + verbs: ["get", "list"] + {{- if .Values.rbac.rules.backstages }} + - apiGroups: ["rhdh.redhat.com"] + resources: ["backstages"] + verbs: ["get", "list"] + {{- end }} + {{- if .Values.rbac.rules.olm }} + - apiGroups: ["operators.coreos.com"] + resources: ["subscriptions", "clusterserviceversions", "installplans", "catalogsources", "operatorgroups"] + verbs: ["get", "list"] + {{- end }} + - apiGroups: [""] + resources: ["pods/exec"] # NOSONAR - required for data collection + verbs: ["create"] + {{- if .Values.gather.withHeapDumps }} + - apiGroups: [""] + resources: ["pods/portforward"] + verbs: ["create"] + {{- end }} + {{- if .Values.rbac.rules.sonataflow }} + - apiGroups: ["sonataflow.org"] + resources: ["sonataflowplatforms", "sonataflows", "sonataflowbuilds", "sonataflowclusterplatforms"] + verbs: ["get", "list"] + {{- end }} + {{- if .Values.rbac.rules.knative }} + - apiGroups: ["operator.knative.dev"] + resources: ["knativeservings", "knativeeventings"] + verbs: ["get", "list"] + - apiGroups: ["operator.serverless.openshift.io"] + resources: ["knativekafkas"] + verbs: ["get", "list"] + {{- end }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ include "rhdh-must-gather.fullname" . }} + labels: + {{- include "rhdh-must-gather.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ include "rhdh-must-gather.fullname" . }} +subjects: + - kind: ServiceAccount + name: {{ include "rhdh-must-gather.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/must-gather/templates/deployment.yaml b/charts/must-gather/templates/deployment.yaml new file mode 100644 index 00000000..27ad1e64 --- /dev/null +++ b/charts/must-gather/templates/deployment.yaml @@ -0,0 +1,192 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "rhdh-must-gather.fullname" . }} + labels: + {{- include "rhdh-must-gather.labels" . | nindent 4 }} + app.kubernetes.io/component: gather +spec: + replicas: 1 + {{- with .Values.strategy }} + strategy: + {{- toYaml . | nindent 4 }} + {{- end }} + selector: + matchLabels: + {{- include "rhdh-must-gather.selectorLabels" . | nindent 6 }} + app.kubernetes.io/component: gather + template: + metadata: + labels: + {{- include "rhdh-must-gather.labels" . | nindent 8 }} + app.kubernetes.io/component: gather + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + annotations: + rhdh-must-gather/run-timestamp: {{ now | date "2006-01-02T15:04:05Z" | quote }} + {{- with .Values.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + serviceAccountName: {{ include "rhdh-must-gather.serviceAccountName" . }} # NOSONAR - SA is bound via ClusterRole/ClusterRoleBinding in rbac.yaml + automountServiceAccountToken: false + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + initContainers: + - name: gather + image: {{ include "rhdh-must-gather.image" (dict "image" .Values.image "defaultTag" .Chart.AppVersion) | quote }} # NOSONAR + {{- with .Values.image.pullPolicy }} + imagePullPolicy: {{ . }} + {{- end }} + {{- with .Values.securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: BASE_COLLECTION_PATH + value: "/must-gather" + - name: LOG_LEVEL + value: {{ .Values.gather.logLevel | lower | quote }} + - name: CMD_TIMEOUT + value: {{ .Values.gather.cmdTimeout | quote }} + {{- if .Values.gather.since }} + - name: MUST_GATHER_SINCE + value: {{ .Values.gather.since | quote }} + {{- end }} + {{- if .Values.gather.sinceTime }} + - name: MUST_GATHER_SINCE_TIME + value: {{ .Values.gather.sinceTime | quote }} + {{- end }} + {{- $nsScope := ne .Values.rbac.scope "cluster" }} + {{- $effectiveNamespaces := .Values.gather.namespaces }} + {{- if $nsScope }} + {{- /* Namespace-scoped RBAC: override to release namespace only (gather.namespaces is ignored) */ -}} + {{- $effectiveNamespaces = list .Release.Namespace }} + {{- end }} + {{- if or $nsScope .Values.gather.withSecrets .Values.gather.withHeapDumps .Values.gather.clusterInfo (not .Values.gather.withOperator) (not .Values.gather.withOrchestrator) (not .Values.gather.withHelm) (not .Values.gather.withPlatform) (not .Values.gather.withRoute) (not .Values.gather.withIngress) (not .Values.gather.withNamespaceInspect) $effectiveNamespaces .Values.gather.extraArgs }} + args: + {{- if .Values.gather.withSecrets }} + - "--with-secrets" + {{- end }} + {{- if .Values.gather.withHeapDumps }} + - "--with-heap-dumps" + {{- end }} + {{- if .Values.gather.clusterInfo }} + - "--cluster-info" + {{- end }} + {{- if not .Values.gather.withOperator }} + - "--without-operator" + {{- end }} + {{- if not .Values.gather.withOrchestrator }} + - "--without-orchestrator" + {{- end }} + {{- if not .Values.gather.withHelm }} + - "--without-helm" + {{- end }} + {{- if or (not .Values.gather.withPlatform) $nsScope }} + - "--without-platform" + {{- end }} + {{- if not .Values.gather.withRoute }} + - "--without-route" + {{- end }} + {{- if not .Values.gather.withIngress }} + - "--without-ingress" + {{- end }} + {{- if not .Values.gather.withNamespaceInspect }} + - "--without-namespace-inspect" + {{- end }} + {{- if $effectiveNamespaces }} + - "--namespaces" + - {{ $effectiveNamespaces | join "," | quote }} + {{- end }} + {{- with .Values.gather.extraArgs }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- end }} + volumeMounts: + - name: output + mountPath: /must-gather + - name: kube-api-access + mountPath: /var/run/secrets/kubernetes.io/serviceaccount + readOnly: true + {{- with .Values.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + containers: + - name: data-holder + image: {{ include "rhdh-must-gather.image" (dict "image" .Values.image "defaultTag" .Chart.AppVersion) | quote }} # NOSONAR + {{- with .Values.image.pullPolicy }} + imagePullPolicy: {{ . }} + {{- end }} + {{- with .Values.securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + command: + - sleep + - infinity + volumeMounts: + - name: output + mountPath: /must-gather + readOnly: true + {{- with .Values.dataHolder.resources }} + resources: + {{- toYaml . | nindent 12 }} + {{- end }} + volumes: + - name: kube-api-access + projected: + defaultMode: 0444 + sources: + - serviceAccountToken: + expirationSeconds: {{ .Values.serviceAccount.tokenExpirationSeconds }} + path: token + - configMap: + name: kube-root-ca.crt + items: + - key: ca.crt + path: ca.crt + - downwardAPI: + items: + - path: namespace + fieldRef: + fieldPath: metadata.namespace + - name: output + ephemeral: + volumeClaimTemplate: + metadata: + labels: + {{- include "rhdh-must-gather.labels" . | nindent 18 }} + spec: + accessModes: + - {{ .Values.persistence.accessMode }} + {{- if .Values.persistence.storageClass }} + storageClassName: {{ .Values.persistence.storageClass | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.persistence.size | quote }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} diff --git a/charts/must-gather/templates/rbac.yaml b/charts/must-gather/templates/rbac.yaml new file mode 100644 index 00000000..46766d5e --- /dev/null +++ b/charts/must-gather/templates/rbac.yaml @@ -0,0 +1,84 @@ +{{- if and .Values.rbac.create (ne .Values.rbac.scope "cluster") -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "rhdh-must-gather.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "rhdh-must-gather.labels" . | nindent 4 }} +rules: + - apiGroups: [""] + resources: ["pods"] + verbs: ["get", "list", "watch"] + - apiGroups: [""] + resources: ["pods/log", "services", "endpoints", "configmaps", "events", "persistentvolumeclaims", "serviceaccounts", "replicationcontrollers", "resourcequotas", "limitranges"] + verbs: ["get", "list"] + {{- if or .Values.gather.withSecrets .Values.gather.withHelm }} + - apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "list"] + {{- end }} + - apiGroups: ["apps"] + resources: ["deployments", "replicasets", "statefulsets", "daemonsets"] + verbs: ["get", "list"] + {{- if .Values.rbac.rules.ingresses }} + - apiGroups: ["networking.k8s.io"] + resources: ["ingresses", "networkpolicies"] + verbs: ["get", "list"] + {{- end }} + {{- if .Values.rbac.rules.routes }} + - apiGroups: ["route.openshift.io"] + resources: ["routes"] + verbs: ["get", "list"] + {{- end }} + - apiGroups: ["rbac.authorization.k8s.io"] + resources: ["roles", "rolebindings"] + verbs: ["get", "list"] + {{- if .Values.rbac.rules.backstages }} + - apiGroups: ["rhdh.redhat.com"] + resources: ["backstages"] + verbs: ["get", "list"] + {{- end }} + {{- if .Values.rbac.rules.olm }} + - apiGroups: ["operators.coreos.com"] + resources: ["subscriptions", "clusterserviceversions", "installplans", "catalogsources", "operatorgroups"] + verbs: ["get", "list"] + {{- end }} + - apiGroups: [""] + resources: ["pods/exec"] # NOSONAR - required for data collection + verbs: ["create"] + {{- if .Values.gather.withHeapDumps }} + - apiGroups: [""] + resources: ["pods/portforward"] + verbs: ["create"] + {{- end }} + {{- if .Values.rbac.rules.sonataflow }} + - apiGroups: ["sonataflow.org"] + resources: ["sonataflowplatforms", "sonataflows", "sonataflowbuilds"] + verbs: ["get", "list"] + {{- end }} + {{- if .Values.rbac.rules.knative }} + - apiGroups: ["operator.knative.dev"] + resources: ["knativeservings", "knativeeventings"] + verbs: ["get", "list"] + - apiGroups: ["operator.serverless.openshift.io"] + resources: ["knativekafkas"] + verbs: ["get", "list"] + {{- end }} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "rhdh-must-gather.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "rhdh-must-gather.labels" . | nindent 4 }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "rhdh-must-gather.fullname" . }} +subjects: + - kind: ServiceAccount + name: {{ include "rhdh-must-gather.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/must-gather/templates/serviceaccount.yaml b/charts/must-gather/templates/serviceaccount.yaml new file mode 100644 index 00000000..20f91245 --- /dev/null +++ b/charts/must-gather/templates/serviceaccount.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "rhdh-must-gather.serviceAccountName" . }} + labels: + {{- include "rhdh-must-gather.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +automountServiceAccountToken: false diff --git a/charts/must-gather/templates/tests/test.yaml b/charts/must-gather/templates/tests/test.yaml new file mode 100644 index 00000000..ba8a662a --- /dev/null +++ b/charts/must-gather/templates/tests/test.yaml @@ -0,0 +1,154 @@ +{{- if .Values.test.enabled -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "rhdh-must-gather.fullname" . }}-test + labels: + {{- include "rhdh-must-gather.labels" . | nindent 4 }} + app.kubernetes.io/component: test + annotations: + helm.sh/hook: test + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded + helm.sh/hook-weight: "-2" +automountServiceAccountToken: false +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "rhdh-must-gather.fullname" . }}-test + labels: + {{- include "rhdh-must-gather.labels" . | nindent 4 }} + app.kubernetes.io/component: test + annotations: + helm.sh/hook: test + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded + helm.sh/hook-weight: "-1" +rules: + - apiGroups: [""] + resources: ["pods"] + verbs: ["get", "watch", "list"] + - apiGroups: [""] + resources: ["pods/exec"] # NOSONAR - exec is required to retrieve must-gather output from the data-holder container during helm test + verbs: ["create"] + - apiGroups: ["apps"] + resources: ["deployments"] + verbs: ["get"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "rhdh-must-gather.fullname" . }}-test + labels: + {{- include "rhdh-must-gather.labels" . | nindent 4 }} + app.kubernetes.io/component: test + annotations: + helm.sh/hook: test + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded + helm.sh/hook-weight: "-1" +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "rhdh-must-gather.fullname" . }}-test +subjects: + - kind: ServiceAccount + name: {{ include "rhdh-must-gather.fullname" . }}-test + namespace: {{ .Release.Namespace }} +--- +apiVersion: v1 +kind: Pod +metadata: + name: {{ include "rhdh-must-gather.fullname" . }}-test + labels: + {{- include "rhdh-must-gather.labels" . | nindent 4 }} + app.kubernetes.io/component: test + annotations: + helm.sh/hook: test + helm.sh/hook-delete-policy: before-hook-creation,hook-succeeded +spec: + serviceAccountName: {{ include "rhdh-must-gather.fullname" . }}-test + automountServiceAccountToken: false + restartPolicy: Never + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 4 }} + {{- end }} + containers: + - name: test + image: {{ include "rhdh-must-gather.image" (dict "image" .Values.test.image "defaultTag" "latest") | quote }} # NOSONAR + {{- with .Values.test.image.pullPolicy }} + imagePullPolicy: {{ . }} + {{- end }} + {{- with .Values.securityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + volumeMounts: + - name: kube-api-access + mountPath: /var/run/secrets/kubernetes.io/serviceaccount + readOnly: true + env: + - name: NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: DEPLOY_NAME + value: {{ include "rhdh-must-gather.fullname" . }} + - name: POD_SELECTOR + value: "app.kubernetes.io/instance={{ .Release.Name }},app.kubernetes.io/component=gather" + command: ["/bin/sh", "-c"] + args: + - | + set -e + echo "Step 1: Waiting for the gather pod to be ready (init container completed)..." + kubectl -n "$NAMESPACE" wait --for=condition=ready pod -l "$POD_SELECTOR" --timeout=3600s + + echo "Step 2: Retrieving gathered data from the data-holder container..." + kubectl -n "$NAMESPACE" exec "deploy/$DEPLOY_NAME" -c data-holder -- tar czf - -C /must-gather . > /tmp/rhdh-must-gather-output.tar.gz + + size=$(stat -c%s /tmp/rhdh-must-gather-output.tar.gz 2>/dev/null || stat -f%z /tmp/rhdh-must-gather-output.tar.gz 2>/dev/null) + file_count=$(tar -tzf /tmp/rhdh-must-gather-output.tar.gz | wc -l) + if [ "$size" -le 0 ] 2>/dev/null || [ "$file_count" -eq 0 ]; then + echo "FAIL: retrieved archive is empty or contains no files" + exit 1 + fi + echo "PASS: gathered data retrieved successfully (${size} bytes)" + resources: + requests: + memory: "64Mi" + cpu: "50m" + ephemeral-storage: "32Mi" + limits: + memory: "128Mi" + cpu: "100m" + ephemeral-storage: "64Mi" + volumes: + - name: kube-api-access + projected: + defaultMode: 0444 + sources: + - serviceAccountToken: + expirationSeconds: {{ .Values.serviceAccount.tokenExpirationSeconds }} + path: token + - configMap: + name: kube-root-ca.crt + items: + - key: ca.crt + path: ca.crt + - downwardAPI: + items: + - path: namespace + fieldRef: + fieldPath: metadata.namespace + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/charts/must-gather/values.schema.json b/charts/must-gather/values.schema.json new file mode 100644 index 00000000..b6696abb --- /dev/null +++ b/charts/must-gather/values.schema.json @@ -0,0 +1,1568 @@ +{ + "$id": "https://raw.githubusercontent.com/redhat-developer/rhdh-chart/main/charts/must-gather/values.schema.json", + "properties": { + "affinity": { + "default": {}, + "description": "Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity", + "properties": { + "nodeAffinity": { + "description": "Node affinity is a group of node affinity scheduling rules.", + "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 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" + }, + "weight": { + "description": "Weight associated with matching the corresponding nodeSelectorTerm, in the range 1-100.", + "type": "integer" + } + }, + "required": [ + "weight", + "preference" + ], + "type": "object" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "requiredDuringSchedulingIgnoredDuringExecution": { + "description": "A node selector represents the union of the results of one or more label queries over a set of nodes; that is, it represents the OR of the selectors represented by the node selector terms.", + "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": "Pod affinity is a group of inter pod affinity scheduling rules.", + "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": "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 selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.", + "properties": { + "matchExpressions": { + "description": "matchExpressions is a list of label selector requirements. The requirements are ANDed.", + "items": { + "description": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.", + "properties": { + "key": { + "description": "key is the label key that the selector applies to.", + "type": "string" + }, + "operator": { + "description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.", + "type": "string" + }, + "values": { + "description": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + } + }, + "required": [ + "key", + "operator" + ], + "type": "object" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "matchLabels": { + "additionalProperties": { + "type": "string" + }, + "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.", + "type": "object" + } + }, + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "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 selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.", + "properties": { + "matchExpressions": { + "description": "matchExpressions is a list of label selector requirements. The requirements are ANDed.", + "items": { + "description": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.", + "properties": { + "key": { + "description": "key is the label key that the selector applies to.", + "type": "string" + }, + "operator": { + "description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.", + "type": "string" + }, + "values": { + "description": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + } + }, + "required": [ + "key", + "operator" + ], + "type": "object" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "matchLabels": { + "additionalProperties": { + "type": "string" + }, + "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.", + "type": "object" + } + }, + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "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.", + "type": "integer" + } + }, + "required": [ + "weight", + "podAffinityTerm" + ], + "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 selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.", + "properties": { + "matchExpressions": { + "description": "matchExpressions is a list of label selector requirements. The requirements are ANDed.", + "items": { + "description": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.", + "properties": { + "key": { + "description": "key is the label key that the selector applies to.", + "type": "string" + }, + "operator": { + "description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.", + "type": "string" + }, + "values": { + "description": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + } + }, + "required": [ + "key", + "operator" + ], + "type": "object" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "matchLabels": { + "additionalProperties": { + "type": "string" + }, + "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.", + "type": "object" + } + }, + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "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 selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.", + "properties": { + "matchExpressions": { + "description": "matchExpressions is a list of label selector requirements. The requirements are ANDed.", + "items": { + "description": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.", + "properties": { + "key": { + "description": "key is the label key that the selector applies to.", + "type": "string" + }, + "operator": { + "description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.", + "type": "string" + }, + "values": { + "description": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + } + }, + "required": [ + "key", + "operator" + ], + "type": "object" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "matchLabels": { + "additionalProperties": { + "type": "string" + }, + "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.", + "type": "object" + } + }, + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "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": "Pod anti affinity is a group of inter pod anti affinity scheduling rules.", + "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": "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 selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.", + "properties": { + "matchExpressions": { + "description": "matchExpressions is a list of label selector requirements. The requirements are ANDed.", + "items": { + "description": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.", + "properties": { + "key": { + "description": "key is the label key that the selector applies to.", + "type": "string" + }, + "operator": { + "description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.", + "type": "string" + }, + "values": { + "description": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + } + }, + "required": [ + "key", + "operator" + ], + "type": "object" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "matchLabels": { + "additionalProperties": { + "type": "string" + }, + "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.", + "type": "object" + } + }, + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "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 selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.", + "properties": { + "matchExpressions": { + "description": "matchExpressions is a list of label selector requirements. The requirements are ANDed.", + "items": { + "description": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.", + "properties": { + "key": { + "description": "key is the label key that the selector applies to.", + "type": "string" + }, + "operator": { + "description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.", + "type": "string" + }, + "values": { + "description": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + } + }, + "required": [ + "key", + "operator" + ], + "type": "object" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "matchLabels": { + "additionalProperties": { + "type": "string" + }, + "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.", + "type": "object" + } + }, + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "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.", + "type": "integer" + } + }, + "required": [ + "weight", + "podAffinityTerm" + ], + "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 selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.", + "properties": { + "matchExpressions": { + "description": "matchExpressions is a list of label selector requirements. The requirements are ANDed.", + "items": { + "description": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.", + "properties": { + "key": { + "description": "key is the label key that the selector applies to.", + "type": "string" + }, + "operator": { + "description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.", + "type": "string" + }, + "values": { + "description": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + } + }, + "required": [ + "key", + "operator" + ], + "type": "object" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "matchLabels": { + "additionalProperties": { + "type": "string" + }, + "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.", + "type": "object" + } + }, + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "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 selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.", + "properties": { + "matchExpressions": { + "description": "matchExpressions is a list of label selector requirements. The requirements are ANDed.", + "items": { + "description": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.", + "properties": { + "key": { + "description": "key is the label key that the selector applies to.", + "type": "string" + }, + "operator": { + "description": "operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.", + "type": "string" + }, + "values": { + "description": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + } + }, + "required": [ + "key", + "operator" + ], + "type": "object" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "matchLabels": { + "additionalProperties": { + "type": "string" + }, + "description": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.", + "type": "object" + } + }, + "type": "object", + "x-kubernetes-map-type": "atomic" + }, + "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" + } + }, + "title": "Affinity for pod assignment", + "type": "object" + }, + "dataHolder": { + "additionalProperties": false, + "properties": { + "resources": { + "additionalProperties": false, + "properties": { + "limits": { + "additionalProperties": false, + "properties": { + "cpu": { + "default": "100m", + "title": "CPU limit.", + "type": "string" + }, + "ephemeral-storage": { + "default": "64Mi", + "title": "Ephemeral storage limit.", + "type": "string" + }, + "memory": { + "default": "128Mi", + "title": "Memory limit.", + "type": "string" + } + }, + "title": "Resource limits.", + "type": "object" + }, + "requests": { + "additionalProperties": false, + "properties": { + "cpu": { + "default": "50m", + "title": "CPU request.", + "type": "string" + }, + "ephemeral-storage": { + "default": "32Mi", + "title": "Ephemeral storage request.", + "type": "string" + }, + "memory": { + "default": "64Mi", + "title": "Memory request.", + "type": "string" + } + }, + "title": "Resource requests.", + "type": "object" + } + }, + "title": "Resource requests and limits for the data holder container.", + "type": "object" + } + }, + "title": "Data holder container configuration. Stays alive after the gather init container completes so you can exec in and retrieve the output.", + "type": "object" + }, + "fullnameOverride": { + "default": "", + "title": "Override the full release name.", + "type": "string" + }, + "gather": { + "additionalProperties": false, + "properties": { + "clusterInfo": { + "default": false, + "title": "Enable collection of cluster-wide info (disabled by default).", + "type": "boolean" + }, + "cmdTimeout": { + "default": "30", + "title": "Command timeout for individual kubectl/helm commands (seconds).", + "type": "string" + }, + "extraArgs": { + "default": [], + "items": { + "type": "string" + }, + "title": "Additional custom arguments to pass to the gather script.", + "type": "array" + }, + "logLevel": { + "default": "info", + "enum": [ + "info", + "INFO", + "debug", + "DEBUG", + "trace", + "TRACE", + "" + ], + "title": "Log level for the gather script.", + "type": "string" + }, + "namespaces": { + "default": [], + "items": { + "type": "string" + }, + "title": "Limit collection to specific namespaces (only effective when rbac.scope is 'cluster'). Ignored when rbac.scope is 'namespace'. Example: ['rhdh-prod', 'rhdh-staging'].", + "type": "array" + }, + "since": { + "default": "", + "title": "Relative time for log collection (e.g., '2h', '30m').", + "type": "string" + }, + "sinceTime": { + "default": "", + "title": "Absolute timestamp for log collection (RFC3339 format).", + "type": "string" + }, + "withHeapDumps": { + "default": false, + "title": "Enable collection of heap dumps (disabled by default).", + "type": "boolean" + }, + "withHelm": { + "default": true, + "title": "Enable collection of Helm release information.", + "type": "boolean" + }, + "withIngress": { + "default": true, + "title": "Enable collection of Ingress resources.", + "type": "boolean" + }, + "withNamespaceInspect": { + "default": true, + "title": "Enable namespace-level inspection.", + "type": "boolean" + }, + "withOperator": { + "default": true, + "title": "Enable collection of RHDH Operator resources.", + "type": "boolean" + }, + "withOrchestrator": { + "default": true, + "title": "Enable collection of Orchestrator resources.", + "type": "boolean" + }, + "withPlatform": { + "default": true, + "title": "Enable collection of platform-level resources.", + "type": "boolean" + }, + "withRoute": { + "default": true, + "title": "Enable collection of OpenShift Route resources.", + "type": "boolean" + }, + "withSecrets": { + "default": false, + "title": "Enable collection of Secrets (disabled by default).", + "type": "boolean" + } + }, + "title": "Gather script configuration.", + "type": "object" + }, + "image": { + "additionalProperties": false, + "properties": { + "digest": { + "default": "", + "title": "Image digest (e.g., sha256:abc123...). Can be used with or without tag.", + "type": "string" + }, + "pullPolicy": { + "default": "", + "enum": [ + "Always", + "IfNotPresent", + "Never", + "" + ], + "title": "Image pull policy.", + "type": "string" + }, + "registry": { + "default": "quay.io", + "title": "Image registry.", + "type": "string" + }, + "repository": { + "default": "rhdh-community/rhdh-must-gather", + "title": "Image repository.", + "type": "string" + }, + "tag": { + "default": "latest", + "title": "Overrides the image tag whose default is the chart appVersion.", + "type": "string" + } + }, + "title": "Container image configuration.", + "type": "object" + }, + "imagePullSecrets": { + "default": [], + "items": { + "properties": { + "name": { + "type": "string" + } + }, + "type": "object" + }, + "title": "Secrets for pulling images from a private registry.", + "type": "array" + }, + "nameOverride": { + "default": "", + "title": "Override the chart name.", + "type": "string" + }, + "nodeSelector": { + "additionalProperties": { + "type": "string" + }, + "default": {}, + "description": "Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector", + "title": "Node labels for pod assignment", + "type": "object" + }, + "persistence": { + "additionalProperties": false, + "properties": { + "accessMode": { + "default": "ReadWriteOnce", + "enum": [ + "ReadWriteOnce", + "ReadOnlyMany", + "ReadWriteMany" + ], + "title": "Access mode.", + "type": "string" + }, + "size": { + "default": "1Gi", + "title": "Storage size.", + "type": "string" + }, + "storageClass": { + "default": "", + "title": "Storage class (empty = use cluster default).", + "type": "string" + } + }, + "title": "Persistent volume configuration for storing gathered data.", + "type": "object" + }, + "podAnnotations": { + "default": {}, + "title": "Pod annotations.", + "type": "object" + }, + "podLabels": { + "default": {}, + "title": "Pod labels.", + "type": "object" + }, + "podSecurityContext": { + "description": "SecurityContext holds security configuration that will be applied to a container. Some fields are present in both SecurityContext and PodSecurityContext. When both are set, the values in SecurityContext take precedence.", + "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 defines a pod or container's AppArmor settings.", + "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:\n Localhost - a profile pre-loaded on the node.\n RuntimeDefault - the container runtime's default profile.\n Unconfined - no AppArmor enforcement.", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object", + "x-kubernetes-unions": [ + { + "discriminator": "type", + "fields-to-discriminateBy": { + "localhostProfile": "LocalhostProfile" + } + } + ] + }, + "capabilities": { + "description": "Adds and removes POSIX capabilities from running containers.", + "properties": { + "add": { + "description": "Added capabilities", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "drop": { + "description": "Removed capabilities", + "items": { + "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.", + "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.", + "type": "integer" + }, + "seLinuxOptions": { + "description": "SELinuxOptions are the labels to be applied to the container", + "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": "SeccompProfile defines a pod/container's seccomp profile settings. Only one profile source may be set.", + "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:\n\nLocalhost - 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", + "x-kubernetes-unions": [ + { + "discriminator": "type", + "fields-to-discriminateBy": { + "localhostProfile": "LocalhostProfile" + } + } + ] + }, + "windowsOptions": { + "description": "WindowsSecurityContextOptions contain Windows-specific options and credentials.", + "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" + }, + "rbac": { + "additionalProperties": false, + "properties": { + "create": { + "default": true, + "title": "Create RBAC resources (Role/ClusterRole and bindings).", + "type": "boolean" + }, + "rules": { + "additionalProperties": false, + "description": "Disable a rule if the installing user cannot grant those permissions (Kubernetes requires you to already hold any permission you grant). The gather script handles missing permissions gracefully.", + "properties": { + "backstages": { + "default": true, + "title": "rhdh.redhat.com \u2014 Backstage custom resources.", + "type": "boolean" + }, + "ingresses": { + "default": true, + "title": "networking.k8s.io \u2014 Ingresses, NetworkPolicies.", + "type": "boolean" + }, + "knative": { + "default": true, + "title": "operator.knative.dev, operator.serverless.openshift.io \u2014 Knative/Serverless.", + "type": "boolean" + }, + "olm": { + "default": true, + "title": "operators.coreos.com \u2014 OLM resources (subscriptions, CSVs, etc.).", + "type": "boolean" + }, + "platform": { + "default": true, + "title": "config.openshift.io \u2014 ClusterVersions, Infrastructures (cluster scope only).", + "type": "boolean" + }, + "routes": { + "default": true, + "title": "route.openshift.io \u2014 OpenShift Routes.", + "type": "boolean" + }, + "sonataflow": { + "default": true, + "title": "sonataflow.org \u2014 SonataFlow custom resources.", + "type": "boolean" + } + }, + "title": "Toggle individual API-group rules in the Role/ClusterRole.", + "type": "object" + }, + "scope": { + "default": "cluster", + "description": "'cluster' creates a ClusterRole with cluster-wide read access. 'namespace' creates a namespace-scoped Role limited to the release namespace only (gather.namespaces is ignored; collection is restricted to the release namespace).", + "enum": [ + "cluster", + "namespace" + ], + "title": "Scope of the RBAC role.", + "type": "string" + } + }, + "title": "RBAC configuration.", + "type": "object" + }, + "resources": { + "additionalProperties": false, + "properties": { + "limits": { + "additionalProperties": false, + "properties": { + "cpu": { + "default": "500m", + "title": "CPU limit.", + "type": "string" + }, + "ephemeral-storage": { + "default": "128Mi", + "title": "Ephemeral storage limit.", + "type": "string" + }, + "memory": { + "default": "512Mi", + "title": "Memory limit.", + "type": "string" + } + }, + "title": "Resource limits.", + "type": "object" + }, + "requests": { + "additionalProperties": false, + "properties": { + "cpu": { + "default": "100m", + "title": "CPU request.", + "type": "string" + }, + "ephemeral-storage": { + "default": "64Mi", + "title": "Ephemeral storage request.", + "type": "string" + }, + "memory": { + "default": "128Mi", + "title": "Memory request.", + "type": "string" + } + }, + "title": "Resource requests.", + "type": "object" + } + }, + "title": "Resource requests and limits for the gather container.", + "type": "object" + }, + "securityContext": { + "description": "SecurityContext holds security configuration that will be applied to a container. Some fields are present in both SecurityContext and PodSecurityContext. When both are set, the values in SecurityContext take precedence.", + "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 defines a pod or container's AppArmor settings.", + "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:\n Localhost - a profile pre-loaded on the node.\n RuntimeDefault - the container runtime's default profile.\n Unconfined - no AppArmor enforcement.", + "type": "string" + } + }, + "required": [ + "type" + ], + "type": "object", + "x-kubernetes-unions": [ + { + "discriminator": "type", + "fields-to-discriminateBy": { + "localhostProfile": "LocalhostProfile" + } + } + ] + }, + "capabilities": { + "description": "Adds and removes POSIX capabilities from running containers.", + "properties": { + "add": { + "description": "Added capabilities", + "items": { + "type": "string" + }, + "type": "array", + "x-kubernetes-list-type": "atomic" + }, + "drop": { + "description": "Removed capabilities", + "items": { + "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.", + "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.", + "type": "integer" + }, + "seLinuxOptions": { + "description": "SELinuxOptions are the labels to be applied to the container", + "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": "SeccompProfile defines a pod/container's seccomp profile settings. Only one profile source may be set.", + "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:\n\nLocalhost - 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", + "x-kubernetes-unions": [ + { + "discriminator": "type", + "fields-to-discriminateBy": { + "localhostProfile": "LocalhostProfile" + } + } + ] + }, + "windowsOptions": { + "description": "WindowsSecurityContextOptions contain Windows-specific options and credentials.", + "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": { + "additionalProperties": false, + "properties": { + "annotations": { + "default": {}, + "title": "Annotations to add to the service account.", + "type": "object" + }, + "name": { + "default": "", + "title": "The name of the service account to use. If not set, a name is generated using the fullname template.", + "type": "string" + }, + "tokenExpirationSeconds": { + "default": 3600, + "minimum": 600, + "title": "Expiration (in seconds) for the bound service account token projected into the gather init container.", + "type": "integer" + } + }, + "title": "Service account configuration.", + "type": "object" + }, + "strategy": { + "default": { + "type": "Recreate" + }, + "description": "Ref: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy", + "properties": { + "type": { + "default": "Recreate", + "enum": [ + "Recreate", + "RollingUpdate" + ], + "title": "Strategy type.", + "type": "string" + } + }, + "title": "Deployment strategy.", + "type": "object" + }, + "test": { + "additionalProperties": false, + "properties": { + "enabled": { + "default": true, + "title": "Enable the Helm test.", + "type": "boolean" + }, + "image": { + "additionalProperties": false, + "properties": { + "digest": { + "default": "", + "title": "Image digest (e.g., sha256:abc123...). Can be used with or without tag.", + "type": "string" + }, + "pullPolicy": { + "default": "", + "enum": [ + "Always", + "IfNotPresent", + "Never", + "" + ], + "title": "Image pull policy.", + "type": "string" + }, + "registry": { + "default": "docker.io", + "title": "Image registry.", + "type": "string" + }, + "repository": { + "default": "bitnami/kubectl", + "title": "Image repository.", + "type": "string" + }, + "tag": { + "default": "latest", + "title": "Image tag.", + "type": "string" + } + }, + "title": "Image for the test pod.", + "type": "object" + } + }, + "title": "Helm test configuration.", + "type": "object" + }, + "tolerations": { + "default": [], + "description": "Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/", + "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.", + "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" + }, + "title": "Node tolerations for server scheduling to nodes with taints", + "type": "array" + } + }, + "title": "Root Schema", + "type": "object" +} \ No newline at end of file diff --git a/charts/must-gather/values.schema.tmpl.json b/charts/must-gather/values.schema.tmpl.json new file mode 100644 index 00000000..f6305517 --- /dev/null +++ b/charts/must-gather/values.schema.tmpl.json @@ -0,0 +1,528 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://raw.githubusercontent.com/redhat-developer/rhdh-chart/main/charts/must-gather/values.schema.json", + "type": "object", + "title": "Root Schema", + "properties": { + "image": { + "title": "Container image configuration.", + "type": "object", + "additionalProperties": false, + "properties": { + "registry": { + "title": "Image registry.", + "type": "string", + "default": "quay.io" + }, + "repository": { + "title": "Image repository.", + "type": "string", + "default": "rhdh-community/rhdh-must-gather" + }, + "pullPolicy": { + "title": "Image pull policy.", + "type": "string", + "default": "", + "enum": [ + "Always", + "IfNotPresent", + "Never", + "" + ] + }, + "tag": { + "title": "Overrides the image tag whose default is the chart appVersion.", + "type": "string", + "default": "latest" + }, + "digest": { + "title": "Image digest (e.g., sha256:abc123...). Can be used with or without tag.", + "type": "string", + "default": "" + } + } + }, + "imagePullSecrets": { + "title": "Secrets for pulling images from a private registry.", + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "type": "string" + } + } + }, + "default": [] + }, + "nameOverride": { + "title": "Override the chart name.", + "type": "string", + "default": "" + }, + "fullnameOverride": { + "title": "Override the full release name.", + "type": "string", + "default": "" + }, + "serviceAccount": { + "title": "Service account configuration.", + "type": "object", + "additionalProperties": false, + "properties": { + "tokenExpirationSeconds": { + "title": "Expiration (in seconds) for the bound service account token projected into the gather init container.", + "type": "integer", + "default": 3600, + "minimum": 600 + }, + "annotations": { + "title": "Annotations to add to the service account.", + "type": "object", + "default": {} + }, + "name": { + "title": "The name of the service account to use. If not set, a name is generated using the fullname template.", + "type": "string", + "default": "" + } + } + }, + "rbac": { + "title": "RBAC configuration.", + "type": "object", + "additionalProperties": false, + "properties": { + "create": { + "title": "Create RBAC resources (Role/ClusterRole and bindings).", + "type": "boolean", + "default": true + }, + "scope": { + "title": "Scope of the RBAC role.", + "description": "'cluster' creates a ClusterRole with cluster-wide read access. 'namespace' creates a namespace-scoped Role limited to the release namespace only (gather.namespaces is ignored; collection is restricted to the release namespace).", + "type": "string", + "default": "cluster", + "enum": ["cluster", "namespace"] + }, + "rules": { + "title": "Toggle individual API-group rules in the Role/ClusterRole.", + "description": "Disable a rule if the installing user cannot grant those permissions (Kubernetes requires you to already hold any permission you grant). The gather script handles missing permissions gracefully.", + "type": "object", + "additionalProperties": false, + "properties": { + "backstages": { + "title": "rhdh.redhat.com — Backstage custom resources.", + "type": "boolean", + "default": true + }, + "olm": { + "title": "operators.coreos.com — OLM resources (subscriptions, CSVs, etc.).", + "type": "boolean", + "default": true + }, + "routes": { + "title": "route.openshift.io — OpenShift Routes.", + "type": "boolean", + "default": true + }, + "sonataflow": { + "title": "sonataflow.org — SonataFlow custom resources.", + "type": "boolean", + "default": true + }, + "knative": { + "title": "operator.knative.dev, operator.serverless.openshift.io — Knative/Serverless.", + "type": "boolean", + "default": true + }, + "ingresses": { + "title": "networking.k8s.io — Ingresses, NetworkPolicies.", + "type": "boolean", + "default": true + }, + "platform": { + "title": "config.openshift.io — ClusterVersions, Infrastructures (cluster scope only).", + "type": "boolean", + "default": true + } + } + } + } + }, + "strategy": { + "title": "Deployment strategy.", + "description": "Ref: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy", + "type": "object", + "properties": { + "type": { + "title": "Strategy type.", + "type": "string", + "default": "Recreate", + "enum": ["Recreate", "RollingUpdate"] + } + }, + "default": { + "type": "Recreate" + } + }, + "gather": { + "title": "Gather script configuration.", + "type": "object", + "additionalProperties": false, + "properties": { + "logLevel": { + "title": "Log level for the gather script.", + "type": "string", + "default": "info", + "enum": [ + "info", + "INFO", + "debug", + "DEBUG", + "trace", + "TRACE", + "" + ] + }, + "cmdTimeout": { + "title": "Command timeout for individual kubectl/helm commands (seconds).", + "type": "string", + "default": "30" + }, + "since": { + "title": "Relative time for log collection (e.g., '2h', '30m').", + "type": "string", + "default": "" + }, + "sinceTime": { + "title": "Absolute timestamp for log collection (RFC3339 format).", + "type": "string", + "default": "" + }, + "withSecrets": { + "title": "Enable collection of Secrets (disabled by default).", + "type": "boolean", + "default": false + }, + "withHeapDumps": { + "title": "Enable collection of heap dumps (disabled by default).", + "type": "boolean", + "default": false + }, + "clusterInfo": { + "title": "Enable collection of cluster-wide info (disabled by default).", + "type": "boolean", + "default": false + }, + "withOperator": { + "title": "Enable collection of RHDH Operator resources.", + "type": "boolean", + "default": true + }, + "withOrchestrator": { + "title": "Enable collection of Orchestrator resources.", + "type": "boolean", + "default": true + }, + "withHelm": { + "title": "Enable collection of Helm release information.", + "type": "boolean", + "default": true + }, + "withPlatform": { + "title": "Enable collection of platform-level resources.", + "type": "boolean", + "default": true + }, + "withRoute": { + "title": "Enable collection of OpenShift Route resources.", + "type": "boolean", + "default": true + }, + "withIngress": { + "title": "Enable collection of Ingress resources.", + "type": "boolean", + "default": true + }, + "withNamespaceInspect": { + "title": "Enable namespace-level inspection.", + "type": "boolean", + "default": true + }, + "namespaces": { + "title": "Limit collection to specific namespaces (only effective when rbac.scope is 'cluster'). Ignored when rbac.scope is 'namespace'. Example: ['rhdh-prod', 'rhdh-staging'].", + "type": "array", + "items": { + "type": "string" + }, + "default": [] + }, + "extraArgs": { + "title": "Additional custom arguments to pass to the gather script.", + "type": "array", + "items": { + "type": "string" + }, + "default": [] + } + } + }, + "resources": { + "title": "Resource requests and limits for the gather container.", + "type": "object", + "additionalProperties": false, + "properties": { + "requests": { + "title": "Resource requests.", + "type": "object", + "additionalProperties": false, + "properties": { + "memory": { + "title": "Memory request.", + "type": "string", + "default": "128Mi" + }, + "cpu": { + "title": "CPU request.", + "type": "string", + "default": "100m" + }, + "ephemeral-storage": { + "title": "Ephemeral storage request.", + "type": "string", + "default": "64Mi" + } + } + }, + "limits": { + "title": "Resource limits.", + "type": "object", + "additionalProperties": false, + "properties": { + "memory": { + "title": "Memory limit.", + "type": "string", + "default": "512Mi" + }, + "cpu": { + "title": "CPU limit.", + "type": "string", + "default": "500m" + }, + "ephemeral-storage": { + "title": "Ephemeral storage limit.", + "type": "string", + "default": "128Mi" + } + } + } + } + }, + "persistence": { + "title": "Persistent volume configuration for storing gathered data.", + "type": "object", + "additionalProperties": false, + "properties": { + "size": { + "title": "Storage size.", + "type": "string", + "default": "1Gi" + }, + "storageClass": { + "title": "Storage class (empty = use cluster default).", + "type": "string", + "default": "" + }, + "accessMode": { + "title": "Access mode.", + "type": "string", + "default": "ReadWriteOnce", + "enum": [ + "ReadWriteOnce", + "ReadOnlyMany", + "ReadWriteMany" + ] + } + } + }, + "dataHolder": { + "title": "Data holder container configuration. Stays alive after the gather init container completes so you can exec in and retrieve the output.", + "type": "object", + "additionalProperties": false, + "properties": { + "resources": { + "title": "Resource requests and limits for the data holder container.", + "type": "object", + "additionalProperties": false, + "properties": { + "requests": { + "title": "Resource requests.", + "type": "object", + "additionalProperties": false, + "properties": { + "memory": { + "title": "Memory request.", + "type": "string", + "default": "64Mi" + }, + "cpu": { + "title": "CPU request.", + "type": "string", + "default": "50m" + }, + "ephemeral-storage": { + "title": "Ephemeral storage request.", + "type": "string", + "default": "32Mi" + } + } + }, + "limits": { + "title": "Resource limits.", + "type": "object", + "additionalProperties": false, + "properties": { + "memory": { + "title": "Memory limit.", + "type": "string", + "default": "128Mi" + }, + "cpu": { + "title": "CPU limit.", + "type": "string", + "default": "100m" + }, + "ephemeral-storage": { + "title": "Ephemeral storage limit.", + "type": "string", + "default": "64Mi" + } + } + } + } + } + } + }, + "test": { + "title": "Helm test configuration.", + "type": "object", + "additionalProperties": false, + "properties": { + "enabled": { + "title": "Enable the Helm test.", + "type": "boolean", + "default": true + }, + "image": { + "title": "Image for the test pod.", + "type": "object", + "additionalProperties": false, + "properties": { + "registry": { + "title": "Image registry.", + "type": "string", + "default": "docker.io" + }, + "repository": { + "title": "Image repository.", + "type": "string", + "default": "bitnami/kubectl" + }, + "tag": { + "title": "Image tag.", + "type": "string", + "default": "latest" + }, + "digest": { + "title": "Image digest (e.g., sha256:abc123...). Can be used with or without tag.", + "type": "string", + "default": "" + }, + "pullPolicy": { + "title": "Image pull policy.", + "type": "string", + "default": "", + "enum": [ + "Always", + "IfNotPresent", + "Never", + "" + ] + } + } + } + } + }, + "podAnnotations": { + "title": "Pod annotations.", + "type": "object", + "default": {} + }, + "podLabels": { + "title": "Pod labels.", + "type": "object", + "default": {} + }, + "podSecurityContext": { + "title": "Security settings for a Pod.", + "description": "The security settings that you specify for a Pod apply to all Containers in the Pod. Ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod", + "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.33.4/_definitions.json#/definitions/io.k8s.api.core.v1.SecurityContext", + "default": { + "runAsNonRoot": true, + "seccompProfile": { + "type": "RuntimeDefault" + } + } + }, + "securityContext": { + "title": "Security settings for a Container.", + "description": "Ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container", + "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.33.4/_definitions.json#/definitions/io.k8s.api.core.v1.SecurityContext", + "default": { + "allowPrivilegeEscalation": false, + "capabilities": { + "drop": ["ALL"] + } + } + }, + "nodeSelector": { + "title": "Node labels for pod assignment", + "description": "Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector", + "type": "object", + "additionalProperties": { + "type": "string" + }, + "default": {} + }, + "tolerations": { + "title": "Node tolerations for server scheduling to nodes with taints", + "description": "Ref: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/", + "type": "array", + "items": { + "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.33.4/_definitions.json#/definitions/io.k8s.api.core.v1.Toleration" + }, + "default": [] + }, + "affinity": { + "default": {}, + "description": "Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity", + "properties": { + "nodeAffinity": { + "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.33.4/_definitions.json#/definitions/io.k8s.api.core.v1.NodeAffinity", + "description": "Describes node affinity scheduling rules for the pod." + }, + "podAffinity": { + "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.33.4/_definitions.json#/definitions/io.k8s.api.core.v1.PodAffinity", + "description": "Describes pod affinity scheduling rules (e.g. co-locate this pod in the same node, zone, etc. as some other pod(s))." + }, + "podAntiAffinity": { + "$ref": "https://raw.githubusercontent.com/yannh/kubernetes-json-schema/master/v1.33.4/_definitions.json#/definitions/io.k8s.api.core.v1.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))." + } + }, + "title": "Affinity for pod assignment", + "type": "object" + } + } +} diff --git a/charts/must-gather/values.yaml b/charts/must-gather/values.yaml new file mode 100644 index 00000000..6549be92 --- /dev/null +++ b/charts/must-gather/values.yaml @@ -0,0 +1,171 @@ +# -- Default values for rhdh-must-gather. +# -- This is a YAML-formatted file. +# -- Declare variables to be passed into your templates. + +# -- Container image configuration +image: + registry: quay.io + repository: rhdh-community/rhdh-must-gather + pullPolicy: "" + # -- Overrides the image tag whose default is the chart appVersion. + tag: "latest" + # -- Image digest (e.g., sha256:abc123...). Can be used with or without tag. + digest: "" + +# -- Secrets for pulling images from a private registry +imagePullSecrets: [] + +# -- Override the chart name +nameOverride: "" +fullnameOverride: "" + +# -- Service account configuration +serviceAccount: + # -- Expiration (in seconds) for the bound service account token projected + # -- into the gather init container (minimum 600). + tokenExpirationSeconds: 3600 + # -- Annotations to add to the service account + annotations: {} + # -- The name of the service account to use. + # -- If not set, a name is generated using the fullname template. + name: "" + +# -- RBAC configuration +rbac: + # -- Create RBAC resources (Role/ClusterRole and bindings) + create: true + # -- Scope of the RBAC role: "cluster" creates a ClusterRole with cluster-wide read access; + # -- "namespace" creates a namespace-scoped Role limited to the release namespace only + # -- (gather.namespaces is ignored; collection is restricted to the release namespace) + scope: cluster + # -- Control which API-group rules are included in the Role/ClusterRole. + # -- Disable a rule if the installing user cannot grant those permissions + # -- (Kubernetes requires you to already hold any permission you grant). + # -- The gather script handles missing permissions gracefully, so disabling + # -- a rule here does not require disabling the corresponding gather.with* flag. + rules: + # -- rhdh.redhat.com — Backstage custom resources + backstages: true + # -- operators.coreos.com — OLM resources (subscriptions, CSVs, etc.) + olm: true + # -- route.openshift.io — OpenShift Routes + routes: true + # -- sonataflow.org — SonataFlow custom resources + sonataflow: true + # -- operator.knative.dev, operator.serverless.openshift.io — Knative/Serverless + knative: true + # -- networking.k8s.io — Ingresses, NetworkPolicies + ingresses: true + # -- config.openshift.io — ClusterVersions, Infrastructures (cluster scope only) + platform: true + +# -- Deployment strategy +strategy: + type: Recreate + +# -- Gather script configuration +gather: + # -- Log level: info, INFO, debug, DEBUG, trace, TRACE + logLevel: "info" + # -- Command timeout for individual kubectl/helm commands (seconds) + cmdTimeout: "30" + # -- Relative time for log collection (e.g., "2h", "30m") + since: "" + # -- Absolute timestamp for log collection (RFC3339 format) + sinceTime: "" + + # -- Optional collection features (disabled by default) + withSecrets: false + withHeapDumps: false + clusterInfo: false + + # -- Collection features (enabled by default; set to false to skip) + withOperator: true + withOrchestrator: true + withHelm: true + withPlatform: true + withRoute: true + withIngress: true + withNamespaceInspect: true + + # -- Limit collection to specific namespaces (only effective when rbac.scope is "cluster"). + # -- Ignored when rbac.scope is "namespace" (collection is restricted to the release namespace). + # -- Example: ["rhdh-prod", "rhdh-staging"] + namespaces: [] + + # -- Additional custom arguments to pass to the gather script + extraArgs: [] + +# -- Resource requests and limits for the gather container +resources: + requests: + memory: "128Mi" + cpu: "100m" + ephemeral-storage: "64Mi" + limits: + memory: "512Mi" + cpu: "500m" + ephemeral-storage: "128Mi" + +# -- Persistent volume configuration for storing gathered data +persistence: + # -- Storage size + size: 1Gi + # -- Storage class (empty = use cluster default) + storageClass: "" + # -- Access mode + accessMode: ReadWriteOnce + +# -- Data holder container configuration +# -- Runs alongside the gather container and stays alive so you can exec in and retrieve the output. +dataHolder: + # -- Resource requests and limits for the data-holder container + resources: + requests: + memory: "64Mi" + cpu: "50m" + ephemeral-storage: "32Mi" + limits: + memory: "128Mi" + cpu: "100m" + ephemeral-storage: "64Mi" + +# -- Helm test configuration +test: + # -- Enable the Helm test + enabled: true + # -- Image for the test pod + image: + registry: docker.io + repository: bitnami/kubectl + tag: "latest" + digest: "" + pullPolicy: "" + +# -- Pod annotations +podAnnotations: {} + +# -- Pod labels +podLabels: {} + +# -- Pod security context +podSecurityContext: + runAsNonRoot: true + seccompProfile: + type: RuntimeDefault + +# -- Container security context +securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - ALL + +# -- Node selector for pod scheduling +nodeSelector: {} + +# -- Tolerations for pod scheduling +tolerations: [] + +# -- Affinity rules for pod scheduling +affinity: {}