From bc31c1a92970450394861b8d458b09c3b2f91cef Mon Sep 17 00:00:00 2001 From: abimichel Date: Fri, 26 Jan 2024 13:56:39 -0800 Subject: [PATCH 01/29] new file: auto-label.yaml --- auto-label.yaml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 auto-label.yaml diff --git a/auto-label.yaml b/auto-label.yaml new file mode 100644 index 0000000..8ef583e --- /dev/null +++ b/auto-label.yaml @@ -0,0 +1,26 @@ +apiVersion: kyverno.io/v1 +kind: Policy +metadata: + annotations: + policies.kyverno.io/subject: Label + policies.kyverno.io/title: Add Labels to all OpenMetadata pods + name: add-labels +spec: + background: false + failurePolicy: Ignore + validationFailureAction: audit + rules: + - match: + any: + - resources: + kinds: + - Pod + selector: + matchLabels: + ## WIP ## + mutate: + patchStrategicMerge: + metadata: + labels: + DataClass: Medium + name: add-data-class-label \ No newline at end of file From 48344cf192750ed7ec73454b9c5bbfc77ca97705 Mon Sep 17 00:00:00 2001 From: abimichel Date: Tue, 30 Jan 2024 15:47:54 -0800 Subject: [PATCH 02/29] update to chart version 1.2.8 --- .gitignore | 1 + charts/deps/Chart.yaml | 4 +- charts/deps/values.yaml | 10 +- charts/openmetadata/Chart.yaml | 4 +- charts/openmetadata/README.md | 30 +- charts/openmetadata/templates/_helpers.tpl | 340 +++--------------- charts/openmetadata/templates/deployment.yaml | 114 +++++- charts/openmetadata/templates/secrets.yaml | 290 ++++++++++++++- .../templates/servicemonitor.yaml | 3 + charts/openmetadata/values.schema.json | 51 ++- charts/openmetadata/values.yaml | 33 +- 11 files changed, 555 insertions(+), 325 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c120ca9 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +charts-1.2.4 \ No newline at end of file diff --git a/charts/deps/Chart.yaml b/charts/deps/Chart.yaml index 7cdb45c..e9b6d5b 100644 --- a/charts/deps/Chart.yaml +++ b/charts/deps/Chart.yaml @@ -16,13 +16,13 @@ type: application # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.2.3 +version: 1.2.8 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "1.2.2" +appVersion: "1.2.5" home: https://open-metadata.org/ diff --git a/charts/deps/values.yaml b/charts/deps/values.yaml index f2c4368..8cd49f8 100644 --- a/charts/deps/values.yaml +++ b/charts/deps/values.yaml @@ -15,7 +15,7 @@ mysql: primary: extraFlags: "--sort_buffer_size=10M" persistence: - size: 1Gi + size: 50Gi service: nodePort: 3306 initdbScripts: @@ -40,7 +40,7 @@ opensearch: imagePullPolicy: Always opensearchJavaOpts: "-Xmx1g -Xms1g" persistence: - size: 3Gi + size: 30Gi protocol: http config: opensearch.yml: | @@ -60,11 +60,9 @@ airflow: enabled: true airflow: image: - repository: artifacts.developer.gov.bc.ca/docker-remote/openmetadata/server - tag: 1.2.2 + repository: docker.getcollate.io/openmetadata/ingestion + tag: 1.2.5 pullPolicy: "IfNotPresent" - imagePullSecrets: - - name: "artifactory-pull" executor: "KubernetesExecutor" config: # This is required for OpenMetadata UI to fetch status of DAGs diff --git a/charts/openmetadata/Chart.yaml b/charts/openmetadata/Chart.yaml index 12aa43f..db82c2d 100644 --- a/charts/openmetadata/Chart.yaml +++ b/charts/openmetadata/Chart.yaml @@ -16,13 +16,13 @@ type: application # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 1.2.4 +version: 1.2.8 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "1.2.2" +appVersion: "1.2.5" home: https://open-metadata.org/ diff --git a/charts/openmetadata/README.md b/charts/openmetadata/README.md index 38e8dee..f5ea26f 100644 --- a/charts/openmetadata/README.md +++ b/charts/openmetadata/README.md @@ -14,6 +14,7 @@ kubectl create secret generic airflow-secrets --from-literal=openmetadata-airflo ``` The above commands sets the passwords as an example. Change to any password of choice. + Run the following command to install openmetadata with default configuration. ``` @@ -22,6 +23,7 @@ helm install openmetadata open-metadata/openmetadata ``` If the default configuration is not applicable, you can update the values listed below in a `values.yaml` file and run + ``` helm install openmetadata open-metadata/openmetadata --values <> ``` @@ -29,8 +31,9 @@ helm install openmetadata open-metadata/openmetadata --values < \ # --from-literal=aws-secret-access-key-secret= web: + enabled: true uriPath: "/api" hsts: enabled: false @@ -262,10 +274,10 @@ networkPolicy: # callbackUrl: "" image: - repository: artifacts.developer.gov.bc.ca/docker-remote/openmetadata/server + repository: docker.getcollate.io/openmetadata/server # Overrides the image tag whose default is the chart appVersion. tag: "" - pullPolicy: "IfNotPresent" + pullPolicy: "Always" sidecars: [] # - name: "busybox" @@ -281,8 +293,7 @@ sidecars: [] # mountPath: /usr/share/extras # readOnly: true -imagePullSecrets: - - name: "artifactory-pull" +imagePullSecrets: [] nameOverride: "" fullnameOverride: "openmetadata" @@ -294,6 +305,7 @@ serviceAccount: # The name of the service account to use. # If not set and create is true, a name is generated using the fullname template name: "" +automountServiceAccountToken: true podSecurityContext: {} # fsGroup: 2000 securityContext: {} @@ -315,6 +327,7 @@ serviceMonitor: enabled: false interval: 30s annotations: {} + labels: {} ingress: enabled: false @@ -348,19 +361,23 @@ extraVolumeMounts: [] # mountPath: /usr/share/extras # readOnly: true +# Provision for InitContainers to be running after the `run-db-migration` InitContainer extraInitContainers: [] +# Provision for InitContainers to be running before the `run-db-migration` InitContainer +preMigrateInitContainers: [] + resources: {} # We usually recommend not to specify default resources and to leave this as a conscious # choice for the user. This also increases chances charts run on environments with little # resources, such as Minikube. If you do want to specify resources, uncomment the following # lines, adjust them as necessary, and remove the curly braces after 'resources:'. # limits: -# cpu: 100m -# memory: 128Mi +# cpu: 1 +# memory: 2048Mi # requests: -# cpu: 100m -# memory: 128Mi +# cpu: 500m +# memory: 256Mi nodeSelector: {} From d5ed1d761d9692c4b2abb7e97d487dc0f7c3d556 Mon Sep 17 00:00:00 2001 From: abimichel Date: Tue, 30 Jan 2024 15:55:59 -0800 Subject: [PATCH 03/29] helm dependency build --- charts/deps/charts/airflow-8.8.0/.helmignore | 47 + charts/deps/charts/airflow-8.8.0/CHANGELOG.md | 822 ++++++ .../deps/charts/airflow-8.8.0/CONTRIBUTING.md | 90 + charts/deps/charts/airflow-8.8.0/Chart.lock | 9 + charts/deps/charts/airflow-8.8.0/Chart.yaml | 26 + charts/deps/charts/airflow-8.8.0/README.md | 553 ++++ .../charts/airflow-8.8.0/airflow-8.8.0.tar | Bin 0 -> 948736 bytes .../charts/postgresql/.helmignore | 2 + .../charts/postgresql/Chart.yaml | 18 + .../airflow-8.8.0/charts/postgresql/README.md | 587 +++++ .../charts/postgresql/ci/default-values.yaml | 1 + .../ci/shmvolume-disabled-values.yaml | 2 + .../charts/postgresql/files/README.md | 1 + .../charts/postgresql/files/conf.d/README.md | 4 + .../docker-entrypoint-initdb.d/README.md | 3 + .../charts/postgresql/templates/NOTES.txt | 81 + .../charts/postgresql/templates/_helpers.tpl | 420 +++ .../postgresql/templates/configmap.yaml | 26 + .../templates/extended-config-configmap.yaml | 21 + .../templates/initialization-configmap.yaml | 24 + .../templates/metrics-configmap.yaml | 13 + .../postgresql/templates/metrics-svc.yaml | 26 + .../postgresql/templates/networkpolicy.yaml | 38 + .../postgresql/templates/prometheusrule.yaml | 23 + .../charts/postgresql/templates/secrets.yaml | 23 + .../postgresql/templates/serviceaccount.yaml | 11 + .../postgresql/templates/servicemonitor.yaml | 33 + .../templates/statefulset-slaves.yaml | 299 +++ .../postgresql/templates/statefulset.yaml | 458 ++++ .../postgresql/templates/svc-headless.yaml | 19 + .../charts/postgresql/templates/svc-read.yaml | 31 + .../charts/postgresql/templates/svc.yaml | 38 + .../charts/postgresql/values-production.yaml | 520 ++++ .../charts/postgresql/values.schema.json | 103 + .../charts/postgresql/values.yaml | 526 ++++ .../airflow-8.8.0/charts/redis/.helmignore | 3 + .../airflow-8.8.0/charts/redis/Chart.yaml | 16 + .../airflow-8.8.0/charts/redis/README.md | 518 ++++ .../charts/redis/ci/default-values.yaml | 1 + .../charts/redis/ci/dev-values.yaml | 9 + .../charts/redis/ci/extra-flags-values.yaml | 11 + .../redis/ci/insecure-sentinel-values.yaml | 524 ++++ .../redis/ci/production-sentinel-values.yaml | 524 ++++ .../charts/redis/ci/production-values.yaml | 525 ++++ .../charts/redis/ci/redis-lib-values.yaml | 13 + .../redis/ci/redisgraph-module-values.yaml | 10 + .../charts/redis/templates/NOTES.txt | 124 + .../charts/redis/templates/_helpers.tpl | 355 +++ .../charts/redis/templates/configmap.yaml | 52 + .../charts/redis/templates/headless-svc.yaml | 24 + .../redis/templates/health-configmap.yaml | 134 + .../redis/templates/metrics-prometheus.yaml | 30 + .../charts/redis/templates/metrics-svc.yaml | 30 + .../charts/redis/templates/networkpolicy.yaml | 73 + .../redis/templates/prometheusrule.yaml | 23 + .../charts/redis/templates/psp.yaml | 42 + .../templates/redis-master-statefulset.yaml | 419 +++ .../redis/templates/redis-master-svc.yaml | 39 + .../charts/redis/templates/redis-role.yaml | 21 + .../redis/templates/redis-rolebinding.yaml | 18 + .../redis/templates/redis-serviceaccount.yaml | 11 + .../templates/redis-slave-statefulset.yaml | 437 ++++ .../redis/templates/redis-slave-svc.yaml | 40 + .../templates/redis-with-sentinel-svc.yaml | 40 + .../charts/redis/templates/secret.yaml | 14 + .../charts/redis/values-production.yaml | 630 +++++ .../charts/redis/values.schema.json | 168 ++ .../airflow-8.8.0/charts/redis/values.yaml | 631 +++++ .../files/pod_template.kubernetes-helm-yaml | 87 + .../airflow-8.8.0/files/webserver_config.py | 10 + .../sample-values-CeleryExecutor.yaml | 333 +++ ...ample-values-CeleryKubernetesExecutor.yaml | 361 +++ .../sample-values-KubernetesExecutor.yaml | 305 +++ .../charts/airflow-8.8.0/templates/NOTES.txt | 229 ++ .../templates/_helpers/common.tpl | 186 ++ .../airflow-8.8.0/templates/_helpers/pods.tpl | 588 +++++ .../templates/_helpers/validate-values.tpl | 223 ++ .../config/configmap-pod-template.yaml | 18 + .../templates/config/secret-config-envs.yaml | 217 ++ .../templates/config/secret-known-hosts.yaml | 13 + .../config/secret-local-settings.yaml | 13 + .../config/secret-webserver-config.yaml | 17 + .../templates/db-migrations/_helpers/code.tpl | 136 + .../db-migrations-deployment.yaml | 119 + .../db-migrations/db-migrations-job.yaml | 109 + .../db-migrations/db-migrations-secret.yaml | 14 + .../templates/extra-manifests.yaml | 9 + .../templates/flower/flower-deployment.yaml | 165 ++ .../flower/flower-ingress-v1beta1.yaml | 51 + .../templates/flower/flower-ingress.yaml | 68 + .../templates/flower/flower-pdb.yaml | 24 + .../templates/flower/flower-service.yaml | 39 + .../pgbouncer/_helpers/pgbouncer.tpl | 118 + .../pgbouncer/pgbouncer-deployment.yaml | 218 ++ .../templates/pgbouncer/pgbouncer-pdb.yaml | 24 + .../templates/pgbouncer/pgbouncer-secret.yaml | 18 + .../pgbouncer/pgbouncer-service.yaml | 22 + .../airflow-8.8.0/templates/pvc-dags.yaml | 24 + .../airflow-8.8.0/templates/pvc-logs.yaml | 24 + .../templates/rbac/airflow-role.yaml | 46 + .../templates/rbac/airflow-rolebinding.yaml | 19 + .../rbac/airflow-serviceaccount.yaml | 15 + .../scheduler/scheduler-deployment.yaml | 251 ++ .../templates/scheduler/scheduler-pdb.yaml | 24 + .../templates/sync/_helpers/global_code.tpl | 130 + .../sync/_helpers/sync_connections.tpl | 219 ++ .../templates/sync/_helpers/sync_pools.tpl | 206 ++ .../templates/sync/_helpers/sync_users.tpl | 240 ++ .../sync/_helpers/sync_variables.tpl | 129 + .../sync/sync-connections-deployment.yaml | 150 ++ .../templates/sync/sync-connections-job.yaml | 140 + .../sync/sync-connections-secret.yaml | 14 + .../templates/sync/sync-pools-deployment.yaml | 120 + .../templates/sync/sync-pools-job.yaml | 110 + .../templates/sync/sync-pools-secret.yaml | 14 + .../templates/sync/sync-users-deployment.yaml | 150 ++ .../templates/sync/sync-users-job.yaml | 140 + .../templates/sync/sync-users-secret.yaml | 14 + .../sync/sync-variables-deployment.yaml | 150 ++ .../templates/sync/sync-variables-job.yaml | 140 + .../templates/sync/sync-variables-secret.yaml | 14 + .../triggerer/triggerer-deployment.yaml | 173 ++ .../templates/triggerer/triggerer-pdb.yaml | 24 + .../webserver/webserver-deployment.yaml | 164 ++ .../webserver/webserver-ingress-v1beta1.yaml | 51 + .../webserver/webserver-ingress.yaml | 69 + .../templates/webserver/webserver-pdb.yaml | 24 + .../webserver/webserver-prometheus-rule.yaml | 18 + .../webserver/webserver-service-monitor.yaml | 25 + .../webserver/webserver-service.yaml | 42 + .../templates/worker/worker-hpa.yaml | 21 + .../templates/worker/worker-pdb.yaml | 24 + .../templates/worker/worker-service.yaml | 23 + .../templates/worker/worker-statefulset.yaml | 225 ++ charts/deps/charts/airflow-8.8.0/values.yaml | 2260 +++++++++++++++++ charts/deps/charts/mysql-9.7.2/.helmignore | 21 + charts/deps/charts/mysql-9.7.2/Chart.lock | 6 + charts/deps/charts/mysql-9.7.2/Chart.yaml | 29 + charts/deps/charts/mysql-9.7.2/README.md | 551 ++++ .../mysql-9.7.2/charts/common/.helmignore | 22 + .../mysql-9.7.2/charts/common/Chart.yaml | 24 + .../mysql-9.7.2/charts/common/README.md | 233 ++ .../charts/common/templates/_affinities.tpl | 106 + .../charts/common/templates/_capabilities.tpl | 154 ++ .../charts/common/templates/_errors.tpl | 23 + .../charts/common/templates/_images.tpl | 80 + .../charts/common/templates/_ingress.tpl | 68 + .../charts/common/templates/_labels.tpl | 18 + .../charts/common/templates/_names.tpl | 66 + .../charts/common/templates/_secrets.tpl | 165 ++ .../charts/common/templates/_storage.tpl | 23 + .../charts/common/templates/_tplvalues.tpl | 13 + .../charts/common/templates/_utils.tpl | 62 + .../charts/common/templates/_warnings.tpl | 14 + .../templates/validations/_cassandra.tpl | 72 + .../common/templates/validations/_mariadb.tpl | 103 + .../common/templates/validations/_mongodb.tpl | 108 + .../common/templates/validations/_mysql.tpl | 103 + .../templates/validations/_postgresql.tpl | 129 + .../common/templates/validations/_redis.tpl | 76 + .../templates/validations/_validations.tpl | 46 + .../mysql-9.7.2/charts/common/values.yaml | 5 + .../deps/charts/mysql-9.7.2/mysql-9.7.2.tar | Bin 0 -> 305664 bytes .../charts/mysql-9.7.2/templates/NOTES.txt | 75 + .../charts/mysql-9.7.2/templates/_helpers.tpl | 161 ++ .../mysql-9.7.2/templates/extra-list.yaml | 4 + .../mysql-9.7.2/templates/metrics-svc.yaml | 29 + .../mysql-9.7.2/templates/networkpolicy.yaml | 40 + .../templates/primary/configmap.yaml | 18 + .../primary/initialization-configmap.yaml | 17 + .../mysql-9.7.2/templates/primary/pdb.yaml | 25 + .../templates/primary/statefulset.yaml | 388 +++ .../templates/primary/svc-headless.yaml | 29 + .../mysql-9.7.2/templates/primary/svc.yaml | 52 + .../mysql-9.7.2/templates/prometheusrule.yaml | 22 + .../charts/mysql-9.7.2/templates/role.yaml | 24 + .../mysql-9.7.2/templates/rolebinding.yaml | 21 + .../templates/secondary/configmap.yaml | 18 + .../mysql-9.7.2/templates/secondary/pdb.yaml | 25 + .../templates/secondary/statefulset.yaml | 369 +++ .../templates/secondary/svc-headless.yaml | 31 + .../mysql-9.7.2/templates/secondary/svc.yaml | 54 + .../charts/mysql-9.7.2/templates/secrets.yaml | 78 + .../mysql-9.7.2/templates/serviceaccount.yaml | 23 + .../mysql-9.7.2/templates/servicemonitor.yaml | 49 + .../charts/mysql-9.7.2/values.schema.json | 195 ++ charts/deps/charts/mysql-9.7.2/values.yaml | 1245 +++++++++ .../deps/charts/opensearch-2.12.1/.helmignore | 23 + .../charts/opensearch-2.12.1/CHANGELOG.md | 282 ++ .../deps/charts/opensearch-2.12.1/Chart.yaml | 13 + .../deps/charts/opensearch-2.12.1/README.md | 184 ++ .../ci/ci-ingress-class-name-values.yaml | 429 ++++ .../ci/ci-rbac-enabled-values.yaml | 429 ++++ .../opensearch-2.12.1/ci/ci-values.yaml | 416 +++ .../opensearch-2.12.1/opensearch-2.12.1.tar | Bin 0 -> 143872 bytes .../opensearch-2.12.1/templates/NOTES.txt | 2 + .../opensearch-2.12.1/templates/_helpers.tpl | 144 ++ .../templates/configmap.yaml | 18 + .../templates/extraManifests.yaml | 4 + .../opensearch-2.12.1/templates/ingress.yaml | 62 + .../templates/networkpolicy.yaml | 17 + .../templates/poddisruptionbudget.yaml | 17 + .../templates/podsecuritypolicy.yaml | 17 + .../opensearch-2.12.1/templates/role.yaml | 22 + .../templates/rolebinding.yaml | 21 + .../templates/securityconfig.yaml | 19 + .../opensearch-2.12.1/templates/service.yaml | 72 + .../templates/serviceaccount.yaml | 17 + .../templates/statefulset.yaml | 541 ++++ .../deps/charts/opensearch-2.12.1/values.yaml | 501 ++++ 210 files changed, 28329 insertions(+) create mode 100644 charts/deps/charts/airflow-8.8.0/.helmignore create mode 100644 charts/deps/charts/airflow-8.8.0/CHANGELOG.md create mode 100644 charts/deps/charts/airflow-8.8.0/CONTRIBUTING.md create mode 100644 charts/deps/charts/airflow-8.8.0/Chart.lock create mode 100644 charts/deps/charts/airflow-8.8.0/Chart.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/README.md create mode 100644 charts/deps/charts/airflow-8.8.0/airflow-8.8.0.tar create mode 100644 charts/deps/charts/airflow-8.8.0/charts/postgresql/.helmignore create mode 100644 charts/deps/charts/airflow-8.8.0/charts/postgresql/Chart.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/postgresql/README.md create mode 100644 charts/deps/charts/airflow-8.8.0/charts/postgresql/ci/default-values.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/postgresql/ci/shmvolume-disabled-values.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/postgresql/files/README.md create mode 100644 charts/deps/charts/airflow-8.8.0/charts/postgresql/files/conf.d/README.md create mode 100644 charts/deps/charts/airflow-8.8.0/charts/postgresql/files/docker-entrypoint-initdb.d/README.md create mode 100644 charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/NOTES.txt create mode 100644 charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/_helpers.tpl create mode 100644 charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/configmap.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/extended-config-configmap.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/initialization-configmap.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/metrics-configmap.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/metrics-svc.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/networkpolicy.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/prometheusrule.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/secrets.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/serviceaccount.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/servicemonitor.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/statefulset-slaves.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/statefulset.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/svc-headless.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/svc-read.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/svc.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/postgresql/values-production.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/postgresql/values.schema.json create mode 100644 charts/deps/charts/airflow-8.8.0/charts/postgresql/values.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/redis/.helmignore create mode 100644 charts/deps/charts/airflow-8.8.0/charts/redis/Chart.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/redis/README.md create mode 100644 charts/deps/charts/airflow-8.8.0/charts/redis/ci/default-values.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/redis/ci/dev-values.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/redis/ci/extra-flags-values.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/redis/ci/insecure-sentinel-values.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/redis/ci/production-sentinel-values.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/redis/ci/production-values.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/redis/ci/redis-lib-values.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/redis/ci/redisgraph-module-values.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/redis/templates/NOTES.txt create mode 100644 charts/deps/charts/airflow-8.8.0/charts/redis/templates/_helpers.tpl create mode 100644 charts/deps/charts/airflow-8.8.0/charts/redis/templates/configmap.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/redis/templates/headless-svc.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/redis/templates/health-configmap.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/redis/templates/metrics-prometheus.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/redis/templates/metrics-svc.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/redis/templates/networkpolicy.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/redis/templates/prometheusrule.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/redis/templates/psp.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/redis/templates/redis-master-statefulset.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/redis/templates/redis-master-svc.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/redis/templates/redis-role.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/redis/templates/redis-rolebinding.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/redis/templates/redis-serviceaccount.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/redis/templates/redis-slave-statefulset.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/redis/templates/redis-slave-svc.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/redis/templates/redis-with-sentinel-svc.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/redis/templates/secret.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/redis/values-production.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/charts/redis/values.schema.json create mode 100644 charts/deps/charts/airflow-8.8.0/charts/redis/values.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/files/pod_template.kubernetes-helm-yaml create mode 100644 charts/deps/charts/airflow-8.8.0/files/webserver_config.py create mode 100644 charts/deps/charts/airflow-8.8.0/sample-values-CeleryExecutor.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/sample-values-CeleryKubernetesExecutor.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/sample-values-KubernetesExecutor.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/NOTES.txt create mode 100644 charts/deps/charts/airflow-8.8.0/templates/_helpers/common.tpl create mode 100644 charts/deps/charts/airflow-8.8.0/templates/_helpers/pods.tpl create mode 100644 charts/deps/charts/airflow-8.8.0/templates/_helpers/validate-values.tpl create mode 100644 charts/deps/charts/airflow-8.8.0/templates/config/configmap-pod-template.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/config/secret-config-envs.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/config/secret-known-hosts.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/config/secret-local-settings.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/config/secret-webserver-config.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/db-migrations/_helpers/code.tpl create mode 100644 charts/deps/charts/airflow-8.8.0/templates/db-migrations/db-migrations-deployment.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/db-migrations/db-migrations-job.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/db-migrations/db-migrations-secret.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/extra-manifests.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/flower/flower-deployment.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/flower/flower-ingress-v1beta1.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/flower/flower-ingress.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/flower/flower-pdb.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/flower/flower-service.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/pgbouncer/_helpers/pgbouncer.tpl create mode 100644 charts/deps/charts/airflow-8.8.0/templates/pgbouncer/pgbouncer-deployment.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/pgbouncer/pgbouncer-pdb.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/pgbouncer/pgbouncer-secret.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/pgbouncer/pgbouncer-service.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/pvc-dags.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/pvc-logs.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/rbac/airflow-role.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/rbac/airflow-rolebinding.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/rbac/airflow-serviceaccount.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/scheduler/scheduler-deployment.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/scheduler/scheduler-pdb.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/sync/_helpers/global_code.tpl create mode 100644 charts/deps/charts/airflow-8.8.0/templates/sync/_helpers/sync_connections.tpl create mode 100644 charts/deps/charts/airflow-8.8.0/templates/sync/_helpers/sync_pools.tpl create mode 100644 charts/deps/charts/airflow-8.8.0/templates/sync/_helpers/sync_users.tpl create mode 100644 charts/deps/charts/airflow-8.8.0/templates/sync/_helpers/sync_variables.tpl create mode 100644 charts/deps/charts/airflow-8.8.0/templates/sync/sync-connections-deployment.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/sync/sync-connections-job.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/sync/sync-connections-secret.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/sync/sync-pools-deployment.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/sync/sync-pools-job.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/sync/sync-pools-secret.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/sync/sync-users-deployment.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/sync/sync-users-job.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/sync/sync-users-secret.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/sync/sync-variables-deployment.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/sync/sync-variables-job.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/sync/sync-variables-secret.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/triggerer/triggerer-deployment.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/triggerer/triggerer-pdb.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/webserver/webserver-deployment.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/webserver/webserver-ingress-v1beta1.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/webserver/webserver-ingress.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/webserver/webserver-pdb.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/webserver/webserver-prometheus-rule.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/webserver/webserver-service-monitor.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/webserver/webserver-service.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/worker/worker-hpa.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/worker/worker-pdb.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/worker/worker-service.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/templates/worker/worker-statefulset.yaml create mode 100644 charts/deps/charts/airflow-8.8.0/values.yaml create mode 100644 charts/deps/charts/mysql-9.7.2/.helmignore create mode 100644 charts/deps/charts/mysql-9.7.2/Chart.lock create mode 100644 charts/deps/charts/mysql-9.7.2/Chart.yaml create mode 100644 charts/deps/charts/mysql-9.7.2/README.md create mode 100644 charts/deps/charts/mysql-9.7.2/charts/common/.helmignore create mode 100644 charts/deps/charts/mysql-9.7.2/charts/common/Chart.yaml create mode 100644 charts/deps/charts/mysql-9.7.2/charts/common/README.md create mode 100644 charts/deps/charts/mysql-9.7.2/charts/common/templates/_affinities.tpl create mode 100644 charts/deps/charts/mysql-9.7.2/charts/common/templates/_capabilities.tpl create mode 100644 charts/deps/charts/mysql-9.7.2/charts/common/templates/_errors.tpl create mode 100644 charts/deps/charts/mysql-9.7.2/charts/common/templates/_images.tpl create mode 100644 charts/deps/charts/mysql-9.7.2/charts/common/templates/_ingress.tpl create mode 100644 charts/deps/charts/mysql-9.7.2/charts/common/templates/_labels.tpl create mode 100644 charts/deps/charts/mysql-9.7.2/charts/common/templates/_names.tpl create mode 100644 charts/deps/charts/mysql-9.7.2/charts/common/templates/_secrets.tpl create mode 100644 charts/deps/charts/mysql-9.7.2/charts/common/templates/_storage.tpl create mode 100644 charts/deps/charts/mysql-9.7.2/charts/common/templates/_tplvalues.tpl create mode 100644 charts/deps/charts/mysql-9.7.2/charts/common/templates/_utils.tpl create mode 100644 charts/deps/charts/mysql-9.7.2/charts/common/templates/_warnings.tpl create mode 100644 charts/deps/charts/mysql-9.7.2/charts/common/templates/validations/_cassandra.tpl create mode 100644 charts/deps/charts/mysql-9.7.2/charts/common/templates/validations/_mariadb.tpl create mode 100644 charts/deps/charts/mysql-9.7.2/charts/common/templates/validations/_mongodb.tpl create mode 100644 charts/deps/charts/mysql-9.7.2/charts/common/templates/validations/_mysql.tpl create mode 100644 charts/deps/charts/mysql-9.7.2/charts/common/templates/validations/_postgresql.tpl create mode 100644 charts/deps/charts/mysql-9.7.2/charts/common/templates/validations/_redis.tpl create mode 100644 charts/deps/charts/mysql-9.7.2/charts/common/templates/validations/_validations.tpl create mode 100644 charts/deps/charts/mysql-9.7.2/charts/common/values.yaml create mode 100644 charts/deps/charts/mysql-9.7.2/mysql-9.7.2.tar create mode 100644 charts/deps/charts/mysql-9.7.2/templates/NOTES.txt create mode 100644 charts/deps/charts/mysql-9.7.2/templates/_helpers.tpl create mode 100644 charts/deps/charts/mysql-9.7.2/templates/extra-list.yaml create mode 100644 charts/deps/charts/mysql-9.7.2/templates/metrics-svc.yaml create mode 100644 charts/deps/charts/mysql-9.7.2/templates/networkpolicy.yaml create mode 100644 charts/deps/charts/mysql-9.7.2/templates/primary/configmap.yaml create mode 100644 charts/deps/charts/mysql-9.7.2/templates/primary/initialization-configmap.yaml create mode 100644 charts/deps/charts/mysql-9.7.2/templates/primary/pdb.yaml create mode 100644 charts/deps/charts/mysql-9.7.2/templates/primary/statefulset.yaml create mode 100644 charts/deps/charts/mysql-9.7.2/templates/primary/svc-headless.yaml create mode 100644 charts/deps/charts/mysql-9.7.2/templates/primary/svc.yaml create mode 100644 charts/deps/charts/mysql-9.7.2/templates/prometheusrule.yaml create mode 100644 charts/deps/charts/mysql-9.7.2/templates/role.yaml create mode 100644 charts/deps/charts/mysql-9.7.2/templates/rolebinding.yaml create mode 100644 charts/deps/charts/mysql-9.7.2/templates/secondary/configmap.yaml create mode 100644 charts/deps/charts/mysql-9.7.2/templates/secondary/pdb.yaml create mode 100644 charts/deps/charts/mysql-9.7.2/templates/secondary/statefulset.yaml create mode 100644 charts/deps/charts/mysql-9.7.2/templates/secondary/svc-headless.yaml create mode 100644 charts/deps/charts/mysql-9.7.2/templates/secondary/svc.yaml create mode 100644 charts/deps/charts/mysql-9.7.2/templates/secrets.yaml create mode 100644 charts/deps/charts/mysql-9.7.2/templates/serviceaccount.yaml create mode 100644 charts/deps/charts/mysql-9.7.2/templates/servicemonitor.yaml create mode 100644 charts/deps/charts/mysql-9.7.2/values.schema.json create mode 100644 charts/deps/charts/mysql-9.7.2/values.yaml create mode 100644 charts/deps/charts/opensearch-2.12.1/.helmignore create mode 100644 charts/deps/charts/opensearch-2.12.1/CHANGELOG.md create mode 100644 charts/deps/charts/opensearch-2.12.1/Chart.yaml create mode 100644 charts/deps/charts/opensearch-2.12.1/README.md create mode 100644 charts/deps/charts/opensearch-2.12.1/ci/ci-ingress-class-name-values.yaml create mode 100644 charts/deps/charts/opensearch-2.12.1/ci/ci-rbac-enabled-values.yaml create mode 100644 charts/deps/charts/opensearch-2.12.1/ci/ci-values.yaml create mode 100644 charts/deps/charts/opensearch-2.12.1/opensearch-2.12.1.tar create mode 100644 charts/deps/charts/opensearch-2.12.1/templates/NOTES.txt create mode 100644 charts/deps/charts/opensearch-2.12.1/templates/_helpers.tpl create mode 100644 charts/deps/charts/opensearch-2.12.1/templates/configmap.yaml create mode 100644 charts/deps/charts/opensearch-2.12.1/templates/extraManifests.yaml create mode 100644 charts/deps/charts/opensearch-2.12.1/templates/ingress.yaml create mode 100644 charts/deps/charts/opensearch-2.12.1/templates/networkpolicy.yaml create mode 100644 charts/deps/charts/opensearch-2.12.1/templates/poddisruptionbudget.yaml create mode 100644 charts/deps/charts/opensearch-2.12.1/templates/podsecuritypolicy.yaml create mode 100644 charts/deps/charts/opensearch-2.12.1/templates/role.yaml create mode 100644 charts/deps/charts/opensearch-2.12.1/templates/rolebinding.yaml create mode 100644 charts/deps/charts/opensearch-2.12.1/templates/securityconfig.yaml create mode 100644 charts/deps/charts/opensearch-2.12.1/templates/service.yaml create mode 100644 charts/deps/charts/opensearch-2.12.1/templates/serviceaccount.yaml create mode 100644 charts/deps/charts/opensearch-2.12.1/templates/statefulset.yaml create mode 100644 charts/deps/charts/opensearch-2.12.1/values.yaml diff --git a/charts/deps/charts/airflow-8.8.0/.helmignore b/charts/deps/charts/airflow-8.8.0/.helmignore new file mode 100644 index 0000000..095b158 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/.helmignore @@ -0,0 +1,47 @@ +# Patterns to ignore when building the helm package + +## Git +.git +.gitignore + +## JetBrains +.idea/ +*.iml +*.ipr +*.iws + +## VSCode +.vscode/* +*.code-workspace +.history/ + +## Vim +[._]*.s[a-v][a-z] +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] +Session.vim +Sessionx.vim +.netrwhist +*~ +[._]*.un~ + +## Emacs +*~ +\#*\# +/.emacs.desktop +/.emacs.desktop.lock +*.elc +.\#* + +## macOS +.DS_Store +.AppleDouble +.LSOverride +._* + +## Chart Documentation +/ci +/docs +/examples diff --git a/charts/deps/charts/airflow-8.8.0/CHANGELOG.md b/charts/deps/charts/airflow-8.8.0/CHANGELOG.md new file mode 100644 index 0000000..c416b87 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/CHANGELOG.md @@ -0,0 +1,822 @@ +# Changelog + +All notable changes to the `User-Community Airflow Helm Chart` will be documented in this file. + +This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html) and [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). + +## [Unreleased] + +TBD + +## [8.8.0] - 2023-08-28 + +> 🟨 __NOTES__ 🟨 +> +> - the default airflow image is now `apache/airflow:2.6.3-python3.9`, but you can still use any supported version of airflow, see the [airflow version support matrix](https://github.com/airflow-helm/charts/tree/main/charts/airflow#airflow-version-support) +> - this is the first version of the chart with support for airflow 2.7, along with pretty much all previous versions of airflow, see the [airflow version support matrix](https://github.com/airflow-helm/charts/tree/main/charts/airflow#airflow-version-support) + +> 🟦 __OTHER__ 🟦 +> +> - If you appreciate the `User-Community Airflow Helm Chart` please consider supporting us! +> - [give a ⭐ on GitHub](https://github.com/airflow-helm/charts/stargazers) +> - [give a ⭐ on ArtifactHub](https://artifacthub.io/packages/helm/airflow-helm/airflow) + +### Changed +- the default airflow image is now `apache/airflow:2.6.3-python3.9` (see the [airflow version support matrix](https://github.com/airflow-helm/charts/tree/main/charts/airflow#airflow-version-support)) + +### Added +- add liveness probe for celery workers ([#766](https://github.com/airflow-helm/charts/pull/766)) +- support `include_deferred` in pool sync for airflow 2.7.0 ([#775](https://github.com/airflow-helm/charts/pull/775)) + +### Fixed + +- sync-users for airflow 2.7.0 ([#772](https://github.com/airflow-helm/charts/pull/772)) +- invalid pgbouncer-certs volume spec when empty ([#777](https://github.com/airflow-helm/charts/pull/777)) + +## [8.7.1] - 2023-05-13 + +> 🟨 __NOTES__ 🟨 +> +> - this is the first version of the chart with support for airflow 2.6, along with pretty much all previous versions of airflow, see the [airflow version support matrix](https://github.com/airflow-helm/charts/tree/main/charts/airflow#airflow-version-support) + +### Fixed + +- fixed liveness probes in airflow 2.6.0 ([#743](https://github.com/airflow-helm/charts/pull/743)) + +## [8.7.0] - 2023-04-06 + +> 🟥 __WARNINGS__ 🟥 +> +> - if you use a custom `pgbouncer.image.tag`, you MUST update it to `1.18.0-patch.1` or later, as we now require the `openssl` package to be installed for generating self-signed certificates +> - if you use a custom `postgresql.image`, please take note that `postgresql.image.registry` is now `ghcr.io` by default (rather than `docker.io`) +> - if you use "Azure File" for logs persistence, you MUST NOT update to airflow 2.5.1, 2.5.2, or 2.5.3: +> - there is an [issue in these versions](https://github.com/apache/airflow/issues/29112) that will cause your tasks to fail +> - if you wish to use these versions, you will need to use a different method of logs persistence, for example [the `Azure Blob Storage` remote provider](https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/monitoring/log-persistence.md#option-2---remote-providers) + +> 🟨 __NOTES__ 🟨 +> +> - the default airflow image is now `apache/airflow:2.5.3-python3.8`, but you can still use any supported version of airflow, see the [airflow version support matrix](https://github.com/airflow-helm/charts/tree/main/charts/airflow#airflow-version-support) +> - when upgrading to airflow 2.5, you may wish to rename your kubernetes `aiflow.config` from `AIRFLOW__KUBERNETES__*` to `AIRFLOW__KUBERNETES_EXECUTOR__*`, as the former was deprecated by airflow 2.5 +> - the chart should no longer be forever "out of sync" in apps like ArgoCD, as this issue was resolved by [#718](https://github.com/airflow-helm/charts/pull/718) + +### Changed +- the default airflow image is now `apache/airflow:2.5.3-python3.8` (see the [airflow version support matrix](https://github.com/airflow-helm/charts/tree/main/charts/airflow#airflow-version-support)) +- the default git-sync image is now `registry.k8s.io/git-sync/git-sync:v3.6.5` +- the default pgbouncer image is now `ghcr.io/airflow-helm/pgbouncer:1.18.0-patch.1` +- the default embedded postgres image is now `ghcr.io/airflow-helm/postgresql-bitnami:11.16-patch.0` (our new [custom image](https://github.com/airflow-helm/charts/tree/main/images/postgresql-bitnami/11/alpine), with support for ARM64) +- the default embedded redis image is now `bitnami/redis:5.0.14-debian-10-r173` +- we now generate self-signed certificates for pgbouncer using a startup script, fixing ArgoCD being forever "out of sync" ([#718](https://github.com/airflow-helm/charts/pull/718)) + +### Added +- feature for automatically changing pool slots on cron schedules ([#649](https://github.com/airflow-helm/charts/pull/649)) +- ability to disable chart-managed `webserver_config.py` file with `web.webserverConfig.enabled` value ([#631](https://github.com/airflow-helm/charts/pull/631)) +- added `dags.gitSync.submodules` value ([#620](https://github.com/airflow-helm/charts/pull/620)) + +### Fixed +- ensure Kubernetes 1.25+ support by updating default api versions for `PodDisruptionBudget` and `HorizontalPodAutoscaler` ([#685](https://github.com/airflow-helm/charts/pull/685)) +- now also set `kubernetes_executor` airflow configs, as `kubernetes` ones are deprecated ([#719](https://github.com/airflow-helm/charts/pull/719)) +- fix typo in error message ([#696](https://github.com/airflow-helm/charts/pull/696)) + +## [8.6.1] - 2022-06-22 + +> 🟥 __WARNINGS__ 🟥 +> +> - When using `extraPipPackages`, Pods will now fail to start if the `apache-airflow` version would be changed from the image version, +> see `airflow.protectedPipPackages` for more information. +> (NOTE: for critical deployments we STRONGLY recommend [embedding extra packages into the image](docs/faq/configuration/extra-python-packages.md#option-2---embedded-into-container-image) instead of using `extraPipPackages`) +> - The maximum length for a helm release-name has been lowered to `40` characters (down from `43`). +> Existing deployments with a helm release-name between `41` and `43` characters MUST enable the `allowLongReleaseName` value to continue using the chart. +> (NOTE: the embedded Postgres and Redis will not work with a release-name between `41` and `43` characters). + +> 🟨 __NOTES__ 🟨 +> +> - The chart now works with Airflow 2.3, however, please note the default image tag is still `2.2.5-python3.8`. +> - Storing [logs under `airflow.extraVolumeMounts` (Pod Volumes)](docs/faq/monitoring/log-persistence.md#option-3---pod-volumes) is now a supported usage pattern. +> - The [Scheduler "task creation check"](docs/faq/monitoring/scheduler-liveness-probe.md#scheduler-task-creation-check) now has the `scheduler.livenessProbe.taskCreationCheck.schedulerAgeBeforeCheck` +> value to ensure the scheduler has time to create tasks before starting the check. +> - Airflow 2.3.0 introduced BETA support for ARM CPUs (like Apple Silicon). +> The chart's current default images for Postgres/Redis only support `amd64`, +> however, you may test our drop-in replacements that support both `arm64` and `amd64`: +> - [`bitnami/postgresql`](https://hub.docker.com/r/bitnami/postgresql/) → [`ghcr.io/airflow-helm/postgresql-bitnami`](https://ghcr.io/airflow-helm/postgresql-bitnami) +> - [`bitnami/redis`](https://hub.docker.com/r/bitnami/redis/) → `TBA` + +### Changed +- update `.helmignore` file to exclude docs ([#593](https://github.com/airflow-helm/charts/pull/593)) +- require release-name to have <= 40 characters ([#589](https://github.com/airflow-helm/charts/pull/589)) + +### Added +- add `airflow.protectedPipPackages` ([#610](https://github.com/airflow-helm/charts/pull/610)) +- allow using `extraVolumeMounts` for log storage ([#585](https://github.com/airflow-helm/charts/pull/585)) +- minimum scheduler age before task-creation-check ([#612](https://github.com/airflow-helm/charts/pull/612)) + +### Fixed +- fix some breaking changes from airflow 2.3.0 ([#592](https://github.com/airflow-helm/charts/pull/592)) +- fix wait-for-db-migrations in airflow 2.3.0 ([#576](https://github.com/airflow-helm/charts/pull/576)) +- fix pgbouncer liveness probe in minikube ([#560](https://github.com/airflow-helm/charts/pull/560)) +- use rsync for extraPipPackages ([#599](https://github.com/airflow-helm/charts/pull/599)) +- set `AIRFLOW__LOGGING__WORKER_LOG_SERVER_PORT` ([#608](https://github.com/airflow-helm/charts/pull/608)) +- only set `SQLALCHEMY_DATABASE_URI` in airflow 1.10 ([#609](https://github.com/airflow-helm/charts/pull/609)) + +## [8.6.0] - 2022-04-13 + +> 🟥 __WARNINGS__ 🟥 +> +> - If you have `logs.persistence.enabled` set to `true`, you MUST disable `scheduler.logCleanup.enabled` and `workers.logCleanup.enabled` or the upgrade will fail +> - If using airflow `2.0.X` or `2.1.X`, you should set `triggerer.enabled` to `false`, as the triggerer was added in airflow `2.2.0` and will fail in older versions +> - If you currently pin `dags.gitSync.image.tag`, please update to the new default of `v3.5.0` +> - If you currently pin `pgbouncer.image.tag`, please update to the new default of `1.17.0-patch.0` +> - If you currently pin `pgbouncer.maxClientConnections`, please update to the new default of `1000` + +> 🟨 __NOTES__ 🟨 +> +> - Consider enabling the new [Scheduler "task creation check"](docs/faq/monitoring/scheduler-liveness-probe.md#scheduler-task-creation-check) to prevent deadlocks, especially if using airflow versions before `2.1.1` +> - If you disabled PgBouncer because of incompatibility with "Azure PostgreSQL", you can re-enable it if you [set `pgbouncer.authType = scram-sha-256` and `pgbouncer.serverSSL = verify-ca`](docs/faq/database/pgbouncer.md) +> - This chart version fixes [an issue that caused `db-migrations` to hang](https://github.com/airflow-helm/charts/pull/529) when PgBouncer is enabled with airflow `2.2.0+` +> - While NOT recommended, you can now set [external database](docs/faq/database/external-database.md) and [external redis](docs/faq/database/external-redis.md) passwords with a plain-text value +> - If you are using [celery worker autoscaling](docs/faq/configuration/autoscaling-celery-workers.md), you must set a non-empty value for `workers.logCleanup.resources.requests` +> - The new ["log-cleanup sidecar"](docs/faq/monitoring/log-cleanup.md) is enabled by default on schedulers and workers +> - The new [PgBouncer startupProbe](https://github.com/airflow-helm/charts/pull/547) will only work in Kubernetes 1.18+ +> - The [`extraManifests` value](docs/faq/kubernetes/extra-manifests.md) has been significantly improved + +### Changed +- the default `airflow.image` is now `apache/airflow:2.2.5-python3.8` (see the [airflow version support matrix](https://github.com/airflow-helm/charts/tree/main/charts/airflow#airflow-version-support)) +- support helm templating in `extraManifests` by allowing string elements ([docs](docs/faq/kubernetes/extra-manifests.md)) ([#523](https://github.com/airflow-helm/charts/pull/523)) +- update default `dags.gitSync.image.tag` to `v3.5.0` ([#544](https://github.com/airflow-helm/charts/pull/544)) +- update default `pgbouncer.image.tag` to `1.17.0-patch.0` ([#552](https://github.com/airflow-helm/charts/pull/552)) +- update default `pgbouncer.maxClientConnections` to `1000` ([#543](https://github.com/airflow-helm/charts/pull/543)) + +### Added +- add "airflow triggerer" Deployment ([#555](https://github.com/airflow-helm/charts/pull/555)) +- add "log-cleanup sidecar" to scheduler and worker ([docs](docs/faq/monitoring/log-cleanup.md)) ([#554](https://github.com/airflow-helm/charts/pull/554)) +- add "task creation check" to scheduler liveness probe ([docs](docs/faq/monitoring/scheduler-liveness-probe.md#scheduler-task-creation-check)) ([#549](https://github.com/airflow-helm/charts/pull/549)) +- allow setting database passwords with values & setting database usernames from secrets ([docs](docs/faq/database/external-database.md#option-1---postgres)) ([#553](https://github.com/airflow-helm/charts/pull/553)) +- allow `airflow.users[].roles` to specify a list of roles ([docs](docs/faq/security/airflow-users.md)) ([#539](https://github.com/airflow-helm/charts/pull/539)) +- add `pgbouncer.authType` value ([docs](docs/faq/database/pgbouncer.md)) ([#498](https://github.com/airflow-helm/charts/pull/498)) +- add `ingressClassName` values to ingress ([docs](docs/faq/kubernetes/ingress.md)) ([#527](https://github.com/airflow-helm/charts/pull/527)) +- add `airflow.clusterDomain` value ([#441](https://github.com/airflow-helm/charts/pull/441)) +- add `labels` values for `sync` and `db-migrations` ([#467](https://github.com/airflow-helm/charts/pull/467)) +- add `airflow.kubernetesPodTemplate.extraContainers` value ([#456](https://github.com/airflow-helm/charts/pull/456)) +- add `airflow.kubernetesPodTemplate.extraInitContainers` value ([#446](https://github.com/airflow-helm/charts/pull/446)) +- add `airflow.kubernetesPodTemplate.shareProcessNamespace` value ([#408](https://github.com/airflow-helm/charts/pull/408)) +- add `airflow.kubernetesPodTemplate.podLabels` value ([#534](https://github.com/airflow-helm/charts/pull/534)) + +### Fixed +- fix `airflow.{fernetKey,webserverSecretKey}` overshadowing `_CMD` and `_SECRET` configs ([docs-1](docs/faq/security/set-fernet-key.md), [docs-2](docs/faq/security/set-webserver-secret-key.md)) ([#508](https://github.com/airflow-helm/charts/pull/508)) +- fix PG_ADVISORY_LOCK not being released when using pgbouncer ([#529](https://github.com/airflow-helm/charts/pull/529)) +- only set `CONNECTION_CHECK_MAX_COUNT` once ([#533](https://github.com/airflow-helm/charts/pull/533)) +- set `DUMB_INIT_SETSID=0` for celery workers (fix warm shutdown) ([#550](https://github.com/airflow-helm/charts/pull/550)) +- replace pgbouncer readinessProbe with startupProbe ([#547](https://github.com/airflow-helm/charts/pull/547)) +- allow ingress `servicePort` to be string or number ([#530](https://github.com/airflow-helm/charts/pull/530)) +- fix `pgbouncer.livenessProbe.enabled` not being respected ([#546](https://github.com/airflow-helm/charts/pull/546)) +- cast user values with toString before b64enc ([#557](https://github.com/airflow-helm/charts/pull/557)) + +## [8.5.3] - 2022-01-10 + +> 🟥 __WARNINGS__ 🟥 +> +> - Update to this version if you are using Kubernetes 1.20+ to prevent the scheduler's liveness probe causing a restart loop (see issue: [#484](https://github.com/airflow-helm/charts/issues/484)) +> - If you currently set `scheduler.livenessProbe.timeoutSeconds` or `pgbouncer.livenessProbe.timeoutSeconds` in your values, ensure you update them to the new default of `60` + +### Changed +- the default `airflow.image` is now `apache/airflow:2.1.4-python3.8` (see the [airflow version support matrix](https://github.com/airflow-helm/charts/tree/main/charts/airflow#airflow-version-support)) + +### Fixed +- increase default `timeoutSeconds` for liveness probes ([#496](https://github.com/airflow-helm/charts/pull/496)) +- typo in `GIT_SYNC_MAX_SYNC_FAILURES` environment variable name ([#462](https://github.com/airflow-helm/charts/pull/462)) + +## [8.5.2] - 2021-08-25 + +> 🟥 __WARNINGS__ 🟥 +> +> - You must stop URL-encoding special characters in `externalDatabase.user`, the chart will now automatically do this for you. For example, don't replace `@` with `%40` anymore. + +### Changed +- special characters in `externalDatabase.user` are now automatically url-encoded ([#407](https://github.com/airflow-helm/charts/pull/407)) + +### Fixed +- self-signed certificates are now only generated for `client_tls_key_file` and `client_tls_cert_file` PgBouncer configs ([#404](https://github.com/airflow-helm/charts/pull/404)) +- flower pods are now correctly affected by default: nodeSelector, affinity, tolerations ([#405](https://github.com/airflow-helm/charts/pull/405)) + +## [8.5.1] - 2021-08-23 +### Fixed +- fixed PgBouncer not working if `externalDatabase.database` or `postgresql.postgresqlDatabase` is not `"airflow"` ([#398](https://github.com/airflow-helm/charts/pull/398)) + +## [8.5.0] - 2021-08-19 + +> 🟥 __WARNINGS__ 🟥 +> +> - If using Kubernetes 1.18 or earlier, you MUST set `ingress.apiVersion` to `networking.k8s.io/v1beta1` +> - If using Kubernetes 1.22+, you MUST set `ingress.apiVersion` to `networking.k8s.io/v1` (this is default) +> - You must set a custom value for `airflow.webserverSecretKey` to ensure your airflow's security + +> 🟨 __NOTES__ 🟨 +> +> - This is an important upgrade for Postgres users, as it implements [PgBouncer](https://www.pgbouncer.org/) support, which should eliminate Postgres "too many connections" errors. +> - If you are using the `XXXX.securityContext` values, consider using the new global `airflow.defaultSecurityContext` value, so that you don't have to update your values in future. +> - If you are using the `XXXX.{nodeSelector,affinity,tolerations}` values, consider using the new global `airflow.{defaultNodeSelector,defaultAffinity,defaultTolerations}` values, so that you don't have to update your values in future. +> - To revert to using a post-install Job for `db-migrations`, set `airflow.dbMigrations.runAsJob` to `true` +> - The new default of `airflow.defaultSecurityContext = {fsGroup: 0}` should prevent filesystem permission errors in mounted volumes + +### Changed +- the default `airflow.image` is now `apache/airflow:2.1.2-python3.8` (see the [airflow version support matrix](https://github.com/airflow-helm/charts/tree/main/charts/airflow#airflow-version-support)) +- the default `airflow.image.gid` is now `0` ([#388](https://github.com/airflow-helm/charts/pull/388)) +- the Kubernetes Ingress now uses `networking.k8s.io/v1` for `apiVersion` by default ([#381](https://github.com/airflow-helm/charts/pull/381)) +- we now include git-sync containers in all Deployments ([#390](https://github.com/airflow-helm/charts/pull/390)) +- we now use the official `/entrypoint` of the airflow container ([#386](https://github.com/airflow-helm/charts/pull/386)) +- any `airflow.extraPipPackages` are now installed in snyc Jobs/Deployments ([#354](https://github.com/airflow-helm/charts/pull/354)) +- we now include `airflow.{config,extraEnv}` in the pip-install containers ([#365](https://github.com/airflow-helm/charts/pull/365)) +- we now include `airflow.{config,extraEnv}` in the git-sync containers ([#380](https://github.com/airflow-helm/charts/pull/380)) +- we now include `airflow.extraContainers` in the flower Deployment ([#379](https://github.com/airflow-helm/charts/pull/379)) +- the KubernetesExecutor pod-template now respects the `airflow.image.*` values ([#352](https://github.com/airflow-helm/charts/pull/352)) +- added values validation for `externalDatabase.type` ([#348](https://github.com/airflow-helm/charts/pull/348)) + +### Added +- PgBouncer is now supported (and enabled by default), see the new `pgbouncer.*` values ([#341](https://github.com/airflow-helm/charts/pull/341), [#330](https://github.com/airflow-helm/charts/pull/330)) +- created a new Deployment called `db-migrations` to manage airflow database schema upgrades ([#345](https://github.com/airflow-helm/charts/pull/345)) +- added the `airflow.webserverSecretKey` value with default `"THIS IS UNSAFE!"` ([#346](https://github.com/airflow-helm/charts/pull/346)) +- added the `airflow.defaultSecurityContext` value with default `{fsGroup: 0}` ([#367](https://github.com/airflow-helm/charts/pull/367)) +- added `airflow.{defaultNodeSelector,defaultAffinity,defaultTolerations}` values ([#372](https://github.com/airflow-helm/charts/pull/372)) +- added `airflow.localSettings.*` values to make specifying `airflow_local_settings.py` easier ([#374](https://github.com/airflow-helm/charts/pull/374)) + +### Fixed +- fixed the scheduler livenessProbe command ([#351](https://github.com/airflow-helm/charts/pull/351)) +- made the sync-users deployment close its db connection after each loop ([#320](https://github.com/airflow-helm/charts/pull/320)) +- stopped using `stringData` in Kubernetes Secrets ([#356](https://github.com/airflow-helm/charts/pull/356), [#391](https://github.com/airflow-helm/charts/pull/391)) +- fixed typos in sync/_helpers templates ([#366](https://github.com/airflow-helm/charts/pull/366), [#387](https://github.com/airflow-helm/charts/pull/387)) +- always include `airflow.env` last ([#385](https://github.com/airflow-helm/charts/pull/385)) + +### Removed +- removed the broken `flower.oauthDomains` value ([#383](https://github.com/airflow-helm/charts/pull/383)) + +### Docs +- significant rewrite of the post-install NOTES.txt ([#358](https://github.com/airflow-helm/charts/pull/358)) +- general cleanup of `values.yaml` docstrings ([#389](https://github.com/airflow-helm/charts/pull/389)) + +## [8.4.1] - 2021-07-12 +### Fixed +- remove Job dependency on `.Release.Revision` to prevent immutability errors ([#298](https://github.com/airflow-helm/charts/pull/298)) + - (important for tools like [argo-cd](https://github.com/argoproj/argo-cd/) which never run `helm install ...`, causing `.Release.Revision` to never be incremented) + +## [8.4.0] - 2021-07-09 + +> 🟥 __WARNINGS__ 🟥 +> +> - The meaning of `airflow.{usersUpdate,connectionsUpdate,poolsUpdate,variablesUpdate}` have changed: +> - If `true`, a Deployment will perpetually sync `airflow.{users,connections,pools,variables}`, reverting changes made in the airflow UI +> - If `false`, a single Job is created after each `helm upgrade ...` to sync `airflow.{users,connections,pools,variables}` once + +> 🟨 __NOTES__ 🟨 +> +> - You may now use Secrets and ConfigMaps to define your `airflow.{users,connections,pools,variables}`: +> - [How to manage airflow users?](docs/faq/security/airflow-users.md) +> - [How to manage airflow connections?](docs/faq/dags/airflow-connections.md) +> - [How to manage airflow variables?](docs/faq/dags/airflow-variables.md) +> - [How to manage airflow pools?](docs/faq/dags/airflow-pools.md) + +### Changed +- the default `airflow.image` is now `apache/airflow:2.1.1-python3.8` (see the [airflow version support matrix](https://github.com/airflow-helm/charts/tree/main/charts/airflow#airflow-version-support)) ([#286](https://github.com/airflow-helm/charts/issues/286)) +- the `Chart.yaml` now explicitly specifies `apiVersion=v2` (requiring helm 3) ([#278](https://github.com/airflow-helm/charts/issues/278)) +- the `requirements.yaml` file was removed in preference of the `v2` dependencies method (specifying in `Chart.yaml`) ([#278](https://github.com/airflow-helm/charts/issues/278)) +- git-sync containers are now deployed in webserver, regardless of `airflow.legacyCommands` ([#288](https://github.com/airflow-helm/charts/pull/288)) +- `wait-for-db-migrations` init-containers now work properly when `airflow.legacyCommands=true` ([#271](https://github.com/airflow-helm/charts/pull/271)) +- improve validation of `{logs,dags}.persistence.accessMode` ([#269](https://github.com/airflow-helm/charts/pull/269)) + +### Added +- allow referencing Secrets/ConfigMaps in `airflow.{users,connections,pools,variables}` ([#281](https://github.com/airflow-helm/charts/pull/281)) +- removed the need for `helmWait` value ([#266](https://github.com/airflow-helm/charts/pull/266)) + +### Fixed +- include volumeMounts in init-containers ([#255](https://github.com/airflow-helm/charts/pull/255)) +- add `release` to worker Service selector ([#267](https://github.com/airflow-helm/charts/pull/267)) +- mount `dags-data` with `readOnly=true` if `accessMode=ReadOnlyMany` ([#268](https://github.com/airflow-helm/charts/pull/268)) +- only validate `ingress.{web,flower}.path` if `ingress.enabled=true` ([#270](https://github.com/airflow-helm/charts/pull/270)) +- multiple Schedulers could run if `legacyCommands=true` (due to rollingUpdate) ([#272](https://github.com/airflow-helm/charts/pull/272)) + +## [8.3.2] - 2021-06-30 +### Docs +- added this changelog ([#231](https://github.com/airflow-helm/charts/issues/231)) +- add description to each section of the README ([#162](https://github.com/airflow-helm/charts/issues/162)) +- add airflow <--> chart version support matrix ([#137](https://github.com/airflow-helm/charts/issues/137)) +- improve the README formatting + +## [8.3.1] - 2021-06-29 +### Docs +- fix(example): hpa of gke example doesn't work ([#225](https://github.com/airflow-helm/charts/issues/225)) + +## [8.3.0] - 2021-06-23 +### Added +- Add support for GIT_SYNC_MAX_FAILURES ([#182](https://github.com/airflow-helm/charts/issues/182)) + - `dags.gitSync.maxFailures` + +## [8.2.0] - 2021-06-03 +### Added +- Add redis properties configuration for external redis ([#200](https://github.com/airflow-helm/charts/issues/200)) + - `externalRedis.properties` + +## [8.1.3] - 2021-05-21 +### Docs +- Typo in docs for `airflow.pools` in README.md ([#207](https://github.com/airflow-helm/charts/issues/207)) +- README implies that Helm 2 is supported, but its not ([#184](https://github.com/airflow-helm/charts/issues/184)) + +## [8.1.2] - 2021-05-21 +### Fixed +- run jobs with airflow serviceAccount ([#201](https://github.com/airflow-helm/charts/issues/201)) + +## [8.1.1] - 2021-05-21 +### Docs +- Remove references to `workers.celery.instances` (which was removed in 8.0.0) ([#202](https://github.com/airflow-helm/charts/issues/202)) + +## [8.1.0] - 2021-05-11 +### Added +- Add `airflow.kubernetesPodTemplate.resources` value ([#175](https://github.com/airflow-helm/charts/issues/175)) + +## [8.0.9] - 2021-04-27 +### Fixed +- make check-db timeout 60s ([#181](https://github.com/airflow-helm/charts/issues/181)) +- move to `pip install --user` ([#168](https://github.com/airflow-helm/charts/issues/168)) ([#169](https://github.com/airflow-helm/charts/issues/169)) + +## [8.0.8] - 2021-04-20 +### Fixed +- don't include git-sync containers in webserver for airflow 2.0 ([#152](https://github.com/airflow-helm/charts/issues/152)) +- ensure dags git repo is cloned before containers start ([#124](https://github.com/airflow-helm/charts/issues/124)) +- introduce timeout for check-db init-container ([#153](https://github.com/airflow-helm/charts/issues/153)) +- only include git-sync init-container in pod_template if enabled ([#158](https://github.com/airflow-helm/charts/issues/158)) + +### Docs +- add docs for `externalDatabase.properties` in README + +## [8.0.7] - 2021-04-16 +### Fixed +- only include `checksum/config-pod-template` annotation for kubernetes_like executors ([#150](https://github.com/airflow-helm/charts/issues/150)) +- give more information in value validation errors ([#150](https://github.com/airflow-helm/charts/issues/150)) +- prevent embedded postgres/redis being enabled at same time as external ([#150](https://github.com/airflow-helm/charts/issues/150)) +- use _helper variable in pod_template envFrom ([#150](https://github.com/airflow-helm/charts/issues/150)) +- include `airflow.podAnnotations` in jobs ([#140](https://github.com/airflow-helm/charts/issues/140)) +- add int64 to validation, so int variables set in bash work ([#136](https://github.com/airflow-helm/charts/issues/136)) +- add missing pod labels to upgrade-db job ([#150](https://github.com/airflow-helm/charts/issues/150)) +- fix validation for wildcard ingress paths ([#144](https://github.com/airflow-helm/charts/issues/144)) +- fix incorrect variable usage for variablesUpdate ([#139](https://github.com/airflow-helm/charts/issues/139)) +- add validation for airflow version compatibility with `airflow.legacyCommands` state ([#150](https://github.com/airflow-helm/charts/issues/150)) +- make `ingress.web/flower.tls.secretName` optional ([#41](https://github.com/airflow-helm/charts/issues/41)) +- fix support for passwords with bash special characters ([#147](https://github.com/airflow-helm/charts/issues/147)) + +### Docs +- fix dockerfile code blocks in README ([#150](https://github.com/airflow-helm/charts/issues/150)) +- fix typo in connections example ([#148](https://github.com/airflow-helm/charts/issues/148)) +- add docs for using non-default airflow versions ([#150](https://github.com/airflow-helm/charts/issues/150)) + +## [8.0.6] - 2021-04-10 +### Fixed +- fix volume definition for logs-data with existing claim ([#128](https://github.com/airflow-helm/charts/issues/128)) + +## [8.0.5] - 2021-04-06 +### Fixed +- extract probe path from AIRFLOW__WEBSERVER__BASE_URL + ingress path validation ([#120](https://github.com/airflow-helm/charts/issues/120)) + +## [8.0.4] - 2021-04-05 +### Fixed +- add "Release" to template context dict ([#121](https://github.com/airflow-helm/charts/issues/121)) + +## [8.0.3] - 2021-04-05 +### Fixed +- fix wrong value for envFrom in pod_template ([#122](https://github.com/airflow-helm/charts/issues/122)) + +## [8.0.2] - 2021-03-28 +### Fixed +- properly fixes the following issues (which were not properly fixed in `8.0.1`): + - `extraVolumeMounts` and `extraVolumes` parsing error ([#98](https://github.com/airflow-helm/charts/issues/98)) + - Flower deployment fails with `airflow.extraVolumeMounts` set ([#101](https://github.com/airflow-helm/charts/issues/101)) +- fixes some bad wording on the `airflow.config.AIRFLOW__CORE__DAGS_FOLDER` value validation ([#108](https://github.com/airflow-helm/charts/issues/108)) +- addresses an issue with our PYTHONPATH when using `*.extraPipPackages`, which was overriding anything that the user set with `airflow.extraEnv` ([#106](https://github.com/airflow-helm/charts/issues/106)) +- fixes the PYTHONPATH not being set when using `airflow.kubernetesPodTemplate.extraPipPackages` with `pod_template.yaml` ([#108](https://github.com/airflow-helm/charts/issues/108)) + +## [8.0.1] - 2021-03-27 + +> 🟥 __WARNINGS__ 🟥 +> +> - Ensure any previous `upgrade-db` Jobs are manually removed from your Kubernetes before installing with `helmWait=true` (see issue: [#99](https://github.com/airflow-helm/charts/issues/99)) + +### Added +- Added new value `helmWait`, which should be enabled when the `--wait` flag is used with `helm install` ([#102](https://github.com/airflow-helm/charts/issues/102)) + +### Fixed +- Flower deployment fails with `airflow.extraVolumeMounts` set ([#101](https://github.com/airflow-helm/charts/issues/101)) +- `aiflow.extraVolumeMounts` and `airflow.extraVolumes` parsing error ([#98](https://github.com/airflow-helm/charts/issues/98)) +- Validation helper incorrectly requires `workers.enabled=true`([#97](https://github.com/airflow-helm/charts/issues/97)) + +## [8.0.0] - 2021-03-27 + +> 🟥 __WARNINGS__ 🟥 +> +> - This is a MAJOR update, meaning there are BREAKING changes + +> 🟨 __NOTES__ 🟨 +> +> Upgrading Tips: +> - to continue using Airflow `1.10.X`, please set `airflow.legacyCommands=true` +> - you might want to start from a fresh `values.yaml` file +> - if you decide to also upgrade to airflow `2.X.X` check your [dags are compatible](https://airflow.apache.org/docs/apache-airflow/stable/upgrading-to-2.html#step-5-upgrade-airflow-dags) + +### Added +#### Feature Highlights: +- native support for "KubernetesExecutor", and "CeleryKubernetesExecutor", see the new `airflow.kubernetesPodTemplate.*` values +- native support for "webserver_config.py", see the new `web.webserverConfig.*` values +- native support for [Airflow 2.0's HA scheduler](https://airflow.apache.org/docs/apache-airflow/stable/scheduler.html#running-more-than-one-scheduler), see the new `scheduler.replicas` value +- significantly improved git-sync system by moving to [kubernetes/git-sync](https://github.com/kubernetes/git-sync) +- significantly improved pip installs by moving to an init-container +- added docs for [How to integrate airflow with LDAP or OAUTH?](docs/faq/security/ldap-oauth.md) +- general cleanup of almost every helm file +- significant docs/README rewrite + +#### Other Features: +- added `airflow.users` to help you create/update airflow web users: + - __WARNING:__ default settings create an admin user (user: __admin__ - password: __admin__), disable by setting `airflow.users` to `[]` +- added `airflow.connections` to help you create/update airflow connections: +- added `airflow.variables` to help you create/update airflow variables: +- added `airflow.pools` to help you create/update airflow pools: +- flower Pods are now affected by `airflow.extraPipPackages`, `airflow.extraVolumeMounts`, `airlfow.extraVolumes` +- you no longer need to set `web.readinessProbe.scheme` or `web.livenessProbe.scheme`, we now only use HTTPS if `AIRFLOW__WEBSERVER__WEB_SERVER_SSL_CERT` and `AIRFLOW__WEBSERVER__WEB_SERVER_SSL_KEY` are set +- airflow db upgrades are now managed with a post "helm upgrade" Job, meaning it only runs once per upgrade (rather than each time the scheduler starts) + +#### VALUES - Added: +
+Expand + +- `airflow.legacyCommands` +- `airflow.image.uid` +- `airflow.image.gid` +- `airflow.users` +- `airflow.usersUpdate` +- `airflow.connections` +- `airflow.connectionsUpdate` +- `airflow.variables` +- `airflow.variablesUpdate` +- `airflow.pools` +- `airflow.poolsUpdate` +- `airflow.kubernetesPodTemplate.stringOverride` +- `airflow.kubernetesPodTemplate.nodeSelector` +- `airflow.kubernetesPodTemplate.affinity` +- `airflow.kubernetesPodTemplate.tolerations` +- `airflow.kubernetesPodTemplate.podAnnotations` +- `airflow.kubernetesPodTemplate.securityContext` +- `airflow.kubernetesPodTemplate.extraPipPackages` +- `airflow.kubernetesPodTemplate.extraVolumeMounts` +- `airflow.kubernetesPodTemplate.extraVolumes` +- `scheduler.replicas` +- `scheduler.livenessProbe.timeoutSeconds` +- `scheduler.extraPipPackages` +- `scheduler.extraVolumeMounts` +- `scheduler.extraVolumes` +- `web.webserverConfig.stringOverride` +- `web.webserverConfig.existingSecret` +- `web.extraVolumeMounts` +- `web.extraVolumes` +- `workers.extraPipPackages` +- `workers.extraVolumeMounts` +- `workers.extraVolumes` +- `flower.readinessProbe.enabled` +- `flower.readinessProbe.initialDelaySeconds` +- `flower.readinessProbe.periodSeconds` +- `flower.readinessProbe.timeoutSeconds` +- `flower.readinessProbe.failureThreshold` +- `flower.livenessProbe.enabled` +- `flower.livenessProbe.initialDelaySeconds` +- `flower.livenessProbe.periodSeconds` +- `flower.livenessProbe.timeoutSeconds` +- `flower.livenessProbe.failureThreshold` +- `flower.extraPipPackages` +- `flower.extraVolumeMounts` +- `flower.extraVolumes` +- `dags.gitSync.enabled` +- `dags.gitSync.image.repository` +- `dags.gitSync.image.tag` +- `dags.gitSync.image.pullPolicy` +- `dags.gitSync.image.uid` +- `dags.gitSync.image.gid` +- `dags.gitSync.resources` +- `dags.gitSync.repo` +- `dags.gitSync.repoSubPath` +- `dags.gitSync.branch` +- `dags.gitSync.revision` +- `dags.gitSync.depth` +- `dags.gitSync.syncWait` +- `dags.gitSync.syncTimeout` +- `dags.gitSync.httpSecret` +- `dags.gitSync.httpSecretUsernameKey` +- `dags.gitSync.httpSecretPasswordKey` +- `dags.gitSync.sshSecret` +- `dags.gitSync.sshSecretKey` +- `dags.gitSync.sshKnownHosts` + +
+ +### Changed +- the name of the `dags.persistence` PVC has changed from `HELM_RELEASE` to `HELM_RELEASE-dags`: + - __WARNING:__ you must manually migrate your dags to the new PVC if you had `dags.persistence.enabled = true` (but were not explicitly setting `dags.persistence.existingClaim`) + - __WARNING:__ be sure to download your dags from the `HELM_RELEASE` volume __BEFORE__ doing the upgrade (as helm may delete the old PVC, during the upgrade) + +#### VALUES - Changed Defaults: +
+Expand + +- `rbac.events` = `true` +- `scheduler.livenessProbe.initialDelaySeconds` = `10` +- `web.readinessProbe.enabled` = `true` +- `web.readinessProbe.timeoutSeconds` = `5` +- `web.livenessProbe.periodSeconds` = `10` +- `web.readinessProbe.failureThreshold` = `6` +- `web.livenessProbe.initialDelaySeconds` = `10` +- `web.livenessProbe.timeoutSeconds` = `5` +- `web.livenessProbe.failureThreshold` = `6` +- `scheduler.podDisruptionBudget.enabled` = `false` + +
+ +### Removed +- the `XXX.extraConfigmapMounts`, `XXX.secretsDir`, `XXX.secrets`, `XXX.secretsMap` values have been removed, and replaced with `XXX.extraVolumes` and `XXX.extraVolumeMounts`, which use typical Kubernetes volume-mount syntax +- the `dags.installRequirements` value has been removed, please instead use the `XXX.extraPipPackages` values, this change was made for two main reasons: + 1. allowed us to move the pip-install commands into an init-container, which greatly simplifies pod-startup, and removes the need to set any kind of readiness-probe delay in Webserver/Flower Pods + 2. the installRequirements command only ran at Pod start up, meaning you would have to restart all your pods if you updated the `requirements.txt` in your git repo (which isn't very declarative) + +#### VALUES - Removed: +
+Expand + +- `airflow.extraConfigmapMounts` +- `scheduler.initialStartupDelay` +- `scheduler.preinitdb` +- `scheduler.initdb` +- `scheduler.connections` +- `scheduler.refreshConnections` +- `scheduler.existingSecretConnections` +- `scheduler.pools` +- `scheduler.variables` +- `scheduler.secretsDir` +- `scheduler.secrets` +- `scheduler.secretsMap` +- `web.initialStartupDelay` +- `web.minReadySeconds` +- `web.baseUrl` +- `web.serializeDAGs` +- `web.readinessProbe.scheme` +- `web.readinessProbe.successThreshold` +- `web.livenessProbe.scheme` +- `web.livenessProbe.successThreshold` +- `web.secretsDir` +- `web.secrets` +- `web.secretsMap` +- `workers.celery.instances` +- `workers.initialStartupDelay` +- `workers.secretsDir` +- `workers.secrets` +- `workers.secretsMap` +- `flower.initialStartupDelay` +- `flower.minReadySeconds` +- `flower.extraConfigmapMounts` +- `flower.urlPrefix` +- `flower.secretsDir` +- `flower.secrets` +- `flower.secretsMap` +- `dags.doNotPickle` +- `dags.installRequirements` +- `dags.git.url` +- `dags.git.ref` +- `dags.git.secret` +- `dags.git.sshKeyscan` +- `dags.git.privateKeyName` +- `dags.git.repoHost` +- `dags.git.repoPort` +- `dags.git.gitSync.enabled` +- `dags.git.gitSync.resources` +- `dags.git.gitSync.image` +- `dags.git.gitSync.refreshTime` +- `dags.git.gitSync.mountPath` +- `dags.git.gitSync.syncSubPath` +- `dags.initContainer.enabled` +- `dags.initContainer.resources` +- `dags.initContainer.image.repository` +- `dags.initContainer.image.tag` +- `dags.initContainer.image.pullPolicy` +- `dags.initContainer.mountPath` +- `dags.initContainer.syncSubPath` +- `ingress.web.livenessPath` +- `ingress.flower.livenessPath` + +
+ +## [7.16.0] - 2020-12-23 +### Added +- scheduler kubernetes secrets ([#48](https://github.com/airflow-helm/charts/issues/48)) + - `scheduler.secretsDir` + - `scheduler.secrets` + - `scheduler.secretsMap` + +## [7.15.0] - 2020-12-15 +### Changed +- We now use `airflow upgradedb || airflow db upgrade` instead of `airflow initdb` with the following values ([#39](https://github.com/airflow-helm/charts/issues/39)) + - `scheduler.initdb` + - `scheduler.preinitdb` +- Changed image `pullPolicy` values defaults ([#39](https://github.com/airflow-helm/charts/issues/39)) + - `dags.git.gitSync.image.pullPolicy = IfNotPresent` + - `dags.initContainer.image.pullPolicy = IfNotPresent` + +### Docs +- Update docs for Dag Storage option 1 ([#33](https://github.com/airflow-helm/charts/issues/33)) + +## [7.14.3] - 2020-11-24 +### Fixed +- fix quoting of "$" in connections ([#18](https://github.com/airflow-helm/charts/issues/18)) + +## [7.14.2] - 2020-11-24 +### Docs +- improve README ([#17](https://github.com/airflow-helm/charts/issues/17)) + +## [7.14.1] - 2020-11-24 +### Fixed +- Allow local development with Skaffold ([#7](https://github.com/airflow-helm/charts/issues/7)) + +## [7.14.0] - 2020-11-05 + +> 🟨 __NOTES__ 🟨 +> +> - This is the first version after migrating to the [new repo](https://github.com/airflow-helm/charts/tree/main/charts/airflow) +> - All versions before `7.14.0` are ONLY available in the [legacy repo](https://github.com/helm/charts/tree/master/stable/airflow) +> - There were NO changes from `7.13.2` in this version + +## 7.13.0 - 20XX-XX-XX +### Added +- `flower.oauthDomains` + +## 7.12.0 - 20XX-XX-XX +### Added +- `ingress.web.labels` +- `ingress.flower.labels` +- `ingress.flower.precedingPaths` +- `ingress.flower.succeedingPaths` + +## 7.11.0 - 20XX-XX-XX +### Added +- You can now use an externally created Secret to store airflow connections. (Rather than storing them in plain-text with `scheduler.connections`) + - `scheduler.existingSecretConnections` + +## 7.10.0 - 20XX-XX-XX +### Changed +- We now make use of the _CMD variables for `AIRFLOW__CORE__SQL_ALCHEMY_CONN_CMD`, `AIRFLOW__CELERY__RESULT_BACKEND_CMD`, and `AIRFLOW__CELERY__BROKER_URL_CMD`: + - This fixes the Scheduler liveness probe implemented in `7.8.0` + - This allows using `kubectl exec` to run commands like `airflow create_user` +- Configs passed with `airflow.config` are now passed as-defined in your values.yaml + - This fixes an issue where people had to escape `"` characters in JSON strings + +## 7.9.0 - 20XX-XX-XX +### Changed +- You can now give the airflow ServiceAccount GET/LIST on Event resources + - This is needed for `KubernetesPodOperator(log_events_on_failure=True)` + - To enable, set `rbac.events = true` (Default: `false`) + +## 7.8.0 - 20XX-XX-XX +### Added +- The scheduler now has a liveness probe which will force the pod to restart if it becomes unhealthy for more than some threshold of time (default: 150sec) + - __WARNING:__ this is on by default, but can be disabled with: `scheduler.livenessProbe.enabled` + - `scheduler.livenessProbe.enabled` + - `scheduler.livenessProbe.initialDelaySeconds` + - `scheduler.livenessProbe.periodSeconds` + - `scheduler.livenessProbe.failureThreshold` + +### Changed +- Upgraded default airflow image to `1.10.12` + +## 7.7.0 - 20XX-XX-XX +### Fixed +- `redis.existingSecretKey` in `values.yaml` was corrected to `redis.existingSecretPasswordKey` (to align with [stable/redis](https://github.com/helm/charts/tree/master/stable/redis)) + +## 7.6.0 - 20XX-XX-XX + +> 🟨 __NOTES__ 🟨 +> +> - We now annotate all pods with `cluster-autoscaler.kubernetes.io/safe-to-evict` by default (disable using the `*.safeToEvict` values) +> - GKE's cluster-autoscaler will not honor a `gracefulTerminationPeriod` of more than 10min, if your jobs need more than this amount of time to finish, please set `workers.safeToEvict = false` + +### Added +- You can now configure `safe-to-evict` annotations (so that pods with emptyDir Volumes can be evicted by cluster-autoscaler) + - `flower.safeToEvict` + - `scheduler.safeToEvict` + - `web.safeToEvict` + - `workers.safeToEvict` +- You can now create PodDisruptionBudgets for all components: {flower, webserver, worker} + - `flower.podDisruptionBudget.*` + - `web.podDisruptionBudget.*` + - `workers.podDisruptionBudget.*` +- You can now run multiple instances of flower + - `flower.replicas` +- You can now specify minReadySeconds for flower + - `flower.minReadySeconds` + +### Changed +- The chart YAML has been refactored +- Default values of embedded charts (postgres, redis) have been set with `safe-to-evit` annotations: + - `postgresql.master.podAnnotations = {"cluster-autoscaler.kubernetes.io/safe-to-evict": "true"}` + - `redis.master.podAnnotations = {"cluster-autoscaler.kubernetes.io/safe-to-evict": "true"}` + - `redis.slave.podAnnotations = {"cluster-autoscaler.kubernetes.io/safe-to-evict": "true"}` +- The chart now forces the correct INTERNAL ports to be used (NOTE: this will not prevent you changing Service/Ingress ports) + +## 7.5.0 - 20XX-XX-XX +### Added +- Added an ability to setup external database connection properties for TLS or other advanced parameters + - `externalDatabase.properties` + +## 7.4.0 - 20XX-XX-XX +### Added +- `workers.celery.gracefullTerminationPeriod` + - __WARNING:__ if you currently use a high value of `workers.terminationPeriod`, consider lowering it to 60 and setting a high value for `workers.celery.gracefullTerminationPeriod` + +### Changed +- Reduced how likely it is for a celery worker to receive SIGKILL with graceful termination enabled. New celery worker graceful shutdown lifecycle: + 1. prevent worker accepting new tasks + 1. wait AT MOST `workers.celery.gracefullTerminationPeriod` for tasks to finish + 1. send SIGTERM to worker + 1. wait AT MOST `workers.terminationPeriod` for kill to finish + 1. send SIGKILL to worker + +## 7.3.0 - 20XX-XX-XX +### Added +- Added an ability to specify a specific port for Flower when using NodePort service type + - `flower.service.nodePort.http` + +## 7.2.0 - 20XX-XX-XX +### Added +- Fixed Flower's liveness probe when Basic Authentication is enabled for Flower. You can specify a basic auth value via a Kubernetes Secret using the values `flower.basicAuthSecret` and `flower.basicAuthSecretKey`. The secret value will get encoded and included in the liveness probe's header. + +## 7.1.0 - 20XX-XX-XX +### Added +#### Feature Highlights: +- We have dramatically reduced the start time of airflow pods. +- This was mostly achieved by removing arbitrary delays in the start commands for airflow pods. +- If you still want these delays, please set the added `*.initialStartupDelay` to non-zero values. +- We have improved support for when `airflow.executor` is set to `KubernetesExecutor`: + - redis configs/components are no longer deployed + - we now set `AIRFLOW__KUBERNETES__NAMESPACE`, `AIRFLOW__KUBERNETES__WORKER_SERVICE_ACCOUNT_NAME`, and `AIRFLOW__KUBERNETES__ENV_FROM_CONFIGMAP_REF` +- We have fixed an error caused by including a `'` in your redis/postgres/mysql password. +- We have reverted a change in 7.0.0 which prevented the use of airflow docker images with embedded DAGs. + - (Just ensure that `dags.initContainer.enabled` and `git.gitSync.enabled` are `false`) +- The `AIRFLOW__CORE__SQL_ALCHEMY_CONN`, `AIRFLOW__CELERY__RESULT_BACKEND`, and `AIRFLOW__CELERY__BROKER_URL` environment variables are now available if you `kubectl exec ...` into airflow Pods. +- We have improved the script used when `workers.celery.gracefullTermination` is `true`. +- We have fixed an error with pools in `scheduler.pools` not being added to the scheduler. +- We have fixed an error with the `scheduler.preinitdb` container not knowing the database connection string. + +#### New Values: +- `scheduler.initialStartupDelay` +- `workers.initialStartupDelay` +- `flower.initialStartupDelay` +- `web.readinessProbe.enabled` +- `web.livenessProbe.enabled` + +### Changed +- `airflow.fernetKey` + - ~~Is now `""` by default, to enforce that users generate a custom one.~~ + ~~(However, please consider using `airflow.extraEnv` to define it from a pre-created secret)~~ + __(We have undone this change in `7.1.1`, but we still encourage you to set a custom fernetKey!)__ +- `dags.installRequirements` + - Is now `false` by default, as this was an unintended change with the 7.0.0 upgrade. + +## 7.0.0 - 20XX-XX-XX + +> 🟨 __NOTES__ 🟨 +> +> - To read about versions `7.0.0` and before, please see the [legacy repo](https://github.com/helm/charts/tree/master/stable/airflow). + +[Unreleased]: https://github.com/airflow-helm/charts/compare/airflow-8.8.0...HEAD +[8.8.0]: https://github.com/airflow-helm/charts/compare/airflow-8.7.1...airflow-8.8.0 +[8.7.1]: https://github.com/airflow-helm/charts/compare/airflow-8.7.0...airflow-8.7.1 +[8.7.0]: https://github.com/airflow-helm/charts/compare/airflow-8.6.1...airflow-8.7.0 +[8.6.1]: https://github.com/airflow-helm/charts/compare/airflow-8.6.0...airflow-8.6.1 +[8.6.0]: https://github.com/airflow-helm/charts/compare/airflow-8.5.3...airflow-8.6.0 +[8.5.3]: https://github.com/airflow-helm/charts/compare/airflow-8.5.2...airflow-8.5.3 +[8.5.2]: https://github.com/airflow-helm/charts/compare/airflow-8.5.1...airflow-8.5.2 +[8.5.1]: https://github.com/airflow-helm/charts/compare/airflow-8.5.0...airflow-8.5.1 +[8.5.0]: https://github.com/airflow-helm/charts/compare/airflow-8.4.1...airflow-8.5.0 +[8.4.1]: https://github.com/airflow-helm/charts/compare/airflow-8.4.0...airflow-8.4.1 +[8.4.0]: https://github.com/airflow-helm/charts/compare/airflow-8.3.2...airflow-8.4.0 +[8.3.2]: https://github.com/airflow-helm/charts/compare/airflow-8.3.1...airflow-8.3.2 +[8.3.1]: https://github.com/airflow-helm/charts/compare/airflow-8.3.0...airflow-8.3.1 +[8.3.0]: https://github.com/airflow-helm/charts/compare/airflow-8.2.0...airflow-8.3.0 +[8.2.0]: https://github.com/airflow-helm/charts/compare/airflow-8.1.3...airflow-8.2.0 +[8.1.3]: https://github.com/airflow-helm/charts/compare/airflow-8.1.2...airflow-8.1.3 +[8.1.2]: https://github.com/airflow-helm/charts/compare/airflow-8.1.1...airflow-8.1.2 +[8.1.1]: https://github.com/airflow-helm/charts/compare/airflow-8.1.0...airflow-8.1.1 +[8.1.0]: https://github.com/airflow-helm/charts/compare/airflow-8.0.9...airflow-8.1.0 +[8.0.9]: https://github.com/airflow-helm/charts/compare/airflow-8.0.8...airflow-8.0.9 +[8.0.8]: https://github.com/airflow-helm/charts/compare/airflow-8.0.7...airflow-8.0.8 +[8.0.7]: https://github.com/airflow-helm/charts/compare/airflow-8.0.6...airflow-8.0.7 +[8.0.6]: https://github.com/airflow-helm/charts/compare/airflow-8.0.5...airflow-8.0.6 +[8.0.5]: https://github.com/airflow-helm/charts/compare/airflow-8.0.4...airflow-8.0.5 +[8.0.4]: https://github.com/airflow-helm/charts/compare/airflow-8.0.3...airflow-8.0.4 +[8.0.3]: https://github.com/airflow-helm/charts/compare/airflow-8.0.2...airflow-8.0.3 +[8.0.2]: https://github.com/airflow-helm/charts/compare/airflow-8.0.1...airflow-8.0.2 +[8.0.1]: https://github.com/airflow-helm/charts/compare/airflow-8.0.0...airflow-8.0.1 +[8.0.0]: https://github.com/airflow-helm/charts/compare/airflow-7.16.0...airflow-8.0.0 +[7.16.0]: https://github.com/airflow-helm/charts/compare/airflow-7.15.0...airflow-7.16.0 +[7.15.0]: https://github.com/airflow-helm/charts/compare/airflow-7.14.3...airflow-7.15.0 +[7.14.3]: https://github.com/airflow-helm/charts/compare/airflow-7.14.2...airflow-7.14.3 +[7.14.2]: https://github.com/airflow-helm/charts/compare/airflow-7.14.1...airflow-7.14.2 +[7.14.1]: https://github.com/airflow-helm/charts/compare/airflow-7.14.0...airflow-7.14.1 +[7.14.0]: https://github.com/airflow-helm/charts/compare/airflow-7.14.0...airflow-7.14.0 \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/CONTRIBUTING.md b/charts/deps/charts/airflow-8.8.0/CONTRIBUTING.md new file mode 100644 index 0000000..d91514e --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/CONTRIBUTING.md @@ -0,0 +1,90 @@ +# Contributing Guidelines + +Contributions are welcome via GitHub pull requests. + +## Sign Your Work + +To certify you agree to the [Developer Certificate of Origin](https://developercertificate.org/) you must sign-off each commit message using `git commit --signoff`, or manually write the following: +```text +This is my commit message + +Signed-off-by: John Smith +``` + +The text of the agreement is: +```text +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +1 Letterman Drive +Suite D4700 +San Francisco, CA, 94129 + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. +``` + +## Semantic Commit Messages + +All commit messages and PR names must pass the [zeke/semantic-pull-requests](https://github.com/zeke/semantic-pull-requests) check. + +Here are some example semantic commit messages: +- `feat: a new feature` +- `fix: a bug fix` +- `docs: documentation only change` +- `style: fix formatting/white-space/etc` +- `refactor: code change that neither fixes a bug nor adds a feature` +- `test: add or update tests` +- `ci: changes to CI configs` +- `chore: other changes that dont modify code` +- `revert: revert a commit` + +## Documentation + +Most non-patch changes will require documentation updates. + +If you __ADD a value__: +- ensure the value has a descriptive docstring in `values.yaml` +- ensure the value is listed under `Helm Values` in [README.md](README.md#helm-values) + - Note, only directly include the value if it's a top-level value like `airflow.level_1`, otherwise only include `airflow.level_1.*` + +If you __bump the version__: +- add a heading for the new version to [CHANGELOG.md](CHANGELOG.md) (and comparison link, at bottom of file) + +## Linting + +Please ensure `ct lint` from [chart-testing](https://github.com/helm/chart-testing) succeeds. + +## Versioning + +The chart `version` should follow [SemVer](https://semver.org/): +- If you __REMOVE/CHANGE a value__ → bump a MAJOR version +- If you __ADD a value__ → bump a MINOR version +- If you __fix a bug__ → bump a PATCH version diff --git a/charts/deps/charts/airflow-8.8.0/Chart.lock b/charts/deps/charts/airflow-8.8.0/Chart.lock new file mode 100644 index 0000000..5dde93b --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/Chart.lock @@ -0,0 +1,9 @@ +dependencies: +- name: postgresql + repository: https://charts.helm.sh/stable + version: 8.6.4 +- name: redis + repository: https://charts.helm.sh/stable + version: 10.5.7 +digest: sha256:bfe30fcaf72a0609856bf1a5dab2b941e8ef89b30ba935aa820743a6758f4fc4 +generated: "2021-07-09T14:11:48.265069+10:00" diff --git a/charts/deps/charts/airflow-8.8.0/Chart.yaml b/charts/deps/charts/airflow-8.8.0/Chart.yaml new file mode 100644 index 0000000..83a4a2b --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/Chart.yaml @@ -0,0 +1,26 @@ +apiVersion: v2 +appVersion: 2.6.3 +dependencies: +- condition: postgresql.enabled + name: postgresql + repository: https://charts.helm.sh/stable + version: 8.6.4 +- condition: redis.enabled + name: redis + repository: https://charts.helm.sh/stable + version: 10.5.7 +description: Airflow Helm Chart (User Community) - the standard way to deploy Apache + Airflow on Kubernetes with Helm +home: https://github.com/airflow-helm/charts/tree/main/charts/airflow +icon: https://avatars.githubusercontent.com/u/71061241 +keywords: +- airflow +- dag +- workflow +maintainers: +- name: thesuperzapper + url: https://github.com/thesuperzapper +name: airflow +sources: +- https://github.com/airflow-helm/charts/tree/main/charts/airflow +version: 8.8.0 diff --git a/charts/deps/charts/airflow-8.8.0/README.md b/charts/deps/charts/airflow-8.8.0/README.md new file mode 100644 index 0000000..9b99229 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/README.md @@ -0,0 +1,553 @@ +# Airflow Helm Chart (User Community) + +
+ +The `User-Community Airflow Helm Chart` is the standard way to deploy [Apache Airflow](https://airflow.apache.org/) on [Kubernetes](https://kubernetes.io/) with [Helm](https://helm.sh/). +
+Originally created in 2017, it has since helped thousands of companies create production-ready deployments of Airflow on Kubernetes. + +
+ +

+ + Downloads + + + Contributors + + + License + + + Latest Release + + + ArtifactHub + +

+ +

+ + GitHub Stars + + + ArtifactHub Stars + +

+ +

+ + GitHub Discussions + + + GitHub Issues + +

+ +
+ +## History + +The `User-Community Airflow Helm Chart` chart has a long history of being the standard way to deploy Apache Airflow on Kubernetes. + +Here is a brief overview of the chart's development from 2017 until today: + +- From October 2017 until December 2018, the chart was called `kube-airflow` and was developed in [`gsemet/kube-airflow`](https://github.com/gsemet/kube-airflow) +- From December 2018 until November 2020, the chart was called `stable/airflow` and was developed in [`helm/charts`](https://github.com/helm/charts/tree/master/stable/airflow) +- Since November 2020, the chart has been called `Airflow Helm Chart (User Community)` and is developed in `airflow-helm/charts` + +Please note, this chart is __independent__ from the official chart in the `apache/airflow` repo, which was forked from Astronomer's proprietary chart in May 2021. + +## Project Goals + +1. Ease of Use +2. Great Documentation +3. Support for older Airflow Versions +4. Support for Kubernetes GitOps Tools (like ArgoCD) + +## Key Features + +- __Support for Airflow Versions:__ + - [`1.10` | `2.0` | `2.1` | `2.2` | `2.3` | `2.4` | `2.5` | `2.6` | `2.7`](#airflow-version-support) +- __Support for Airflow Executors:__ + - [`CeleryExecutor` | `KubernetesExecutor` | `CeleryKubernetesExecutor`](#airflow-executor-support) +- __Easily Connect with your Database:__ + - [`Connect to Postgres`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/database/external-database.md#option-1---postgres) | + [`Configure PgBouncer`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/database/pgbouncer.md) | + [`Connect to MySQL`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/database/external-database.md#option-2---mysql) +- __Declaratively Manage Airflow Configs:__ + - [`Users`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/security/airflow-users.md) | + [`Connections`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/dags/airflow-connections.md) | + [`Variables`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/dags/airflow-variables.md) | + [`Pools`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/dags/airflow-pools.md) +- __Load Airflow DAGs:__ + - [`Load from Git-Sync`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/dags/load-dag-definitions.md#option-1---git-sync-sidecar) | + [`Load from Volume`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/dags/load-dag-definitions.md#option-2---persistent-volume-claim) | + [`Embed Into Image`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/dags/load-dag-definitions.md#option-3---embedded-into-container-image) +- __Manage Airflow Logs:__ + - [`Persist on Volume`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/monitoring/log-persistence.md#option-1---persistent-volume-claim) | + [`Persist on Remote Provider`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/monitoring/log-persistence.md#option-2---remote-providers) | + [`Automatic Log Cleanup`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/monitoring/log-cleanup.md) +- __Install Extra Python Packages:__ + - [`Install with Init-Containers`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/configuration/extra-python-packages.md#option-1---init-containers) | + [`Embed Into Image`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/configuration/extra-python-packages.md#option-2---embedded-into-container-image) +- __Automatically Restart Unhealthy Airflow Schedulers:__ + - [`Heartbeat Check`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/monitoring/scheduler-liveness-probe.md#scheduler-heartbeat-check) | + [`Task Creation Check`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/monitoring/scheduler-liveness-probe.md#scheduler-task-creation-check) + +
+ +## Guides + +#### [`Quickstart Guide`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/guides/quickstart.md) + +#### [`Upgrade Guide`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/guides/upgrade.md) + +#### [`Uninstall Guide`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/guides/uninstall.md) + +## Frequently Asked Questions + +- __Configuration:__ + - [`Set Airflow Version`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/configuration/airflow-version.md) + - [`Manage Airflow Configs`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/configuration/airflow-configs.md) + - [`Manage Airflow Plugins`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/configuration/airflow-plugins.md) + - [`Install Extra Python/Pip Packages`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/configuration/extra-python-packages.md) + - [`Configure Celery Worker Autoscaling`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/configuration/autoscaling-celery-workers.md) +- __DAGs:__ + - [`Load Airflow DAGs`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/dags/load-dag-definitions.md) + - [`Manage Airflow Connections`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/dags/airflow-connections.md) + - [`Manage Airflow Variables`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/dags/airflow-variables.md) + - [`Manage Airflow Pools`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/dags/airflow-pools.md) +- __Security:__ + - [`Manage Airflow Users`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/security/airflow-users.md) + - [`Integrate Airflow with LDAP or OAUTH`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/security/ldap-oauth.md) + - [`Set Airflow Fernet Encryption Key`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/security/set-fernet-key.md) + - [`Set Airflow Webserver Secret Key`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/security/set-webserver-secret-key.md) +- __Monitoring:__ + - [`Manage Airflow Logs`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/monitoring/log-persistence.md) + - [`Manage Airflow Logs Cleanup`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/monitoring/log-cleanup.md) + - [`Configure Scheduler Liveness Probe`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/monitoring/scheduler-liveness-probe.md) + - [`Integrate Airflow with Prometheus`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/monitoring/prometheus.md) +- __Databases:__ + - [`Configure Database (Built-In)`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/database/embedded-database.md) + - [`Configure Database (External)`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/database/external-database.md) + - [`Configure PgBouncer`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/database/pgbouncer.md) + - [`Configure Redis (Built-In)`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/database/embedded-redis.md) + - [`Configure Redis (External)`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/database/external-redis.md) +- __Kubernetes:__ + - [`Configure Kubernetes Ingress`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/kubernetes/ingress.md) + - [`Mount Extra Persistent Volumes`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/kubernetes/mount-persistent-volumes.md) + - [`Mount Files from Secrets/ConfigMaps`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/kubernetes/mount-files.md) + - [`Mount Environment Variables from Secrets/ConfigMaps`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/kubernetes/mount-environment-variables.md) + - [`Configure Pod Affinity/Selectors/Tolerations`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/kubernetes/affinity-node-selectors-tolerations.md) + - [`Include Extra Kubernetes Manifests`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/kubernetes/extra-manifests.md) + +## Examples + +- __Custom Values Starting Points:__ + - [`CeleryExecutor`](sample-values-CeleryExecutor.yaml) + - [`KubernetesExecutor`](sample-values-KubernetesExecutor.yaml) + - [`CeleryKubernetesExecutor`](sample-values-CeleryKubernetesExecutor.yaml) +- __Real-World Examples:__ + - [`Minikube / Kind / K3D`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/examples/minikube) + - [`Google Kubernetes Engine (GKE)`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/examples/google-gke) + +
+ +## Airflow Version Support + +The following table lists the __airflow versions__ supported by this chart (set the version with [`airflow.image.tag`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/docs/faq/configuration/airflow-version.md) value). + +Chart Version →
Airflow Version ↓ | `7.0.0` - `7.16.0` | `8.0.0` - `8.5.3` | `8.6.0` | `8.6.1 - 8.7.0` | `8.7.1` | `8.8.0+` +--- | --- | --- | --- | --- | --- | --- +`1.10.X` | ✔️ | ✔️ [1] | ✔️️ [1] | ✔️️ [1] | ✔️️ [1] | ✔️️ [1] +`2.0.X` | ❌ | ✔️ | ✔️ | ✔️ | ✔️️ | ✔️️ +`2.1.X` | ❌ | ✔️ | ✔️ | ✔️ | ✔️️ | ✔️️ +`2.2.X` | ❌ | ⚠️ [2] | ✔️️ | ✔️ | ✔️️ | ✔️️ +`2.3.X` | ❌ | ❌ | ❌ | ✔️️ | ✔️️ | ✔️️ +`2.4.X` | ❌ | ❌ | ❌ | ✔️️ | ✔️️ | ✔️️ +`2.5.X` | ❌ | ❌ | ❌ | ✔️️ | ✔️️ | ✔️️ +`2.6.X` | ❌ | ❌ | ❌ | ❌ | ✔️️ | ✔️️ +`2.7.X` | ❌ | ❌ | ❌ | ❌ | ❌ | ✔️️ + +[1] you must set `airflow.legacyCommands = true` when using airflow version `1.10.X`
+[2] the [Deferrable Operators & Triggers](https://airflow.apache.org/docs/apache-airflow/stable/concepts/deferring.html) feature won't work, as there is no `airflow triggerer` Deployment + +## Airflow Executor Support + +The following table lists the [__airflow executors__](https://airflow.apache.org/docs/apache-airflow/stable/executor/index.html) supported by this chart (set by `airflow.executor` value). + +Chart Version →
Airflow Executor ↓ | `7.X.X` | `8.X.X` | +--- | --- | --- +`CeleryExecutor` | ✔️ | ✔️ +`KubernetesExecutor` | ⚠️️ [1] | ✔️ +`CeleryKubernetesExecutor` | ❌ | ✔️ + +[1] we encourage you to use chart version `8.X.X`, so you can use the `airflow.kubernetesPodTemplate.*` values (requires airflow `1.10.11+`) + +## Helm Values + +The following is a summary of the __helm values__ provided by this chart (see full list in [`values.yaml`](https://github.com/airflow-helm/charts/tree/main/charts/airflow/values.yaml) file). + +> click the `▶` symbol to expand + +
+airflow.* + +Parameter | Description | Default +--- | --- | --- +`airflow.legacyCommands` | if we use legacy 1.10 airflow commands | `false` +`airflow.image.*` | configs for the airflow container image | `` +`airflow.executor` | the airflow executor type to use | `CeleryExecutor` +`airflow.fernetKey` | the fernet encryption key (sets `AIRFLOW__CORE__FERNET_KEY`) | `7T512UXSSmBOkpWimFHIVb8jK6lfmSAvx4mO6Arehnc=` +`airflow.webserverSecretKey` | the secret_key for flask (sets `AIRFLOW__WEBSERVER__SECRET_KEY`) | `THIS IS UNSAFE!` +`airflow.config` | environment variables for airflow configs | `{}` +`airflow.users` | a list of users to create | `` +`airflow.usersTemplates` | bash-like templates to be used in `airflow.users` | `` +`airflow.usersUpdate` | if we create a Deployment to perpetually sync `airflow.users` | `true` +`airflow.connections` | a list airflow connections to create | `` +`airflow.connectionsTemplates` | bash-like templates to be used in `airflow.connections` | `` +`airflow.connectionsUpdate` | if we create a Deployment to perpetually sync `airflow.connections` | `true` +`airflow.variables` | a list airflow variables to create | `` +`airflow.variablesTemplates` | bash-like templates to be used in `airflow.variables` | `` +`airflow.variablesUpdate` | if we create a Deployment to perpetually sync `airflow.variables` | `true` +`airflow.pools` | a list airflow pools to create | `` +`airflow.poolsUpdate` | if we create a Deployment to perpetually sync `airflow.pools` | `true` +`airflow.defaultNodeSelector` | default nodeSelector for airflow Pods (is overridden by pod-specific values) | `{}` +`airflow.defaultAffinity` | default affinity configs for airflow Pods (is overridden by pod-specific values) | `{}` +`airflow.defaultTolerations` | default toleration configs for airflow Pods (is overridden by pod-specific values) | `[]` +`airflow.defaultSecurityContext` | default securityContext configs for Pods (is overridden by pod-specific values) | `{fsGroup: 0}` +`airflow.podAnnotations` | extra annotations for airflow Pods | `{}` +`airflow.extraPipPackages` | extra pip packages to install in airflow Pods | `[]` +`airflow.protectedPipPackages` | pip packages that are protected from upgrade/downgrade by `extraPipPackages` | `["apache-airflow"]` +`airflow.extraEnv` | extra environment variables for the airflow Pods | `[]` +`airflow.extraContainers` | extra containers for the airflow Pods | `[]` +`airflow.extraVolumeMounts` | extra VolumeMounts for the airflow Pods | `[]` +`airflow.extraVolumes` | extra Volumes for the airflow Pods | `[]` +`airflow.clusterDomain` | kubernetes cluster domain name | `cluster.local` +`airflow.localSettings.*` | airflow_local_settings.py | `` +`airflow.kubernetesPodTemplate.*` | pod_template.yaml | `` +`airflow.dbMigrations.*` | db-migrations Deployment | `` +`airflow.sync.*` | Sync Deployments | `` + +
+
+ +
+scheduler.* + +Parameter | Description | Default +--- | --- | --- +`scheduler.replicas` | the number of scheduler Pods to run | `1` +`scheduler.resources` | resource requests/limits for the scheduler Pods | `{}` +`scheduler.nodeSelector` | the nodeSelector configs for the scheduler Pods | `{}` +`scheduler.affinity` | the affinity configs for the scheduler Pods | `{}` +`scheduler.tolerations` | the toleration configs for the scheduler Pods | `[]` +`scheduler.securityContext` | the security context for the scheduler Pods | `{}` +`scheduler.labels` | labels for the scheduler Deployment | `{}` +`scheduler.podLabels` | Pod labels for the scheduler Deployment | `{}` +`scheduler.annotations` | annotations for the scheduler Deployment | `{}` +`scheduler.podAnnotations` | Pod annotations for the scheduler Deployment | `{}` +`scheduler.safeToEvict` | if we add the annotation: "cluster-autoscaler.kubernetes.io/safe-to-evict" = "true" | `true` +`scheduler.podDisruptionBudget.*` | configs for the PodDisruptionBudget of the scheduler | `` +`scheduler.logCleanup.*` | configs for the log-cleanup sidecar of the scheduler | `` +`scheduler.numRuns` | the value of the `airflow --num_runs` parameter used to run the airflow scheduler | `-1` +`scheduler.extraPipPackages` | extra pip packages to install in the scheduler Pods | `[]` +`scheduler.extraVolumeMounts` | extra VolumeMounts for the scheduler Pods | `[]` +`scheduler.extraVolumes` | extra Volumes for the scheduler Pods | `[]` +`scheduler.livenessProbe.*` | configs for the scheduler Pods' liveness probe | `` +`scheduler.extraInitContainers` | extra init containers to run in the scheduler Pods | `[]` + +
+ +
+web.* + +Parameter | Description | Default +--- | --- | --- +`web.webserverConfig.*` | configs to generate webserver_config.py | `` +`web.replicas` | the number of web Pods to run | `1` +`web.resources` | resource requests/limits for the airflow web pods | `{}` +`web.nodeSelector` | the number of web Pods to run | `{}` +`web.affinity` | the affinity configs for the web Pods | `{}` +`web.tolerations` | the toleration configs for the web Pods | `[]` +`web.securityContext` | the security context for the web Pods | `{}` +`web.labels` | labels for the web Deployment | `{}` +`web.podLabels` | Pod labels for the web Deployment | `{}` +`web.annotations` | annotations for the web Deployment | `{}` +`web.podAnnotations` | Pod annotations for the web Deployment | `{}` +`web.safeToEvict` | if we add the annotation: "cluster-autoscaler.kubernetes.io/safe-to-evict" = "true" | `true` +`web.podDisruptionBudget.*` | configs for the PodDisruptionBudget of the web Deployment | `` +`web.service.*` | configs for the Service of the web pods | `` +`web.readinessProbe.*` | configs for the web Pods' readiness probe | `` +`web.livenessProbe.*` | configs for the web Pods' liveness probe | `` +`web.extraPipPackages` | extra pip packages to install in the web Pods | `[]` +`web.extraVolumeMounts` | extra VolumeMounts for the web Pods | `[]` +`web.extraVolumes` | extra Volumes for the web Pods | `[]` + +
+ +
+workers.* + +Parameter | Description | Default +--- | --- | --- +`workers.enabled` | if the airflow workers StatefulSet should be deployed | `true` +`workers.replicas` | the number of workers Pods to run | `1` +`workers.resources` | resource requests/limits for the airflow worker Pods | `{}` +`workers.nodeSelector` | the nodeSelector configs for the worker Pods | `{}` +`workers.affinity` | the affinity configs for the worker Pods | `{}` +`workers.tolerations` | the toleration configs for the worker Pods | `[]` +`workers.securityContext` | the security context for the worker Pods | `{}` +`workers.labels` | labels for the worker StatefulSet | `{}` +`workers.podLabels` | Pod labels for the worker StatefulSet | `{}` +`workers.annotations` | annotations for the worker StatefulSet | `{}` +`workers.podAnnotations` | Pod annotations for the worker StatefulSet | `{}` +`workers.safeToEvict` | if we add the annotation: "cluster-autoscaler.kubernetes.io/safe-to-evict" = "true" | `true` +`workers.podDisruptionBudget.*` | configs for the PodDisruptionBudget of the worker StatefulSet | `` +`workers.autoscaling.*` | configs for the HorizontalPodAutoscaler of the worker Pods | `` +`workers.celery.*` | configs for the celery worker Pods | `` +`workers.terminationPeriod` | how many seconds to wait after SIGTERM before SIGKILL of the celery worker | `60` +`workers.logCleanup.*` | configs for the log-cleanup sidecar of the worker Pods | `` +`workers.livenessProbe.*` | configs for the worker Pods' liveness probe | `` +`workers.extraPipPackages` | extra pip packages to install in the worker Pods | `[]` +`workers.extraVolumeMounts` | extra VolumeMounts for the worker Pods | `[]` +`workers.extraVolumes` | extra Volumes for the worker Pods | `[]` + +
+ +
+triggerer.* + +Parameter | Description | Default +--- | --- | --- +`triggerer.enabled` | if the triggerer should be deployed | `true` +`triggerer.replicas` | the number of triggerer Pods to run | `1` +`triggerer.resources` | resource requests/limits for the airflow triggerer Pods | `{}` +`triggerer.nodeSelector` | the nodeSelector configs for the triggerer Pods | `{}` +`triggerer.affinity` | the affinity configs for the triggerer Pods | `{}` +`triggerer.tolerations` | the toleration configs for the triggerer Pods | `[]` +`triggerer.securityContext` | the security context for the triggerer Pods | `{}` +`triggerer.labels` | labels for the triggerer Deployment | `{}` +`triggerer.podLabels` | Pod labels for the triggerer Deployment | `{}` +`triggerer.annotations` | annotations for the triggerer Deployment | `{}` +`triggerer.podAnnotations` | Pod annotations for the triggerer Deployment | `{}` +`triggerer.safeToEvict` | if we add the annotation: "cluster-autoscaler.kubernetes.io/safe-to-evict" = "true" | `true` +`triggerer.podDisruptionBudget.*` | configs for the PodDisruptionBudget of the triggerer Deployment | `` +`triggerer.capacity` | maximum number of triggers each triggerer will run at once (sets `AIRFLOW__TRIGGERER__DEFAULT_CAPACITY`) | `1000` +`triggerer.livenessProbe.*` | configs for the triggerer Pods' liveness probe | `` +`triggerer.extraPipPackages` | extra pip packages to install in the triggerer Pods | `[]` +`triggerer.extraVolumeMounts` | extra VolumeMounts for the triggerer Pods | `[]` +`triggerer.extraVolumes` | extra Volumes for the triggerer Pods | `[]` + +
+ +
+flower.* + +Parameter | Description | Default +--- | --- | --- +`flower.enabled` | if the Flower UI should be deployed | `true` +`flower.resources` | resource requests/limits for the flower Pods | `{}` +`flower.nodeSelector` | the nodeSelector configs for the flower Pods | `{}` +`flower.affinity` | the affinity configs for the flower Pods | `{}` +`flower.tolerations` | the toleration configs for the flower Pods | `[]` +`flower.securityContext` | the security context for the flower Pods | `{}` +`flower.labels` | labels for the flower Deployment | `{}` +`flower.podLabels` | Pod labels for the flower Deployment | `{}` +`flower.annotations` | annotations for the flower Deployment | `{}` +`flower.podAnnotations` | Pod annotations for the flower Deployment | `{}` +`flower.safeToEvict` | if we add the annotation: "cluster-autoscaler.kubernetes.io/safe-to-evict" = "true" | `true` +`flower.podDisruptionBudget.*` | configs for the PodDisruptionBudget of the flower Deployment | `` +`flower.basicAuthSecret` | the name of a pre-created secret containing the basic authentication value for flower | `""` +`flower.basicAuthSecretKey` | the key within `flower.basicAuthSecret` containing the basic authentication string | `""` +`flower.service.*` | configs for the Service of the flower Pods | `` +`flower.extraPipPackages` | extra pip packages to install in the flower Pod | `[]` +`flower.extraVolumeMounts` | extra VolumeMounts for the flower Pods | `[]` +`flower.extraVolumes` | extra Volumes for the flower Pods | `[]` + +
+ +
+logs.* + +Parameter | Description | Default +--- | --- | --- +`logs.path` | the airflow logs folder | `/opt/airflow/logs` +`logs.persistence.*` | configs for the logs PVC | `` + +
+ +
+dags.* + +Parameter | Description | Default +--- | --- | --- +`dags.path` | the airflow dags folder | `/opt/airflow/dags` +`dags.persistence.*` | configs for the dags PVC | `` +`dags.gitSync.*` | configs for the git-sync sidecar | `` + +
+ +
+ingress.* + +Parameter | Description | Default +--- | --- | --- +`ingress.enabled` | if we should deploy Ingress resources | `false` +`ingress.apiVersion` | the `apiVersion` to use for Ingress resources | `networking.k8s.io/v1` +`ingress.web.*` | configs for the Ingress of the web Service | `` +`ingress.flower.*` | configs for the Ingress of the flower Service | `` + +
+ +
+rbac.* + +Parameter | Description | Default +--- | --- | --- +`rbac.create` | if Kubernetes RBAC resources are created | `true` +`rbac.events` | if the created RBAR role has GET/LIST access to Event resources | `false` + +
+ +
+serviceAccount.* + +Parameter | Description | Default +--- | --- | --- +`serviceAccount.create` | if a Kubernetes ServiceAccount is created | `true` +`serviceAccount.name` | the name of the ServiceAccount | `""` +`serviceAccount.annotations` | annotations for the ServiceAccount | `{}` + +
+ +
+extraManifests + +Parameter | Description | Default +--- | --- | --- +`extraManifests` | a list of extra Kubernetes manifests that will be deployed alongside the chart | `[]` + +
+ +
+pgbouncer.* + +Parameter | Description | Default +--- | --- | --- +`pgbouncer.enabled` | if the pgbouncer Deployment is created | `true` +`pgbouncer.image.*` | configs for the pgbouncer container image | `` +`pgbouncer.resources` | resource requests/limits for the pgbouncer Pods | `{}` +`pgbouncer.nodeSelector` | the nodeSelector configs for the pgbouncer Pods | `{}` +`pgbouncer.affinity` | the affinity configs for the pgbouncer Pods | `{}` +`pgbouncer.tolerations` | the toleration configs for the pgbouncer Pods | `[]` +`pgbouncer.securityContext` | the security context for the pgbouncer Pods | `{}` +`pgbouncer.labels` | labels for the pgbouncer Deployment | `{}` +`pgbouncer.podLabels` | Pod labels for the pgbouncer Deployment | `{}` +`pgbouncer.annotations` | annotations for the pgbouncer Deployment | `{}` +`pgbouncer.podAnnotations` | Pod annotations for the pgbouncer Deployment | `{}` +`pgbouncer.safeToEvict` | if we add the annotation: "cluster-autoscaler.kubernetes.io/safe-to-evict" = "true" | `true` +`pgbouncer.podDisruptionBudget.*` | configs for the PodDisruptionBudget of the pgbouncer | `` +`pgbouncer.livenessProbe.*` | configs for the pgbouncer Pods' liveness probe | `` +`pgbouncer.startupProbe.*` | configs for the pgbouncer Pods' startup probe | `` +`pgbouncer.terminationGracePeriodSeconds` | the maximum number of seconds to wait for queries upon pod termination, before force killing | `120` +`pgbouncer.authType` | sets pgbouncer config: `auth_type` | `md5` +`pgbouncer.maxClientConnections` | sets pgbouncer config: `max_client_conn` | `1000` +`pgbouncer.poolSize` | sets pgbouncer config: `default_pool_size` | `20` +`pgbouncer.logDisconnections` | sets pgbouncer config: `log_disconnections` | `0` +`pgbouncer.logConnections` | sets pgbouncer config: `log_connections` | `0` +`pgbouncer.clientSSL.*` | ssl configs for: clients -> pgbouncer | `` +`pgbouncer.serverSSL.*` | ssl configs for: pgbouncer -> postgres | `` + +
+ +
+postgresql.* + +Parameter | Description | Default +--- | --- | --- +`postgresql.enabled` | if the `stable/postgresql` chart is used | `true` +`postgresql.image.*` | configs for the postgres container image | `` +`postgresql.postgresqlDatabase` | the postgres database to use | `airflow` +`postgresql.postgresqlUsername` | the postgres user to create | `postgres` +`postgresql.postgresqlPassword` | the postgres user's password | `airflow` +`postgresql.existingSecret` | the name of a pre-created secret containing the postgres password | `""` +`postgresql.existingSecretKey` | the key within `postgresql.passwordSecret` containing the password string | `postgresql-password` +`postgresql.persistence.*` | configs for the PVC of postgresql | `` +`postgresql.master.*` | configs for the postgres StatefulSet | `` + +
+ +
+externalDatabase.* + +Parameter | Description | Default +--- | --- | --- +`externalDatabase.type` | the type of external database | `postgres` +`externalDatabase.host` | the host of the external database | `localhost` +`externalDatabase.port` | the port of the external database | `5432` +`externalDatabase.database` | the database/scheme to use within the the external database | `airflow` +`externalDatabase.user` | the username for the external database | `airflow` +`externalDatabase.userSecret` | the name of a pre-created secret containing the external database user | `""` +`externalDatabase.userSecretKey` | the key within `externalDatabase.userSecret` containing the user string | `postgresql-user` +`externalDatabase.password` | the password for the external database | `""` +`externalDatabase.passwordSecret` | the name of a pre-created secret containing the external database password | `""` +`externalDatabase.passwordSecretKey` | the key within `externalDatabase.passwordSecret` containing the password string | `postgresql-password` +`externalDatabase.properties` | extra connection-string properties for the external database | `""` + +
+ +
+redis.* + +Parameter | Description | Default +--- | --- | --- +`redis.enabled` | if the `stable/redis` chart is used | `true` +`redis.image.*` | configs for the redis container image | `` +`redis.password` | the redis password | `airflow` +`redis.existingSecret` | the name of a pre-created secret containing the redis password | `""` +`redis.existingSecretPasswordKey` | the key within `redis.existingSecret` containing the password string | `redis-password` +`redis.cluster.*` | configs for redis cluster mode | `` +`redis.master.*` | configs for the redis master StatefulSet | `` +`redis.slave.*` | configs for the redis slave StatefulSet | `` + +
+ +
+externalRedis.* + +Parameter | Description | Default +--- | --- | --- +`externalRedis.host` | the host of the external redis | `localhost` +`externalRedis.port` | the port of the external redis | `6379` +`externalRedis.databaseNumber` | the database number to use within the external redis | `1` +`externalRedis.password` | the password for the external redis | `""` +`externalRedis.passwordSecret` | the name of a pre-created secret containing the external redis password | `""` +`externalRedis.passwordSecretKey` | the key within `externalRedis.passwordSecret` containing the password string | `redis-password` +`externalDatabase.properties` | extra connection-string properties for the external redis | `""` + +
+ +
+serviceMonitor.* + +Parameter | Description | Default +--- | --- | --- +`serviceMonitor.enabled` | if ServiceMonitor resources should be deployed | `false` +`serviceMonitor.selector` | labels for ServiceMonitor, so that Prometheus can select it | `{ prometheus: "kube-prometheus" }` +`serviceMonitor.path` | the ServiceMonitor web endpoint path | `/admin/metrics` +`serviceMonitor.interval` | the ServiceMonitor web endpoint path | `30s` + +
+ +
+prometheusRule.* + +Parameter | Description | Default +--- | --- | --- +`prometheusRule.enabled` | if the PrometheusRule resources should be deployed | `false` +`prometheusRule.additionalLabels` | labels for PrometheusRule, so that Prometheus can select it | `{}` +`prometheusRule.groups` | alerting rules for Prometheus | `[]` + +
diff --git a/charts/deps/charts/airflow-8.8.0/airflow-8.8.0.tar b/charts/deps/charts/airflow-8.8.0/airflow-8.8.0.tar new file mode 100644 index 0000000000000000000000000000000000000000..76e618bd3e1bed5ad2f4af8508d2f0b6d4786820 GIT binary patch literal 948736 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 V&F&RZ@__dt3sk?*&S1Y3hP(fw<{FjGVw2 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%0A#(-+HjSuz5gQ^Un*`y2* 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

<*1B(O??GiIb+_b} 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 z

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 instead of `stable/`) + +```bash +$ helm repo add bitnami https://charts.bitnami.com/bitnami +$ helm install my-release bitnami/ # Helm 3 +$ helm install --name my-release bitnami/ # Helm 2 +``` + +To update an exisiting _stable_ deployment with a chart hosted in the bitnami repository you can execute + +```bash +$ helm repo add bitnami https://charts.bitnami.com/bitnami +$ helm upgrade my-release bitnami/ +``` + +Issues and PRs related to the chart itself will be redirected to `bitnami/charts` GitHub repository. In the same way, we'll be happy to answer questions related to this migration process in [this issue](https://github.com/helm/charts/issues/20969) created as a common place for discussion. + +## TL;DR; + +```console +$ helm install my-release stable/postgresql +``` + +## Introduction + +This chart bootstraps a [PostgreSQL](https://github.com/bitnami/bitnami-docker-postgresql) deployment on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. + +Bitnami charts can be used with [Kubeapps](https://kubeapps.com/) for deployment and management of Helm Charts in clusters. This chart has been tested to work with NGINX Ingress, cert-manager, fluentd and Prometheus on top of the [BKPR](https://kubeprod.io/). + +## Prerequisites + +- Kubernetes 1.12+ +- Helm 2.11+ or Helm 3.0-beta3+ +- PV provisioner support in the underlying infrastructure + +## Installing the Chart +To install the chart with the release name `my-release`: + +```console +$ helm install my-release stable/postgresql +``` + +The command deploys PostgreSQL on the Kubernetes cluster in the default configuration. The [Parameters](#parameters) section lists the parameters that can be configured during installation. + +> **Tip**: List all releases using `helm list` + +## Uninstalling the Chart + +To uninstall/delete the `my-release` deployment: + +```console +$ helm delete my-release +``` + +The command removes all the Kubernetes components associated with the chart and deletes the release. + +## Parameters + +The following tables lists the configurable parameters of the PostgreSQL chart and their default values. + +| Parameter | Description | Default | +|-----------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------| +| `global.imageRegistry` | Global Docker Image registry | `nil` | +| `global.postgresql.postgresqlDatabase` | PostgreSQL database (overrides `postgresqlDatabase`) | `nil` | +| `global.postgresql.postgresqlUsername` | PostgreSQL username (overrides `postgresqlUsername`) | `nil` | +| `global.postgresql.existingSecret` | Name of existing secret to use for PostgreSQL passwords (overrides `existingSecret`) | `nil` | +| `global.postgresql.postgresqlPassword` | PostgreSQL admin password (overrides `postgresqlPassword`) | `nil` | +| `global.postgresql.servicePort` | PostgreSQL port (overrides `service.port`) | `nil` | +| `global.postgresql.replicationPassword` | Replication user password (overrides `replication.password`) | `nil` | +| `global.imagePullSecrets` | Global Docker registry secret names as an array | `[]` (does not add image pull secrets to deployed pods) | +| `global.storageClass` | Global storage class for dynamic provisioning | `nil` | +| `image.registry` | PostgreSQL Image registry | `docker.io` | +| `image.repository` | PostgreSQL Image name | `bitnami/postgresql` | +| `image.tag` | PostgreSQL Image tag | `{TAG_NAME}` | +| `image.pullPolicy` | PostgreSQL Image pull policy | `IfNotPresent` | +| `image.pullSecrets` | Specify Image pull secrets | `nil` (does not add image pull secrets to deployed pods) | +| `image.debug` | Specify if debug values should be set | `false` | +| `nameOverride` | String to partially override postgresql.fullname template with a string (will prepend the release name) | `nil` | +| `fullnameOverride` | String to fully override postgresql.fullname template with a string | `nil` | +| `volumePermissions.enabled` | Enable init container that changes volume permissions in the data directory (for cases where the default k8s `runAsUser` and `fsUser` values do not work) | `false` | +| `volumePermissions.image.registry` | Init container volume-permissions image registry | `docker.io` | +| `volumePermissions.image.repository` | Init container volume-permissions image name | `bitnami/minideb` | +| `volumePermissions.image.tag` | Init container volume-permissions image tag | `buster` | +| `volumePermissions.image.pullPolicy` | Init container volume-permissions image pull policy | `Always` | +| `volumePermissions.securityContext.runAsUser` | User ID for the init container (when facing issues in OpenShift or uid unknown, try value "auto") | `0` | +| `usePasswordFile` | Have the secrets mounted as a file instead of env vars | `false` | +| `ldap.enabled` | Enable LDAP support | `false` | +| `ldap.existingSecret` | Name of existing secret to use for LDAP passwords | `nil` | +| `ldap.url` | LDAP URL beginning in the form `ldap[s]://host[:port]/basedn[?[attribute][?[scope][?[filter]]]]` | `nil` | +| `ldap.server` | IP address or name of the LDAP server. | `nil` | +| `ldap.port` | Port number on the LDAP server to connect to | `nil` | +| `ldap.scheme` | Set to `ldaps` to use LDAPS. | `nil` | +| `ldap.tls` | Set to `1` to use TLS encryption | `nil` | +| `ldap.prefix` | String to prepend to the user name when forming the DN to bind | `nil` | +| `ldap.suffix` | String to append to the user name when forming the DN to bind | `nil` | +| `ldap.search_attr` | Attribute to match agains the user name in the search | `nil` | +| `ldap.search_filter` | The search filter to use when doing search+bind authentication | `nil` | +| `ldap.baseDN` | Root DN to begin the search for the user in | `nil` | +| `ldap.bindDN` | DN of user to bind to LDAP | `nil` | +| `ldap.bind_password` | Password for the user to bind to LDAP | `nil` | +| `replication.enabled` | Enable replication | `false` | +| `replication.user` | Replication user | `repl_user` | +| `replication.password` | Replication user password | `repl_password` | +| `replication.slaveReplicas` | Number of slaves replicas | `1` | +| `replication.synchronousCommit` | Set synchronous commit mode. Allowed values: `on`, `remote_apply`, `remote_write`, `local` and `off` | `off` | +| `replication.numSynchronousReplicas` | Number of replicas that will have synchronous replication. Note: Cannot be greater than `replication.slaveReplicas`. | `0` | +| `replication.applicationName` | Cluster application name. Useful for advanced replication settings | `my_application` | +| `existingSecret` | Name of existing secret to use for PostgreSQL passwords. The secret has to contain the keys `postgresql-postgres-password` which is the password for `postgresqlUsername` when it is different of `postgres`, `postgresql-password` which will override `postgresqlPassword`, `postgresql-replication-password` which will override `replication.password` and `postgresql-ldap-password` which will be sed to authenticate on LDAP. | `nil` | +| `postgresqlPostgresPassword` | PostgreSQL admin password (used when `postgresqlUsername` is not `postgres`) | _random 10 character alphanumeric string_ | +| `postgresqlUsername` | PostgreSQL admin user | `postgres` | +| `postgresqlPassword` | PostgreSQL admin password | _random 10 character alphanumeric string_ | +| `postgresqlDatabase` | PostgreSQL database | `nil` | +| `postgresqlDataDir` | PostgreSQL data dir folder | `/bitnami/postgresql` (same value as persistence.mountPath) | +| `extraEnv` | Any extra environment variables you would like to pass on to the pod. The value is evaluated as a template. | `[]` | +| `extraEnvVarsCM` | Name of a Config Map containing extra environment variables you would like to pass on to the pod. | `nil` | +| `postgresqlInitdbArgs` | PostgreSQL initdb extra arguments | `nil` | +| `postgresqlInitdbWalDir` | PostgreSQL location for transaction log | `nil` | +| `postgresqlConfiguration` | Runtime Config Parameters | `nil` | +| `postgresqlExtendedConf` | Extended Runtime Config Parameters (appended to main or default configuration) | `nil` | +| `pgHbaConfiguration` | Content of pg_hba.conf | `nil (do not create pg_hba.conf)` | +| `configurationConfigMap` | ConfigMap with the PostgreSQL configuration files (Note: Overrides `postgresqlConfiguration` and `pgHbaConfiguration`). The value is evaluated as a template. | `nil` | +| `extendedConfConfigMap` | ConfigMap with the extended PostgreSQL configuration files. The value is evaluated as a template. | `nil` | +| `initdbScripts` | Dictionary of initdb scripts | `nil` | +| `initdbUsername` | PostgreSQL user to execute the .sql and sql.gz scripts | `nil` | +| `initdbPassword` | Password for the user specified in `initdbUsername` | `nil` | +| `initdbScriptsConfigMap` | ConfigMap with the initdb scripts (Note: Overrides `initdbScripts`). The value is evaluated as a template. | `nil` | +| `initdbScriptsSecret` | Secret with initdb scripts that contain sensitive information (Note: can be used with `initdbScriptsConfigMap` or `initdbScripts`). The value is evaluated as a template. | `nil` | +| `service.type` | Kubernetes Service type | `ClusterIP` | +| `service.port` | PostgreSQL port | `5432` | +| `service.nodePort` | Kubernetes Service nodePort | `nil` | +| `service.annotations` | Annotations for PostgreSQL service, the value is evaluated as a template. | {} | +| `service.loadBalancerIP` | loadBalancerIP if service type is `LoadBalancer` | `nil` | +| `service.loadBalancerSourceRanges` | Address that are allowed when svc is LoadBalancer | [] | +| `schedulerName` | Name of the k8s scheduler (other than default) | `nil` | +| `shmVolume.enabled` | Enable emptyDir volume for /dev/shm for master and slave(s) Pod(s) | `true` | +| `shmVolume.chmod.enabled` | Run at init chmod 777 of the /dev/shm (ignored if `volumePermissions.enabled` is `false`) | `true` | +| `persistence.enabled` | Enable persistence using PVC | `true` | +| `persistence.existingClaim` | Provide an existing `PersistentVolumeClaim`, the value is evaluated as a template. | `nil` | +| `persistence.mountPath` | Path to mount the volume at | `/bitnami/postgresql` | +| `persistence.subPath` | Subdirectory of the volume to mount at | `""` | +| `persistence.storageClass` | PVC Storage Class for PostgreSQL volume | `nil` | +| `persistence.accessModes` | PVC Access Mode for PostgreSQL volume | `[ReadWriteOnce]` | +| `persistence.size` | PVC Storage Request for PostgreSQL volume | `8Gi` | +| `persistence.annotations` | Annotations for the PVC | `{}` | +| `master.nodeSelector` | Node labels for pod assignment (postgresql master) | `{}` | +| `master.affinity` | Affinity labels for pod assignment (postgresql master) | `{}` | +| `master.tolerations` | Toleration labels for pod assignment (postgresql master) | `[]` | +| `master.anotations` | Map of annotations to add to the statefulset (postgresql master) | `{}` | +| `master.labels` | Map of labels to add to the statefulset (postgresql master) | `{}` | +| `master.podAnnotations` | Map of annotations to add to the pods (postgresql master) | `{}` | +| `master.podLabels` | Map of labels to add to the pods (postgresql master) | `{}` | +| `master.priorityClassName` | Priority Class to use for each pod (postgresql master) | `nil` | +| `master.extraInitContainers` | Additional init containers to add to the pods (postgresql master) | `[]` | +| `master.extraVolumeMounts` | Additional volume mounts to add to the pods (postgresql master) | `[]` | +| `master.extraVolumes` | Additional volumes to add to the pods (postgresql master) | `[]` | +| `master.sidecars` | Add additional containers to the pod | `[]` | +| `slave.nodeSelector` | Node labels for pod assignment (postgresql slave) | `{}` | +| `slave.affinity` | Affinity labels for pod assignment (postgresql slave) | `{}` | +| `slave.tolerations` | Toleration labels for pod assignment (postgresql slave) | `[]` | +| `slave.anotations` | Map of annotations to add to the statefulsets (postgresql slave) | `{}` | +| `slave.labels` | Map of labels to add to the statefulsets (postgresql slave) | `{}` | +| `slave.podAnnotations` | Map of annotations to add to the pods (postgresql slave) | `{}` | +| `slave.podLabels` | Map of labels to add to the pods (postgresql slave) | `{}` | +| `slave.priorityClassName` | Priority Class to use for each pod (postgresql slave) | `nil` | +| `slave.extraInitContainers` | Additional init containers to add to the pods (postgresql slave) | `[]` | +| `slave.extraVolumeMounts` | Additional volume mounts to add to the pods (postgresql slave) | `[]` | +| `slave.extraVolumes` | Additional volumes to add to the pods (postgresql slave) | `[]` | +| `slave.sidecars` | Add additional containers to the pod | `[]` | +| `terminationGracePeriodSeconds` | Seconds the pod needs to terminate gracefully | `nil` | +| `resources` | CPU/Memory resource requests/limits | Memory: `256Mi`, CPU: `250m` | +| `securityContext.enabled` | Enable security context | `true` | +| `securityContext.fsGroup` | Group ID for the container | `1001` | +| `securityContext.runAsUser` | User ID for the container | `1001` | +| `serviceAccount.enabled` | Enable service account (Note: Service Account will only be automatically created if `serviceAccount.name` is not set) | `false` | +| `serviceAcccount.name` | Name of existing service account | `nil` | +| `livenessProbe.enabled` | Would you like a livenessProbe to be enabled | `true` | +| `networkPolicy.enabled` | Enable NetworkPolicy | `false` | +| `networkPolicy.allowExternal` | Don't require client label for connections | `true` | +| `networkPolicy.explicitNamespacesSelector` | A Kubernetes LabelSelector to explicitly select namespaces from which ingress traffic could be allowed | `nil` | +| `livenessProbe.initialDelaySeconds` | Delay before liveness probe is initiated | 30 | +| `livenessProbe.periodSeconds` | How often to perform the probe | 10 | +| `livenessProbe.timeoutSeconds` | When the probe times out | 5 | +| `livenessProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded. | 6 | +| `livenessProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed | 1 | +| `readinessProbe.enabled` | would you like a readinessProbe to be enabled | `true` | +| `readinessProbe.initialDelaySeconds` | Delay before readiness probe is initiated | 5 | +| `readinessProbe.periodSeconds` | How often to perform the probe | 10 | +| `readinessProbe.timeoutSeconds` | When the probe times out | 5 | +| `readinessProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded. | 6 | +| `readinessProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed | 1 | +| `metrics.enabled` | Start a prometheus exporter | `false` | +| `metrics.service.type` | Kubernetes Service type | `ClusterIP` | +| `service.clusterIP` | Static clusterIP or None for headless services | `nil` | +| `metrics.service.annotations` | Additional annotations for metrics exporter pod | `{ prometheus.io/scrape: "true", prometheus.io/port: "9187"}` | +| `metrics.service.loadBalancerIP` | loadBalancerIP if redis metrics service type is `LoadBalancer` | `nil` | +| `metrics.serviceMonitor.enabled` | Set this to `true` to create ServiceMonitor for Prometheus operator | `false` | +| `metrics.serviceMonitor.additionalLabels` | Additional labels that can be used so ServiceMonitor will be discovered by Prometheus | `{}` | +| `metrics.serviceMonitor.namespace` | Optional namespace in which to create ServiceMonitor | `nil` | +| `metrics.serviceMonitor.interval` | Scrape interval. If not set, the Prometheus default scrape interval is used | `nil` | +| `metrics.serviceMonitor.scrapeTimeout` | Scrape timeout. If not set, the Prometheus default scrape timeout is used | `nil` | +| `metrics.prometheusRule.enabled` | Set this to true to create prometheusRules for Prometheus operator | `false` | +| `metrics.prometheusRule.additionalLabels` | Additional labels that can be used so prometheusRules will be discovered by Prometheus | `{}` | +| `metrics.prometheusRule.namespace` | namespace where prometheusRules resource should be created | the same namespace as postgresql | +| `metrics.prometheusRule.rules` | [rules](https://prometheus.io/docs/prometheus/latest/configuration/alerting_rules/) to be created, check values for an example. | `[]` | +| `metrics.image.registry` | PostgreSQL Image registry | `docker.io` | +| `metrics.image.repository` | PostgreSQL Image name | `bitnami/postgres-exporter` | +| `metrics.image.tag` | PostgreSQL Image tag | `{TAG_NAME}` | +| `metrics.image.pullPolicy` | PostgreSQL Image pull policy | `IfNotPresent` | +| `metrics.image.pullSecrets` | Specify Image pull secrets | `nil` (does not add image pull secrets to deployed pods) | +| `metrics.customMetrics` | Additional custom metrics | `nil` | +| `metrics.securityContext.enabled` | Enable security context for metrics | `false` | +| `metrics.securityContext.runAsUser` | User ID for the container for metrics | `1001` | +| `metrics.livenessProbe.initialDelaySeconds` | Delay before liveness probe is initiated | 30 | +| `metrics.livenessProbe.periodSeconds` | How often to perform the probe | 10 | +| `metrics.livenessProbe.timeoutSeconds` | When the probe times out | 5 | +| `metrics.livenessProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded. | 6 | +| `metrics.livenessProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed | 1 | +| `metrics.readinessProbe.enabled` | would you like a readinessProbe to be enabled | `true` | +| `metrics.readinessProbe.initialDelaySeconds` | Delay before liveness probe is initiated | 5 | +| `metrics.readinessProbe.periodSeconds` | How often to perform the probe | 10 | +| `metrics.readinessProbe.timeoutSeconds` | When the probe times out | 5 | +| `metrics.readinessProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded. | 6 | +| `metrics.readinessProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed | 1 | +| `updateStrategy` | Update strategy policy | `{type: "RollingUpdate"}` | + +Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example, + +```console +$ helm install my-release \ + --set postgresqlPassword=secretpassword,postgresqlDatabase=my-database \ + stable/postgresql +``` + +The above command sets the PostgreSQL `postgres` account password to `secretpassword`. Additionally it creates a database named `my-database`. + +Alternatively, a YAML file that specifies the values for the parameters can be provided while installing the chart. For example, + +```console +$ helm install my-release -f values.yaml stable/postgresql +``` + +> **Tip**: You can use the default [values.yaml](values.yaml) + +## Configuration and installation details + +### [Rolling VS Immutable tags](https://docs.bitnami.com/containers/how-to/understand-rolling-tags-containers/) + +It is strongly recommended to use immutable tags in a production environment. This ensures your deployment does not change automatically if the same tag is updated with a different image. + +Bitnami will release a new chart updating its containers if a new version of the main container, significant changes, or critical vulnerabilities exist. + +### Production configuration and horizontal scaling + +This chart includes a `values-production.yaml` file where you can find some parameters oriented to production configuration in comparison to the regular `values.yaml`. You can use this file instead of the default one. + +- Enable replication: +```diff +- replication.enabled: false ++ replication.enabled: true +``` + +- Number of slaves replicas: +```diff +- replication.slaveReplicas: 1 ++ replication.slaveReplicas: 2 +``` + +- Set synchronous commit mode: +```diff +- replication.synchronousCommit: "off" ++ replication.synchronousCommit: "on" +``` + +- Number of replicas that will have synchronous replication: +```diff +- replication.numSynchronousReplicas: 0 ++ replication.numSynchronousReplicas: 1 +``` + +- Start a prometheus exporter: +```diff +- metrics.enabled: false ++ metrics.enabled: true +``` + +To horizontally scale this chart, you can use the `--replicas` flag to modify the number of nodes in your PostgreSQL deployment. Also you can use the `values-production.yaml` file or modify the parameters shown above. + +### Change PostgreSQL version + +To modify the PostgreSQL version used in this chart you can specify a [valid image tag](https://hub.docker.com/r/bitnami/postgresql/tags/) using the `image.tag` parameter. For example, `image.tag=12.0.0` + +### postgresql.conf / pg_hba.conf files as configMap + +This helm chart also supports to customize the whole configuration file. + +Add your custom file to "files/postgresql.conf" in your working directory. This file will be mounted as configMap to the containers and it will be used for configuring the PostgreSQL server. + +Alternatively, you can specify PostgreSQL configuration parameters using the `postgresqlConfiguration` parameter as a dict, using camelCase, e.g. {"sharedBuffers": "500MB"}. + +In addition to these options, you can also set an external ConfigMap with all the configuration files. This is done by setting the `configurationConfigMap` parameter. Note that this will override the two previous options. + +### Allow settings to be loaded from files other than the default `postgresql.conf` + +If you don't want to provide the whole PostgreSQL configuration file and only specify certain parameters, you can add your extended `.conf` files to "files/conf.d/" in your working directory. +Those files will be mounted as configMap to the containers adding/overwriting the default configuration using the `include_dir` directive that allows settings to be loaded from files other than the default `postgresql.conf`. + +Alternatively, you can also set an external ConfigMap with all the extra configuration files. This is done by setting the `extendedConfConfigMap` parameter. Note that this will override the previous option. + +### Initialize a fresh instance + +The [Bitnami PostgreSQL](https://github.com/bitnami/bitnami-docker-postgresql) image allows you to use your custom scripts to initialize a fresh instance. In order to execute the scripts, they must be located inside the chart folder `files/docker-entrypoint-initdb.d` so they can be consumed as a ConfigMap. + +Alternatively, you can specify custom scripts using the `initdbScripts` parameter as dict. + +In addition to these options, you can also set an external ConfigMap with all the initialization scripts. This is done by setting the `initdbScriptsConfigMap` parameter. Note that this will override the two previous options. If your initialization scripts contain sensitive information such as credentials or passwords, you can use the `initdbScriptsSecret` parameter. + +The allowed extensions are `.sh`, `.sql` and `.sql.gz`. + +### Sidecars + +If you need additional containers to run within the same pod as PostgreSQL (e.g. an additional metrics or logging exporter), you can do so via the `sidecars` config parameter. Simply define your container according to the Kubernetes container spec. + +```yaml +# For the PostgreSQL master +master: + sidecars: + - name: your-image-name + image: your-image + imagePullPolicy: Always + ports: + - name: portname + containerPort: 1234 +# For the PostgreSQL replicas +slave: + sidecars: + - name: your-image-name + image: your-image + imagePullPolicy: Always + ports: + - name: portname + containerPort: 1234 +``` + +### Metrics + +The chart optionally can start a metrics exporter for [prometheus](https://prometheus.io). The metrics endpoint (port 9187) is not exposed and it is expected that the metrics are collected from inside the k8s cluster using something similar as the described in the [example Prometheus scrape configuration](https://github.com/prometheus/prometheus/blob/master/documentation/examples/prometheus-kubernetes.yml). + +The exporter allows to create custom metrics from additional SQL queries. See the Chart's `values.yaml` for an example and consult the [exporters documentation](https://github.com/wrouesnel/postgres_exporter#adding-new-metrics-via-a-config-file) for more details. + +### Use of global variables + +In more complex scenarios, we may have the following tree of dependencies + +``` + +--------------+ + | | + +------------+ Chart 1 +-----------+ + | | | | + | --------+------+ | + | | | + | | | + | | | + | | | + v v v ++-------+------+ +--------+------+ +--------+------+ +| | | | | | +| PostgreSQL | | Sub-chart 1 | | Sub-chart 2 | +| | | | | | ++--------------+ +---------------+ +---------------+ +``` + +The three charts below depend on the parent chart Chart 1. However, subcharts 1 and 2 may need to connect to PostgreSQL as well. In order to do so, subcharts 1 and 2 need to know the PostgreSQL credentials, so one option for deploying could be deploy Chart 1 with the following parameters: + +``` +postgresql.postgresqlPassword=testtest +subchart1.postgresql.postgresqlPassword=testtest +subchart2.postgresql.postgresqlPassword=testtest +postgresql.postgresqlDatabase=db1 +subchart1.postgresql.postgresqlDatabase=db1 +subchart2.postgresql.postgresqlDatabase=db1 +``` + +If the number of dependent sub-charts increases, installing the chart with parameters can become increasingly difficult. An alternative would be to set the credentials using global variables as follows: + +``` +global.postgresql.postgresqlPassword=testtest +global.postgresql.postgresqlDatabase=db1 +``` + +This way, the credentials will be available in all of the subcharts. + +## Persistence + +The [Bitnami PostgreSQL](https://github.com/bitnami/bitnami-docker-postgresql) image stores the PostgreSQL data and configurations at the `/bitnami/postgresql` path of the container. + +Persistent Volume Claims are used to keep the data across deployments. This is known to work in GCE, AWS, and minikube. +See the [Parameters](#parameters) section to configure the PVC or to disable persistence. + +If you already have data in it, you will fail to sync to standby nodes for all commits, details can refer to [code](https://github.com/bitnami/bitnami-docker-postgresql/blob/8725fe1d7d30ebe8d9a16e9175d05f7ad9260c93/9.6/debian-9/rootfs/libpostgresql.sh#L518-L556). If you need to use those data, please covert them to sql and import after `helm install` finished. + +## NetworkPolicy + +To enable network policy for PostgreSQL, install [a networking plugin that implements the Kubernetes NetworkPolicy spec](https://kubernetes.io/docs/tasks/administer-cluster/declare-network-policy#before-you-begin), and set `networkPolicy.enabled` to `true`. + +For Kubernetes v1.5 & v1.6, you must also turn on NetworkPolicy by setting the DefaultDeny namespace annotation. Note: this will enforce policy for _all_ pods in the namespace: + +```console +$ kubectl annotate namespace default "net.beta.kubernetes.io/network-policy={\"ingress\":{\"isolation\":\"DefaultDeny\"}}" +``` + +With NetworkPolicy enabled, traffic will be limited to just port 5432. + +For more precise policy, set `networkPolicy.allowExternal=false`. This will only allow pods with the generated client label to connect to PostgreSQL. +This label will be displayed in the output of a successful install. + +## Differences between Bitnami PostgreSQL image and [Docker Official](https://hub.docker.com/_/postgres) image + +- The Docker Official PostgreSQL image does not support replication. If you pass any replication environment variable, this would be ignored. The only environment variables supported by the Docker Official image are POSTGRES_USER, POSTGRES_DB, POSTGRES_PASSWORD, POSTGRES_INITDB_ARGS, POSTGRES_INITDB_WALDIR and PGDATA. All the remaining environment variables are specific to the Bitnami PostgreSQL image. +- The Bitnami PostgreSQL image is non-root by default. This requires that you run the pod with `securityContext` and updates the permissions of the volume with an `initContainer`. A key benefit of this configuration is that the pod follows security best practices and is prepared to run on Kubernetes distributions with hard security constraints like OpenShift. +- For OpenShift, one may either define the runAsUser and fsGroup accordingly, or try this more dynamic option: volumePermissions.securityContext.runAsUser="auto",securityContext.enabled=false,shmVolume.chmod.enabled=false + +### Deploy chart using Docker Official PostgreSQL Image + +From chart version 4.0.0, it is possible to use this chart with the Docker Official PostgreSQL image. +Besides specifying the new Docker repository and tag, it is important to modify the PostgreSQL data directory and volume mount point. Basically, the PostgreSQL data dir cannot be the mount point directly, it has to be a subdirectory. + +``` +helm install postgres \ + --set image.repository=postgres \ + --set image.tag=10.6 \ + --set postgresqlDataDir=/data/pgdata \ + --set persistence.mountPath=/data/ \ + stable/postgresql +``` + +## Upgrade + +It's necessary to specify the existing passwords while performing an upgrade to ensure the secrets are not updated with invalid randomly generated passwords. Remember to specify the existing values of the `postgresqlPassword` and `replication.password` parameters when upgrading the chart: + +```bash +$ helm upgrade my-release stable/postgresql \ + --set postgresqlPassword=[POSTGRESQL_PASSWORD] \ + --set replication.password=[REPLICATION_PASSWORD] +``` + +> Note: you need to substitute the placeholders _[POSTGRESQL_PASSWORD]_, and _[REPLICATION_PASSWORD]_ with the values obtained from instructions in the installation notes. + +## 8.0.0 + +Prefixes the port names with their protocols to comply with Istio conventions. + +If you depend on the port names in your setup, make sure to update them to reflect this change. + +## 7.1.0 + +Adds support for LDAP configuration. + +## 7.0.0 + +Helm performs a lookup for the object based on its group (apps), version (v1), and kind (Deployment). Also known as its GroupVersionKind, or GVK. Changing the GVK is considered a compatibility breaker from Kubernetes' point of view, so you cannot "upgrade" those objects to the new GVK in-place. Earlier versions of Helm 3 did not perform the lookup correctly which has since been fixed to match the spec. + +In https://github.com/helm/charts/pull/17281 the `apiVersion` of the statefulset resources was updated to `apps/v1` in tune with the api's deprecated, resulting in compatibility breakage. + +This major version bump signifies this change. + +## 6.5.7 + +In this version, the chart will use PostgreSQL with the Postgis extension included. The version used with Postgresql version 10, 11 and 12 is Postgis 2.5. It has been compiled with the following dependencies: + + - protobuf + - protobuf-c + - json-c + - geos + - proj + +## 5.0.0 + +In this version, the **chart is using PostgreSQL 11 instead of PostgreSQL 10**. You can find the main difference and notable changes in the following links: [https://www.postgresql.org/about/news/1894/](https://www.postgresql.org/about/news/1894/) and [https://www.postgresql.org/about/featurematrix/](https://www.postgresql.org/about/featurematrix/). + +For major releases of PostgreSQL, the internal data storage format is subject to change, thus complicating upgrades, you can see some errors like the following one in the logs: + +```bash +Welcome to the Bitnami postgresql container +Subscribe to project updates by watching https://github.com/bitnami/bitnami-docker-postgresql +Submit issues and feature requests at https://github.com/bitnami/bitnami-docker-postgresql/issues +Send us your feedback at containers@bitnami.com + +INFO ==> ** Starting PostgreSQL setup ** +NFO ==> Validating settings in POSTGRESQL_* env vars.. +INFO ==> Initializing PostgreSQL database... +INFO ==> postgresql.conf file not detected. Generating it... +INFO ==> pg_hba.conf file not detected. Generating it... +INFO ==> Deploying PostgreSQL with persisted data... +INFO ==> Configuring replication parameters +INFO ==> Loading custom scripts... +INFO ==> Enabling remote connections +INFO ==> Stopping PostgreSQL... +INFO ==> ** PostgreSQL setup finished! ** + +INFO ==> ** Starting PostgreSQL ** + [1] FATAL: database files are incompatible with server + [1] DETAIL: The data directory was initialized by PostgreSQL version 10, which is not compatible with this version 11.3. +``` +In this case, you should migrate the data from the old chart to the new one following an approach similar to that described in [this section](https://www.postgresql.org/docs/current/upgrading.html#UPGRADING-VIA-PGDUMPALL) from the official documentation. Basically, create a database dump in the old chart, move and restore it in the new one. + +### 4.0.0 + +This chart will use by default the Bitnami PostgreSQL container starting from version `10.7.0-r68`. This version moves the initialization logic from node.js to bash. This new version of the chart requires setting the `POSTGRES_PASSWORD` in the slaves as well, in order to properly configure the `pg_hba.conf` file. Users from previous versions of the chart are advised to upgrade immediately. + +IMPORTANT: If you do not want to upgrade the chart version then make sure you use the `10.7.0-r68` version of the container. Otherwise, you will get this error + +``` +The POSTGRESQL_PASSWORD environment variable is empty or not set. Set the environment variable ALLOW_EMPTY_PASSWORD=yes to allow the container to be started with blank passwords. This is recommended only for development +``` + +### 3.0.0 + +This releases make it possible to specify different nodeSelector, affinity and tolerations for master and slave pods. +It also fixes an issue with `postgresql.master.fullname` helper template not obeying fullnameOverride. + +#### Breaking changes + +- `affinty` has been renamed to `master.affinity` and `slave.affinity`. +- `tolerations` has been renamed to `master.tolerations` and `slave.tolerations`. +- `nodeSelector` has been renamed to `master.nodeSelector` and `slave.nodeSelector`. + +### 2.0.0 + +In order to upgrade from the `0.X.X` branch to `1.X.X`, you should follow the below steps: + + - Obtain the service name (`SERVICE_NAME`) and password (`OLD_PASSWORD`) of the existing postgresql chart. You can find the instructions to obtain the password in the NOTES.txt, the service name can be obtained by running + + ```console +$ kubectl get svc + ``` + +- Install (not upgrade) the new version + +```console +$ helm repo update +$ helm install my-release stable/postgresql +``` + +- Connect to the new pod (you can obtain the name by running `kubectl get pods`): + +```console +$ kubectl exec -it NAME bash +``` + +- Once logged in, create a dump file from the previous database using `pg_dump`, for that we should connect to the previous postgresql chart: + +```console +$ pg_dump -h SERVICE_NAME -U postgres DATABASE_NAME > /tmp/backup.sql +``` + +After run above command you should be prompted for a password, this password is the previous chart password (`OLD_PASSWORD`). +This operation could take some time depending on the database size. + +- Once you have the backup file, you can restore it with a command like the one below: + +```console +$ psql -U postgres DATABASE_NAME < /tmp/backup.sql +``` + +In this case, you are accessing to the local postgresql, so the password should be the new one (you can find it in NOTES.txt). + +If you want to restore the database and the database schema does not exist, it is necessary to first follow the steps described below. + +```console +$ psql -U postgres +postgres=# drop database DATABASE_NAME; +postgres=# create database DATABASE_NAME; +postgres=# create user USER_NAME; +postgres=# alter role USER_NAME with password 'BITNAMI_USER_PASSWORD'; +postgres=# grant all privileges on database DATABASE_NAME to USER_NAME; +postgres=# alter database DATABASE_NAME owner to USER_NAME; +``` diff --git a/charts/deps/charts/airflow-8.8.0/charts/postgresql/ci/default-values.yaml b/charts/deps/charts/airflow-8.8.0/charts/postgresql/ci/default-values.yaml new file mode 100644 index 0000000..fc2ba60 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/postgresql/ci/default-values.yaml @@ -0,0 +1 @@ +# Leave this file empty to ensure that CI runs builds against the default configuration in values.yaml. diff --git a/charts/deps/charts/airflow-8.8.0/charts/postgresql/ci/shmvolume-disabled-values.yaml b/charts/deps/charts/airflow-8.8.0/charts/postgresql/ci/shmvolume-disabled-values.yaml new file mode 100644 index 0000000..347d3b4 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/postgresql/ci/shmvolume-disabled-values.yaml @@ -0,0 +1,2 @@ +shmVolume: + enabled: false diff --git a/charts/deps/charts/airflow-8.8.0/charts/postgresql/files/README.md b/charts/deps/charts/airflow-8.8.0/charts/postgresql/files/README.md new file mode 100644 index 0000000..1813a2f --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/postgresql/files/README.md @@ -0,0 +1 @@ +Copy here your postgresql.conf and/or pg_hba.conf files to use it as a config map. diff --git a/charts/deps/charts/airflow-8.8.0/charts/postgresql/files/conf.d/README.md b/charts/deps/charts/airflow-8.8.0/charts/postgresql/files/conf.d/README.md new file mode 100644 index 0000000..184c187 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/postgresql/files/conf.d/README.md @@ -0,0 +1,4 @@ +If you don't want to provide the whole configuration file and only specify certain parameters, you can copy here your extended `.conf` files. +These files will be injected as a config maps and add/overwrite the default configuration using the `include_dir` directive that allows settings to be loaded from files other than the default `postgresql.conf`. + +More info in the [bitnami-docker-postgresql README](https://github.com/bitnami/bitnami-docker-postgresql#configuration-file). diff --git a/charts/deps/charts/airflow-8.8.0/charts/postgresql/files/docker-entrypoint-initdb.d/README.md b/charts/deps/charts/airflow-8.8.0/charts/postgresql/files/docker-entrypoint-initdb.d/README.md new file mode 100644 index 0000000..cba3809 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/postgresql/files/docker-entrypoint-initdb.d/README.md @@ -0,0 +1,3 @@ +You can copy here your custom `.sh`, `.sql` or `.sql.gz` file so they are executed during the first boot of the image. + +More info in the [bitnami-docker-postgresql](https://github.com/bitnami/bitnami-docker-postgresql#initializing-a-new-instance) repository. \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/NOTES.txt b/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/NOTES.txt new file mode 100644 index 0000000..64d7353 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/NOTES.txt @@ -0,0 +1,81 @@ +This Helm chart is deprecated + +Given the `stable` deprecation timeline (https://github.com/helm/charts#deprecation-timeline), the Bitnami maintained Helm chart is now located at bitnami/charts (https://github.com/bitnami/charts/). + +The Bitnami repository is already included in the Hubs and we will continue providing the same cadence of updates, support, etc that we've been keeping here these years. Installation instructions are very similar, just adding the _bitnami_ repo and using it during the installation (`bitnami/` instead of `stable/`) + +```bash +$ helm repo add bitnami https://charts.bitnami.com/bitnami +$ helm install my-release bitnami/ # Helm 3 +$ helm install --name my-release bitnami/ # Helm 2 +``` + +To update an exisiting _stable_ deployment with a chart hosted in the bitnami repository you can execute + +```bash +$ helm repo add bitnami https://charts.bitnami.com/bitnami +$ helm upgrade my-release bitnami/ +``` + +Issues and PRs related to the chart itself will be redirected to `bitnami/charts` GitHub repository. In the same way, we'll be happy to answer questions related to this migration process in this issue (https://github.com/helm/charts/issues/20969) created as a common place for discussion. + +** Please be patient while the chart is being deployed ** + +PostgreSQL can be accessed via port {{ template "postgresql.port" . }} on the following DNS name from within your cluster: + + {{ template "postgresql.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local - Read/Write connection +{{- if .Values.replication.enabled }} + {{ template "postgresql.fullname" . }}-read.{{ .Release.Namespace }}.svc.cluster.local - Read only connection +{{- end }} + +{{- if and .Values.postgresqlPostgresPassword (not (eq .Values.postgresqlUsername "postgres")) }} + +To get the password for "postgres" run: + + export POSTGRES_ADMIN_PASSWORD=$(kubectl get secret --namespace {{ .Release.Namespace }} {{ template "postgresql.secretName" . }} -o jsonpath="{.data.postgresql-postgres-password}" | base64 --decode) +{{- end }} + +To get the password for "{{ template "postgresql.username" . }}" run: + + export POSTGRES_PASSWORD=$(kubectl get secret --namespace {{ .Release.Namespace }} {{ template "postgresql.secretName" . }} -o jsonpath="{.data.postgresql-password}" | base64 --decode) + +To connect to your database run the following command: + + kubectl run {{ template "postgresql.fullname" . }}-client --rm --tty -i --restart='Never' --namespace {{ .Release.Namespace }} --image {{ template "postgresql.image" . }} --env="PGPASSWORD=$POSTGRES_PASSWORD" {{- if and (.Values.networkPolicy.enabled) (not .Values.networkPolicy.allowExternal) }} + --labels="{{ template "postgresql.fullname" . }}-client=true" {{- end }} --command -- psql --host {{ template "postgresql.fullname" . }} -U {{ .Values.postgresqlUsername }} -d {{- if .Values.postgresqlDatabase }} {{ .Values.postgresqlDatabase }}{{- else }} postgres{{- end }} -p {{ template "postgresql.port" . }} + +{{ if and (.Values.networkPolicy.enabled) (not .Values.networkPolicy.allowExternal) }} +Note: Since NetworkPolicy is enabled, only pods with label {{ template "postgresql.fullname" . }}-client=true" will be able to connect to this PostgreSQL cluster. +{{- end }} + +To connect to your database from outside the cluster execute the following commands: + +{{- if contains "NodePort" .Values.service.type }} + + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "postgresql.fullname" . }}) + {{ if (include "postgresql.password" . ) }}PGPASSWORD="$POSTGRES_PASSWORD" {{ end }}psql --host $NODE_IP --port $NODE_PORT -U {{ .Values.postgresqlUsername }} -d {{- if .Values.postgresqlDatabase }} {{ .Values.postgresqlDatabase }}{{- else }} postgres{{- end }} + +{{- else if contains "LoadBalancer" .Values.service.type }} + + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + Watch the status with: 'kubectl get svc --namespace {{ .Release.Namespace }} -w {{ template "postgresql.fullname" . }}' + + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "postgresql.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + {{ if (include "postgresql.password" . ) }}PGPASSWORD="$POSTGRES_PASSWORD" {{ end }}psql --host $SERVICE_IP --port {{ template "postgresql.port" . }} -U {{ .Values.postgresqlUsername }} -d {{- if .Values.postgresqlDatabase }} {{ .Values.postgresqlDatabase }}{{- else }} postgres{{- end }} + +{{- else if contains "ClusterIP" .Values.service.type }} + + kubectl port-forward --namespace {{ .Release.Namespace }} svc/{{ template "postgresql.fullname" . }} {{ template "postgresql.port" . }}:{{ template "postgresql.port" . }} & + {{ if (include "postgresql.password" . ) }}PGPASSWORD="$POSTGRES_PASSWORD" {{ end }}psql --host 127.0.0.1 -U {{ .Values.postgresqlUsername }} -d {{- if .Values.postgresqlDatabase }} {{ .Values.postgresqlDatabase }}{{- else }} postgres{{- end }} -p {{ template "postgresql.port" . }} + +{{- end }} + +{{- include "postgresql.validateValues" . -}} + +{{- if and (contains "bitnami/" .Values.image.repository) (not (.Values.image.tag | toString | regexFind "-r\\d+$|sha256:")) }} + +WARNING: Rolling tag detected ({{ .Values.image.repository }}:{{ .Values.image.tag }}), please note that it is strongly recommended to avoid using rolling tags in a production environment. ++info https://docs.bitnami.com/containers/how-to/understand-rolling-tags-containers/ + +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/_helpers.tpl b/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/_helpers.tpl new file mode 100644 index 0000000..3ee5572 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/_helpers.tpl @@ -0,0 +1,420 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "postgresql.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). +*/}} +{{- define "postgresql.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 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). +*/}} +{{- define "postgresql.master.fullname" -}} +{{- $name := default .Chart.Name .Values.nameOverride -}} +{{- $fullname := default (printf "%s-%s" .Release.Name $name) .Values.fullnameOverride -}} +{{- if .Values.replication.enabled -}} +{{- printf "%s-%s" $fullname "master" | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s" $fullname | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for networkpolicy. +*/}} +{{- define "postgresql.networkPolicy.apiVersion" -}} +{{- if semverCompare ">=1.4-0, <1.7-0" .Capabilities.KubeVersion.GitVersion -}} +"extensions/v1beta1" +{{- else if semverCompare "^1.7-0" .Capabilities.KubeVersion.GitVersion -}} +"networking.k8s.io/v1" +{{- end -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "postgresql.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Return the proper PostgreSQL image name +*/}} +{{- define "postgresql.image" -}} +{{- $registryName := .Values.image.registry -}} +{{- $repositoryName := .Values.image.repository -}} +{{- $tag := .Values.image.tag | toString -}} +{{/* +Helm 2.11 supports the assignment of a value to a variable defined in a different scope, +but Helm 2.9 and 2.10 doesn't support it, so we need to implement this if-else logic. +Also, we can't use a single if because lazy evaluation is not an option +*/}} +{{- if .Values.global }} + {{- if .Values.global.imageRegistry }} + {{- printf "%s/%s:%s" .Values.global.imageRegistry $repositoryName $tag -}} + {{- else -}} + {{- printf "%s/%s:%s" $registryName $repositoryName $tag -}} + {{- end -}} +{{- else -}} + {{- printf "%s/%s:%s" $registryName $repositoryName $tag -}} +{{- end -}} +{{- end -}} + +{{/* +Return PostgreSQL postgres user password +*/}} +{{- define "postgresql.postgres.password" -}} +{{- if .Values.global.postgresql.postgresqlPostgresPassword }} + {{- .Values.global.postgresql.postgresqlPostgresPassword -}} +{{- else if .Values.postgresqlPostgresPassword -}} + {{- .Values.postgresqlPostgresPassword -}} +{{- else -}} + {{- randAlphaNum 10 -}} +{{- end -}} +{{- end -}} + +{{/* +Return PostgreSQL password +*/}} +{{- define "postgresql.password" -}} +{{- if .Values.global.postgresql.postgresqlPassword }} + {{- .Values.global.postgresql.postgresqlPassword -}} +{{- else if .Values.postgresqlPassword -}} + {{- .Values.postgresqlPassword -}} +{{- else -}} + {{- randAlphaNum 10 -}} +{{- end -}} +{{- end -}} + +{{/* +Return PostgreSQL replication password +*/}} +{{- define "postgresql.replication.password" -}} +{{- if .Values.global.postgresql.replicationPassword }} + {{- .Values.global.postgresql.replicationPassword -}} +{{- else if .Values.replication.password -}} + {{- .Values.replication.password -}} +{{- else -}} + {{- randAlphaNum 10 -}} +{{- end -}} +{{- end -}} + +{{/* +Return PostgreSQL username +*/}} +{{- define "postgresql.username" -}} +{{- if .Values.global.postgresql.postgresqlUsername }} + {{- .Values.global.postgresql.postgresqlUsername -}} +{{- else -}} + {{- .Values.postgresqlUsername -}} +{{- end -}} +{{- end -}} + + +{{/* +Return PostgreSQL replication username +*/}} +{{- define "postgresql.replication.username" -}} +{{- if .Values.global.postgresql.replicationUser }} + {{- .Values.global.postgresql.replicationUser -}} +{{- else -}} + {{- .Values.replication.user -}} +{{- end -}} +{{- end -}} + +{{/* +Return PostgreSQL port +*/}} +{{- define "postgresql.port" -}} +{{- if .Values.global.postgresql.servicePort }} + {{- .Values.global.postgresql.servicePort -}} +{{- else -}} + {{- .Values.service.port -}} +{{- end -}} +{{- end -}} + +{{/* +Return PostgreSQL created database +*/}} +{{- define "postgresql.database" -}} +{{- if .Values.global.postgresql.postgresqlDatabase }} + {{- .Values.global.postgresql.postgresqlDatabase -}} +{{- else if .Values.postgresqlDatabase -}} + {{- .Values.postgresqlDatabase -}} +{{- end -}} +{{- end -}} + +{{/* +Return the proper image name to change the volume permissions +*/}} +{{- define "postgresql.volumePermissions.image" -}} +{{- $registryName := .Values.volumePermissions.image.registry -}} +{{- $repositoryName := .Values.volumePermissions.image.repository -}} +{{- $tag := .Values.volumePermissions.image.tag | toString -}} +{{/* +Helm 2.11 supports the assignment of a value to a variable defined in a different scope, +but Helm 2.9 and 2.10 doesn't support it, so we need to implement this if-else logic. +Also, we can't use a single if because lazy evaluation is not an option +*/}} +{{- if .Values.global }} + {{- if .Values.global.imageRegistry }} + {{- printf "%s/%s:%s" .Values.global.imageRegistry $repositoryName $tag -}} + {{- else -}} + {{- printf "%s/%s:%s" $registryName $repositoryName $tag -}} + {{- end -}} +{{- else -}} + {{- printf "%s/%s:%s" $registryName $repositoryName $tag -}} +{{- end -}} +{{- end -}} + +{{/* +Return the proper PostgreSQL metrics image name +*/}} +{{- define "postgresql.metrics.image" -}} +{{- $registryName := default "docker.io" .Values.metrics.image.registry -}} +{{- $repositoryName := .Values.metrics.image.repository -}} +{{- $tag := default "latest" .Values.metrics.image.tag | toString -}} +{{/* +Helm 2.11 supports the assignment of a value to a variable defined in a different scope, +but Helm 2.9 and 2.10 doesn't support it, so we need to implement this if-else logic. +Also, we can't use a single if because lazy evaluation is not an option +*/}} +{{- if .Values.global }} + {{- if .Values.global.imageRegistry }} + {{- printf "%s/%s:%s" .Values.global.imageRegistry $repositoryName $tag -}} + {{- else -}} + {{- printf "%s/%s:%s" $registryName $repositoryName $tag -}} + {{- end -}} +{{- else -}} + {{- printf "%s/%s:%s" $registryName $repositoryName $tag -}} +{{- end -}} +{{- end -}} + +{{/* +Get the password secret. +*/}} +{{- define "postgresql.secretName" -}} +{{- if .Values.global.postgresql.existingSecret }} + {{- printf "%s" .Values.global.postgresql.existingSecret -}} +{{- else if .Values.existingSecret -}} + {{- printf "%s" .Values.existingSecret -}} +{{- else -}} + {{- printf "%s" (include "postgresql.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Return true if a secret object should be created +*/}} +{{- define "postgresql.createSecret" -}} +{{- if .Values.global.postgresql.existingSecret }} +{{- else if .Values.existingSecret -}} +{{- else -}} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Get the configuration ConfigMap name. +*/}} +{{- define "postgresql.configurationCM" -}} +{{- if .Values.configurationConfigMap -}} +{{- printf "%s" (tpl .Values.configurationConfigMap $) -}} +{{- else -}} +{{- printf "%s-configuration" (include "postgresql.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Get the extended configuration ConfigMap name. +*/}} +{{- define "postgresql.extendedConfigurationCM" -}} +{{- if .Values.extendedConfConfigMap -}} +{{- printf "%s" (tpl .Values.extendedConfConfigMap $) -}} +{{- else -}} +{{- printf "%s-extended-configuration" (include "postgresql.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Get the initialization scripts ConfigMap name. +*/}} +{{- define "postgresql.initdbScriptsCM" -}} +{{- if .Values.initdbScriptsConfigMap -}} +{{- printf "%s" (tpl .Values.initdbScriptsConfigMap $) -}} +{{- else -}} +{{- printf "%s-init-scripts" (include "postgresql.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Get the initialization scripts Secret name. +*/}} +{{- define "postgresql.initdbScriptsSecret" -}} +{{- printf "%s" (tpl .Values.initdbScriptsSecret $) -}} +{{- end -}} + +{{/* +Get the metrics ConfigMap name. +*/}} +{{- define "postgresql.metricsCM" -}} +{{- printf "%s-metrics" (include "postgresql.fullname" .) -}} +{{- end -}} + +{{/* +Return the proper Docker Image Registry Secret Names +*/}} +{{- define "postgresql.imagePullSecrets" -}} +{{/* +Helm 2.11 supports the assignment of a value to a variable defined in a different scope, +but Helm 2.9 and 2.10 does not support it, so we need to implement this if-else logic. +Also, we can not use a single if because lazy evaluation is not an option +*/}} +{{- if .Values.global }} +{{- if .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range .Values.global.imagePullSecrets }} + - name: {{ . }} +{{- end }} +{{- else if or .Values.image.pullSecrets .Values.metrics.image.pullSecrets .Values.volumePermissions.image.pullSecrets }} +imagePullSecrets: +{{- range .Values.image.pullSecrets }} + - name: {{ . }} +{{- end }} +{{- range .Values.metrics.image.pullSecrets }} + - name: {{ . }} +{{- end }} +{{- range .Values.volumePermissions.image.pullSecrets }} + - name: {{ . }} +{{- end }} +{{- end -}} +{{- else if or .Values.image.pullSecrets .Values.metrics.image.pullSecrets .Values.volumePermissions.image.pullSecrets }} +imagePullSecrets: +{{- range .Values.image.pullSecrets }} + - name: {{ . }} +{{- end }} +{{- range .Values.metrics.image.pullSecrets }} + - name: {{ . }} +{{- end }} +{{- range .Values.volumePermissions.image.pullSecrets }} + - name: {{ . }} +{{- end }} +{{- end -}} +{{- end -}} + +{{/* +Get the readiness probe command +*/}} +{{- define "postgresql.readinessProbeCommand" -}} +- | +{{- if (include "postgresql.database" .) }} + exec pg_isready -U {{ include "postgresql.username" . | quote }} -d {{ (include "postgresql.database" .) | quote }} -h 127.0.0.1 -p {{ template "postgresql.port" . }} +{{- else }} + exec pg_isready -U {{ include "postgresql.username" . | quote }} -h 127.0.0.1 -p {{ template "postgresql.port" . }} +{{- end }} +{{- if contains "bitnami/" .Values.image.repository }} + [ -f /opt/bitnami/postgresql/tmp/.initialized ] || [ -f /bitnami/postgresql/.initialized ] +{{- end -}} +{{- end -}} + +{{/* +Return the proper Storage Class +*/}} +{{- define "postgresql.storageClass" -}} +{{/* +Helm 2.11 supports the assignment of a value to a variable defined in a different scope, +but Helm 2.9 and 2.10 does not support it, so we need to implement this if-else logic. +*/}} +{{- if .Values.global -}} + {{- if .Values.global.storageClass -}} + {{- if (eq "-" .Values.global.storageClass) -}} + {{- printf "storageClassName: \"\"" -}} + {{- else }} + {{- printf "storageClassName: %s" .Values.global.storageClass -}} + {{- end -}} + {{- else -}} + {{- if .Values.persistence.storageClass -}} + {{- if (eq "-" .Values.persistence.storageClass) -}} + {{- printf "storageClassName: \"\"" -}} + {{- else }} + {{- printf "storageClassName: %s" .Values.persistence.storageClass -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- else -}} + {{- if .Values.persistence.storageClass -}} + {{- if (eq "-" .Values.persistence.storageClass) -}} + {{- printf "storageClassName: \"\"" -}} + {{- else }} + {{- printf "storageClassName: %s" .Values.persistence.storageClass -}} + {{- end -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Renders a value that contains template. +Usage: +{{ include "postgresql.tplValue" ( dict "value" .Values.path.to.the.Value "context" $) }} +*/}} +{{- define "postgresql.tplValue" -}} + {{- if typeIs "string" .value }} + {{- tpl .value .context }} + {{- else }} + {{- tpl (.value | toYaml) .context }} + {{- end }} +{{- end -}} + +{{/* +Return the appropriate apiVersion for statefulset. +*/}} +{{- define "postgresql.statefulset.apiVersion" -}} +{{- if semverCompare "<1.14-0" .Capabilities.KubeVersion.GitVersion -}} +{{- print "apps/v1beta2" -}} +{{- else -}} +{{- print "apps/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Compile all warnings into a single message, and call fail. +*/}} +{{- define "postgresql.validateValues" -}} +{{- $messages := list -}} +{{- $messages := append $messages (include "postgresql.validateValues.ldapConfigurationMethod" .) -}} +{{- $messages := without $messages "" -}} +{{- $message := join "\n" $messages -}} + +{{- if $message -}} +{{- printf "\nVALUES VALIDATION:\n%s" $message | fail -}} +{{- end -}} +{{- end -}} + +{{/* +Validate values of Postgresql - If ldap.url is used then you don't need the other settings for ldap +*/}} +{{- define "postgresql.validateValues.ldapConfigurationMethod" -}} +{{- if and .Values.ldap.enabled (and (not (empty .Values.ldap.url)) (not (empty .Values.ldap.server))) }} +postgresql: ldap.url, ldap.server + You cannot set both `ldap.url` and `ldap.server` at the same time. + Please provide a unique way to configure LDAP. + More info at https://www.postgresql.org/docs/current/auth-ldap.html +{{- end -}} +{{- end -}} diff --git a/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/configmap.yaml b/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/configmap.yaml new file mode 100644 index 0000000..d2178c0 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/configmap.yaml @@ -0,0 +1,26 @@ +{{ if and (or (.Files.Glob "files/postgresql.conf") (.Files.Glob "files/pg_hba.conf") .Values.postgresqlConfiguration .Values.pgHbaConfiguration) (not .Values.configurationConfigMap) }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "postgresql.fullname" . }}-configuration + labels: + app: {{ template "postgresql.name" . }} + chart: {{ template "postgresql.chart" . }} + release: {{ .Release.Name | quote }} + heritage: {{ .Release.Service | quote }} +data: +{{- if (.Files.Glob "files/postgresql.conf") }} +{{ (.Files.Glob "files/postgresql.conf").AsConfig | indent 2 }} +{{- else if .Values.postgresqlConfiguration }} + postgresql.conf: | +{{- range $key, $value := default dict .Values.postgresqlConfiguration }} + {{ $key | snakecase }}={{ $value }} +{{- end }} +{{- end }} +{{- if (.Files.Glob "files/pg_hba.conf") }} +{{ (.Files.Glob "files/pg_hba.conf").AsConfig | indent 2 }} +{{- else if .Values.pgHbaConfiguration }} + pg_hba.conf: | +{{ .Values.pgHbaConfiguration | indent 4 }} +{{- end }} +{{ end }} diff --git a/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/extended-config-configmap.yaml b/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/extended-config-configmap.yaml new file mode 100644 index 0000000..8a41195 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/extended-config-configmap.yaml @@ -0,0 +1,21 @@ +{{- if and (or (.Files.Glob "files/conf.d/*.conf") .Values.postgresqlExtendedConf) (not .Values.extendedConfConfigMap)}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "postgresql.fullname" . }}-extended-configuration + labels: + app: {{ template "postgresql.name" . }} + chart: {{ template "postgresql.chart" . }} + release: {{ .Release.Name | quote }} + heritage: {{ .Release.Service | quote }} +data: +{{- with .Files.Glob "files/conf.d/*.conf" }} +{{ .AsConfig | indent 2 }} +{{- end }} +{{ with .Values.postgresqlExtendedConf }} + override.conf: | +{{- range $key, $value := . }} + {{ $key | snakecase }}={{ $value }} +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/initialization-configmap.yaml b/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/initialization-configmap.yaml new file mode 100644 index 0000000..8eb5e05 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/initialization-configmap.yaml @@ -0,0 +1,24 @@ +{{- if and (or (.Files.Glob "files/docker-entrypoint-initdb.d/*.{sh,sql,sql.gz}") .Values.initdbScripts) (not .Values.initdbScriptsConfigMap) }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "postgresql.fullname" . }}-init-scripts + labels: + app: {{ template "postgresql.name" . }} + chart: {{ template "postgresql.chart" . }} + release: {{ .Release.Name | quote }} + heritage: {{ .Release.Service | quote }} +{{- with .Files.Glob "files/docker-entrypoint-initdb.d/*.sql.gz" }} +binaryData: +{{- range $path, $bytes := . }} + {{ base $path }}: {{ $.Files.Get $path | b64enc | quote }} +{{- end }} +{{- end }} +data: +{{- with .Files.Glob "files/docker-entrypoint-initdb.d/*.{sh,sql}" }} +{{ .AsConfig | indent 2 }} +{{- end }} +{{- with .Values.initdbScripts }} +{{ toYaml . | indent 2 }} +{{- end }} +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/metrics-configmap.yaml b/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/metrics-configmap.yaml new file mode 100644 index 0000000..524aa2f --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/metrics-configmap.yaml @@ -0,0 +1,13 @@ +{{- if and .Values.metrics.enabled .Values.metrics.customMetrics }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "postgresql.metricsCM" . }} + labels: + app: {{ template "postgresql.name" . }} + chart: {{ template "postgresql.chart" . }} + release: {{ .Release.Name | quote }} + heritage: {{ .Release.Service | quote }} +data: + custom-metrics.yaml: {{ toYaml .Values.metrics.customMetrics | quote }} +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/metrics-svc.yaml b/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/metrics-svc.yaml new file mode 100644 index 0000000..c610f09 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/metrics-svc.yaml @@ -0,0 +1,26 @@ +{{- if .Values.metrics.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "postgresql.fullname" . }}-metrics + labels: + app: {{ template "postgresql.name" . }} + chart: {{ template "postgresql.chart" . }} + release: {{ .Release.Name | quote }} + heritage: {{ .Release.Service | quote }} + annotations: +{{ toYaml .Values.metrics.service.annotations | indent 4 }} +spec: + type: {{ .Values.metrics.service.type }} + {{- if and (eq .Values.metrics.service.type "LoadBalancer") .Values.metrics.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.metrics.service.loadBalancerIP }} + {{- end }} + ports: + - name: http-metrics + port: 9187 + targetPort: http-metrics + selector: + app: {{ template "postgresql.name" . }} + release: {{ .Release.Name }} + role: master +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/networkpolicy.yaml b/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/networkpolicy.yaml new file mode 100644 index 0000000..ea1fc9b --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/networkpolicy.yaml @@ -0,0 +1,38 @@ +{{- if .Values.networkPolicy.enabled }} +kind: NetworkPolicy +apiVersion: {{ template "postgresql.networkPolicy.apiVersion" . }} +metadata: + name: {{ template "postgresql.fullname" . }} + labels: + app: {{ template "postgresql.name" . }} + chart: {{ template "postgresql.chart" . }} + release: {{ .Release.Name | quote }} + heritage: {{ .Release.Service | quote }} +spec: + podSelector: + matchLabels: + app: {{ template "postgresql.name" . }} + release: {{ .Release.Name | quote }} + ingress: + # Allow inbound connections + - ports: + - port: {{ template "postgresql.port" . }} + {{- if not .Values.networkPolicy.allowExternal }} + from: + - podSelector: + matchLabels: + {{ template "postgresql.fullname" . }}-client: "true" + {{- if .Values.networkPolicy.explicitNamespacesSelector }} + namespaceSelector: +{{ toYaml .Values.networkPolicy.explicitNamespacesSelector | indent 12 }} + {{- end }} + - podSelector: + matchLabels: + app: {{ template "postgresql.name" . }} + release: {{ .Release.Name | quote }} + role: slave + {{- end }} + # Allow prometheus scrapes + - ports: + - port: 9187 +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/prometheusrule.yaml b/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/prometheusrule.yaml new file mode 100644 index 0000000..44f1242 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/prometheusrule.yaml @@ -0,0 +1,23 @@ +{{- if and .Values.metrics.enabled .Values.metrics.prometheusRule.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: PrometheusRule +metadata: + name: {{ template "postgresql.fullname" . }} +{{- with .Values.metrics.prometheusRule.namespace }} + namespace: {{ . }} +{{- end }} + labels: + app: {{ template "postgresql.name" . }} + chart: {{ template "postgresql.chart" . }} + release: {{ .Release.Name | quote }} + heritage: {{ .Release.Service | quote }} +{{- with .Values.metrics.prometheusRule.additionalLabels }} +{{ toYaml . | indent 4 }} +{{- end }} +spec: +{{- with .Values.metrics.prometheusRule.rules }} + groups: + - name: {{ template "postgresql.name" $ }} + rules: {{ tpl (toYaml .) $ | nindent 8 }} +{{- end }} +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/secrets.yaml b/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/secrets.yaml new file mode 100644 index 0000000..094d18b --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/secrets.yaml @@ -0,0 +1,23 @@ +{{- if (include "postgresql.createSecret" .) }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "postgresql.fullname" . }} + labels: + app: {{ template "postgresql.name" . }} + chart: {{ template "postgresql.chart" . }} + release: {{ .Release.Name | quote }} + heritage: {{ .Release.Service | quote }} +type: Opaque +data: + {{- if and .Values.postgresqlPostgresPassword (not (eq .Values.postgresqlUsername "postgres")) }} + postgresql-postgres-password: {{ include "postgresql.postgres.password" . | b64enc | quote }} + {{- end }} + postgresql-password: {{ include "postgresql.password" . | b64enc | quote }} + {{- if .Values.replication.enabled }} + postgresql-replication-password: {{ include "postgresql.replication.password" . | b64enc | quote }} + {{- end }} + {{- if (and .Values.ldap.enabled .Values.ldap.bind_password)}} + postgresql-ldap-password: {{ .Values.ldap.bind_password | b64enc | quote }} + {{- end }} +{{- end -}} diff --git a/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/serviceaccount.yaml b/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/serviceaccount.yaml new file mode 100644 index 0000000..27e5b51 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/serviceaccount.yaml @@ -0,0 +1,11 @@ +{{- if and (.Values.serviceAccount.enabled) (not .Values.serviceAccount.name) }} +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + app: {{ template "postgresql.name" . }} + chart: {{ template "postgresql.chart" . }} + release: {{ .Release.Name | quote }} + heritage: {{ .Release.Service | quote }} + name: {{ template "postgresql.fullname" . }} +{{- end }} \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/servicemonitor.yaml b/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/servicemonitor.yaml new file mode 100644 index 0000000..f3a529a --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/servicemonitor.yaml @@ -0,0 +1,33 @@ +{{- if and .Values.metrics.enabled .Values.metrics.serviceMonitor.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ include "postgresql.fullname" . }} + {{- if .Values.metrics.serviceMonitor.namespace }} + namespace: {{ .Values.metrics.serviceMonitor.namespace }} + {{- end }} + labels: + app: {{ template "postgresql.name" . }} + chart: {{ template "postgresql.chart" . }} + release: {{ .Release.Name | quote }} + heritage: {{ .Release.Service | quote }} + {{- if .Values.metrics.serviceMonitor.additionalLabels }} +{{ toYaml .Values.metrics.serviceMonitor.additionalLabels | indent 4 }} + {{- end }} +spec: + endpoints: + - port: http-metrics + {{- if .Values.metrics.serviceMonitor.interval }} + interval: {{ .Values.metrics.serviceMonitor.interval }} + {{- end }} + {{- if .Values.metrics.serviceMonitor.scrapeTimeout }} + scrapeTimeout: {{ .Values.metrics.serviceMonitor.scrapeTimeout }} + {{- end }} + namespaceSelector: + matchNames: + - {{ .Release.Namespace }} + selector: + matchLabels: + app: {{ template "postgresql.name" . }} + release: {{ .Release.Name }} +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/statefulset-slaves.yaml b/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/statefulset-slaves.yaml new file mode 100644 index 0000000..3290ff7 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/statefulset-slaves.yaml @@ -0,0 +1,299 @@ +{{- if .Values.replication.enabled }} +apiVersion: {{ template "postgresql.statefulset.apiVersion" . }} +kind: StatefulSet +metadata: + name: "{{ template "postgresql.fullname" . }}-slave" + labels: + app: {{ template "postgresql.name" . }} + chart: {{ template "postgresql.chart" . }} + release: {{ .Release.Name | quote }} + heritage: {{ .Release.Service | quote }} +{{- with .Values.slave.labels }} +{{ toYaml . | indent 4 }} +{{- end }} +{{- with .Values.slave.annotations }} + annotations: +{{ toYaml . | indent 4 }} +{{- end }} +spec: + serviceName: {{ template "postgresql.fullname" . }}-headless + replicas: {{ .Values.replication.slaveReplicas }} + selector: + matchLabels: + app: {{ template "postgresql.name" . }} + release: {{ .Release.Name | quote }} + role: slave + template: + metadata: + name: {{ template "postgresql.fullname" . }} + labels: + app: {{ template "postgresql.name" . }} + chart: {{ template "postgresql.chart" . }} + release: {{ .Release.Name | quote }} + heritage: {{ .Release.Service | quote }} + role: slave +{{- with .Values.slave.podLabels }} +{{ toYaml . | indent 8 }} +{{- end }} +{{- with .Values.slave.podAnnotations }} + annotations: +{{ toYaml . | indent 8 }} +{{- end }} + spec: + {{- if .Values.schedulerName }} + schedulerName: "{{ .Values.schedulerName }}" + {{- end }} +{{- include "postgresql.imagePullSecrets" . | indent 6 }} + {{- if .Values.slave.nodeSelector }} + nodeSelector: +{{ toYaml .Values.slave.nodeSelector | indent 8 }} + {{- end }} + {{- if .Values.slave.affinity }} + affinity: +{{ toYaml .Values.slave.affinity | indent 8 }} + {{- end }} + {{- if .Values.slave.tolerations }} + tolerations: +{{ toYaml .Values.slave.tolerations | indent 8 }} + {{- end }} + {{- if .Values.terminationGracePeriodSeconds }} + terminationGracePeriodSeconds: {{ .Values.terminationGracePeriodSeconds }} + {{- end }} + {{- if .Values.securityContext.enabled }} + securityContext: + fsGroup: {{ .Values.securityContext.fsGroup }} + {{- end }} + {{- if .Values.serviceAccount.enabled }} + serviceAccountName: {{ default (include "postgresql.fullname" . ) .Values.serviceAccount.name}} + {{- end }} + {{- if or .Values.slave.extraInitContainers (and .Values.volumePermissions.enabled (or .Values.persistence.enabled (and .Values.shmVolume.enabled .Values.shmVolume.chmod.enabled))) }} + initContainers: + {{- if and .Values.volumePermissions.enabled (or .Values.persistence.enabled (and .Values.shmVolume.enabled .Values.shmVolume.chmod.enabled)) }} + - name: init-chmod-data + image: {{ template "postgresql.volumePermissions.image" . }} + imagePullPolicy: {{ .Values.volumePermissions.image.pullPolicy | quote }} + {{- if .Values.resources }} + resources: {{- toYaml .Values.resources | nindent 12 }} + {{- end }} + command: + - /bin/sh + - -cx + - | + {{ if .Values.persistence.enabled }} + mkdir -p {{ .Values.persistence.mountPath }}/data + chmod 700 {{ .Values.persistence.mountPath }}/data + find {{ .Values.persistence.mountPath }} -mindepth 1 -maxdepth 1 -not -name ".snapshot" -not -name "lost+found" | \ + {{- if eq ( toString ( .Values.volumePermissions.securityContext.runAsUser )) "auto" }} + xargs chown -R `id -u`:`id -G | cut -d " " -f2` + {{- else }} + xargs chown -R {{ .Values.securityContext.runAsUser }}:{{ .Values.securityContext.fsGroup }} + {{- end }} + {{- end }} + {{- if and .Values.shmVolume.enabled .Values.shmVolume.chmod.enabled }} + chmod -R 777 /dev/shm + {{- end }} + {{- if eq ( toString ( .Values.volumePermissions.securityContext.runAsUser )) "auto" }} + securityContext: + {{- else }} + securityContext: + runAsUser: {{ .Values.volumePermissions.securityContext.runAsUser }} + {{- end }} + volumeMounts: + {{ if .Values.persistence.enabled }} + - name: data + mountPath: {{ .Values.persistence.mountPath }} + subPath: {{ .Values.persistence.subPath }} + {{- end }} + {{- if .Values.shmVolume.enabled }} + - name: dshm + mountPath: /dev/shm + {{- end }} + {{- end }} + {{- if .Values.slave.extraInitContainers }} +{{ tpl .Values.slave.extraInitContainers . | indent 8 }} + {{- end }} + {{- end }} + {{- if .Values.slave.priorityClassName }} + priorityClassName: {{ .Values.slave.priorityClassName }} + {{- end }} + containers: + - name: {{ template "postgresql.fullname" . }} + image: {{ template "postgresql.image" . }} + imagePullPolicy: "{{ .Values.image.pullPolicy }}" + {{- if .Values.resources }} + resources: {{- toYaml .Values.resources | nindent 12 }} + {{- end }} + {{- if .Values.securityContext.enabled }} + securityContext: + runAsUser: {{ .Values.securityContext.runAsUser }} + {{- end }} + env: + - name: BITNAMI_DEBUG + value: {{ ternary "true" "false" .Values.image.debug | quote }} + - name: POSTGRESQL_VOLUME_DIR + value: "{{ .Values.persistence.mountPath }}" + - name: POSTGRESQL_PORT_NUMBER + value: "{{ template "postgresql.port" . }}" + {{- if .Values.persistence.mountPath }} + - name: PGDATA + value: {{ .Values.postgresqlDataDir | quote }} + {{- end }} + - name: POSTGRES_REPLICATION_MODE + value: "slave" + - name: POSTGRES_REPLICATION_USER + value: {{ include "postgresql.replication.username" . | quote }} + {{- if .Values.usePasswordFile }} + - name: POSTGRES_REPLICATION_PASSWORD_FILE + value: "/opt/bitnami/postgresql/secrets/postgresql-replication-password" + {{- else }} + - name: POSTGRES_REPLICATION_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "postgresql.secretName" . }} + key: postgresql-replication-password + {{- end }} + - name: POSTGRES_CLUSTER_APP_NAME + value: {{ .Values.replication.applicationName }} + - name: POSTGRES_MASTER_HOST + value: {{ template "postgresql.fullname" . }} + - name: POSTGRES_MASTER_PORT_NUMBER + value: {{ include "postgresql.port" . | quote }} + {{- if and .Values.postgresqlPostgresPassword (not (eq .Values.postgresqlUsername "postgres")) }} + {{- if .Values.usePasswordFile }} + - name: POSTGRES_POSTGRES_PASSWORD_FILE + value: "/opt/bitnami/postgresql/secrets/postgresql-postgres-password" + {{- else }} + - name: POSTGRES_POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "postgresql.secretName" . }} + key: postgresql-postgres-password + {{- end }} + {{- end }} + {{- if .Values.usePasswordFile }} + - name: POSTGRES_PASSWORD_FILE + value: "/opt/bitnami/postgresql/secrets/postgresql-password" + {{- else }} + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "postgresql.secretName" . }} + key: postgresql-password + {{- end }} + ports: + - name: tcp-postgresql + containerPort: {{ template "postgresql.port" . }} + {{- if .Values.livenessProbe.enabled }} + livenessProbe: + exec: + command: + - /bin/sh + - -c + {{- if (include "postgresql.database" .) }} + - exec pg_isready -U {{ include "postgresql.username" . | quote }} -d {{ (include "postgresql.database" .) | quote }} -h 127.0.0.1 -p {{ template "postgresql.port" . }} + {{- else }} + - exec pg_isready -U {{ include "postgresql.username" . | quote }} -h 127.0.0.1 -p {{ template "postgresql.port" . }} + {{- end }} + initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }} + successThreshold: {{ .Values.livenessProbe.successThreshold }} + failureThreshold: {{ .Values.livenessProbe.failureThreshold }} + {{- end }} + {{- if .Values.readinessProbe.enabled }} + readinessProbe: + exec: + command: + - /bin/sh + - -c + - -e + {{- include "postgresql.readinessProbeCommand" . | nindent 16 }} + initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }} + successThreshold: {{ .Values.readinessProbe.successThreshold }} + failureThreshold: {{ .Values.readinessProbe.failureThreshold }} + {{- end }} + volumeMounts: + {{- if .Values.usePasswordFile }} + - name: postgresql-password + mountPath: /opt/bitnami/postgresql/secrets/ + {{- end }} + {{- if .Values.shmVolume.enabled }} + - name: dshm + mountPath: /dev/shm + {{- end }} + {{- if .Values.persistence.enabled }} + - name: data + mountPath: {{ .Values.persistence.mountPath }} + subPath: {{ .Values.persistence.subPath }} + {{ end }} + {{- if or (.Files.Glob "files/conf.d/*.conf") .Values.postgresqlExtendedConf .Values.extendedConfConfigMap }} + - name: postgresql-extended-config + mountPath: /bitnami/postgresql/conf/conf.d/ + {{- end }} + {{- if or (.Files.Glob "files/postgresql.conf") (.Files.Glob "files/pg_hba.conf") .Values.postgresqlConfiguration .Values.pgHbaConfiguration .Values.configurationConfigMap }} + - name: postgresql-config + mountPath: /bitnami/postgresql/conf + {{- end }} + {{- if .Values.slave.extraVolumeMounts }} + {{- toYaml .Values.slave.extraVolumeMounts | nindent 12 }} + {{- end }} +{{- if .Values.slave.sidecars }} +{{- include "postgresql.tplValue" ( dict "value" .Values.slave.sidecars "context" $ ) | nindent 8 }} +{{- end }} + volumes: + {{- if .Values.usePasswordFile }} + - name: postgresql-password + secret: + secretName: {{ template "postgresql.secretName" . }} + {{- end }} + {{- if or (.Files.Glob "files/postgresql.conf") (.Files.Glob "files/pg_hba.conf") .Values.postgresqlConfiguration .Values.pgHbaConfiguration .Values.configurationConfigMap}} + - name: postgresql-config + configMap: + name: {{ template "postgresql.configurationCM" . }} + {{- end }} + {{- if or (.Files.Glob "files/conf.d/*.conf") .Values.postgresqlExtendedConf .Values.extendedConfConfigMap }} + - name: postgresql-extended-config + configMap: + name: {{ template "postgresql.extendedConfigurationCM" . }} + {{- end }} + {{- if .Values.shmVolume.enabled }} + - name: dshm + emptyDir: + medium: Memory + sizeLimit: 1Gi + {{- end }} + {{- if not .Values.persistence.enabled }} + - name: data + emptyDir: {} + {{- end }} + {{- if .Values.slave.extraVolumes }} + {{- toYaml .Values.slave.extraVolumes | nindent 8 }} + {{- end }} + updateStrategy: + type: {{ .Values.updateStrategy.type }} + {{- if (eq "Recreate" .Values.updateStrategy.type) }} + rollingUpdate: null + {{- end }} +{{- if .Values.persistence.enabled }} + volumeClaimTemplates: + - metadata: + name: data + {{- with .Values.persistence.annotations }} + annotations: + {{- range $key, $value := . }} + {{ $key }}: {{ $value }} + {{- end }} + {{- end }} + spec: + accessModes: + {{- range .Values.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.persistence.size | quote }} + {{ include "postgresql.storageClass" . }} +{{- end }} +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/statefulset.yaml b/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/statefulset.yaml new file mode 100644 index 0000000..3390be2 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/statefulset.yaml @@ -0,0 +1,458 @@ +apiVersion: {{ template "postgresql.statefulset.apiVersion" . }} +kind: StatefulSet +metadata: + name: {{ template "postgresql.master.fullname" . }} + labels: + app: {{ template "postgresql.name" . }} + chart: {{ template "postgresql.chart" . }} + release: {{ .Release.Name | quote }} + heritage: {{ .Release.Service | quote }} +{{- with .Values.master.labels }} +{{ toYaml . | indent 4 }} +{{- end }} +{{- with .Values.master.annotations }} + annotations: +{{ toYaml . | indent 4 }} +{{- end }} +spec: + serviceName: {{ template "postgresql.fullname" . }}-headless + replicas: 1 + updateStrategy: + type: {{ .Values.updateStrategy.type }} + {{- if (eq "Recreate" .Values.updateStrategy.type) }} + rollingUpdate: null + {{- end }} + selector: + matchLabels: + app: {{ template "postgresql.name" . }} + release: {{ .Release.Name | quote }} + role: master + template: + metadata: + name: {{ template "postgresql.fullname" . }} + labels: + app: {{ template "postgresql.name" . }} + chart: {{ template "postgresql.chart" . }} + release: {{ .Release.Name | quote }} + heritage: {{ .Release.Service | quote }} + role: master +{{- with .Values.master.podLabels }} +{{ toYaml . | indent 8 }} +{{- end }} +{{- with .Values.master.podAnnotations }} + annotations: +{{ toYaml . | indent 8 }} +{{- end }} + spec: + {{- if .Values.schedulerName }} + schedulerName: "{{ .Values.schedulerName }}" + {{- end }} +{{- include "postgresql.imagePullSecrets" . | indent 6 }} + {{- if .Values.master.nodeSelector }} + nodeSelector: +{{ toYaml .Values.master.nodeSelector | indent 8 }} + {{- end }} + {{- if .Values.master.affinity }} + affinity: +{{ toYaml .Values.master.affinity | indent 8 }} + {{- end }} + {{- if .Values.master.tolerations }} + tolerations: +{{ toYaml .Values.master.tolerations | indent 8 }} + {{- end }} + {{- if .Values.terminationGracePeriodSeconds }} + terminationGracePeriodSeconds: {{ .Values.terminationGracePeriodSeconds }} + {{- end }} + {{- if .Values.securityContext.enabled }} + securityContext: + fsGroup: {{ .Values.securityContext.fsGroup }} + {{- end }} + {{- if .Values.serviceAccount.enabled }} + serviceAccountName: {{ default (include "postgresql.fullname" . ) .Values.serviceAccount.name }} + {{- end }} + {{- if or .Values.master.extraInitContainers (and .Values.volumePermissions.enabled (or .Values.persistence.enabled (and .Values.shmVolume.enabled .Values.shmVolume.chmod.enabled))) }} + initContainers: + {{- if and .Values.volumePermissions.enabled (or .Values.persistence.enabled (and .Values.shmVolume.enabled .Values.shmVolume.chmod.enabled)) }} + - name: init-chmod-data + image: {{ template "postgresql.volumePermissions.image" . }} + imagePullPolicy: {{ .Values.volumePermissions.image.pullPolicy | quote }} + {{- if .Values.resources }} + resources: {{- toYaml .Values.resources | nindent 12 }} + {{- end }} + command: + - /bin/sh + - -cx + - | + {{ if .Values.persistence.enabled }} + mkdir -p {{ .Values.persistence.mountPath }}/data + chmod 700 {{ .Values.persistence.mountPath }}/data + find {{ .Values.persistence.mountPath }} -mindepth 1 -maxdepth 1 -not -name ".snapshot" -not -name "lost+found" | \ + {{- if eq ( toString ( .Values.volumePermissions.securityContext.runAsUser )) "auto" }} + xargs chown -R `id -u`:`id -G | cut -d " " -f2` + {{- else }} + xargs chown -R {{ .Values.securityContext.runAsUser }}:{{ .Values.securityContext.fsGroup }} + {{- end }} + {{- end }} + {{- if and .Values.shmVolume.enabled .Values.shmVolume.chmod.enabled }} + chmod -R 777 /dev/shm + {{- end }} + {{- if eq ( toString ( .Values.volumePermissions.securityContext.runAsUser )) "auto" }} + securityContext: + {{- else }} + securityContext: + runAsUser: {{ .Values.volumePermissions.securityContext.runAsUser }} + {{- end }} + volumeMounts: + {{ if .Values.persistence.enabled }} + - name: data + mountPath: {{ .Values.persistence.mountPath }} + subPath: {{ .Values.persistence.subPath }} + {{- end }} + {{- if .Values.shmVolume.enabled }} + - name: dshm + mountPath: /dev/shm + {{- end }} + {{- end }} + {{- if .Values.master.extraInitContainers }} +{{ tpl .Values.master.extraInitContainers . | indent 8 }} + {{- end }} + {{- end }} + {{- if .Values.master.priorityClassName }} + priorityClassName: {{ .Values.master.priorityClassName }} + {{- end }} + containers: + - name: {{ template "postgresql.fullname" . }} + image: {{ template "postgresql.image" . }} + imagePullPolicy: "{{ .Values.image.pullPolicy }}" + {{- if .Values.resources }} + resources: {{- toYaml .Values.resources | nindent 12 }} + {{- end }} + {{- if .Values.securityContext.enabled }} + securityContext: + runAsUser: {{ .Values.securityContext.runAsUser }} + {{- end }} + env: + - name: BITNAMI_DEBUG + value: {{ ternary "true" "false" .Values.image.debug | quote }} + - name: POSTGRESQL_PORT_NUMBER + value: "{{ template "postgresql.port" . }}" + - name: POSTGRESQL_VOLUME_DIR + value: "{{ .Values.persistence.mountPath }}" + {{- if .Values.postgresqlInitdbArgs }} + - name: POSTGRES_INITDB_ARGS + value: {{ .Values.postgresqlInitdbArgs | quote }} + {{- end }} + {{- if .Values.postgresqlInitdbWalDir }} + - name: POSTGRES_INITDB_WALDIR + value: {{ .Values.postgresqlInitdbWalDir | quote }} + {{- end }} + {{- if .Values.initdbUser }} + - name: POSTGRESQL_INITSCRIPTS_USERNAME + value: {{ .Values.initdbUser }} + {{- end }} + {{- if .Values.initdbPassword }} + - name: POSTGRESQL_INITSCRIPTS_PASSWORD + value: .Values.initdbPassword + {{- end }} + {{- if .Values.persistence.mountPath }} + - name: PGDATA + value: {{ .Values.postgresqlDataDir | quote }} + {{- end }} + {{- if .Values.replication.enabled }} + - name: POSTGRES_REPLICATION_MODE + value: "master" + - name: POSTGRES_REPLICATION_USER + value: {{ include "postgresql.replication.username" . | quote }} + {{- if .Values.usePasswordFile }} + - name: POSTGRES_REPLICATION_PASSWORD_FILE + value: "/opt/bitnami/postgresql/secrets/postgresql-replication-password" + {{- else }} + - name: POSTGRES_REPLICATION_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "postgresql.secretName" . }} + key: postgresql-replication-password + {{- end }} + {{- if not (eq .Values.replication.synchronousCommit "off")}} + - name: POSTGRES_SYNCHRONOUS_COMMIT_MODE + value: {{ .Values.replication.synchronousCommit | quote }} + - name: POSTGRES_NUM_SYNCHRONOUS_REPLICAS + value: {{ .Values.replication.numSynchronousReplicas | quote }} + {{- end }} + - name: POSTGRES_CLUSTER_APP_NAME + value: {{ .Values.replication.applicationName }} + {{- end }} + {{- if and .Values.postgresqlPostgresPassword (not (eq .Values.postgresqlUsername "postgres")) }} + {{- if .Values.usePasswordFile }} + - name: POSTGRES_POSTGRES_PASSWORD_FILE + value: "/opt/bitnami/postgresql/secrets/postgresql-postgres-password" + {{- else }} + - name: POSTGRES_POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "postgresql.secretName" . }} + key: postgresql-postgres-password + {{- end }} + {{- end }} + - name: POSTGRES_USER + value: {{ include "postgresql.username" . | quote }} + {{- if .Values.usePasswordFile }} + - name: POSTGRES_PASSWORD_FILE + value: "/opt/bitnami/postgresql/secrets/postgresql-password" + {{- else }} + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "postgresql.secretName" . }} + key: postgresql-password + {{- end }} + {{- if (include "postgresql.database" .) }} + - name: POSTGRES_DB + value: {{ (include "postgresql.database" .) | quote }} + {{- end }} + {{- if .Values.extraEnv }} + {{- include "postgresql.tplValue" (dict "value" .Values.extraEnv "context" $) | nindent 12 }} + {{- end }} + - name: POSTGRESQL_ENABLE_LDAP + value: {{ ternary "yes" "no" .Values.ldap.enabled | quote }} + {{- if .Values.ldap.enabled }} + - name: POSTGRESQL_LDAP_SERVER + value: {{ .Values.ldap.server }} + - name: POSTGRESQL_LDAP_PORT + value: {{ .Values.ldap.port | quote }} + - name: POSTGRESQL_LDAP_SCHEME + value: {{ .Values.ldap.scheme }} + {{- if .Values.ldap.tls }} + - name: POSTGRESQL_LDAP_TLS + value: "1" + {{- end}} + - name: POSTGRESQL_LDAP_PREFIX + value: {{ .Values.ldap.prefix | quote }} + - name: POSTGRESQL_LDAP_SUFFIX + value: {{ .Values.ldap.suffix | quote}} + - name: POSTGRESQL_LDAP_BASE_DN + value: {{ .Values.ldap.baseDN }} + - name: POSTGRESQL_LDAP_BIND_DN + value: {{ .Values.ldap.bindDN }} + {{- if (not (empty .Values.ldap.bind_password)) }} + - name: POSTGRESQL_LDAP_BIND_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "postgresql.secretName" . }} + key: postgresql-ldap-password + {{- end}} + - name: POSTGRESQL_LDAP_SEARCH_ATTR + value: {{ .Values.ldap.search_attr }} + - name: POSTGRESQL_LDAP_SEARCH_FILTER + value: {{ .Values.ldap.search_filter }} + - name: POSTGRESQL_LDAP_URL + value: {{ .Values.ldap.url }} + {{- end}} + {{- if .Values.extraEnvVarsCM }} + envFrom: + - configMapRef: + name: {{ .Values.extraEnvVarsCM }} + {{- end }} + ports: + - name: tcp-postgresql + containerPort: {{ template "postgresql.port" . }} + {{- if .Values.livenessProbe.enabled }} + livenessProbe: + exec: + command: + - /bin/sh + - -c + {{- if (include "postgresql.database" .) }} + - exec pg_isready -U {{ include "postgresql.username" . | quote }} -d {{ (include "postgresql.database" .) | quote }} -h 127.0.0.1 -p {{ template "postgresql.port" . }} + {{- else }} + - exec pg_isready -U {{ include "postgresql.username" . | quote }} -h 127.0.0.1 -p {{ template "postgresql.port" . }} + {{- end }} + initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }} + successThreshold: {{ .Values.livenessProbe.successThreshold }} + failureThreshold: {{ .Values.livenessProbe.failureThreshold }} + {{- end }} + {{- if .Values.readinessProbe.enabled }} + readinessProbe: + exec: + command: + - /bin/sh + - -c + - -e + {{- include "postgresql.readinessProbeCommand" . | nindent 16 }} + initialDelaySeconds: {{ .Values.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }} + successThreshold: {{ .Values.readinessProbe.successThreshold }} + failureThreshold: {{ .Values.readinessProbe.failureThreshold }} + {{- end }} + volumeMounts: + {{- if or (.Files.Glob "files/docker-entrypoint-initdb.d/*.{sh,sql,sql.gz}") .Values.initdbScriptsConfigMap .Values.initdbScripts }} + - name: custom-init-scripts + mountPath: /docker-entrypoint-initdb.d/ + {{- end }} + {{- if .Values.initdbScriptsSecret }} + - name: custom-init-scripts-secret + mountPath: /docker-entrypoint-initdb.d/secret + {{- end }} + {{- if or (.Files.Glob "files/conf.d/*.conf") .Values.postgresqlExtendedConf .Values.extendedConfConfigMap }} + - name: postgresql-extended-config + mountPath: /bitnami/postgresql/conf/conf.d/ + {{- end }} + {{- if .Values.usePasswordFile }} + - name: postgresql-password + mountPath: /opt/bitnami/postgresql/secrets/ + {{- end }} + {{- if .Values.shmVolume.enabled }} + - name: dshm + mountPath: /dev/shm + {{- end }} + {{- if .Values.persistence.enabled }} + - name: data + mountPath: {{ .Values.persistence.mountPath }} + subPath: {{ .Values.persistence.subPath }} + {{- end }} + {{- if or (.Files.Glob "files/postgresql.conf") (.Files.Glob "files/pg_hba.conf") .Values.postgresqlConfiguration .Values.pgHbaConfiguration .Values.configurationConfigMap }} + - name: postgresql-config + mountPath: /bitnami/postgresql/conf + {{- end }} + {{- if .Values.master.extraVolumeMounts }} + {{- toYaml .Values.master.extraVolumeMounts | nindent 12 }} + {{- end }} +{{- if .Values.master.sidecars }} +{{- include "postgresql.tplValue" ( dict "value" .Values.master.sidecars "context" $ ) | nindent 8 }} +{{- end }} +{{- if .Values.metrics.enabled }} + - name: metrics + image: {{ template "postgresql.metrics.image" . }} + imagePullPolicy: {{ .Values.metrics.image.pullPolicy | quote }} + {{- if .Values.metrics.securityContext.enabled }} + securityContext: + runAsUser: {{ .Values.metrics.securityContext.runAsUser }} + {{- end }} + env: + {{- $database := required "In order to enable metrics you need to specify a database (.Values.postgresqlDatabase or .Values.global.postgresql.postgresqlDatabase)" (include "postgresql.database" .) }} + - name: DATA_SOURCE_URI + value: {{ printf "127.0.0.1:%d/%s?sslmode=disable" (int (include "postgresql.port" .)) $database | quote }} + {{- if .Values.usePasswordFile }} + - name: DATA_SOURCE_PASS_FILE + value: "/opt/bitnami/postgresql/secrets/postgresql-password" + {{- else }} + - name: DATA_SOURCE_PASS + valueFrom: + secretKeyRef: + name: {{ template "postgresql.secretName" . }} + key: postgresql-password + {{- end }} + - name: DATA_SOURCE_USER + value: {{ template "postgresql.username" . }} + {{- if .Values.livenessProbe.enabled }} + livenessProbe: + httpGet: + path: / + port: http-metrics + initialDelaySeconds: {{ .Values.metrics.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.metrics.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.metrics.livenessProbe.timeoutSeconds }} + successThreshold: {{ .Values.metrics.livenessProbe.successThreshold }} + failureThreshold: {{ .Values.metrics.livenessProbe.failureThreshold }} + {{- end }} + {{- if .Values.readinessProbe.enabled }} + readinessProbe: + httpGet: + path: / + port: http-metrics + initialDelaySeconds: {{ .Values.metrics.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.metrics.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.metrics.readinessProbe.timeoutSeconds }} + successThreshold: {{ .Values.metrics.readinessProbe.successThreshold }} + failureThreshold: {{ .Values.metrics.readinessProbe.failureThreshold }} + {{- end }} + volumeMounts: + {{- if .Values.usePasswordFile }} + - name: postgresql-password + mountPath: /opt/bitnami/postgresql/secrets/ + {{- end }} + {{- if .Values.metrics.customMetrics }} + - name: custom-metrics + mountPath: /conf + readOnly: true + args: ["--extend.query-path", "/conf/custom-metrics.yaml"] + {{- end }} + ports: + - name: http-metrics + containerPort: 9187 + {{- if .Values.metrics.resources }} + resources: {{- toYaml .Values.metrics.resources | nindent 12 }} + {{- end }} +{{- end }} + volumes: + {{- if or (.Files.Glob "files/postgresql.conf") (.Files.Glob "files/pg_hba.conf") .Values.postgresqlConfiguration .Values.pgHbaConfiguration .Values.configurationConfigMap}} + - name: postgresql-config + configMap: + name: {{ template "postgresql.configurationCM" . }} + {{- end }} + {{- if or (.Files.Glob "files/conf.d/*.conf") .Values.postgresqlExtendedConf .Values.extendedConfConfigMap }} + - name: postgresql-extended-config + configMap: + name: {{ template "postgresql.extendedConfigurationCM" . }} + {{- end }} + {{- if .Values.usePasswordFile }} + - name: postgresql-password + secret: + secretName: {{ template "postgresql.secretName" . }} + {{- end }} + {{- if or (.Files.Glob "files/docker-entrypoint-initdb.d/*.{sh,sql,sql.gz}") .Values.initdbScriptsConfigMap .Values.initdbScripts }} + - name: custom-init-scripts + configMap: + name: {{ template "postgresql.initdbScriptsCM" . }} + {{- end }} + {{- if .Values.initdbScriptsSecret }} + - name: custom-init-scripts-secret + secret: + secretName: {{ template "postgresql.initdbScriptsSecret" . }} + {{- end }} + {{- if .Values.master.extraVolumes }} + {{- toYaml .Values.master.extraVolumes | nindent 8 }} + {{- end }} + {{- if and .Values.metrics.enabled .Values.metrics.customMetrics }} + - name: custom-metrics + configMap: + name: {{ template "postgresql.metricsCM" . }} + {{- end }} + {{- if .Values.shmVolume.enabled }} + - name: dshm + emptyDir: + medium: Memory + sizeLimit: 1Gi + {{- end }} +{{- if and .Values.persistence.enabled .Values.persistence.existingClaim }} + - name: data + persistentVolumeClaim: +{{- with .Values.persistence.existingClaim }} + claimName: {{ tpl . $ }} +{{- end }} +{{- else if not .Values.persistence.enabled }} + - name: data + emptyDir: {} +{{- else if and .Values.persistence.enabled (not .Values.persistence.existingClaim) }} + volumeClaimTemplates: + - metadata: + name: data + {{- with .Values.persistence.annotations }} + annotations: + {{- range $key, $value := . }} + {{ $key }}: {{ $value }} + {{- end }} + {{- end }} + spec: + accessModes: + {{- range .Values.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.persistence.size | quote }} + {{ include "postgresql.storageClass" . }} +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/svc-headless.yaml b/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/svc-headless.yaml new file mode 100644 index 0000000..5c71f46 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/svc-headless.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ template "postgresql.fullname" . }}-headless + labels: + app: {{ template "postgresql.name" . }} + chart: {{ template "postgresql.chart" . }} + release: {{ .Release.Name | quote }} + heritage: {{ .Release.Service | quote }} +spec: + type: ClusterIP + clusterIP: None + ports: + - name: tcp-postgresql + port: {{ template "postgresql.port" . }} + targetPort: tcp-postgresql + selector: + app: {{ template "postgresql.name" . }} + release: {{ .Release.Name | quote }} diff --git a/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/svc-read.yaml b/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/svc-read.yaml new file mode 100644 index 0000000..d9492e2 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/svc-read.yaml @@ -0,0 +1,31 @@ +{{- if .Values.replication.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "postgresql.fullname" . }}-read + labels: + app: {{ template "postgresql.name" . }} + chart: {{ template "postgresql.chart" . }} + release: {{ .Release.Name | quote }} + heritage: {{ .Release.Service | quote }} +{{- with .Values.service.annotations }} + annotations: +{{ toYaml . | indent 4 }} +{{- end }} +spec: + type: {{ .Values.service.type }} + {{- if and .Values.service.loadBalancerIP (eq .Values.service.type "LoadBalancer") }} + loadBalancerIP: {{ .Values.service.loadBalancerIP }} + {{- end }} + ports: + - name: tcp-postgresql + port: {{ template "postgresql.port" . }} + targetPort: tcp-postgresql + {{- if .Values.service.nodePort }} + nodePort: {{ .Values.service.nodePort }} + {{- end }} + selector: + app: {{ template "postgresql.name" . }} + release: {{ .Release.Name | quote }} + role: slave +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/svc.yaml b/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/svc.yaml new file mode 100644 index 0000000..0baea4a --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/postgresql/templates/svc.yaml @@ -0,0 +1,38 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ template "postgresql.fullname" . }} + labels: + app: {{ template "postgresql.name" . }} + chart: {{ template "postgresql.chart" . }} + release: {{ .Release.Name | quote }} + heritage: {{ .Release.Service | quote }} +{{- with .Values.service.annotations }} + annotations: +{{ tpl (toYaml .) $ | indent 4 }} +{{- end }} +spec: + type: {{ .Values.service.type }} + {{- if and .Values.service.loadBalancerIP (eq .Values.service.type "LoadBalancer") }} + loadBalancerIP: {{ .Values.service.loadBalancerIP }} + {{- end }} + {{- if and (eq .Values.service.type "LoadBalancer") .Values.service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: + {{ with .Values.service.loadBalancerSourceRanges }} +{{ toYaml . | indent 4 }} +{{- end }} + {{- end }} + {{- if and (eq .Values.service.type "ClusterIP") .Values.service.clusterIP }} + clusterIP: {{ .Values.service.clusterIP }} + {{- end }} + ports: + - name: tcp-postgresql + port: {{ template "postgresql.port" . }} + targetPort: tcp-postgresql + {{- if .Values.service.nodePort }} + nodePort: {{ .Values.service.nodePort }} + {{- end }} + selector: + app: {{ template "postgresql.name" . }} + release: {{ .Release.Name | quote }} + role: master diff --git a/charts/deps/charts/airflow-8.8.0/charts/postgresql/values-production.yaml b/charts/deps/charts/airflow-8.8.0/charts/postgresql/values-production.yaml new file mode 100644 index 0000000..8da0b3d --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/postgresql/values-production.yaml @@ -0,0 +1,520 @@ +## Global Docker image parameters +## Please, note that this will override the image parameters, including dependencies, configured to use the global value +## Current available global Docker image parameters: imageRegistry and imagePullSecrets +## +global: + postgresql: {} +# imageRegistry: myRegistryName +# imagePullSecrets: +# - myRegistryKeySecretName +# storageClass: myStorageClass + +## Bitnami PostgreSQL image version +## ref: https://hub.docker.com/r/bitnami/postgresql/tags/ +## +image: + registry: docker.io + repository: bitnami/postgresql + tag: 11.7.0-debian-10-r9 + ## Specify a imagePullPolicy + ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images + ## + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + + ## Set to true if you would like to see extra information on logs + ## It turns BASH and NAMI debugging in minideb + ## ref: https://github.com/bitnami/minideb-extras/#turn-on-bash-debugging + debug: false + +## String to partially override postgresql.fullname template (will maintain the release name) +## +# nameOverride: + +## String to fully override postgresql.fullname template +## +# fullnameOverride: + +## +## Init containers parameters: +## volumePermissions: Change the owner of the persist volume mountpoint to RunAsUser:fsGroup +## +volumePermissions: + enabled: false + image: + registry: docker.io + repository: bitnami/minideb + tag: buster + ## Specify a imagePullPolicy + ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images + ## + pullPolicy: Always + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + ## Init container Security Context + ## Note: the chown of the data folder is done to securityContext.runAsUser + ## and not the below volumePermissions.securityContext.runAsUser + ## When runAsUser is set to special value "auto", init container will try to chwon the + ## data folder to autodetermined user&group, using commands: `id -u`:`id -G | cut -d" " -f2` + ## "auto" is especially useful for OpenShift which has scc with dynamic userids (and 0 is not allowed). + ## You may want to use this volumePermissions.securityContext.runAsUser="auto" in combination with + ## pod securityContext.enabled=false and shmVolume.chmod.enabled=false + ## + securityContext: + runAsUser: 0 + +## Use an alternate scheduler, e.g. "stork". +## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ +## +# schedulerName: + +## Pod Security Context +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ +## +securityContext: + enabled: true + fsGroup: 1001 + runAsUser: 1001 + +## Pod Service Account +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ +serviceAccount: + enabled: false + ## Name of an already existing service account. Setting this value disables the automatic service account creation. + # name: + +replication: + enabled: true + user: repl_user + password: repl_password + slaveReplicas: 2 + ## Set synchronous commit mode: on, off, remote_apply, remote_write and local + ## ref: https://www.postgresql.org/docs/9.6/runtime-config-wal.html#GUC-WAL-LEVEL + synchronousCommit: "on" + ## From the number of `slaveReplicas` defined above, set the number of those that will have synchronous replication + ## NOTE: It cannot be > slaveReplicas + numSynchronousReplicas: 1 + ## Replication Cluster application name. Useful for defining multiple replication policies + applicationName: my_application + +## PostgreSQL admin password (used when `postgresqlUsername` is not `postgres`) +## ref: https://github.com/bitnami/bitnami-docker-postgresql/blob/master/README.md#creating-a-database-user-on-first-run (see note!) +# postgresqlPostgresPassword: + +## PostgreSQL user (has superuser privileges if username is `postgres`) +## ref: https://github.com/bitnami/bitnami-docker-postgresql/blob/master/README.md#setting-the-root-password-on-first-run +postgresqlUsername: postgres + +## PostgreSQL password +## ref: https://github.com/bitnami/bitnami-docker-postgresql/blob/master/README.md#setting-the-root-password-on-first-run +## +# postgresqlPassword: + +## PostgreSQL password using existing secret +## existingSecret: secret + +## Mount PostgreSQL secret as a file instead of passing environment variable +# usePasswordFile: false + +## Create a database +## ref: https://github.com/bitnami/bitnami-docker-postgresql/blob/master/README.md#creating-a-database-on-first-run +## +# postgresqlDatabase: + +## PostgreSQL data dir +## ref: https://github.com/bitnami/bitnami-docker-postgresql/blob/master/README.md +## +postgresqlDataDir: /bitnami/postgresql/data + +## An array to add extra environment variables +## For example: +## extraEnv: +## - name: FOO +## value: "bar" +## +# extraEnv: +extraEnv: [] + +## Name of a ConfigMap containing extra env vars +## +# extraEnvVarsCM: + +## Specify extra initdb args +## ref: https://github.com/bitnami/bitnami-docker-postgresql/blob/master/README.md +## +# postgresqlInitdbArgs: + +## Specify a custom location for the PostgreSQL transaction log +## ref: https://github.com/bitnami/bitnami-docker-postgresql/blob/master/README.md +## +# postgresqlInitdbWalDir: + +## PostgreSQL configuration +## Specify runtime configuration parameters as a dict, using camelCase, e.g. +## {"sharedBuffers": "500MB"} +## Alternatively, you can put your postgresql.conf under the files/ directory +## ref: https://www.postgresql.org/docs/current/static/runtime-config.html +## +# postgresqlConfiguration: + +## PostgreSQL extended configuration +## As above, but _appended_ to the main configuration +## Alternatively, you can put your *.conf under the files/conf.d/ directory +## https://github.com/bitnami/bitnami-docker-postgresql#allow-settings-to-be-loaded-from-files-other-than-the-default-postgresqlconf +## +# postgresqlExtendedConf: + +## PostgreSQL client authentication configuration +## Specify content for pg_hba.conf +## Default: do not create pg_hba.conf +## Alternatively, you can put your pg_hba.conf under the files/ directory +# pgHbaConfiguration: |- +# local all all trust +# host all all localhost trust +# host mydatabase mysuser 192.168.0.0/24 md5 + +## ConfigMap with PostgreSQL configuration +## NOTE: This will override postgresqlConfiguration and pgHbaConfiguration +# configurationConfigMap: + +## ConfigMap with PostgreSQL extended configuration +# extendedConfConfigMap: + +## initdb scripts +## Specify dictionary of scripts to be run at first boot +## Alternatively, you can put your scripts under the files/docker-entrypoint-initdb.d directory +## +# initdbScripts: +# my_init_script.sh: | +# #!/bin/sh +# echo "Do something." + +## Specify the PostgreSQL username and password to execute the initdb scripts +# initdbUser: +# initdbPassword: + +## ConfigMap with scripts to be run at first boot +## NOTE: This will override initdbScripts +# initdbScriptsConfigMap: + +## Secret with scripts to be run at first boot (in case it contains sensitive information) +## NOTE: This can work along initdbScripts or initdbScriptsConfigMap +# initdbScriptsSecret: + +## Optional duration in seconds the pod needs to terminate gracefully. +## ref: https://kubernetes.io/docs/concepts/workloads/pods/pod/#termination-of-pods +## +# terminationGracePeriodSeconds: 30 + +## LDAP configuration +## +ldap: + enabled: false + url: "" + server: "" + port: "" + prefix: "" + suffix: "" + baseDN: "" + bindDN: "" + bind_password: + search_attr: "" + search_filter: "" + scheme: "" + tls: false + +## PostgreSQL service configuration +service: + ## PosgresSQL service type + type: ClusterIP + # clusterIP: None + port: 5432 + + ## Specify the nodePort value for the LoadBalancer and NodePort service types. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport + ## + # nodePort: + + ## Provide any additional annotations which may be required. + ## The value is evaluated as a template, so, for example, the value can depend on .Release or .Chart + annotations: {} + ## Set the LoadBalancer service type to internal only. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer + ## + # loadBalancerIP: + + ## Load Balancer sources + ## https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/#restrict-access-for-loadbalancer-service + ## + # loadBalancerSourceRanges: + # - 10.10.10.0/24 + +## Start master and slave(s) pod(s) without limitations on shm memory. +## By default docker and containerd (and possibly other container runtimes) +## limit `/dev/shm` to `64M` (see e.g. the +## [docker issue](https://github.com/docker-library/postgres/issues/416) and the +## [containerd issue](https://github.com/containerd/containerd/issues/3654), +## which could be not enough if PostgreSQL uses parallel workers heavily. +## +shmVolume: + ## Set `shmVolume.enabled` to `true` to mount a new tmpfs volume to remove + ## this limitation. + ## + enabled: true + ## Set to `true` to `chmod 777 /dev/shm` on a initContainer. + ## This option is ingored if `volumePermissions.enabled` is `false` + ## + chmod: + enabled: true + +## PostgreSQL data Persistent Volume Storage Class +## If defined, storageClassName: +## If set to "-", storageClassName: "", which disables dynamic provisioning +## If undefined (the default) or set to null, no storageClassName spec is +## set, choosing the default provisioner. (gp2 on AWS, standard on +## GKE, AWS & OpenStack) +## +persistence: + enabled: true + ## A manually managed Persistent Volume and Claim + ## If defined, PVC must be created manually before volume will be bound + ## The value is evaluated as a template, so, for example, the name can depend on .Release or .Chart + ## + # existingClaim: + + ## The path the volume will be mounted at, useful when using different + ## PostgreSQL images. + ## + mountPath: /bitnami/postgresql + + ## The subdirectory of the volume to mount to, useful in dev environments + ## and one PV for multiple services. + ## + subPath: "" + + # storageClass: "-" + accessModes: + - ReadWriteOnce + size: 8Gi + annotations: {} + +## updateStrategy for PostgreSQL StatefulSet and its slaves StatefulSets +## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies +updateStrategy: + type: RollingUpdate + +## +## PostgreSQL Master parameters +## +master: + ## Node, affinity, tolerations, and priorityclass settings for pod assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector + ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity + ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#taints-and-tolerations-beta-feature + ## ref: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption + nodeSelector: {} + affinity: {} + tolerations: [] + labels: {} + annotations: {} + podLabels: {} + podAnnotations: {} + priorityClassName: "" + ## Additional PostgreSQL Master Volume mounts + ## + extraVolumeMounts: [] + ## Additional PostgreSQL Master Volumes + ## + extraVolumes: [] + ## Add sidecars to the pod + ## + ## For example: + ## sidecars: + ## - name: your-image-name + ## image: your-image + ## imagePullPolicy: Always + ## ports: + ## - name: portname + ## containerPort: 1234 + sidecars: [] + +## +## PostgreSQL Slave parameters +## +slave: + ## Node, affinity, tolerations, and priorityclass settings for pod assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector + ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity + ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#taints-and-tolerations-beta-feature + ## ref: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption + nodeSelector: {} + affinity: {} + tolerations: [] + labels: {} + annotations: {} + podLabels: {} + podAnnotations: {} + priorityClassName: "" + extraInitContainers: | + # - name: do-something + # image: busybox + # command: ['do', 'something'] + ## Additional PostgreSQL Slave Volume mounts + ## + extraVolumeMounts: [] + ## Additional PostgreSQL Slave Volumes + ## + extraVolumes: [] + ## Add sidecars to the pod + ## + ## For example: + ## sidecars: + ## - name: your-image-name + ## image: your-image + ## imagePullPolicy: Always + ## ports: + ## - name: portname + ## containerPort: 1234 + sidecars: [] + +## Configure resource requests and limits +## ref: http://kubernetes.io/docs/user-guide/compute-resources/ +## +resources: + requests: + memory: 256Mi + cpu: 250m + +networkPolicy: + ## Enable creation of NetworkPolicy resources. Only Ingress traffic is filtered for now. + ## + enabled: false + + ## The Policy model to apply. When set to false, only pods with the correct + ## client label will have network access to the port PostgreSQL is listening + ## on. When true, PostgreSQL will accept connections from any source + ## (with the correct destination port). + ## + allowExternal: true + + ## if explicitNamespacesSelector is missing or set to {}, only client Pods that are in the networkPolicy's namespace + ## and that match other criteria, the ones that have the good label, can reach the DB. + ## But sometimes, we want the DB to be accessible to clients from other namespaces, in this case, we can use this + ## LabelSelector to select these namespaces, note that the networkPolicy's namespace should also be explicitly added. + ## + # explicitNamespacesSelector: + # matchLabels: + # role: frontend + # matchExpressions: + # - {key: role, operator: In, values: [frontend]} + +## Configure extra options for liveness and readiness probes +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes) +livenessProbe: + enabled: true + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 + +readinessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 + +## Configure metrics exporter +## +metrics: + enabled: true + # resources: {} + service: + type: ClusterIP + annotations: + prometheus.io/scrape: "true" + prometheus.io/port: "9187" + loadBalancerIP: + serviceMonitor: + enabled: false + additionalLabels: {} + # namespace: monitoring + # interval: 30s + # scrapeTimeout: 10s + ## Custom PrometheusRule to be defined + ## The value is evaluated as a template, so, for example, the value can depend on .Release or .Chart + ## ref: https://github.com/coreos/prometheus-operator#customresourcedefinitions + prometheusRule: + enabled: false + additionalLabels: {} + namespace: "" + rules: [] + ## These are just examples rules, please adapt them to your needs. + ## Make sure to constraint the rules to the current postgresql service. + # - alert: HugeReplicationLag + # expr: pg_replication_lag{service="{{ template "postgresql.fullname" . }}-metrics"} / 3600 > 1 + # for: 1m + # labels: + # severity: critical + # annotations: + # description: replication for {{ template "postgresql.fullname" . }} PostgreSQL is lagging by {{ "{{ $value }}" }} hour(s). + # summary: PostgreSQL replication is lagging by {{ "{{ $value }}" }} hour(s). + image: + registry: docker.io + repository: bitnami/postgres-exporter + tag: 0.8.0-debian-10-r28 + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + ## Define additional custom metrics + ## ref: https://github.com/wrouesnel/postgres_exporter#adding-new-metrics-via-a-config-file + # customMetrics: + # pg_database: + # query: "SELECT d.datname AS name, CASE WHEN pg_catalog.has_database_privilege(d.datname, 'CONNECT') THEN pg_catalog.pg_database_size(d.datname) ELSE 0 END AS size FROM pg_catalog.pg_database d where datname not in ('template0', 'template1', 'postgres')" + # metrics: + # - name: + # usage: "LABEL" + # description: "Name of the database" + # - size_bytes: + # usage: "GAUGE" + # description: "Size of the database in bytes" + ## Pod Security Context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ + ## + securityContext: + enabled: false + runAsUser: 1001 + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes) + ## Configure extra options for liveness and readiness probes + livenessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 + + readinessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 diff --git a/charts/deps/charts/airflow-8.8.0/charts/postgresql/values.schema.json b/charts/deps/charts/airflow-8.8.0/charts/postgresql/values.schema.json new file mode 100644 index 0000000..ac2de6e --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/postgresql/values.schema.json @@ -0,0 +1,103 @@ +{ + "$schema": "http://json-schema.org/schema#", + "type": "object", + "properties": { + "postgresqlUsername": { + "type": "string", + "title": "Admin user", + "form": true + }, + "postgresqlPassword": { + "type": "string", + "title": "Password", + "form": true + }, + "persistence": { + "type": "object", + "properties": { + "size": { + "type": "string", + "title": "Persistent Volume Size", + "form": true, + "render": "slider", + "sliderMin": 1, + "sliderMax": 100, + "sliderUnit": "Gi" + } + } + }, + "resources": { + "type": "object", + "title": "Required Resources", + "description": "Configure resource requests", + "form": true, + "properties": { + "requests": { + "type": "object", + "properties": { + "memory": { + "type": "string", + "form": true, + "render": "slider", + "title": "Memory Request", + "sliderMin": 10, + "sliderMax": 2048, + "sliderUnit": "Mi" + }, + "cpu": { + "type": "string", + "form": true, + "render": "slider", + "title": "CPU Request", + "sliderMin": 10, + "sliderMax": 2000, + "sliderUnit": "m" + } + } + } + } + }, + "replication": { + "type": "object", + "form": true, + "title": "Replication Details", + "properties": { + "enabled": { + "type": "boolean", + "title": "Enable Replication", + "form": true + }, + "slaveReplicas": { + "type": "integer", + "title": "Slave Replicas", + "form": true, + "hidden": { + "condition": false, + "value": "replication.enabled" + } + } + } + }, + "volumePermissions": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "form": true, + "title": "Enable Init Containers", + "description": "Change the owner of the persist volume mountpoint to RunAsUser:fsGroup" + } + } + }, + "metrics": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "title": "Configure metrics exporter", + "form": true + } + } + } + } +} diff --git a/charts/deps/charts/airflow-8.8.0/charts/postgresql/values.yaml b/charts/deps/charts/airflow-8.8.0/charts/postgresql/values.yaml new file mode 100644 index 0000000..d336ea0 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/postgresql/values.yaml @@ -0,0 +1,526 @@ +## Global Docker image parameters +## Please, note that this will override the image parameters, including dependencies, configured to use the global value +## Current available global Docker image parameters: imageRegistry and imagePullSecrets +## +global: + postgresql: {} +# imageRegistry: myRegistryName +# imagePullSecrets: +# - myRegistryKeySecretName +# storageClass: myStorageClass + +## Bitnami PostgreSQL image version +## ref: https://hub.docker.com/r/bitnami/postgresql/tags/ +## +image: + registry: docker.io + repository: bitnami/postgresql + tag: 11.7.0-debian-10-r9 + ## Specify a imagePullPolicy + ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images + ## + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + + ## Set to true if you would like to see extra information on logs + ## It turns BASH and NAMI debugging in minideb + ## ref: https://github.com/bitnami/minideb-extras/#turn-on-bash-debugging + debug: false + +## String to partially override postgresql.fullname template (will maintain the release name) +## +# nameOverride: + +## String to fully override postgresql.fullname template +## +# fullnameOverride: + +## +## Init containers parameters: +## volumePermissions: Change the owner of the persist volume mountpoint to RunAsUser:fsGroup +## +volumePermissions: + enabled: false + image: + registry: docker.io + repository: bitnami/minideb + tag: buster + ## Specify a imagePullPolicy + ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images + ## + pullPolicy: Always + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + ## Init container Security Context + ## Note: the chown of the data folder is done to securityContext.runAsUser + ## and not the below volumePermissions.securityContext.runAsUser + ## When runAsUser is set to special value "auto", init container will try to chwon the + ## data folder to autodetermined user&group, using commands: `id -u`:`id -G | cut -d" " -f2` + ## "auto" is especially useful for OpenShift which has scc with dynamic userids (and 0 is not allowed). + ## You may want to use this volumePermissions.securityContext.runAsUser="auto" in combination with + ## pod securityContext.enabled=false and shmVolume.chmod.enabled=false + ## + securityContext: + runAsUser: 0 + +## Use an alternate scheduler, e.g. "stork". +## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ +## +# schedulerName: + + +## Pod Security Context +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ +## +securityContext: + enabled: true + fsGroup: 1001 + runAsUser: 1001 + +## Pod Service Account +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ +serviceAccount: + enabled: false + ## Name of an already existing service account. Setting this value disables the automatic service account creation. + # name: + +replication: + enabled: false + user: repl_user + password: repl_password + slaveReplicas: 1 + ## Set synchronous commit mode: on, off, remote_apply, remote_write and local + ## ref: https://www.postgresql.org/docs/9.6/runtime-config-wal.html#GUC-WAL-LEVEL + synchronousCommit: "off" + ## From the number of `slaveReplicas` defined above, set the number of those that will have synchronous replication + ## NOTE: It cannot be > slaveReplicas + numSynchronousReplicas: 0 + ## Replication Cluster application name. Useful for defining multiple replication policies + applicationName: my_application + +## PostgreSQL admin password (used when `postgresqlUsername` is not `postgres`) +## ref: https://github.com/bitnami/bitnami-docker-postgresql/blob/master/README.md#creating-a-database-user-on-first-run (see note!) +# postgresqlPostgresPassword: + +## PostgreSQL user (has superuser privileges if username is `postgres`) +## ref: https://github.com/bitnami/bitnami-docker-postgresql/blob/master/README.md#setting-the-root-password-on-first-run +postgresqlUsername: postgres + +## PostgreSQL password +## ref: https://github.com/bitnami/bitnami-docker-postgresql/blob/master/README.md#setting-the-root-password-on-first-run +## +# postgresqlPassword: + +## PostgreSQL password using existing secret +## existingSecret: secret + +## Mount PostgreSQL secret as a file instead of passing environment variable +# usePasswordFile: false + +## Create a database +## ref: https://github.com/bitnami/bitnami-docker-postgresql/blob/master/README.md#creating-a-database-on-first-run +## +# postgresqlDatabase: + +## PostgreSQL data dir +## ref: https://github.com/bitnami/bitnami-docker-postgresql/blob/master/README.md +## +postgresqlDataDir: /bitnami/postgresql/data + +## An array to add extra environment variables +## For example: +## extraEnv: +## - name: FOO +## value: "bar" +## +# extraEnv: +extraEnv: [] + +## Name of a ConfigMap containing extra env vars +## +# extraEnvVarsCM: + +## Specify extra initdb args +## ref: https://github.com/bitnami/bitnami-docker-postgresql/blob/master/README.md +## +# postgresqlInitdbArgs: + +## Specify a custom location for the PostgreSQL transaction log +## ref: https://github.com/bitnami/bitnami-docker-postgresql/blob/master/README.md +## +# postgresqlInitdbWalDir: + +## PostgreSQL configuration +## Specify runtime configuration parameters as a dict, using camelCase, e.g. +## {"sharedBuffers": "500MB"} +## Alternatively, you can put your postgresql.conf under the files/ directory +## ref: https://www.postgresql.org/docs/current/static/runtime-config.html +## +# postgresqlConfiguration: + +## PostgreSQL extended configuration +## As above, but _appended_ to the main configuration +## Alternatively, you can put your *.conf under the files/conf.d/ directory +## https://github.com/bitnami/bitnami-docker-postgresql#allow-settings-to-be-loaded-from-files-other-than-the-default-postgresqlconf +## +# postgresqlExtendedConf: + +## PostgreSQL client authentication configuration +## Specify content for pg_hba.conf +## Default: do not create pg_hba.conf +## Alternatively, you can put your pg_hba.conf under the files/ directory +# pgHbaConfiguration: |- +# local all all trust +# host all all localhost trust +# host mydatabase mysuser 192.168.0.0/24 md5 + +## ConfigMap with PostgreSQL configuration +## NOTE: This will override postgresqlConfiguration and pgHbaConfiguration +# configurationConfigMap: + +## ConfigMap with PostgreSQL extended configuration +# extendedConfConfigMap: + +## initdb scripts +## Specify dictionary of scripts to be run at first boot +## Alternatively, you can put your scripts under the files/docker-entrypoint-initdb.d directory +## +# initdbScripts: +# my_init_script.sh: | +# #!/bin/sh +# echo "Do something." + +## ConfigMap with scripts to be run at first boot +## NOTE: This will override initdbScripts +# initdbScriptsConfigMap: + +## Secret with scripts to be run at first boot (in case it contains sensitive information) +## NOTE: This can work along initdbScripts or initdbScriptsConfigMap +# initdbScriptsSecret: + +## Specify the PostgreSQL username and password to execute the initdb scripts +# initdbUser: +# initdbPassword: + +## Optional duration in seconds the pod needs to terminate gracefully. +## ref: https://kubernetes.io/docs/concepts/workloads/pods/pod/#termination-of-pods +## +# terminationGracePeriodSeconds: 30 + +## LDAP configuration +## +ldap: + enabled: false + url: "" + server: "" + port: "" + prefix: "" + suffix: "" + baseDN: "" + bindDN: "" + bind_password: + search_attr: "" + search_filter: "" + scheme: "" + tls: false + +## PostgreSQL service configuration +service: + ## PosgresSQL service type + type: ClusterIP + # clusterIP: None + port: 5432 + + ## Specify the nodePort value for the LoadBalancer and NodePort service types. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport + ## + # nodePort: + + ## Provide any additional annotations which may be required. + ## The value is evaluated as a template, so, for example, the value can depend on .Release or .Chart + annotations: {} + ## Set the LoadBalancer service type to internal only. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer + ## + # loadBalancerIP: + + ## Load Balancer sources + ## https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/#restrict-access-for-loadbalancer-service + ## + # loadBalancerSourceRanges: + # - 10.10.10.0/24 + +## Start master and slave(s) pod(s) without limitations on shm memory. +## By default docker and containerd (and possibly other container runtimes) +## limit `/dev/shm` to `64M` (see e.g. the +## [docker issue](https://github.com/docker-library/postgres/issues/416) and the +## [containerd issue](https://github.com/containerd/containerd/issues/3654), +## which could be not enough if PostgreSQL uses parallel workers heavily. +## +shmVolume: + ## Set `shmVolume.enabled` to `true` to mount a new tmpfs volume to remove + ## this limitation. + ## + enabled: true + ## Set to `true` to `chmod 777 /dev/shm` on a initContainer. + ## This option is ingored if `volumePermissions.enabled` is `false` + ## + chmod: + enabled: true + +## PostgreSQL data Persistent Volume Storage Class +## If defined, storageClassName: +## If set to "-", storageClassName: "", which disables dynamic provisioning +## If undefined (the default) or set to null, no storageClassName spec is +## set, choosing the default provisioner. (gp2 on AWS, standard on +## GKE, AWS & OpenStack) +## +persistence: + enabled: true + ## A manually managed Persistent Volume and Claim + ## If defined, PVC must be created manually before volume will be bound + ## The value is evaluated as a template, so, for example, the name can depend on .Release or .Chart + ## + # existingClaim: + + ## The path the volume will be mounted at, useful when using different + ## PostgreSQL images. + ## + mountPath: /bitnami/postgresql + + ## The subdirectory of the volume to mount to, useful in dev environments + ## and one PV for multiple services. + ## + subPath: "" + + # storageClass: "-" + accessModes: + - ReadWriteOnce + size: 8Gi + annotations: {} + +## updateStrategy for PostgreSQL StatefulSet and its slaves StatefulSets +## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies +updateStrategy: + type: RollingUpdate + +## +## PostgreSQL Master parameters +## +master: + ## Node, affinity, tolerations, and priorityclass settings for pod assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector + ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity + ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#taints-and-tolerations-beta-feature + ## ref: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption + nodeSelector: {} + affinity: {} + tolerations: [] + labels: {} + annotations: {} + podLabels: {} + podAnnotations: {} + priorityClassName: "" + extraInitContainers: | + # - name: do-something + # image: busybox + # command: ['do', 'something'] + + ## Additional PostgreSQL Master Volume mounts + ## + extraVolumeMounts: [] + ## Additional PostgreSQL Master Volumes + ## + extraVolumes: [] + ## Add sidecars to the pod + ## + ## For example: + ## sidecars: + ## - name: your-image-name + ## image: your-image + ## imagePullPolicy: Always + ## ports: + ## - name: portname + ## containerPort: 1234 + sidecars: [] + +## +## PostgreSQL Slave parameters +## +slave: + ## Node, affinity, tolerations, and priorityclass settings for pod assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector + ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity + ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#taints-and-tolerations-beta-feature + ## ref: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption + nodeSelector: {} + affinity: {} + tolerations: [] + labels: {} + annotations: {} + podLabels: {} + podAnnotations: {} + priorityClassName: "" + extraInitContainers: | + # - name: do-something + # image: busybox + # command: ['do', 'something'] + ## Additional PostgreSQL Slave Volume mounts + ## + extraVolumeMounts: [] + ## Additional PostgreSQL Slave Volumes + ## + extraVolumes: [] + ## Add sidecars to the pod + ## + ## For example: + ## sidecars: + ## - name: your-image-name + ## image: your-image + ## imagePullPolicy: Always + ## ports: + ## - name: portname + ## containerPort: 1234 + sidecars: [] + +## Configure resource requests and limits +## ref: http://kubernetes.io/docs/user-guide/compute-resources/ +## +resources: + requests: + memory: 256Mi + cpu: 250m + +networkPolicy: + ## Enable creation of NetworkPolicy resources. Only Ingress traffic is filtered for now. + ## + enabled: false + + ## The Policy model to apply. When set to false, only pods with the correct + ## client label will have network access to the port PostgreSQL is listening + ## on. When true, PostgreSQL will accept connections from any source + ## (with the correct destination port). + ## + allowExternal: true + + ## if explicitNamespacesSelector is missing or set to {}, only client Pods that are in the networkPolicy's namespace + ## and that match other criteria, the ones that have the good label, can reach the DB. + ## But sometimes, we want the DB to be accessible to clients from other namespaces, in this case, we can use this + ## LabelSelector to select these namespaces, note that the networkPolicy's namespace should also be explicitly added. + ## + # explicitNamespacesSelector: + # matchLabels: + # role: frontend + # matchExpressions: + # - {key: role, operator: In, values: [frontend]} + +## Configure extra options for liveness and readiness probes +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes) +livenessProbe: + enabled: true + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 + +readinessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 + +## Configure metrics exporter +## +metrics: + enabled: false + # resources: {} + service: + type: ClusterIP + annotations: + prometheus.io/scrape: "true" + prometheus.io/port: "9187" + loadBalancerIP: + serviceMonitor: + enabled: false + additionalLabels: {} + # namespace: monitoring + # interval: 30s + # scrapeTimeout: 10s + ## Custom PrometheusRule to be defined + ## The value is evaluated as a template, so, for example, the value can depend on .Release or .Chart + ## ref: https://github.com/coreos/prometheus-operator#customresourcedefinitions + prometheusRule: + enabled: false + additionalLabels: {} + namespace: "" + rules: [] + ## These are just examples rules, please adapt them to your needs. + ## Make sure to constraint the rules to the current postgresql service. + # - alert: HugeReplicationLag + # expr: pg_replication_lag{service="{{ template "postgresql.fullname" . }}-metrics"} / 3600 > 1 + # for: 1m + # labels: + # severity: critical + # annotations: + # description: replication for {{ template "postgresql.fullname" . }} PostgreSQL is lagging by {{ "{{ $value }}" }} hour(s). + # summary: PostgreSQL replication is lagging by {{ "{{ $value }}" }} hour(s). + image: + registry: docker.io + repository: bitnami/postgres-exporter + tag: 0.8.0-debian-10-r28 + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + ## Define additional custom metrics + ## ref: https://github.com/wrouesnel/postgres_exporter#adding-new-metrics-via-a-config-file + # customMetrics: + # pg_database: + # query: "SELECT d.datname AS name, CASE WHEN pg_catalog.has_database_privilege(d.datname, 'CONNECT') THEN pg_catalog.pg_database_size(d.datname) ELSE 0 END AS size FROM pg_catalog.pg_database d where datname not in ('template0', 'template1', 'postgres')" + # metrics: + # - name: + # usage: "LABEL" + # description: "Name of the database" + # - size_bytes: + # usage: "GAUGE" + # description: "Size of the database in bytes" + ## Pod Security Context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ + ## + securityContext: + enabled: false + runAsUser: 1001 + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes) + ## Configure extra options for liveness and readiness probes + livenessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 + + readinessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + successThreshold: 1 diff --git a/charts/deps/charts/airflow-8.8.0/charts/redis/.helmignore b/charts/deps/charts/airflow-8.8.0/charts/redis/.helmignore new file mode 100644 index 0000000..b2767ae --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/redis/.helmignore @@ -0,0 +1,3 @@ +.git +# OWNERS file for Kubernetes +OWNERS diff --git a/charts/deps/charts/airflow-8.8.0/charts/redis/Chart.yaml b/charts/deps/charts/airflow-8.8.0/charts/redis/Chart.yaml new file mode 100644 index 0000000..6f41ef7 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/redis/Chart.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +appVersion: 5.0.7 +deprecated: true +description: DEPRECATED Open source, advanced key-value store. It is often referred + to as a data structure server since keys can contain strings, hashes, lists, sets + and sorted sets. +home: http://redis.io/ +icon: https://bitnami.com/assets/stacks/redis/img/redis-stack-220x234.png +keywords: +- redis +- keyvalue +- database +name: redis +sources: +- https://github.com/bitnami/bitnami-docker-redis +version: 10.5.7 diff --git a/charts/deps/charts/airflow-8.8.0/charts/redis/README.md b/charts/deps/charts/airflow-8.8.0/charts/redis/README.md new file mode 100644 index 0000000..4e5b1cf --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/redis/README.md @@ -0,0 +1,518 @@ + +# Redis + +[Redis](http://redis.io/) is an advanced key-value cache and store. It is often referred to as a data structure server since keys can contain strings, hashes, lists, sets, sorted sets, bitmaps and hyperloglogs. + +## This Helm chart is deprecated + +Given the [`stable` deprecation timeline](https://github.com/helm/charts#deprecation-timeline), the Bitnami maintained Redis Helm chart is now located at [bitnami/charts](https://github.com/bitnami/charts/). + +The Bitnami repository is already included in the Hubs and we will continue providing the same cadence of updates, support, etc that we've been keeping here these years. Installation instructions are very similar, just adding the _bitnami_ repo and using it during the installation (`bitnami/` instead of `stable/`) + +```bash +$ helm repo add bitnami https://charts.bitnami.com/bitnami +$ helm install my-release bitnami/ # Helm 3 +$ helm install --name my-release bitnami/ # Helm 2 +``` + +To update an exisiting _stable_ deployment with a chart hosted in the bitnami repository you can execute + +```bash +$ helm repo add bitnami https://charts.bitnami.com/bitnami +$ helm upgrade my-release bitnami/ +``` + +Issues and PRs related to the chart itself will be redirected to `bitnami/charts` GitHub repository. In the same way, we'll be happy to answer questions related to this migration process in [this issue](https://github.com/helm/charts/issues/20969) created as a common place for discussion. + +## TL;DR; + +```bash +# Testing configuration +$ helm install my-release stable/redis +``` + +```bash +# Production configuration +$ helm install my-release stable/redis --values values-production.yaml +``` + +## Introduction + +This chart bootstraps a [Redis](https://github.com/bitnami/bitnami-docker-redis) deployment on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. + +Bitnami charts can be used with [Kubeapps](https://kubeapps.com/) for deployment and management of Helm Charts in clusters. This chart has been tested to work with NGINX Ingress, cert-manager, fluentd and Prometheus on top of the [BKPR](https://kubeprod.io/). + +## Prerequisites + +- Kubernetes 1.12+ +- Helm 2.11+ or Helm 3.0-beta3+ +- PV provisioner support in the underlying infrastructure + +## Installing the Chart + +To install the chart with the release name `my-release`: + +```bash +$ helm install my-release stable/redis +``` + +The command deploys Redis on the Kubernetes cluster in the default configuration. The [Parameters](#parameters) section lists the parameters that can be configured during installation. + +> **Tip**: List all releases using `helm list` + +## Uninstalling the Chart + +To uninstall/delete the `my-release` deployment: + +```bash +$ helm delete my-release +``` + +The command removes all the Kubernetes components associated with the chart and deletes the release. + +## Parameters + +The following table lists the configurable parameters of the Redis chart and their default values. + +| Parameter | Description | Default | +| --------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- | +| `global.imageRegistry` | Global Docker image registry | `nil` | +| `global.imagePullSecrets` | Global Docker registry secret names as an array | `[]` (does not add image pull secrets to deployed pods) | +| `global.storageClass` | Global storage class for dynamic provisioning | `nil` | +| `global.redis.password` | Redis password (overrides `password`) | `nil` | +| `image.registry` | Redis Image registry | `docker.io` | +| `image.repository` | Redis Image name | `bitnami/redis` | +| `image.tag` | Redis Image tag | `{TAG_NAME}` | +| `image.pullPolicy` | Image pull policy | `IfNotPresent` | +| `image.pullSecrets` | Specify docker-registry secret names as an array | `nil` | +| `nameOverride` | String to partially override redis.fullname template with a string (will prepend the release name) | `nil` | +| `fullnameOverride` | String to fully override redis.fullname template with a string | `nil` | +| `cluster.enabled` | Use master-slave topology | `true` | +| `cluster.slaveCount` | Number of slaves | `1` | +| `existingSecret` | Name of existing secret object (for password authentication) | `nil` | +| `existingSecretPasswordKey` | Name of key containing password to be retrieved from the existing secret | `nil` | +| `usePassword` | Use password | `true` | +| `usePasswordFile` | Mount passwords as files instead of environment variables | `false` | +| `password` | Redis password (ignored if existingSecret set) | Randomly generated | +| `configmap` | Additional common Redis node configuration (this value is evaluated as a template) | See values.yaml | +| `clusterDomain` | Kubernetes DNS Domain name to use | `cluster.local` | +| `networkPolicy.enabled` | Enable NetworkPolicy | `false` | +| `networkPolicy.allowExternal` | Don't require client label for connections | `true` | +| `networkPolicy.ingressNSMatchLabels` | Allow connections from other namespaces | `{}` | +| `networkPolicy.ingressNSPodMatchLabels` | For other namespaces match by pod labels and namespace labels | `{}` | +| `securityContext.enabled` | Enable security context (both redis master and slave pods) | `true` | +| `securityContext.fsGroup` | Group ID for the container (both redis master and slave pods) | `1001` | +| `securityContext.runAsUser` | User ID for the container (both redis master and slave pods) | `1001` | +| `securityContext.sysctls` | Set namespaced sysctls for the container (both redis master and slave pods) | `nil` | +| `serviceAccount.create` | Specifies whether a ServiceAccount should be created | `false` | +| `serviceAccount.name` | The name of the ServiceAccount to create | Generated using the fullname template | +| `rbac.create` | Specifies whether RBAC resources should be created | `false` | +| `rbac.role.rules` | Rules to create | `[]` | +| `metrics.enabled` | Start a side-car prometheus exporter | `false` | +| `metrics.image.registry` | Redis exporter image registry | `docker.io` | +| `metrics.image.repository` | Redis exporter image name | `bitnami/redis-exporter` | +| `metrics.image.tag` | Redis exporter image tag | `{TAG_NAME}` | +| `metrics.image.pullPolicy` | Image pull policy | `IfNotPresent` | +| `metrics.image.pullSecrets` | Specify docker-registry secret names as an array | `nil` | +| `metrics.extraArgs` | Extra arguments for the binary; possible values [here](https://github.com/oliver006/redis_exporter#flags) | {} | +| `metrics.podLabels` | Additional labels for Metrics exporter pod | {} | +| `metrics.podAnnotations` | Additional annotations for Metrics exporter pod | {} | +| `metrics.resources` | Exporter resource requests/limit | Memory: `256Mi`, CPU: `100m` | +| `metrics.serviceMonitor.enabled` | if `true`, creates a Prometheus Operator ServiceMonitor (also requires `metrics.enabled` to be `true`) | `false` | +| `metrics.serviceMonitor.namespace` | Optional namespace which Prometheus is running in | `nil` | +| `metrics.serviceMonitor.interval` | How frequently to scrape metrics (use by default, falling back to Prometheus' default) | `nil` | +| `metrics.serviceMonitor.selector` | Default to kube-prometheus install (CoreOS recommended), but should be set according to Prometheus install | `{ prometheus: kube-prometheus }` | +| `metrics.service.type` | Kubernetes Service type (redis metrics) | `ClusterIP` | +| `metrics.service.annotations` | Annotations for the services to monitor (redis master and redis slave service) | {} | +| `metrics.service.labels` | Additional labels for the metrics service | {} | +| `metrics.service.loadBalancerIP` | loadBalancerIP if redis metrics service type is `LoadBalancer` | `nil` | +| `metrics.priorityClassName` | Metrics exporter pod priorityClassName | {} | +| `metrics.prometheusRule.enabled` | Set this to true to create prometheusRules for Prometheus operator | `false` | +| `metrics.prometheusRule.additionalLabels` | Additional labels that can be used so prometheusRules will be discovered by Prometheus | `{}` | +| `metrics.prometheusRule.namespace` | namespace where prometheusRules resource should be created | Same namespace as redis | +| `metrics.prometheusRule.rules` | [rules](https://prometheus.io/docs/prometheus/latest/configuration/alerting_rules/) to be created, check values for an example. | `[]` | +| `persistence.existingClaim` | Provide an existing PersistentVolumeClaim | `nil` | +| `master.persistence.enabled` | Use a PVC to persist data (master node) | `true` | +| `master.persistence.path` | Path to mount the volume at, to use other images | `/data` | +| `master.persistence.subPath` | Subdirectory of the volume to mount at | `""` | +| `master.persistence.storageClass` | Storage class of backing PVC | `generic` | +| `master.persistence.accessModes` | Persistent Volume Access Modes | `[ReadWriteOnce]` | +| `master.persistence.size` | Size of data volume | `8Gi` | +| `master.persistence.matchLabels` | matchLabels persistent volume selector | `{}` | +| `master.persistence.matchExpressions` | matchExpressions persistent volume selector | `{}` | +| `master.statefulset.updateStrategy` | Update strategy for StatefulSet | onDelete | +| `master.statefulset.rollingUpdatePartition` | Partition update strategy | `nil` | +| `master.podLabels` | Additional labels for Redis master pod | {} | +| `master.podAnnotations` | Additional annotations for Redis master pod | {} | +| `redisPort` | Redis port (in both master and slaves) | `6379` | +| `master.command` | Redis master entrypoint string. The command `redis-server` is executed if this is not provided. | `/run.sh` | +| `master.configmap` | Additional Redis configuration for the master nodes (this value is evaluated as a template) | `nil` | +| `master.disableCommands` | Array of Redis commands to disable (master) | `["FLUSHDB", "FLUSHALL"]` | +| `master.extraFlags` | Redis master additional command line flags | [] | +| `master.nodeSelector` | Redis master Node labels for pod assignment | {"beta.kubernetes.io/arch": "amd64"} | +| `master.tolerations` | Toleration labels for Redis master pod assignment | [] | +| `master.affinity` | Affinity settings for Redis master pod assignment | {} | +| `master.schedulerName` | Name of an alternate scheduler | `nil` | +| `master.service.type` | Kubernetes Service type (redis master) | `ClusterIP` | +| `master.service.port` | Kubernetes Service port (redis master) | `6379` | +| `master.service.nodePort` | Kubernetes Service nodePort (redis master) | `nil` | +| `master.service.annotations` | annotations for redis master service | {} | +| `master.service.labels` | Additional labels for redis master service | {} | +| `master.service.loadBalancerIP` | loadBalancerIP if redis master service type is `LoadBalancer` | `nil` | +| `master.service.loadBalancerSourceRanges` | loadBalancerSourceRanges if redis master service type is `LoadBalancer` | `nil` | +| `master.resources` | Redis master CPU/Memory resource requests/limits | Memory: `256Mi`, CPU: `100m` | +| `master.livenessProbe.enabled` | Turn on and off liveness probe (redis master pod) | `true` | +| `master.livenessProbe.initialDelaySeconds` | Delay before liveness probe is initiated (redis master pod) | `30` | +| `master.livenessProbe.periodSeconds` | How often to perform the probe (redis master pod) | `30` | +| `master.livenessProbe.timeoutSeconds` | When the probe times out (redis master pod) | `5` | +| `master.livenessProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed (redis master pod) | `1` | +| `master.livenessProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded. | `5` | +| `master.readinessProbe.enabled` | Turn on and off readiness probe (redis master pod) | `true` | +| `master.readinessProbe.initialDelaySeconds` | Delay before readiness probe is initiated (redis master pod) | `5` | +| `master.readinessProbe.periodSeconds` | How often to perform the probe (redis master pod) | `10` | +| `master.readinessProbe.timeoutSeconds` | When the probe times out (redis master pod) | `1` | +| `master.readinessProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed (redis master pod) | `1` | +| `master.readinessProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded. | `5` | +| `master.priorityClassName` | Redis Master pod priorityClassName | {} | +| `volumePermissions.enabled` | Enable init container that changes volume permissions in the registry (for cases where the default k8s `runAsUser` and `fsUser` values do not work) | `false` | +| `volumePermissions.image.registry` | Init container volume-permissions image registry | `docker.io` | +| `volumePermissions.image.repository` | Init container volume-permissions image name | `bitnami/minideb` | +| `volumePermissions.image.tag` | Init container volume-permissions image tag | `buster` | +| `volumePermissions.image.pullPolicy` | Init container volume-permissions image pull policy | `Always` | +| `volumePermissions.resources ` | Init container volume-permissions CPU/Memory resource requests/limits | {} | +| `slave.service.type` | Kubernetes Service type (redis slave) | `ClusterIP` | +| `slave.service.nodePort` | Kubernetes Service nodePort (redis slave) | `nil` | +| `slave.service.annotations` | annotations for redis slave service | {} | +| `slave.service.labels` | Additional labels for redis slave service | {} | +| `slave.service.port` | Kubernetes Service port (redis slave) | `6379` | +| `slave.service.loadBalancerIP` | LoadBalancerIP if Redis slave service type is `LoadBalancer` | `nil` | +| `slave.service.loadBalancerSourceRanges` | loadBalancerSourceRanges if Redis slave service type is `LoadBalancer` | `nil` | +| `slave.command` | Redis slave entrypoint array. The docker image's ENTRYPOINT is used if this is not provided. | `/run.sh` | +| `slave.configmap` | Additional Redis configuration for the slave nodes (this value is evaluated as a template) | `nil` | +| `slave.disableCommands` | Array of Redis commands to disable (slave) | `[FLUSHDB, FLUSHALL]` | +| `slave.extraFlags` | Redis slave additional command line flags | `[]` | +| `slave.livenessProbe.enabled` | Turn on and off liveness probe (redis slave pod) | `true` | +| `slave.livenessProbe.initialDelaySeconds` | Delay before liveness probe is initiated (redis slave pod) | `30` | +| `slave.livenessProbe.periodSeconds` | How often to perform the probe (redis slave pod) | `10` | +| `slave.livenessProbe.timeoutSeconds` | When the probe times out (redis slave pod) | `5` | +| `slave.livenessProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed (redis slave pod) | `1` | +| `slave.livenessProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded. | `5` | +| `slave.readinessProbe.enabled` | Turn on and off slave.readiness probe (redis slave pod) | `true` | +| `slave.readinessProbe.initialDelaySeconds` | Delay before slave.readiness probe is initiated (redis slave pod) | `5` | +| `slave.readinessProbe.periodSeconds` | How often to perform the probe (redis slave pod) | `10` | +| `slave.readinessProbe.timeoutSeconds` | When the probe times out (redis slave pod) | `10` | +| `slave.readinessProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed (redis slave pod) | `1` | +| `slave.readinessProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded. (redis slave pod) | `5` | +| `slave.persistence.enabled` | Use a PVC to persist data (slave node) | `true` | +| `slave.persistence.path` | Path to mount the volume at, to use other images | `/data` | +| `slave.persistence.subPath` | Subdirectory of the volume to mount at | `""` | +| `slave.persistence.storageClass` | Storage class of backing PVC | `generic` | +| `slave.persistence.accessModes` | Persistent Volume Access Modes | `[ReadWriteOnce]` | +| `slave.persistence.size` | Size of data volume | `8Gi` | +| `slave.persistence.matchLabels` | matchLabels persistent volume selector | `{}` | +| `slave.persistence.matchExpressions` | matchExpressions persistent volume selector | `{}` | +| `slave.statefulset.updateStrategy` | Update strategy for StatefulSet | onDelete | +| `slave.statefulset.rollingUpdatePartition` | Partition update strategy | `nil` | +| `slave.podLabels` | Additional labels for Redis slave pod | `master.podLabels` | +| `slave.podAnnotations` | Additional annotations for Redis slave pod | `master.podAnnotations` | +| `slave.schedulerName` | Name of an alternate scheduler | `nil` | +| `slave.resources` | Redis slave CPU/Memory resource requests/limits | `{}` | +| `slave.affinity` | Enable node/pod affinity for slaves | {} | +| `slave.priorityClassName` | Redis Slave pod priorityClassName | {} | +| `sentinel.enabled` | Enable sentinel containers | `false` | +| `sentinel.usePassword` | Use password for sentinel containers | `true` | +| `sentinel.masterSet` | Name of the sentinel master set | `mymaster` | +| `sentinel.initialCheckTimeout` | Timeout for querying the redis sentinel service for the active sentinel list | `5` | +| `sentinel.quorum` | Quorum for electing a new master | `2` | +| `sentinel.downAfterMilliseconds` | Timeout for detecting a Redis node is down | `60000` | +| `sentinel.failoverTimeout` | Timeout for performing a election failover | `18000` | +| `sentinel.parallelSyncs` | Number of parallel syncs in the cluster | `1` | +| `sentinel.port` | Redis Sentinel port | `26379` | +| `sentinel.configmap` | Additional Redis configuration for the sentinel nodes (this value is evaluated as a template) | `nil` | +| `sentinel.staticID` | Enable static IDs for sentinel replicas (If disabled IDs will be randomly generated on startup) | `false` | +| `sentinel.service.type` | Kubernetes Service type (redis sentinel) | `ClusterIP` | +| `sentinel.service.nodePort` | Kubernetes Service nodePort (redis sentinel) | `nil` | +| `sentinel.service.annotations` | annotations for redis sentinel service | {} | +| `sentinel.service.labels` | Additional labels for redis sentinel service | {} | +| `sentinel.service.redisPort` | Kubernetes Service port for Redis read only operations | `6379` | +| `sentinel.service.sentinelPort` | Kubernetes Service port for Redis sentinel | `26379` | +| `sentinel.service.redisNodePort` | Kubernetes Service node port for Redis read only operations | `` | +| `sentinel.service.sentinelNodePort` | Kubernetes Service node port for Redis sentinel | `` | +| `sentinel.service.loadBalancerIP` | LoadBalancerIP if Redis sentinel service type is `LoadBalancer` | `nil` | +| `sentinel.livenessProbe.enabled` | Turn on and off liveness probe (redis sentinel pod) | `true` | +| `sentinel.livenessProbe.initialDelaySeconds` | Delay before liveness probe is initiated (redis sentinel pod) | `5` | +| `sentinel.livenessProbe.periodSeconds` | How often to perform the probe (redis sentinel container) | `5` | +| `sentinel.livenessProbe.timeoutSeconds` | When the probe times out (redis sentinel container) | `5` | +| `sentinel.livenessProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed (redis sentinel container) | `1` | +| `sentinel.livenessProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded. | `5` | +| `sentinel.readinessProbe.enabled` | Turn on and off sentinel.readiness probe (redis sentinel pod) | `true` | +| `sentinel.readinessProbe.initialDelaySeconds` | Delay before sentinel.readiness probe is initiated (redis sentinel pod) | `5` | +| `sentinel.readinessProbe.periodSeconds` | How often to perform the probe (redis sentinel pod) | `5` | +| `sentinel.readinessProbe.timeoutSeconds` | When the probe times out (redis sentinel container) | `1` | +| `sentinel.readinessProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed (redis sentinel container) | `1` | +| `sentinel.readinessProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded. (redis sentinel container) | `5` | +| `sentinel.resources` | Redis sentinel CPU/Memory resource requests/limits | `{}` | +| `sentinel.image.registry` | Redis Sentinel Image registry | `docker.io` | +| `sentinel.image.repository` | Redis Sentinel Image name | `bitnami/redis-sentinel` | +| `sentinel.image.tag` | Redis Sentinel Image tag | `{TAG_NAME}` | +| `sentinel.image.pullPolicy` | Image pull policy | `IfNotPresent` | +| `sentinel.image.pullSecrets` | Specify docker-registry secret names as an array | `nil` | +| `sysctlImage.enabled` | Enable an init container to modify Kernel settings | `false` | +| `sysctlImage.command` | sysctlImage command to execute | [] | +| `sysctlImage.registry` | sysctlImage Init container registry | `docker.io` | +| `sysctlImage.repository` | sysctlImage Init container name | `bitnami/minideb` | +| `sysctlImage.tag` | sysctlImage Init container tag | `buster` | +| `sysctlImage.pullPolicy` | sysctlImage Init container pull policy | `Always` | +| `sysctlImage.mountHostSys` | Mount the host `/sys` folder to `/host-sys` | `false` | +| `sysctlImage.resources` | sysctlImage Init container CPU/Memory resource requests/limits | {} | +| `podSecurityPolicy.create` | Specifies whether a PodSecurityPolicy should be created | `false` | + +Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example, + +```bash +$ helm install my-release \ + --set password=secretpassword \ + stable/redis +``` + +The above command sets the Redis server password to `secretpassword`. + +Alternatively, a YAML file that specifies the values for the parameters can be provided while installing the chart. For example, + +```bash +$ helm install my-release -f values.yaml stable/redis +``` + +> **Tip**: You can use the default [values.yaml](values.yaml) + +> **Note for minikube users**: Current versions of minikube (v0.24.1 at the time of writing) provision `hostPath` persistent volumes that are only writable by root. Using chart defaults cause pod failure for the Redis pod as it attempts to write to the `/bitnami` directory. Consider installing Redis with `--set persistence.enabled=false`. See minikube issue [1990](https://github.com/kubernetes/minikube/issues/1990) for more information. + +## Configuration and installation details + +### [Rolling VS Immutable tags](https://docs.bitnami.com/containers/how-to/understand-rolling-tags-containers/) + +It is strongly recommended to use immutable tags in a production environment. This ensures your deployment does not change automatically if the same tag is updated with a different image. + +Bitnami will release a new chart updating its containers if a new version of the main container, significant changes, or critical vulnerabilities exist. + +### Production configuration + +This chart includes a `values-production.yaml` file where you can find some parameters oriented to production configuration in comparison to the regular `values.yaml`. You can use this file instead of the default one. + +- Number of slaves: +```diff +- cluster.slaveCount: 2 ++ cluster.slaveCount: 3 +``` + +- Enable NetworkPolicy: +```diff +- networkPolicy.enabled: false ++ networkPolicy.enabled: true +``` + +- Start a side-car prometheus exporter: +```diff +- metrics.enabled: false ++ metrics.enabled: true +``` + +### Cluster topologies + +#### Default: Master-Slave + +When installing the chart with `cluster.enabled=true`, it will deploy a Redis master StatefulSet (only one master node allowed) and a Redis slave StatefulSet. The slaves will be read-replicas of the master. Two services will be exposed: + + - Redis Master service: Points to the master, where read-write operations can be performed + - Redis Slave service: Points to the slaves, where only read operations are allowed. + +In case the master crashes, the slaves will wait until the master node is respawned again by the Kubernetes Controller Manager. + +#### Master-Slave with Sentinel + +When installing the chart with `cluster.enabled=true` and `sentinel.enabled=true`, it will deploy a Redis master StatefulSet (only one master allowed) and a Redis slave StatefulSet. In this case, the pods will contain en extra container with Redis Sentinel. This container will form a cluster of Redis Sentinel nodes, which will promote a new master in case the actual one fails. In addition to this, only one service is exposed: + + - Redis service: Exposes port 6379 for Redis read-only operations and port 26379 for accesing Redis Sentinel. + +For read-only operations, access the service using port 6379. For write operations, it's necessary to access the Redis Sentinel cluster and query the current master using the command below (using redis-cli or similar: + +``` +SENTINEL get-master-addr-by-name +``` +This command will return the address of the current master, which can be accessed from inside the cluster. + +In case the current master crashes, the Sentinel containers will elect a new master node. + +### Using password file +To use a password file for Redis you need to create a secret containing the password. + +> *NOTE*: It is important that the file with the password must be called `redis-password` + +And then deploy the Helm Chart using the secret name as parameter: + +```console +usePassword=true +usePasswordFile=true +existingSecret=redis-password-file +sentinels.enabled=true +metrics.enabled=true +``` + +### Metrics + +The chart optionally can start a metrics exporter for [prometheus](https://prometheus.io). The metrics endpoint (port 9121) is exposed in the service. Metrics can be scraped from within the cluster using something similar as the described in the [example Prometheus scrape configuration](https://github.com/prometheus/prometheus/blob/master/documentation/examples/prometheus-kubernetes.yml). If metrics are to be scraped from outside the cluster, the Kubernetes API proxy can be utilized to access the endpoint. + +### Host Kernel Settings +Redis may require some changes in the kernel of the host machine to work as expected, in particular increasing the `somaxconn` value and disabling transparent huge pages. +To do so, you can set up a privileged initContainer with the `sysctlImage` config values, for example: +``` +sysctlImage: + enabled: true + mountHostSys: true + command: + - /bin/sh + - -c + - |- + install_packages procps + sysctl -w net.core.somaxconn=10000 + echo never > /host-sys/kernel/mm/transparent_hugepage/enabled +``` + +Alternatively, for Kubernetes 1.12+ you can set `securityContext.sysctls` which will configure sysctls for master and slave pods. Example: + +```yaml +securityContext: + sysctls: + - name: net.core.somaxconn + value: "10000" +``` + +Note that this will not disable transparent huge tables. + +## Persistence + +By default, the chart mounts a [Persistent Volume](http://kubernetes.io/docs/user-guide/persistent-volumes/) at the `/data` path. The volume is created using dynamic volume provisioning. If a Persistent Volume Claim already exists, specify it during installation. + +### Existing PersistentVolumeClaim + +1. Create the PersistentVolume +2. Create the PersistentVolumeClaim +3. Install the chart + +```bash +$ helm install my-release --set persistence.existingClaim=PVC_NAME stable/redis +``` + +## NetworkPolicy + +To enable network policy for Redis, install +[a networking plugin that implements the Kubernetes NetworkPolicy spec](https://kubernetes.io/docs/tasks/administer-cluster/declare-network-policy#before-you-begin), +and set `networkPolicy.enabled` to `true`. + +For Kubernetes v1.5 & v1.6, you must also turn on NetworkPolicy by setting +the DefaultDeny namespace annotation. Note: this will enforce policy for _all_ pods in the namespace: + + kubectl annotate namespace default "net.beta.kubernetes.io/network-policy={\"ingress\":{\"isolation\":\"DefaultDeny\"}}" + +With NetworkPolicy enabled, only pods with the generated client label will be +able to connect to Redis. This label will be displayed in the output +after a successful install. + +With `networkPolicy.ingressNSMatchLabels` pods from other namespaces can connect to redis. Set `networkPolicy.ingressNSPodMatchLabels` to match pod labels in matched namespace. For example, for a namespace labeled `redis=external` and pods in that namespace labeled `redis-client=true` the fields should be set: + +``` +networkPolicy: + enabled: true + ingressNSMatchLabels: + redis: external + ingressNSPodMatchLabels: + redis-client: true +``` + +## Upgrading an existing Release to a new major version + +A major chart version change (like v1.2.3 -> v2.0.0) indicates that there is an +incompatible breaking change needing manual actions. + +### To 10.0.0 + +For releases with `usePassword: true`, the value `sentinel.usePassword` controls whether the password authentication also applies to the sentinel port. This defaults to `true` for a secure configuration, however it is possible to disable to account for the following cases: +* Using a version of redis-sentinel prior to `5.0.1` where the authentication feature was introduced. +* Where redis clients need to be updated to support sentinel authentication. + +If using a master/slave topology, or with `usePassword: false`, no action is required. + +### To 8.0.18 + +For releases with `metrics.enabled: true` the default tag for the exporter image is now `v1.x.x`. This introduces many changes including metrics names. You'll want to use [this dashboard](https://github.com/oliver006/redis_exporter/blob/master/contrib/grafana_prometheus_redis_dashboard.json) now. Please see the [redis_exporter github page](https://github.com/oliver006/redis_exporter#upgrading-from-0x-to-1x) for more details. + +### To 7.0.0 + +This version causes a change in the Redis Master StatefulSet definition, so the command helm upgrade would not work out of the box. As an alternative, one of the following could be done: + + - Recommended: Create a clone of the Redis Master PVC (for example, using projects like [this one](https://github.com/edseymour/pvc-transfer)). Then launch a fresh release reusing this cloned PVC. + + ``` + helm install my-release stable/redis --set persistence.existingClaim= + ``` + + - Alternative (not recommended, do at your own risk): `helm delete --purge` does not remove the PVC assigned to the Redis Master StatefulSet. As a consequence, the following commands can be done to upgrade the release + + ``` + helm delete --purge + helm install stable/redis + ``` + +Previous versions of the chart were not using persistence in the slaves, so this upgrade would add it to them. Another important change is that no values are inherited from master to slaves. For example, in 6.0.0 `slaves.readinessProbe.periodSeconds`, if empty, would be set to `master.readinessProbe.periodSeconds`. This approach lacked transparency and was difficult to maintain. From now on, all the slave parameters must be configured just as it is done with the masters. + +Some values have changed as well: + + - `master.port` and `slave.port` have been changed to `redisPort` (same value for both master and slaves) + - `master.securityContext` and `slave.securityContext` have been changed to `securityContext`(same values for both master and slaves) + +By default, the upgrade will not change the cluster topology. In case you want to use Redis Sentinel, you must explicitly set `sentinel.enabled` to `true`. + +### To 6.0.0 + +Previous versions of the chart were using an init-container to change the permissions of the volumes. This was done in case the `securityContext` directive in the template was not enough for that (for example, with cephFS). In this new version of the chart, this container is disabled by default (which should not affect most of the deployments). If your installation still requires that init container, execute `helm upgrade` with the `--set volumePermissions.enabled=true`. + +### To 5.0.0 + +The default image in this release may be switched out for any image containing the `redis-server` +and `redis-cli` binaries. If `redis-server` is not the default image ENTRYPOINT, `master.command` +must be specified. + +#### Breaking changes +- `master.args` and `slave.args` are removed. Use `master.command` or `slave.command` instead in order to override the image entrypoint, or `master.extraFlags` to pass additional flags to `redis-server`. +- `disableCommands` is now interpreted as an array of strings instead of a string of comma separated values. +- `master.persistence.path` now defaults to `/data`. + +### 4.0.0 + +This version removes the `chart` label from the `spec.selector.matchLabels` +which is immutable since `StatefulSet apps/v1beta2`. It has been inadvertently +added, causing any subsequent upgrade to fail. See https://github.com/helm/charts/issues/7726. + +It also fixes https://github.com/helm/charts/issues/7726 where a deployment `extensions/v1beta1` can not be upgraded if `spec.selector` is not explicitly set. + +Finally, it fixes https://github.com/helm/charts/issues/7803 by removing mutable labels in `spec.VolumeClaimTemplate.metadata.labels` so that it is upgradable. + +In order to upgrade, delete the Redis StatefulSet before upgrading: +```bash +$ kubectl delete statefulsets.apps --cascade=false my-release-redis-master +``` +And edit the Redis slave (and metrics if enabled) deployment: +```bash +kubectl patch deployments my-release-redis-slave --type=json -p='[{"op": "remove", "path": "/spec/selector/matchLabels/chart"}]' +kubectl patch deployments my-release-redis-metrics --type=json -p='[{"op": "remove", "path": "/spec/selector/matchLabels/chart"}]' +``` + +## Notable changes + +### 9.0.0 +The metrics exporter has been changed from a separate deployment to a sidecar container, due to the latest changes in the Redis exporter code. Check the [official page](https://github.com/oliver006/redis_exporter/) for more information. The metrics container image was changed from oliver006/redis_exporter to bitnami/redis-exporter (Bitnami's maintained package of oliver006/redis_exporter). + +### 7.0.0 +In order to improve the performance in case of slave failure, we added persistence to the read-only slaves. That means that we moved from Deployment to StatefulSets. This should not affect upgrades from previous versions of the chart, as the deployments did not contain any persistence at all. + +This version also allows enabling Redis Sentinel containers inside of the Redis Pods (feature disabled by default). In case the master crashes, a new Redis node will be elected as master. In order to query the current master (no redis master service is exposed), you need to query first the Sentinel cluster. Find more information [in this section](#master-slave-with-sentinel). diff --git a/charts/deps/charts/airflow-8.8.0/charts/redis/ci/default-values.yaml b/charts/deps/charts/airflow-8.8.0/charts/redis/ci/default-values.yaml new file mode 100644 index 0000000..fc2ba60 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/redis/ci/default-values.yaml @@ -0,0 +1 @@ +# Leave this file empty to ensure that CI runs builds against the default configuration in values.yaml. diff --git a/charts/deps/charts/airflow-8.8.0/charts/redis/ci/dev-values.yaml b/charts/deps/charts/airflow-8.8.0/charts/redis/ci/dev-values.yaml new file mode 100644 index 0000000..be01913 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/redis/ci/dev-values.yaml @@ -0,0 +1,9 @@ +master: + persistence: + enabled: false + +cluster: + enabled: true + slaveCount: 1 + +usePassword: false diff --git a/charts/deps/charts/airflow-8.8.0/charts/redis/ci/extra-flags-values.yaml b/charts/deps/charts/airflow-8.8.0/charts/redis/ci/extra-flags-values.yaml new file mode 100644 index 0000000..71132f7 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/redis/ci/extra-flags-values.yaml @@ -0,0 +1,11 @@ +master: + extraFlags: + - --maxmemory-policy allkeys-lru + persistence: + enabled: false +slave: + extraFlags: + - --maxmemory-policy allkeys-lru + persistence: + enabled: false +usePassword: false diff --git a/charts/deps/charts/airflow-8.8.0/charts/redis/ci/insecure-sentinel-values.yaml b/charts/deps/charts/airflow-8.8.0/charts/redis/ci/insecure-sentinel-values.yaml new file mode 100644 index 0000000..2e9174f --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/redis/ci/insecure-sentinel-values.yaml @@ -0,0 +1,524 @@ +## Global Docker image parameters +## Please, note that this will override the image parameters, including dependencies, configured to use the global value +## Current available global Docker image parameters: imageRegistry and imagePullSecrets +## +# global: +# imageRegistry: myRegistryName +# imagePullSecrets: +# - myRegistryKeySecretName + +## Bitnami Redis image version +## ref: https://hub.docker.com/r/bitnami/redis/tags/ +## +image: + registry: docker.io + repository: bitnami/redis + ## Bitnami Redis image tag + ## ref: https://github.com/bitnami/bitnami-docker-redis#supported-tags-and-respective-dockerfile-links + ## + tag: 5.0.5-debian-9-r36 + ## Specify a imagePullPolicy + ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images + ## + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + +## Redis pod Security Context +securityContext: + enabled: true + fsGroup: 1001 + runAsUser: 1001 + +## Cluster settings +cluster: + enabled: true + slaveCount: 3 + +## Use redis sentinel in the redis pod. This will disable the master and slave services and +## create one redis service with ports to the sentinel and the redis instances +sentinel: + enabled: true + ## Require password authentication on the sentinel itself + ## ref: https://redis.io/topics/sentinel + usePassword: false + ## Bitnami Redis Sentintel image version + ## ref: https://hub.docker.com/r/bitnami/redis-sentinel/tags/ + ## + image: + registry: docker.io + repository: bitnami/redis-sentinel + ## Bitnami Redis image tag + ## ref: https://github.com/bitnami/bitnami-docker-redis-sentinel#supported-tags-and-respective-dockerfile-links + ## + tag: 5.0.5-debian-9-r37 + ## Specify a imagePullPolicy + ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images + ## + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + masterSet: mymaster + initialCheckTimeout: 5 + quorum: 2 + downAfterMilliseconds: 60000 + failoverTimeout: 18000 + parallelSyncs: 1 + port: 26379 + ## Configure extra options for Redis Sentinel liveness and readiness probes + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes) + ## + livenessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + readinessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 5 + ## Redis Sentinel resource requests and limits + ## ref: http://kubernetes.io/docs/user-guide/compute-resources/ + # resources: + # requests: + # memory: 256Mi + # cpu: 100m + ## Redis Sentinel Service properties + service: + ## Redis Sentinel Service type + type: ClusterIP + sentinelPort: 26379 + redisPort: 6379 + + ## Specify the nodePort value for the LoadBalancer and NodePort service types. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport + ## + # sentinelNodePort: + # redisNodePort: + + ## Provide any additional annotations which may be required. This can be used to + ## set the LoadBalancer service type to internal only. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer + ## + annotations: {} + loadBalancerIP: + +networkPolicy: + ## Specifies whether a NetworkPolicy should be created + ## + enabled: true + + ## The Policy model to apply. When set to false, only pods with the correct + ## client label will have network access to the port Redis is listening + ## on. When true, Redis will accept connections from any source + ## (with the correct destination port). + ## + # allowExternal: true + +serviceAccount: + ## Specifies whether a ServiceAccount should be created + ## + create: false + ## The name of the ServiceAccount to use. + ## If not set and create is true, a name is generated using the fullname template + name: + +rbac: + ## Specifies whether RBAC resources should be created + ## + create: false + + role: + ## Rules to create. It follows the role specification + # rules: + # - apiGroups: + # - extensions + # resources: + # - podsecuritypolicies + # verbs: + # - use + # resourceNames: + # - gce.unprivileged + rules: [] + + +## Use password authentication +usePassword: true +## Redis password (both master and slave) +## Defaults to a random 10-character alphanumeric string if not set and usePassword is true +## ref: https://github.com/bitnami/bitnami-docker-redis#setting-the-server-password-on-first-run +## +password: +## Use existing secret (ignores previous password) +# existingSecret: +## Password key to be retrieved from Redis secret +## +# existingSecretPasswordKey: + +## Mount secrets as files instead of environment variables +usePasswordFile: false + +## Persist data to a persistent volume +persistence: {} + ## A manually managed Persistent Volume and Claim + ## Requires persistence.enabled: true + ## If defined, PVC must be created manually before volume will be bound + # existingClaim: + +# Redis port +redisPort: 6379 + +## +## Redis Master parameters +## +master: + ## Redis command arguments + ## + ## Can be used to specify command line arguments, for example: + ## + command: "/run.sh" + ## Redis additional command line flags + ## + ## Can be used to specify command line flags, for example: + ## + ## extraFlags: + ## - "--maxmemory-policy volatile-ttl" + ## - "--repl-backlog-size 1024mb" + extraFlags: [] + ## Comma-separated list of Redis commands to disable + ## + ## Can be used to disable Redis commands for security reasons. + ## Commands will be completely disabled by renaming each to an empty string. + ## ref: https://redis.io/topics/security#disabling-of-specific-commands + ## + disableCommands: + - FLUSHDB + - FLUSHALL + + ## Redis Master additional pod labels and annotations + ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + podLabels: {} + podAnnotations: {} + + ## Redis Master resource requests and limits + ## ref: http://kubernetes.io/docs/user-guide/compute-resources/ + # resources: + # requests: + # memory: 256Mi + # cpu: 100m + ## Use an alternate scheduler, e.g. "stork". + ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ + ## + # schedulerName: + + ## Configure extra options for Redis Master liveness and readiness probes + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes) + ## + livenessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + readinessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 5 + + ## Redis Master Node selectors and tolerations for pod assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector + ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#taints-and-tolerations-beta-feature + ## + # nodeSelector: {"beta.kubernetes.io/arch": "amd64"} + # tolerations: [] + ## Redis Master pod/node affinity/anti-affinity + ## + affinity: {} + + ## Redis Master Service properties + service: + ## Redis Master Service type + type: ClusterIP + port: 6379 + + ## Specify the nodePort value for the LoadBalancer and NodePort service types. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport + ## + # nodePort: + + ## Provide any additional annotations which may be required. This can be used to + ## set the LoadBalancer service type to internal only. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer + ## + annotations: {} + loadBalancerIP: + + ## Enable persistence using Persistent Volume Claims + ## ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ + ## + persistence: + enabled: true + ## The path the volume will be mounted at, useful when using different + ## Redis images. + path: /data + ## The subdirectory of the volume to mount to, useful in dev environments + ## and one PV for multiple services. + subPath: "" + ## redis data Persistent Volume Storage Class + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + # storageClass: "-" + accessModes: + - ReadWriteOnce + size: 8Gi + + ## Update strategy, can be set to RollingUpdate or onDelete by default. + ## https://kubernetes.io/docs/tutorials/stateful-application/basic-stateful-set/#updating-statefulsets + statefulset: + updateStrategy: RollingUpdate + ## Partition update strategy + ## https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#partitions + # rollingUpdatePartition: + + ## Redis Master pod priorityClassName + # priorityClassName: {} + + +## +## Redis Slave properties +## Note: service.type is a mandatory parameter +## The rest of the parameters are either optional or, if undefined, will inherit those declared in Redis Master +## +slave: + ## Slave Service properties + service: + ## Redis Slave Service type + type: ClusterIP + ## Redis port + port: 6379 + ## Specify the nodePort value for the LoadBalancer and NodePort service types. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport + ## + # nodePort: + + ## Provide any additional annotations which may be required. This can be used to + ## set the LoadBalancer service type to internal only. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer + ## + annotations: {} + loadBalancerIP: + + ## Redis slave port + port: 6379 + + ## Can be used to specify command line arguments, for example: + ## + command: "/run.sh" + ## Redis extra flags + extraFlags: [] + ## List of Redis commands to disable + disableCommands: + - FLUSHDB + - FLUSHALL + + ## Redis Slave pod/node affinity/anti-affinity + ## + affinity: {} + + ## Configure extra options for Redis Slave liveness and readiness probes + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes) + ## + livenessProbe: + enabled: true + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + readinessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 10 + successThreshold: 1 + failureThreshold: 5 + + ## Redis slave Resource + # resources: + # requests: + # memory: 256Mi + # cpu: 100m + + ## Enable persistence using Persistent Volume Claims + ## ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ + ## + persistence: + enabled: true + ## The path the volume will be mounted at, useful when using different + ## Redis images. + path: /data + ## The subdirectory of the volume to mount to, useful in dev environments + ## and one PV for multiple services. + subPath: "" + ## redis data Persistent Volume Storage Class + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + # storageClass: "-" + accessModes: + - ReadWriteOnce + size: 8Gi + + ## Update strategy, can be set to RollingUpdate or onDelete by default. + ## https://kubernetes.io/docs/tutorials/stateful-application/basic-stateful-set/#updating-statefulsets + statefulset: + updateStrategy: RollingUpdate + ## Partition update strategy + ## https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#partitions + # rollingUpdatePartition: + + ## Redis slave selectors and tolerations for pod assignment + # nodeSelector: {"beta.kubernetes.io/arch": "amd64"} + # tolerations: [] + + ## Use an alternate scheduler, e.g. "stork". + ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ + ## + # schedulerName: + + ## Redis slave pod Annotation and Labels + podLabels: {} + podAnnotations: {} + + ## Redis slave pod priorityClassName + # priorityClassName: {} + +## Prometheus Exporter / Metrics +## +metrics: + enabled: true + + image: + registry: docker.io + repository: bitnami/redis-exporter + tag: 1.0.3-debian-9-r0 + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + + ## Metrics exporter resource requests and limits + ## ref: http://kubernetes.io/docs/user-guide/compute-resources/ + ## + # resources: {} + ## Metrics exporter pod priorityClassName + # priorityClassName: {} + service: + type: ClusterIP + ## Use serviceLoadBalancerIP to request a specific static IP, + ## otherwise leave blank + # loadBalancerIP: + annotations: {} + + ## Extra arguments for Metrics exporter, for example: + ## extraArgs: + ## check-keys: myKey,myOtherKey + # extraArgs: {} + + ## Metrics exporter pod Annotation and Labels + podAnnotations: + prometheus.io/scrape: "true" + prometheus.io/port: "9121" + # podLabels: {} + + # Enable this if you're using https://github.com/coreos/prometheus-operator + serviceMonitor: + enabled: false + ## Specify a namespace if needed + # namespace: monitoring + # fallback to the prometheus default unless specified + # interval: 10s + ## Defaults to what's used if you follow CoreOS [Prometheus Install Instructions](https://github.com/helm/charts/tree/master/stable/prometheus-operator#tldr) + ## [Prometheus Selector Label](https://github.com/helm/charts/tree/master/stable/prometheus-operator#prometheus-operator-1) + ## [Kube Prometheus Selector Label](https://github.com/helm/charts/tree/master/stable/prometheus-operator#exporters) + selector: + prometheus: kube-prometheus +## +## Init containers parameters: +## volumePermissions: Change the owner of the persist volume mountpoint to RunAsUser:fsGroup +## +volumePermissions: + enabled: false + image: + registry: docker.io + repository: bitnami/minideb + tag: buster + pullPolicy: Always + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + resources: {} + # resources: + # requests: + # memory: 128Mi + # cpu: 100m + +## Redis config file +## ref: https://redis.io/topics/config +## +configmap: |- + # maxmemory-policy volatile-lru + +## Sysctl InitContainer +## used to perform sysctl operation to modify Kernel settings (needed sometimes to avoid warnings) +sysctlImage: + enabled: false + command: [] + registry: docker.io + repository: bitnami/minideb + tag: buster + pullPolicy: Always + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + mountHostSys: false + resources: {} + # resources: + # requests: + # memory: 128Mi + # cpu: 100m diff --git a/charts/deps/charts/airflow-8.8.0/charts/redis/ci/production-sentinel-values.yaml b/charts/deps/charts/airflow-8.8.0/charts/redis/ci/production-sentinel-values.yaml new file mode 100644 index 0000000..36a00e3 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/redis/ci/production-sentinel-values.yaml @@ -0,0 +1,524 @@ +## Global Docker image parameters +## Please, note that this will override the image parameters, including dependencies, configured to use the global value +## Current available global Docker image parameters: imageRegistry and imagePullSecrets +## +# global: +# imageRegistry: myRegistryName +# imagePullSecrets: +# - myRegistryKeySecretName + +## Bitnami Redis image version +## ref: https://hub.docker.com/r/bitnami/redis/tags/ +## +image: + registry: docker.io + repository: bitnami/redis + ## Bitnami Redis image tag + ## ref: https://github.com/bitnami/bitnami-docker-redis#supported-tags-and-respective-dockerfile-links + ## + tag: 5.0.5-debian-9-r36 + ## Specify a imagePullPolicy + ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images + ## + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + +## Redis pod Security Context +securityContext: + enabled: true + fsGroup: 1001 + runAsUser: 1001 + +## Cluster settings +cluster: + enabled: true + slaveCount: 3 + +## Use redis sentinel in the redis pod. This will disable the master and slave services and +## create one redis service with ports to the sentinel and the redis instances +sentinel: + enabled: true + ## Require password authentication on the sentinel itself + ## ref: https://redis.io/topics/sentinel + usePassword: true + ## Bitnami Redis Sentintel image version + ## ref: https://hub.docker.com/r/bitnami/redis-sentinel/tags/ + ## + image: + registry: docker.io + repository: bitnami/redis-sentinel + ## Bitnami Redis image tag + ## ref: https://github.com/bitnami/bitnami-docker-redis-sentinel#supported-tags-and-respective-dockerfile-links + ## + tag: 5.0.5-debian-9-r37 + ## Specify a imagePullPolicy + ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images + ## + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + masterSet: mymaster + initialCheckTimeout: 5 + quorum: 2 + downAfterMilliseconds: 60000 + failoverTimeout: 18000 + parallelSyncs: 1 + port: 26379 + ## Configure extra options for Redis Sentinel liveness and readiness probes + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes) + ## + livenessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + readinessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 5 + ## Redis Sentinel resource requests and limits + ## ref: http://kubernetes.io/docs/user-guide/compute-resources/ + # resources: + # requests: + # memory: 256Mi + # cpu: 100m + ## Redis Sentinel Service properties + service: + ## Redis Sentinel Service type + type: ClusterIP + sentinelPort: 26379 + redisPort: 6379 + + ## Specify the nodePort value for the LoadBalancer and NodePort service types. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport + ## + # sentinelNodePort: + # redisNodePort: + + ## Provide any additional annotations which may be required. This can be used to + ## set the LoadBalancer service type to internal only. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer + ## + annotations: {} + loadBalancerIP: + +networkPolicy: + ## Specifies whether a NetworkPolicy should be created + ## + enabled: true + + ## The Policy model to apply. When set to false, only pods with the correct + ## client label will have network access to the port Redis is listening + ## on. When true, Redis will accept connections from any source + ## (with the correct destination port). + ## + # allowExternal: true + +serviceAccount: + ## Specifies whether a ServiceAccount should be created + ## + create: false + ## The name of the ServiceAccount to use. + ## If not set and create is true, a name is generated using the fullname template + name: + +rbac: + ## Specifies whether RBAC resources should be created + ## + create: false + + role: + ## Rules to create. It follows the role specification + # rules: + # - apiGroups: + # - extensions + # resources: + # - podsecuritypolicies + # verbs: + # - use + # resourceNames: + # - gce.unprivileged + rules: [] + + +## Use password authentication +usePassword: true +## Redis password (both master and slave) +## Defaults to a random 10-character alphanumeric string if not set and usePassword is true +## ref: https://github.com/bitnami/bitnami-docker-redis#setting-the-server-password-on-first-run +## +password: +## Use existing secret (ignores previous password) +# existingSecret: +## Password key to be retrieved from Redis secret +## +# existingSecretPasswordKey: + +## Mount secrets as files instead of environment variables +usePasswordFile: false + +## Persist data to a persistent volume +persistence: {} + ## A manually managed Persistent Volume and Claim + ## Requires persistence.enabled: true + ## If defined, PVC must be created manually before volume will be bound + # existingClaim: + +# Redis port +redisPort: 6379 + +## +## Redis Master parameters +## +master: + ## Redis command arguments + ## + ## Can be used to specify command line arguments, for example: + ## + command: "/run.sh" + ## Redis additional command line flags + ## + ## Can be used to specify command line flags, for example: + ## + ## extraFlags: + ## - "--maxmemory-policy volatile-ttl" + ## - "--repl-backlog-size 1024mb" + extraFlags: [] + ## Comma-separated list of Redis commands to disable + ## + ## Can be used to disable Redis commands for security reasons. + ## Commands will be completely disabled by renaming each to an empty string. + ## ref: https://redis.io/topics/security#disabling-of-specific-commands + ## + disableCommands: + - FLUSHDB + - FLUSHALL + + ## Redis Master additional pod labels and annotations + ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + podLabels: {} + podAnnotations: {} + + ## Redis Master resource requests and limits + ## ref: http://kubernetes.io/docs/user-guide/compute-resources/ + # resources: + # requests: + # memory: 256Mi + # cpu: 100m + ## Use an alternate scheduler, e.g. "stork". + ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ + ## + # schedulerName: + + ## Configure extra options for Redis Master liveness and readiness probes + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes) + ## + livenessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + readinessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 5 + + ## Redis Master Node selectors and tolerations for pod assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector + ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#taints-and-tolerations-beta-feature + ## + # nodeSelector: {"beta.kubernetes.io/arch": "amd64"} + # tolerations: [] + ## Redis Master pod/node affinity/anti-affinity + ## + affinity: {} + + ## Redis Master Service properties + service: + ## Redis Master Service type + type: ClusterIP + port: 6379 + + ## Specify the nodePort value for the LoadBalancer and NodePort service types. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport + ## + # nodePort: + + ## Provide any additional annotations which may be required. This can be used to + ## set the LoadBalancer service type to internal only. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer + ## + annotations: {} + loadBalancerIP: + + ## Enable persistence using Persistent Volume Claims + ## ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ + ## + persistence: + enabled: true + ## The path the volume will be mounted at, useful when using different + ## Redis images. + path: /data + ## The subdirectory of the volume to mount to, useful in dev environments + ## and one PV for multiple services. + subPath: "" + ## redis data Persistent Volume Storage Class + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + # storageClass: "-" + accessModes: + - ReadWriteOnce + size: 8Gi + + ## Update strategy, can be set to RollingUpdate or onDelete by default. + ## https://kubernetes.io/docs/tutorials/stateful-application/basic-stateful-set/#updating-statefulsets + statefulset: + updateStrategy: RollingUpdate + ## Partition update strategy + ## https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#partitions + # rollingUpdatePartition: + + ## Redis Master pod priorityClassName + # priorityClassName: {} + + +## +## Redis Slave properties +## Note: service.type is a mandatory parameter +## The rest of the parameters are either optional or, if undefined, will inherit those declared in Redis Master +## +slave: + ## Slave Service properties + service: + ## Redis Slave Service type + type: ClusterIP + ## Redis port + port: 6379 + ## Specify the nodePort value for the LoadBalancer and NodePort service types. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport + ## + # nodePort: + + ## Provide any additional annotations which may be required. This can be used to + ## set the LoadBalancer service type to internal only. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer + ## + annotations: {} + loadBalancerIP: + + ## Redis slave port + port: 6379 + + ## Can be used to specify command line arguments, for example: + ## + command: "/run.sh" + ## Redis extra flags + extraFlags: [] + ## List of Redis commands to disable + disableCommands: + - FLUSHDB + - FLUSHALL + + ## Redis Slave pod/node affinity/anti-affinity + ## + affinity: {} + + ## Configure extra options for Redis Slave liveness and readiness probes + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes) + ## + livenessProbe: + enabled: true + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + readinessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 10 + successThreshold: 1 + failureThreshold: 5 + + ## Redis slave Resource + # resources: + # requests: + # memory: 256Mi + # cpu: 100m + + ## Enable persistence using Persistent Volume Claims + ## ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ + ## + persistence: + enabled: true + ## The path the volume will be mounted at, useful when using different + ## Redis images. + path: /data + ## The subdirectory of the volume to mount to, useful in dev environments + ## and one PV for multiple services. + subPath: "" + ## redis data Persistent Volume Storage Class + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + # storageClass: "-" + accessModes: + - ReadWriteOnce + size: 8Gi + + ## Update strategy, can be set to RollingUpdate or onDelete by default. + ## https://kubernetes.io/docs/tutorials/stateful-application/basic-stateful-set/#updating-statefulsets + statefulset: + updateStrategy: RollingUpdate + ## Partition update strategy + ## https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#partitions + # rollingUpdatePartition: + + ## Redis slave selectors and tolerations for pod assignment + # nodeSelector: {"beta.kubernetes.io/arch": "amd64"} + # tolerations: [] + + ## Use an alternate scheduler, e.g. "stork". + ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ + ## + # schedulerName: + + ## Redis slave pod Annotation and Labels + podLabels: {} + podAnnotations: {} + + ## Redis slave pod priorityClassName + # priorityClassName: {} + +## Prometheus Exporter / Metrics +## +metrics: + enabled: true + + image: + registry: docker.io + repository: bitnami/redis-exporter + tag: 1.0.3-debian-9-r0 + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + + ## Metrics exporter resource requests and limits + ## ref: http://kubernetes.io/docs/user-guide/compute-resources/ + ## + # resources: {} + ## Metrics exporter pod priorityClassName + # priorityClassName: {} + service: + type: ClusterIP + ## Use serviceLoadBalancerIP to request a specific static IP, + ## otherwise leave blank + # loadBalancerIP: + annotations: {} + + ## Extra arguments for Metrics exporter, for example: + ## extraArgs: + ## check-keys: myKey,myOtherKey + # extraArgs: {} + + ## Metrics exporter pod Annotation and Labels + podAnnotations: + prometheus.io/scrape: "true" + prometheus.io/port: "9121" + # podLabels: {} + + # Enable this if you're using https://github.com/coreos/prometheus-operator + serviceMonitor: + enabled: false + ## Specify a namespace if needed + # namespace: monitoring + # fallback to the prometheus default unless specified + # interval: 10s + ## Defaults to what's used if you follow CoreOS [Prometheus Install Instructions](https://github.com/helm/charts/tree/master/stable/prometheus-operator#tldr) + ## [Prometheus Selector Label](https://github.com/helm/charts/tree/master/stable/prometheus-operator#prometheus-operator-1) + ## [Kube Prometheus Selector Label](https://github.com/helm/charts/tree/master/stable/prometheus-operator#exporters) + selector: + prometheus: kube-prometheus +## +## Init containers parameters: +## volumePermissions: Change the owner of the persist volume mountpoint to RunAsUser:fsGroup +## +volumePermissions: + enabled: false + image: + registry: docker.io + repository: bitnami/minideb + tag: buster + pullPolicy: Always + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + resources: {} + # resources: + # requests: + # memory: 128Mi + # cpu: 100m + +## Redis config file +## ref: https://redis.io/topics/config +## +configmap: |- + # maxmemory-policy volatile-lru + +## Sysctl InitContainer +## used to perform sysctl operation to modify Kernel settings (needed sometimes to avoid warnings) +sysctlImage: + enabled: false + command: [] + registry: docker.io + repository: bitnami/minideb + tag: buster + pullPolicy: Always + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + mountHostSys: false + resources: {} + # resources: + # requests: + # memory: 128Mi + # cpu: 100m diff --git a/charts/deps/charts/airflow-8.8.0/charts/redis/ci/production-values.yaml b/charts/deps/charts/airflow-8.8.0/charts/redis/ci/production-values.yaml new file mode 100644 index 0000000..6fa9c88 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/redis/ci/production-values.yaml @@ -0,0 +1,525 @@ +## Global Docker image parameters +## Please, note that this will override the image parameters, including dependencies, configured to use the global value +## Current available global Docker image parameters: imageRegistry and imagePullSecrets +## +# global: +# imageRegistry: myRegistryName +# imagePullSecrets: +# - myRegistryKeySecretName + +## Bitnami Redis image version +## ref: https://hub.docker.com/r/bitnami/redis/tags/ +## +image: + registry: docker.io + repository: bitnami/redis + ## Bitnami Redis image tag + ## ref: https://github.com/bitnami/bitnami-docker-redis#supported-tags-and-respective-dockerfile-links + ## + tag: 5.0.5-debian-9-r36 + ## Specify a imagePullPolicy + ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images + ## + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + +## Redis pod Security Context +securityContext: + enabled: true + fsGroup: 1001 + runAsUser: 1001 + +## Cluster settings +cluster: + enabled: true + slaveCount: 3 + +## Use redis sentinel in the redis pod. This will disable the master and slave services and +## create one redis service with ports to the sentinel and the redis instances +sentinel: + enabled: false + ## Require password authentication on the sentinel itself + ## ref: https://redis.io/topics/sentinel + usePassword: true + ## Bitnami Redis Sentintel image version + ## ref: https://hub.docker.com/r/bitnami/redis-sentinel/tags/ + ## + image: + registry: docker.io + repository: bitnami/redis-sentinel + ## Bitnami Redis image tag + ## ref: https://github.com/bitnami/bitnami-docker-redis-sentinel#supported-tags-and-respective-dockerfile-links + ## + tag: 5.0.5-debian-9-r37 + ## Specify a imagePullPolicy + ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images + ## + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + masterSet: mymaster + initialCheckTimeout: 5 + quorum: 2 + downAfterMilliseconds: 60000 + failoverTimeout: 18000 + parallelSyncs: 1 + port: 26379 + ## Configure extra options for Redis Sentinel liveness and readiness probes + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes) + ## + livenessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + readinessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 5 + ## Redis Sentinel resource requests and limits + ## ref: http://kubernetes.io/docs/user-guide/compute-resources/ + # resources: + # requests: + # memory: 256Mi + # cpu: 100m + ## Redis Sentinel Service properties + service: + ## Redis Sentinel Service type + type: ClusterIP + sentinelPort: 26379 + redisPort: 6379 + + ## Specify the nodePort value for the LoadBalancer and NodePort service types. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport + ## + # sentinelNodePort: + # redisNodePort: + + ## Provide any additional annotations which may be required. This can be used to + ## set the LoadBalancer service type to internal only. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer + ## + annotations: {} + loadBalancerIP: + +networkPolicy: + ## Specifies whether a NetworkPolicy should be created + ## + enabled: true + + ## The Policy model to apply. When set to false, only pods with the correct + ## client label will have network access to the port Redis is listening + ## on. When true, Redis will accept connections from any source + ## (with the correct destination port). + ## + # allowExternal: true + +serviceAccount: + ## Specifies whether a ServiceAccount should be created + ## + create: false + ## The name of the ServiceAccount to use. + ## If not set and create is true, a name is generated using the fullname template + name: + +rbac: + ## Specifies whether RBAC resources should be created + ## + create: false + + role: + ## Rules to create. It follows the role specification + # rules: + # - apiGroups: + # - extensions + # resources: + # - podsecuritypolicies + # verbs: + # - use + # resourceNames: + # - gce.unprivileged + rules: [] + + +## Use password authentication +usePassword: true +## Redis password (both master and slave) +## Defaults to a random 10-character alphanumeric string if not set and usePassword is true +## ref: https://github.com/bitnami/bitnami-docker-redis#setting-the-server-password-on-first-run +## +password: +## Use existing secret (ignores previous password) +# existingSecret: +## Password key to be retrieved from Redis secret +## +# existingSecretPasswordKey: + +## Mount secrets as files instead of environment variables +usePasswordFile: false + +## Persist data to a persistent volume +persistence: {} + ## A manually managed Persistent Volume and Claim + ## Requires persistence.enabled: true + ## If defined, PVC must be created manually before volume will be bound + # existingClaim: + +# Redis port +redisPort: 6379 + +## +## Redis Master parameters +## +master: + ## Redis command arguments + ## + ## Can be used to specify command line arguments, for example: + ## + command: "/run.sh" + ## Redis additional command line flags + ## + ## Can be used to specify command line flags, for example: + ## + ## extraFlags: + ## - "--maxmemory-policy volatile-ttl" + ## - "--repl-backlog-size 1024mb" + extraFlags: [] + ## Comma-separated list of Redis commands to disable + ## + ## Can be used to disable Redis commands for security reasons. + ## Commands will be completely disabled by renaming each to an empty string. + ## ref: https://redis.io/topics/security#disabling-of-specific-commands + ## + disableCommands: + - FLUSHDB + - FLUSHALL + + ## Redis Master additional pod labels and annotations + ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + podLabels: {} + podAnnotations: {} + + ## Redis Master resource requests and limits + ## ref: http://kubernetes.io/docs/user-guide/compute-resources/ + # resources: + # requests: + # memory: 256Mi + # cpu: 100m + ## Use an alternate scheduler, e.g. "stork". + ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ + ## + # schedulerName: + + ## Configure extra options for Redis Master liveness and readiness probes + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes) + ## + livenessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + readinessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 5 + + ## Redis Master Node selectors and tolerations for pod assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector + ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#taints-and-tolerations-beta-feature + ## + # nodeSelector: {"beta.kubernetes.io/arch": "amd64"} + # tolerations: [] + ## Redis Master pod/node affinity/anti-affinity + ## + affinity: {} + + ## Redis Master Service properties + service: + ## Redis Master Service type + type: ClusterIP + port: 6379 + + ## Specify the nodePort value for the LoadBalancer and NodePort service types. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport + ## + # nodePort: + + ## Provide any additional annotations which may be required. This can be used to + ## set the LoadBalancer service type to internal only. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer + ## + annotations: {} + loadBalancerIP: + + ## Enable persistence using Persistent Volume Claims + ## ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ + ## + persistence: + enabled: true + ## The path the volume will be mounted at, useful when using different + ## Redis images. + path: /data + ## The subdirectory of the volume to mount to, useful in dev environments + ## and one PV for multiple services. + subPath: "" + ## redis data Persistent Volume Storage Class + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + # storageClass: "-" + accessModes: + - ReadWriteOnce + size: 8Gi + + ## Update strategy, can be set to RollingUpdate or onDelete by default. + ## https://kubernetes.io/docs/tutorials/stateful-application/basic-stateful-set/#updating-statefulsets + statefulset: + updateStrategy: RollingUpdate + ## Partition update strategy + ## https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#partitions + # rollingUpdatePartition: + + ## Redis Master pod priorityClassName + # priorityClassName: {} + + +## +## Redis Slave properties +## Note: service.type is a mandatory parameter +## The rest of the parameters are either optional or, if undefined, will inherit those declared in Redis Master +## +slave: + ## Slave Service properties + service: + ## Redis Slave Service type + type: ClusterIP + ## Redis port + port: 6379 + ## Specify the nodePort value for the LoadBalancer and NodePort service types. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport + ## + # nodePort: + + ## Provide any additional annotations which may be required. This can be used to + ## set the LoadBalancer service type to internal only. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer + ## + annotations: {} + loadBalancerIP: + + ## Redis slave port + port: 6379 + + ## Can be used to specify command line arguments, for example: + ## + command: "/run.sh" + ## Redis extra flags + extraFlags: [] + ## List of Redis commands to disable + disableCommands: + - FLUSHDB + - FLUSHALL + + ## Redis Slave pod/node affinity/anti-affinity + ## + affinity: {} + + ## Configure extra options for Redis Slave liveness and readiness probes + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes) + ## + livenessProbe: + enabled: true + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + readinessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 10 + successThreshold: 1 + failureThreshold: 5 + + ## Redis slave Resource + # resources: + # requests: + # memory: 256Mi + # cpu: 100m + + ## Enable persistence using Persistent Volume Claims + ## ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ + ## + persistence: + enabled: true + ## The path the volume will be mounted at, useful when using different + ## Redis images. + path: /data + ## The subdirectory of the volume to mount to, useful in dev environments + ## and one PV for multiple services. + subPath: "" + ## redis data Persistent Volume Storage Class + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + # storageClass: "-" + accessModes: + - ReadWriteOnce + size: 8Gi + + ## Update strategy, can be set to RollingUpdate or onDelete by default. + ## https://kubernetes.io/docs/tutorials/stateful-application/basic-stateful-set/#updating-statefulsets + statefulset: + updateStrategy: RollingUpdate + ## Partition update strategy + ## https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#partitions + # rollingUpdatePartition: + + ## Redis slave selectors and tolerations for pod assignment + # nodeSelector: {"beta.kubernetes.io/arch": "amd64"} + # tolerations: [] + + ## Use an alternate scheduler, e.g. "stork". + ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ + ## + # schedulerName: + + ## Redis slave pod Annotation and Labels + podLabels: {} + podAnnotations: {} + + ## Redis slave pod priorityClassName + # priorityClassName: {} + +## Prometheus Exporter / Metrics +## +metrics: + enabled: true + + image: + registry: docker.io + repository: bitnami/redis-exporter + tag: 1.0.3-debian-9-r0 + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + + ## Metrics exporter resource requests and limits + ## ref: http://kubernetes.io/docs/user-guide/compute-resources/ + ## + # resources: {} + + ## Extra arguments for Metrics exporter, for example: + ## extraArgs: + ## check-keys: myKey,myOtherKey + # extraArgs: {} + ## Metrics exporter pod priorityClassName + # priorityClassName: {} + service: + type: ClusterIP + ## Use serviceLoadBalancerIP to request a specific static IP, + ## otherwise leave blank + # loadBalancerIP: + annotations: {} + + ## Metrics exporter pod Annotation and Labels + podAnnotations: + prometheus.io/scrape: "true" + prometheus.io/port: "9121" + # podLabels: {} + + # Enable this if you're using https://github.com/coreos/prometheus-operator + serviceMonitor: + enabled: false + ## Specify a namespace if needed + # namespace: monitoring + # fallback to the prometheus default unless specified + # interval: 10s + ## Defaults to what's used if you follow CoreOS [Prometheus Install Instructions](https://github.com/helm/charts/tree/master/stable/prometheus-operator#tldr) + ## [Prometheus Selector Label](https://github.com/helm/charts/tree/master/stable/prometheus-operator#prometheus-operator-1) + ## [Kube Prometheus Selector Label](https://github.com/helm/charts/tree/master/stable/prometheus-operator#exporters) + selector: + prometheus: kube-prometheus + +## +## Init containers parameters: +## volumePermissions: Change the owner of the persist volume mountpoint to RunAsUser:fsGroup +## +volumePermissions: + enabled: false + image: + registry: docker.io + repository: bitnami/minideb + tag: buster + pullPolicy: Always + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + resources: {} + # resources: + # requests: + # memory: 128Mi + # cpu: 100m + +## Redis config file +## ref: https://redis.io/topics/config +## +configmap: |- + # maxmemory-policy volatile-lru + +## Sysctl InitContainer +## used to perform sysctl operation to modify Kernel settings (needed sometimes to avoid warnings) +sysctlImage: + enabled: false + command: [] + registry: docker.io + repository: bitnami/minideb + tag: buster + pullPolicy: Always + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + mountHostSys: false + resources: {} + # resources: + # requests: + # memory: 128Mi + # cpu: 100m diff --git a/charts/deps/charts/airflow-8.8.0/charts/redis/ci/redis-lib-values.yaml b/charts/deps/charts/airflow-8.8.0/charts/redis/ci/redis-lib-values.yaml new file mode 100644 index 0000000..e03382b --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/redis/ci/redis-lib-values.yaml @@ -0,0 +1,13 @@ +## Redis library image +## ref: https://hub.docker.com/r/library/redis/ +## +image: + registry: docker.io + repository: redis + tag: '5.0.5' + +master: + command: "redis-server" + +slave: + command: "redis-server" diff --git a/charts/deps/charts/airflow-8.8.0/charts/redis/ci/redisgraph-module-values.yaml b/charts/deps/charts/airflow-8.8.0/charts/redis/ci/redisgraph-module-values.yaml new file mode 100644 index 0000000..8096020 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/redis/ci/redisgraph-module-values.yaml @@ -0,0 +1,10 @@ +image: + registry: docker.io + repository: redislabs/redisgraph + tag: '1.0.0' + +master: + command: "redis-server" + +slave: + command: "redis-server" diff --git a/charts/deps/charts/airflow-8.8.0/charts/redis/templates/NOTES.txt b/charts/deps/charts/airflow-8.8.0/charts/redis/templates/NOTES.txt new file mode 100644 index 0000000..e1889a1 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/redis/templates/NOTES.txt @@ -0,0 +1,124 @@ +This Helm chart is deprecated + +Given the `stable` deprecation timeline (https://github.com/helm/charts#deprecation-timeline), the Bitnami maintained Redis Helm chart is now located at bitnami/charts (https://github.com/bitnami/charts/). + +The Bitnami repository is already included in the Hubs and we will continue providing the same cadence of updates, support, etc that we've been keeping here these years. Installation instructions are very similar, just adding the _bitnami_ repo and using it during the installation (`bitnami/` instead of `stable/`) + +```bash +$ helm repo add bitnami https://charts.bitnami.com/bitnami +$ helm install my-release bitnami/ # Helm 3 +$ helm install --name my-release bitnami/ # Helm 2 +``` + +To update an exisiting _stable_ deployment with a chart hosted in the bitnami repository you can execute + ```bash $ helm + repo add bitnami https://charts.bitnami.com/bitnami + $ helm upgrade my-release bitnami/ + ``` + + Issues and PRs related to the chart itself will be redirected to `bitnami/charts` GitHub repository. In the same way, we'll be happy to answer questions related to this migration process in this issue (https://github.com/helm/charts/issues/20969) created as a common place for discussion. + +** Please be patient while the chart is being deployed ** + +{{- if contains .Values.master.service.type "LoadBalancer" }} +{{- if not .Values.usePassword }} +{{ if and (not .Values.networkPolicy.enabled) (.Values.networkPolicy.allowExternal) }} + +------------------------------------------------------------------------------- + WARNING + + By specifying "master.service.type=LoadBalancer" and "usePassword=false" you have + most likely exposed the Redis service externally without any authentication + mechanism. + + For security reasons, we strongly suggest that you switch to "ClusterIP" or + "NodePort". As alternative, you can also switch to "usePassword=true" + providing a valid password on "password" parameter. + +------------------------------------------------------------------------------- +{{- end }} +{{- end }} +{{- end }} + +{{- if .Values.cluster.enabled }} +{{- if .Values.sentinel.enabled }} +Redis can be accessed via port {{ .Values.sentinel.service.redisPort }} on the following DNS name from within your cluster: + +{{ template "redis.fullname" . }}.{{ .Release.Namespace }}.svc.{{ .Values.clusterDomain }} for read only operations + +For read/write operations, first access the Redis Sentinel cluster, which is available in port {{ .Values.sentinel.service.sentinelPort }} using the same domain name above. + +{{- else }} +Redis can be accessed via port {{ .Values.redisPort }} on the following DNS names from within your cluster: + +{{ template "redis.fullname" . }}-master.{{ .Release.Namespace }}.svc.{{ .Values.clusterDomain }} for read/write operations +{{ template "redis.fullname" . }}-slave.{{ .Release.Namespace }}.svc.{{ .Values.clusterDomain }} for read-only operations +{{- end }} + +{{- else }} +Redis can be accessed via port {{ .Values.redisPort }} on the following DNS name from within your cluster: + +{{ template "redis.fullname" . }}-master.{{ .Release.Namespace }}.svc.{{ .Values.clusterDomain }} + +{{- end }} + +{{ if .Values.usePassword }} +To get your password run: + + export REDIS_PASSWORD=$(kubectl get secret --namespace {{ .Release.Namespace }} {{ template "redis.secretName" . }} -o jsonpath="{.data.redis-password}" | base64 --decode) +{{- end }} + +To connect to your Redis server: + +1. Run a Redis pod that you can use as a client: + + kubectl run --namespace {{ .Release.Namespace }} {{ template "redis.fullname" . }}-client --rm --tty -i --restart='Never' \ + {{ if .Values.usePassword }} --env REDIS_PASSWORD=$REDIS_PASSWORD \{{ end }} + {{- if and (.Values.networkPolicy.enabled) (not .Values.networkPolicy.allowExternal) }}--labels="{{ template "redis.fullname" . }}-client=true" \{{- end }} + --image {{ template "redis.image" . }} -- bash + +2. Connect using the Redis CLI: + +{{- if .Values.cluster.enabled }} + {{- if .Values.sentinel.enabled }} + redis-cli -h {{ template "redis.fullname" . }} -p {{ .Values.sentinel.service.redisPort }}{{ if .Values.usePassword }} -a $REDIS_PASSWORD{{ end }} # Read only operations + redis-cli -h {{ template "redis.fullname" . }} -p {{ .Values.sentinel.service.sentinelPort }}{{ if .Values.usePassword }} -a $REDIS_PASSWORD{{ end }} # Sentinel access + {{- else }} + redis-cli -h {{ template "redis.fullname" . }}-master{{ if .Values.usePassword }} -a $REDIS_PASSWORD{{ end }} + redis-cli -h {{ template "redis.fullname" . }}-slave{{ if .Values.usePassword }} -a $REDIS_PASSWORD{{ end }} + {{- end }} +{{- else }} + redis-cli -h {{ template "redis.fullname" . }}-master{{ if .Values.usePassword }} -a $REDIS_PASSWORD{{ end }} +{{- end }} + +{{ if and (.Values.networkPolicy.enabled) (not .Values.networkPolicy.allowExternal) }} +Note: Since NetworkPolicy is enabled, only pods with label +{{ template "redis.fullname" . }}-client=true" +will be able to connect to redis. +{{- else -}} + +To connect to your database from outside the cluster execute the following commands: + +{{- if contains "NodePort" .Values.master.service.type }} + + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ template "redis.fullname" . }}-master) + redis-cli -h $NODE_IP -p $NODE_PORT {{- if .Values.usePassword }} -a $REDIS_PASSWORD{{ end }} + +{{- else if contains "LoadBalancer" .Values.master.service.type }} + + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + Watch the status with: 'kubectl get svc --namespace {{ .Release.Namespace }} -w {{ template "redis.fullname" . }}' + + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "redis.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + redis-cli -h $SERVICE_IP -p {{ .Values.master.service.port }} {{- if .Values.usePassword }} -a $REDIS_PASSWORD{{ end }} + +{{- else if contains "ClusterIP" .Values.master.service.type }} + + kubectl port-forward --namespace {{ .Release.Namespace }} svc/{{ template "redis.fullname" . }}-master {{ .Values.redisPort }}:{{ .Values.redisPort }} & + redis-cli -h 127.0.0.1 -p {{ .Values.redisPort }} {{- if .Values.usePassword }} -a $REDIS_PASSWORD{{ end }} + +{{- end }} +{{- end }} + +{{ include "redis.checkRollingTags" . }} diff --git a/charts/deps/charts/airflow-8.8.0/charts/redis/templates/_helpers.tpl b/charts/deps/charts/airflow-8.8.0/charts/redis/templates/_helpers.tpl new file mode 100644 index 0000000..3397a7b --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/redis/templates/_helpers.tpl @@ -0,0 +1,355 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "redis.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Expand the chart plus release name (used by the chart label) +*/}} +{{- define "redis.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version -}} +{{- 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 "redis.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 -}} + +{{/* +Return the appropriate apiVersion for networkpolicy. +*/}} +{{- define "networkPolicy.apiVersion" -}} +{{- if semverCompare ">=1.4-0, <1.7-0" .Capabilities.KubeVersion.GitVersion -}} +{{- print "extensions/v1beta1" -}} +{{- else -}} +{{- print "networking.k8s.io/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiGroup for PodSecurityPolicy. +*/}} +{{- define "podSecurityPolicy.apiGroup" -}} +{{- if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +{{- print "policy" -}} +{{- else -}} +{{- print "extensions" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for PodSecurityPolicy. +*/}} +{{- define "podSecurityPolicy.apiVersion" -}} +{{- if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +{{- print "policy/v1beta1" -}} +{{- else -}} +{{- print "extensions/v1beta1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the proper Redis image name +*/}} +{{- define "redis.image" -}} +{{- $registryName := .Values.image.registry -}} +{{- $repositoryName := .Values.image.repository -}} +{{- $tag := .Values.image.tag | toString -}} +{{/* +Helm 2.11 supports the assignment of a value to a variable defined in a different scope, +but Helm 2.9 and 2.10 doesn't support it, so we need to implement this if-else logic. +Also, we can't use a single if because lazy evaluation is not an option +*/}} +{{- if .Values.global }} + {{- if .Values.global.imageRegistry }} + {{- printf "%s/%s:%s" .Values.global.imageRegistry $repositoryName $tag -}} + {{- else -}} + {{- printf "%s/%s:%s" $registryName $repositoryName $tag -}} + {{- end -}} +{{- else -}} + {{- printf "%s/%s:%s" $registryName $repositoryName $tag -}} +{{- end -}} +{{- end -}} + +{{/* +Return the proper Redis Sentinel image name +*/}} +{{- define "sentinel.image" -}} +{{- $registryName := .Values.sentinel.image.registry -}} +{{- $repositoryName := .Values.sentinel.image.repository -}} +{{- $tag := .Values.sentinel.image.tag | toString -}} +{{/* +Helm 2.11 supports the assignment of a value to a variable defined in a different scope, +but Helm 2.9 and 2.10 doesn't support it, so we need to implement this if-else logic. +Also, we can't use a single if because lazy evaluation is not an option +*/}} +{{- if .Values.global }} + {{- if .Values.global.imageRegistry }} + {{- printf "%s/%s:%s" .Values.global.imageRegistry $repositoryName $tag -}} + {{- else -}} + {{- printf "%s/%s:%s" $registryName $repositoryName $tag -}} + {{- end -}} +{{- else -}} + {{- printf "%s/%s:%s" $registryName $repositoryName $tag -}} +{{- end -}} +{{- end -}} + +{{/* +Return the proper image name (for the metrics image) +*/}} +{{- define "redis.metrics.image" -}} +{{- $registryName := .Values.metrics.image.registry -}} +{{- $repositoryName := .Values.metrics.image.repository -}} +{{- $tag := .Values.metrics.image.tag | toString -}} +{{/* +Helm 2.11 supports the assignment of a value to a variable defined in a different scope, +but Helm 2.9 and 2.10 doesn't support it, so we need to implement this if-else logic. +Also, we can't use a single if because lazy evaluation is not an option +*/}} +{{- if .Values.global }} + {{- if .Values.global.imageRegistry }} + {{- printf "%s/%s:%s" .Values.global.imageRegistry $repositoryName $tag -}} + {{- else -}} + {{- printf "%s/%s:%s" $registryName $repositoryName $tag -}} + {{- end -}} +{{- else -}} + {{- printf "%s/%s:%s" $registryName $repositoryName $tag -}} +{{- end -}} +{{- end -}} + +{{/* +Return the proper image name (for the init container volume-permissions image) +*/}} +{{- define "redis.volumePermissions.image" -}} +{{- $registryName := .Values.volumePermissions.image.registry -}} +{{- $repositoryName := .Values.volumePermissions.image.repository -}} +{{- $tag := .Values.volumePermissions.image.tag | toString -}} +{{/* +Helm 2.11 supports the assignment of a value to a variable defined in a different scope, +but Helm 2.9 and 2.10 doesn't support it, so we need to implement this if-else logic. +Also, we can't use a single if because lazy evaluation is not an option +*/}} +{{- if .Values.global }} + {{- if .Values.global.imageRegistry }} + {{- printf "%s/%s:%s" .Values.global.imageRegistry $repositoryName $tag -}} + {{- else -}} + {{- printf "%s/%s:%s" $registryName $repositoryName $tag -}} + {{- end -}} +{{- else -}} + {{- printf "%s/%s:%s" $registryName $repositoryName $tag -}} +{{- end -}} +{{- end -}} + +{{/* +Create the name of the service account to use +*/}} +{{- define "redis.serviceAccountName" -}} +{{- if .Values.serviceAccount.create -}} + {{ default (include "redis.fullname" .) .Values.serviceAccount.name }} +{{- else -}} + {{ default "default" .Values.serviceAccount.name }} +{{- end -}} +{{- end -}} + +{{/* +Get the password secret. +*/}} +{{- define "redis.secretName" -}} +{{- if .Values.existingSecret -}} +{{- printf "%s" .Values.existingSecret -}} +{{- else -}} +{{- printf "%s" (include "redis.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Get the password key to be retrieved from Redis secret. +*/}} +{{- define "redis.secretPasswordKey" -}} +{{- if and .Values.existingSecret .Values.existingSecretPasswordKey -}} +{{- printf "%s" .Values.existingSecretPasswordKey -}} +{{- else -}} +{{- printf "redis-password" -}} +{{- end -}} +{{- end -}} + +{{/* +Return Redis password +*/}} +{{- define "redis.password" -}} +{{- if not (empty .Values.global.redis.password) }} + {{- .Values.global.redis.password -}} +{{- else if not (empty .Values.password) -}} + {{- .Values.password -}} +{{- else -}} + {{- randAlphaNum 10 -}} +{{- end -}} +{{- end -}} + +{{/* +Return sysctl image +*/}} +{{- define "redis.sysctl.image" -}} +{{- $registryName := default "docker.io" .Values.sysctlImage.registry -}} +{{- $repositoryName := .Values.sysctlImage.repository -}} +{{- $tag := default "buster" .Values.sysctlImage.tag | toString -}} +{{/* +Helm 2.11 supports the assignment of a value to a variable defined in a different scope, +but Helm 2.9 and 2.10 doesn't support it, so we need to implement this if-else logic. +Also, we can't use a single if because lazy evaluation is not an option +*/}} +{{- if .Values.global }} + {{- if .Values.global.imageRegistry }} + {{- printf "%s/%s:%s" .Values.global.imageRegistry $repositoryName $tag -}} + {{- else -}} + {{- printf "%s/%s:%s" $registryName $repositoryName $tag -}} + {{- end -}} +{{- else -}} + {{- printf "%s/%s:%s" $registryName $repositoryName $tag -}} +{{- end -}} +{{- end -}} + +{{/* +Return the proper Docker Image Registry Secret Names +*/}} +{{- define "redis.imagePullSecrets" -}} +{{/* +Helm 2.11 supports the assignment of a value to a variable defined in a different scope, +but Helm 2.9 and 2.10 does not support it, so we need to implement this if-else logic. +Also, we can not use a single if because lazy evaluation is not an option +*/}} +{{- if .Values.global }} +{{- if .Values.global.imagePullSecrets }} +imagePullSecrets: +{{- range .Values.global.imagePullSecrets }} + - name: {{ . }} +{{- end }} +{{- else if or .Values.image.pullSecrets .Values.metrics.image.pullSecrets .Values.sysctlImage.pullSecrets .Values.volumePermissions.image.pullSecrets }} +imagePullSecrets: +{{- range .Values.image.pullSecrets }} + - name: {{ . }} +{{- end }} +{{- range .Values.metrics.image.pullSecrets }} + - name: {{ . }} +{{- end }} +{{- range .Values.sysctlImage.pullSecrets }} + - name: {{ . }} +{{- end }} +{{- range .Values.volumePermissions.image.pullSecrets }} + - name: {{ . }} +{{- end }} +{{- end -}} +{{- else if or .Values.image.pullSecrets .Values.metrics.image.pullSecrets .Values.sysctlImage.pullSecrets .Values.volumePermissions.image.pullSecrets }} +imagePullSecrets: +{{- range .Values.image.pullSecrets }} + - name: {{ . }} +{{- end }} +{{- range .Values.metrics.image.pullSecrets }} + - name: {{ . }} +{{- end }} +{{- range .Values.sysctlImage.pullSecrets }} + - name: {{ . }} +{{- end }} +{{- range .Values.volumePermissions.image.pullSecrets }} + - name: {{ . }} +{{- end }} +{{- end -}} +{{- end -}} + +{{/* Check if there are rolling tags in the images */}} +{{- define "redis.checkRollingTags" -}} +{{- if and (contains "bitnami/" .Values.image.repository) (not (.Values.image.tag | toString | regexFind "-r\\d+$|sha256:")) }} +WARNING: Rolling tag detected ({{ .Values.image.repository }}:{{ .Values.image.tag }}), please note that it is strongly recommended to avoid using rolling tags in a production environment. ++info https://docs.bitnami.com/containers/how-to/understand-rolling-tags-containers/ +{{- end }} +{{- if and (contains "bitnami/" .Values.sentinel.image.repository) (not (.Values.sentinel.image.tag | toString | regexFind "-r\\d+$|sha256:")) }} +WARNING: Rolling tag detected ({{ .Values.sentinel.image.repository }}:{{ .Values.sentinel.image.tag }}), please note that it is strongly recommended to avoid using rolling tags in a production environment. ++info https://docs.bitnami.com/containers/how-to/understand-rolling-tags-containers/ +{{- end }} +{{- end -}} + +{{/* +Return the proper Storage Class for master +*/}} +{{- define "redis.master.storageClass" -}} +{{/* +Helm 2.11 supports the assignment of a value to a variable defined in a different scope, +but Helm 2.9 and 2.10 does not support it, so we need to implement this if-else logic. +*/}} +{{- if .Values.global -}} + {{- if .Values.global.storageClass -}} + {{- if (eq "-" .Values.global.storageClass) -}} + {{- printf "storageClassName: \"\"" -}} + {{- else }} + {{- printf "storageClassName: %s" .Values.global.storageClass -}} + {{- end -}} + {{- else -}} + {{- if .Values.master.persistence.storageClass -}} + {{- if (eq "-" .Values.master.persistence.storageClass) -}} + {{- printf "storageClassName: \"\"" -}} + {{- else }} + {{- printf "storageClassName: %s" .Values.master.persistence.storageClass -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- else -}} + {{- if .Values.master.persistence.storageClass -}} + {{- if (eq "-" .Values.master.persistence.storageClass) -}} + {{- printf "storageClassName: \"\"" -}} + {{- else }} + {{- printf "storageClassName: %s" .Values.master.persistence.storageClass -}} + {{- end -}} + {{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Return the proper Storage Class for slave +*/}} +{{- define "redis.slave.storageClass" -}} +{{/* +Helm 2.11 supports the assignment of a value to a variable defined in a different scope, +but Helm 2.9 and 2.10 does not support it, so we need to implement this if-else logic. +*/}} +{{- if .Values.global -}} + {{- if .Values.global.storageClass -}} + {{- if (eq "-" .Values.global.storageClass) -}} + {{- printf "storageClassName: \"\"" -}} + {{- else }} + {{- printf "storageClassName: %s" .Values.global.storageClass -}} + {{- end -}} + {{- else -}} + {{- if .Values.slave.persistence.storageClass -}} + {{- if (eq "-" .Values.slave.persistence.storageClass) -}} + {{- printf "storageClassName: \"\"" -}} + {{- else }} + {{- printf "storageClassName: %s" .Values.slave.persistence.storageClass -}} + {{- end -}} + {{- end -}} + {{- end -}} +{{- else -}} + {{- if .Values.slave.persistence.storageClass -}} + {{- if (eq "-" .Values.slave.persistence.storageClass) -}} + {{- printf "storageClassName: \"\"" -}} + {{- else }} + {{- printf "storageClassName: %s" .Values.slave.persistence.storageClass -}} + {{- end -}} + {{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/deps/charts/airflow-8.8.0/charts/redis/templates/configmap.yaml b/charts/deps/charts/airflow-8.8.0/charts/redis/templates/configmap.yaml new file mode 100644 index 0000000..d17ec26 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/redis/templates/configmap.yaml @@ -0,0 +1,52 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "redis.fullname" . }} + labels: + app: {{ template "redis.name" . }} + chart: {{ template "redis.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} +data: + redis.conf: |- +{{- if .Values.configmap }} + # User-supplied configuration: +{{ tpl .Values.configmap . | indent 4 }} +{{- end }} + master.conf: |- + dir {{ .Values.master.persistence.path }} +{{- if .Values.master.configmap }} + # User-supplied master configuration: +{{ tpl .Values.master.configmap . | indent 4 }} +{{- end }} +{{- if .Values.master.disableCommands }} +{{- range .Values.master.disableCommands }} + rename-command {{ . }} "" +{{- end }} +{{- end }} + replica.conf: |- + dir {{ .Values.slave.persistence.path }} + slave-read-only yes +{{- if .Values.slave.configmap }} + # User-supplied slave configuration: +{{ tpl .Values.slave.configmap . | indent 4 }} +{{- end }} +{{- if .Values.slave.disableCommands }} +{{- range .Values.slave.disableCommands }} + rename-command {{ . }} "" +{{- end }} +{{- end }} +{{- if .Values.sentinel.enabled }} + sentinel.conf: |- + dir "/tmp" + bind 0.0.0.0 + port {{ .Values.sentinel.port }} + sentinel monitor {{ .Values.sentinel.masterSet }} {{ template "redis.fullname" . }}-master-0.{{ template "redis.fullname" . }}-headless.{{ .Release.Namespace }}.svc.{{ .Values.clusterDomain }} {{ .Values.redisPort }} {{ .Values.sentinel.quorum }} + sentinel down-after-milliseconds {{ .Values.sentinel.masterSet }} {{ .Values.sentinel.downAfterMilliseconds }} + sentinel failover-timeout {{ .Values.sentinel.masterSet }} {{ .Values.sentinel.failoverTimeout }} + sentinel parallel-syncs {{ .Values.sentinel.masterSet }} {{ .Values.sentinel.parallelSyncs }} +{{- if .Values.sentinel.configmap }} + # User-supplied sentinel configuration: +{{ tpl .Values.sentinel.configmap . | indent 4 }} +{{- end }} +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/charts/redis/templates/headless-svc.yaml b/charts/deps/charts/airflow-8.8.0/charts/redis/templates/headless-svc.yaml new file mode 100644 index 0000000..909cbce --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/redis/templates/headless-svc.yaml @@ -0,0 +1,24 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ template "redis.fullname" . }}-headless + labels: + app: {{ template "redis.name" . }} + chart: {{ template "redis.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + type: ClusterIP + clusterIP: None + ports: + - name: redis + port: {{ .Values.redisPort }} + targetPort: redis +{{- if .Values.sentinel.enabled }} + - name: redis-sentinel + port: {{ .Values.sentinel.port }} + targetPort: redis-sentinel +{{- end }} + selector: + app: {{ template "redis.name" . }} + release: {{ .Release.Name }} diff --git a/charts/deps/charts/airflow-8.8.0/charts/redis/templates/health-configmap.yaml b/charts/deps/charts/airflow-8.8.0/charts/redis/templates/health-configmap.yaml new file mode 100644 index 0000000..35c61b5 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/redis/templates/health-configmap.yaml @@ -0,0 +1,134 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "redis.fullname" . }}-health + labels: + app: {{ template "redis.name" . }} + chart: {{ template "redis.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} +data: + ping_readiness_local.sh: |- +{{- if .Values.usePasswordFile }} + password_aux=`cat ${REDIS_PASSWORD_FILE}` + export REDIS_PASSWORD=$password_aux +{{- end }} + response=$( + timeout -s 9 $1 \ + redis-cli \ +{{- if .Values.usePassword }} + -a $REDIS_PASSWORD --no-auth-warning \ +{{- end }} + -h localhost \ + -p $REDIS_PORT \ + ping + ) + if [ "$response" != "PONG" ]; then + echo "$response" + exit 1 + fi + ping_liveness_local.sh: |- +{{- if .Values.usePasswordFile }} + password_aux=`cat ${REDIS_PASSWORD_FILE}` + export REDIS_PASSWORD=$password_aux +{{- end }} + response=$( + timeout -s 9 $1 \ + redis-cli \ +{{- if .Values.usePassword }} + -a $REDIS_PASSWORD --no-auth-warning \ +{{- end }} + -h localhost \ + -p $REDIS_PORT \ + ping + ) + if [ "$response" != "PONG" ] && [ "$response" != "LOADING Redis is loading the dataset in memory" ]; then + echo "$response" + exit 1 + fi +{{- if .Values.sentinel.enabled }} + ping_sentinel.sh: |- +{{- if .Values.usePasswordFile }} + password_aux=`cat ${REDIS_PASSWORD_FILE}` + export REDIS_PASSWORD=$password_aux +{{- end }} + response=$( + timeout -s 9 $1 \ + redis-cli \ +{{- if .Values.usePassword }} + -a $REDIS_PASSWORD --no-auth-warning \ +{{- end }} + -h localhost \ + -p $REDIS_SENTINEL_PORT \ + ping + ) + if [ "$response" != "PONG" ]; then + echo "$response" + exit 1 + fi + parse_sentinels.awk: |- + /ip/ {FOUND_IP=1} + /port/ {FOUND_PORT=1} + /runid/ {FOUND_RUNID=1} + !/ip|port|runid/ { + if (FOUND_IP==1) { + IP=$1; FOUND_IP=0; + } + else if (FOUND_PORT==1) { + PORT=$1; + FOUND_PORT=0; + } else if (FOUND_RUNID==1) { + printf "\nsentinel known-sentinel {{ .Values.sentinel.masterSet }} %s %s %s", IP, PORT, $0; FOUND_RUNID=0; + } + } +{{- end }} + ping_readiness_master.sh: |- +{{- if .Values.usePasswordFile }} + password_aux=`cat ${REDIS_MASTER_PASSWORD_FILE}` + export REDIS_MASTER_PASSWORD=$password_aux +{{- end }} + response=$( + timeout -s 9 $1 \ + redis-cli \ +{{- if .Values.usePassword }} + -a $REDIS_MASTER_PASSWORD --no-auth-warning \ +{{- end }} + -h $REDIS_MASTER_HOST \ + -p $REDIS_MASTER_PORT_NUMBER \ + ping + ) + if [ "$response" != "PONG" ]; then + echo "$response" + exit 1 + fi + ping_liveness_master.sh: |- +{{- if .Values.usePasswordFile }} + password_aux=`cat ${REDIS_MASTER_PASSWORD_FILE}` + export REDIS_MASTER_PASSWORD=$password_aux +{{- end }} + response=$( + timeout -s 9 $1 \ + redis-cli \ +{{- if .Values.usePassword }} + -a $REDIS_MASTER_PASSWORD --no-auth-warning \ +{{- end }} + -h $REDIS_MASTER_HOST \ + -p $REDIS_MASTER_PORT_NUMBER \ + ping + ) + if [ "$response" != "PONG" ] && [ "$response" != "LOADING Redis is loading the dataset in memory" ]; then + echo "$response" + exit 1 + fi + ping_readiness_local_and_master.sh: |- + script_dir="$(dirname "$0")" + exit_status=0 + "$script_dir/ping_readiness_local.sh" $1 || exit_status=$? + "$script_dir/ping_readiness_master.sh" $1 || exit_status=$? + exit $exit_status + ping_liveness_local_and_master.sh: |- + script_dir="$(dirname "$0")" + exit_status=0 + "$script_dir/ping_liveness_local.sh" $1 || exit_status=$? + "$script_dir/ping_liveness_master.sh" $1 || exit_status=$? + exit $exit_status diff --git a/charts/deps/charts/airflow-8.8.0/charts/redis/templates/metrics-prometheus.yaml b/charts/deps/charts/airflow-8.8.0/charts/redis/templates/metrics-prometheus.yaml new file mode 100644 index 0000000..3f33454 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/redis/templates/metrics-prometheus.yaml @@ -0,0 +1,30 @@ +{{- if and (.Values.metrics.enabled) (.Values.metrics.serviceMonitor.enabled) }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ template "redis.fullname" . }} + {{- if .Values.metrics.serviceMonitor.namespace }} + namespace: {{ .Values.metrics.serviceMonitor.namespace }} + {{- end }} + labels: + app: {{ template "redis.name" . }} + chart: {{ template "redis.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + {{- range $key, $value := .Values.metrics.serviceMonitor.selector }} + {{ $key }}: {{ $value | quote }} + {{- end }} +spec: + endpoints: + - port: metrics + {{- if .Values.metrics.serviceMonitor.interval }} + interval: {{ .Values.metrics.serviceMonitor.interval }} + {{- end }} + selector: + matchLabels: + app: {{ template "redis.name" . }} + release: {{ .Release.Name }} + namespaceSelector: + matchNames: + - {{ .Release.Namespace }} +{{- end -}} diff --git a/charts/deps/charts/airflow-8.8.0/charts/redis/templates/metrics-svc.yaml b/charts/deps/charts/airflow-8.8.0/charts/redis/templates/metrics-svc.yaml new file mode 100644 index 0000000..74f6fa8 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/redis/templates/metrics-svc.yaml @@ -0,0 +1,30 @@ +{{- if .Values.metrics.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "redis.fullname" . }}-metrics + labels: + app: {{ template "redis.name" . }} + chart: {{ template "redis.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + {{- if .Values.metrics.service.labels -}} + {{ toYaml .Values.metrics.service.labels | nindent 4 }} + {{- end -}} + {{- if .Values.metrics.service.annotations }} + annotations: {{ toYaml .Values.metrics.service.annotations | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.metrics.service.type }} + {{ if eq .Values.metrics.service.type "LoadBalancer" -}} {{ if .Values.metrics.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.metrics.service.loadBalancerIP }} + {{ end -}} + {{- end -}} + ports: + - name: metrics + port: 9121 + targetPort: metrics + selector: + app: {{ template "redis.name" . }} + release: {{ .Release.Name }} +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/charts/redis/templates/networkpolicy.yaml b/charts/deps/charts/airflow-8.8.0/charts/redis/templates/networkpolicy.yaml new file mode 100644 index 0000000..da05552 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/redis/templates/networkpolicy.yaml @@ -0,0 +1,73 @@ +{{- if .Values.networkPolicy.enabled }} +kind: NetworkPolicy +apiVersion: {{ template "networkPolicy.apiVersion" . }} +metadata: + name: {{ template "redis.fullname" . }} + labels: + app: {{ template "redis.name" . }} + chart: {{ template "redis.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + podSelector: + matchLabels: + app: {{ template "redis.name" . }} + release: {{ .Release.Name }} + {{- if .Values.cluster.enabled }} + policyTypes: + - Ingress + - Egress + egress: + # Allow dns resolution + - ports: + - port: 53 + protocol: UDP + # Allow outbound connections to other cluster pods + - ports: + - port: {{ .Values.redisPort }} + {{- if .Values.sentinel.enabled }} + - port: {{ .Values.sentinel.port }} + {{- end }} + to: + - podSelector: + matchLabels: + app: {{ template "redis.name" . }} + release: {{ .Release.Name }} + {{- end }} + ingress: + # Allow inbound connections + - ports: + - port: {{ .Values.redisPort }} + {{- if .Values.sentinel.enabled }} + - port: {{ .Values.sentinel.port }} + {{- end }} + {{- if not .Values.networkPolicy.allowExternal }} + from: + - podSelector: + matchLabels: + {{ template "redis.fullname" . }}-client: "true" + - podSelector: + matchLabels: + app: {{ template "redis.name" . }} + release: {{ .Release.Name }} + {{- if .Values.networkPolicy.ingressNSMatchLabels }} + - namespaceSelector: + matchLabels: + {{- range $key, $value := .Values.networkPolicy.ingressNSMatchLabels }} + {{ $key | quote }}: {{ $value | quote }} + {{- end }} + {{- if .Values.networkPolicy.ingressNSPodMatchLabels }} + podSelector: + matchLabels: + {{- range $key, $value := .Values.networkPolicy.ingressNSPodMatchLabels }} + {{ $key | quote }}: {{ $value | quote }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + {{- if .Values.metrics.enabled }} + # Allow prometheus scrapes for metrics + - ports: + - port: 9121 + {{- end }} +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/charts/redis/templates/prometheusrule.yaml b/charts/deps/charts/airflow-8.8.0/charts/redis/templates/prometheusrule.yaml new file mode 100644 index 0000000..500c3b3 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/redis/templates/prometheusrule.yaml @@ -0,0 +1,23 @@ +{{- if and .Values.metrics.enabled .Values.metrics.prometheusRule.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: PrometheusRule +metadata: + name: {{ template "redis.fullname" . }} +{{- with .Values.metrics.prometheusRule.namespace }} + namespace: {{ . }} +{{- end }} + labels: + app: {{ template "redis.name" . }} + chart: {{ template "redis.chart" . }} + release: {{ .Release.Name | quote }} + heritage: {{ .Release.Service | quote }} +{{- with .Values.metrics.prometheusRule.additionalLabels }} +{{ toYaml . | indent 4 }} +{{- end }} +spec: +{{- with .Values.metrics.prometheusRule.rules }} + groups: + - name: {{ template "redis.name" $ }} + rules: {{ tpl (toYaml .) $ | nindent 8 }} +{{- end }} +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/charts/redis/templates/psp.yaml b/charts/deps/charts/airflow-8.8.0/charts/redis/templates/psp.yaml new file mode 100644 index 0000000..28ae22a --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/redis/templates/psp.yaml @@ -0,0 +1,42 @@ +{{- if .Values.podSecurityPolicy.create }} +apiVersion: {{ template "podSecurityPolicy.apiVersion" . }} +kind: PodSecurityPolicy +metadata: + name: {{ template "redis.fullname" . }} + labels: + app: {{ template "redis.name" . }} + chart: {{ template "redis.chart" . }} + heritage: {{ .Release.Service }} + release: {{ .Release.Name }} +spec: + allowPrivilegeEscalation: false + fsGroup: + rule: 'MustRunAs' + ranges: + - min: {{ .Values.securityContext.fsGroup }} + max: {{ .Values.securityContext.fsGroup }} + hostIPC: false + hostNetwork: false + hostPID: false + privileged: false + readOnlyRootFilesystem: false + requiredDropCapabilities: + - ALL + runAsUser: + rule: 'MustRunAs' + ranges: + - min: {{ .Values.securityContext.runAsUser }} + max: {{ .Values.securityContext.runAsUser }} + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'MustRunAs' + ranges: + - min: {{ .Values.securityContext.runAsUser }} + max: {{ .Values.securityContext.runAsUser }} + volumes: + - 'configMap' + - 'secret' + - 'emptyDir' + - 'persistentVolumeClaim' +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/charts/redis/templates/redis-master-statefulset.yaml b/charts/deps/charts/airflow-8.8.0/charts/redis/templates/redis-master-statefulset.yaml new file mode 100644 index 0000000..b61c539 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/redis/templates/redis-master-statefulset.yaml @@ -0,0 +1,419 @@ +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ template "redis.fullname" . }}-master + labels: + app: {{ template "redis.name" . }} + chart: {{ template "redis.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + selector: + matchLabels: + app: {{ template "redis.name" . }} + release: {{ .Release.Name }} + role: master + serviceName: {{ template "redis.fullname" . }}-headless + template: + metadata: + labels: + app: {{ template "redis.name" . }} + chart: {{ template "redis.chart" . }} + release: {{ .Release.Name }} + role: master +{{- if .Values.master.podLabels }} +{{ toYaml .Values.master.podLabels | indent 8 }} +{{- end }} +{{- if and .Values.metrics.enabled .Values.metrics.podLabels }} +{{ toYaml .Values.metrics.podLabels | indent 8 }} +{{- end }} + annotations: + checksum/health: {{ include (print $.Template.BasePath "/health-configmap.yaml") . | sha256sum }} + checksum/configmap: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }} + {{- if .Values.master.podAnnotations }} +{{ toYaml .Values.master.podAnnotations | indent 8 }} + {{- end }} + {{- if and .Values.metrics.enabled .Values.metrics.podAnnotations }} +{{ toYaml .Values.metrics.podAnnotations | indent 8 }} + {{- end }} + spec: +{{- include "redis.imagePullSecrets" . | indent 6 }} + {{- if .Values.securityContext.enabled }} + securityContext: + fsGroup: {{ .Values.securityContext.fsGroup }} + {{- if .Values.securityContext.sysctls }} + sysctls: +{{ toYaml .Values.securityContext.sysctls | indent 8 }} + {{- end }} + {{- end }} + serviceAccountName: "{{ template "redis.serviceAccountName" . }}" + {{- if .Values.master.priorityClassName }} + priorityClassName: "{{ .Values.master.priorityClassName }}" + {{- end }} + {{- with .Values.master.affinity }} + affinity: +{{ tpl (toYaml .) $ | indent 8 }} + {{- end }} + {{- if .Values.master.nodeSelector }} + nodeSelector: +{{ toYaml .Values.master.nodeSelector | indent 8 }} + {{- end }} + {{- if .Values.master.tolerations }} + tolerations: +{{ toYaml .Values.master.tolerations | indent 8 }} + {{- end }} + {{- if .Values.master.schedulerName }} + schedulerName: "{{ .Values.master.schedulerName }}" + {{- end }} + containers: + - name: {{ template "redis.fullname" . }} + image: "{{ template "redis.image" . }}" + imagePullPolicy: {{ .Values.image.pullPolicy | quote }} + {{- if .Values.securityContext.enabled }} + securityContext: + runAsUser: {{ .Values.securityContext.runAsUser }} + {{- end }} + command: + - /bin/bash + - -c + - | + {{- if (eq (.Values.securityContext.runAsUser | int) 0) }} + useradd redis + chown -R redis {{ .Values.master.persistence.path }} + {{- end }} + if [[ -n $REDIS_PASSWORD_FILE ]]; then + password_aux=`cat ${REDIS_PASSWORD_FILE}` + export REDIS_PASSWORD=$password_aux + fi + if [[ ! -f /opt/bitnami/redis/etc/master.conf ]];then + cp /opt/bitnami/redis/mounted-etc/master.conf /opt/bitnami/redis/etc/master.conf + fi + if [[ ! -f /opt/bitnami/redis/etc/redis.conf ]];then + cp /opt/bitnami/redis/mounted-etc/redis.conf /opt/bitnami/redis/etc/redis.conf + fi + ARGS=("--port" "${REDIS_PORT}") + {{- if .Values.usePassword }} + ARGS+=("--requirepass" "${REDIS_PASSWORD}") + ARGS+=("--masterauth" "${REDIS_PASSWORD}") + {{- else }} + ARGS+=("--protected-mode" "no") + {{- end }} + ARGS+=("--include" "/opt/bitnami/redis/etc/redis.conf") + ARGS+=("--include" "/opt/bitnami/redis/etc/master.conf") + {{- if .Values.master.extraFlags }} + {{- range .Values.master.extraFlags }} + ARGS+=({{ . | quote }}) + {{- end }} + {{- end }} + {{- if .Values.master.command }} + {{ .Values.master.command }} ${ARGS[@]} + {{- else }} + redis-server "${ARGS[@]}" + {{- end }} + env: + - name: REDIS_REPLICATION_MODE + value: master + {{- if .Values.usePassword }} + {{- if .Values.usePasswordFile }} + - name: REDIS_PASSWORD_FILE + value: "/opt/bitnami/redis/secrets/redis-password" + {{- else }} + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "redis.secretName" . }} + key: {{ template "redis.secretPasswordKey" . }} + {{- end }} + {{- else }} + - name: ALLOW_EMPTY_PASSWORD + value: "yes" + {{- end }} + - name: REDIS_PORT + value: {{ .Values.redisPort | quote }} + ports: + - name: redis + containerPort: {{ .Values.redisPort }} + {{- if .Values.master.livenessProbe.enabled }} + livenessProbe: + initialDelaySeconds: {{ .Values.master.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.master.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.master.livenessProbe.timeoutSeconds }} + successThreshold: {{ .Values.master.livenessProbe.successThreshold }} + failureThreshold: {{ .Values.master.livenessProbe.failureThreshold }} + exec: + command: + - sh + - -c + - /health/ping_liveness_local.sh {{ .Values.master.livenessProbe.timeoutSeconds }} + {{- end }} + {{- if .Values.master.readinessProbe.enabled}} + readinessProbe: + initialDelaySeconds: {{ .Values.master.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.master.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.master.readinessProbe.timeoutSeconds }} + successThreshold: {{ .Values.master.readinessProbe.successThreshold }} + failureThreshold: {{ .Values.master.readinessProbe.failureThreshold }} + exec: + command: + - sh + - -c + - /health/ping_readiness_local.sh {{ .Values.master.livenessProbe.timeoutSeconds }} + {{- end }} + resources: +{{ toYaml .Values.master.resources | indent 10 }} + volumeMounts: + - name: health + mountPath: /health + {{- if .Values.usePasswordFile }} + - name: redis-password + mountPath: /opt/bitnami/redis/secrets/ + {{- end }} + - name: redis-data + mountPath: {{ .Values.master.persistence.path }} + subPath: {{ .Values.master.persistence.subPath }} + - name: config + mountPath: /opt/bitnami/redis/mounted-etc + - name: redis-tmp-conf + mountPath: /opt/bitnami/redis/etc/ + {{- if and .Values.cluster.enabled .Values.sentinel.enabled }} + - name: sentinel + image: "{{ template "sentinel.image" . }}" + imagePullPolicy: {{ .Values.sentinel.image.pullPolicy | quote }} + {{- if .Values.securityContext.enabled }} + securityContext: + runAsUser: {{ .Values.securityContext.runAsUser }} + {{- end }} + command: + - /bin/bash + - -c + - | + if [[ -n $REDIS_PASSWORD_FILE ]]; then + password_aux=`cat ${REDIS_PASSWORD_FILE}` + export REDIS_PASSWORD=$password_aux + fi + if [[ ! -f /opt/bitnami/redis-sentinel/etc/sentinel.conf ]];then + cp /opt/bitnami/redis-sentinel/mounted-etc/sentinel.conf /opt/bitnami/redis-sentinel/etc/sentinel.conf + {{- if .Values.usePassword }} + printf "\nsentinel auth-pass {{ .Values.sentinel.masterSet }} $REDIS_PASSWORD" >> /opt/bitnami/redis-sentinel/etc/sentinel.conf + {{- if .Values.sentinel.usePassword }} + printf "\nrequirepass $REDIS_PASSWORD" >> /opt/bitnami/redis-sentinel/etc/sentinel.conf + {{- end }} + {{- end }} + {{- if .Values.sentinel.staticID }} + printf "\nsentinel myid $(echo $HOSTNAME | openssl sha1 | awk '{ print $2 }')" >> /opt/bitnami/redis-sentinel/etc/sentinel.conf + {{- end }} + fi + echo "Getting information about current running sentinels" + # Get information from existing sentinels + existing_sentinels=$(timeout -s 9 {{ .Values.sentinel.initialCheckTimeout }} redis-cli --raw -h {{ template "redis.fullname" . }} -a "$REDIS_PASSWORD" -p {{ .Values.sentinel.service.sentinelPort }} SENTINEL sentinels {{ .Values.sentinel.masterSet }}) + echo "$existing_sentinels" | awk -f /health/parse_sentinels.awk | tee -a /opt/bitnami/redis-sentinel/etc/sentinel.conf + + redis-server /opt/bitnami/redis-sentinel/etc/sentinel.conf --sentinel + env: + {{- if .Values.usePassword }} + {{- if .Values.usePasswordFile }} + - name: REDIS_PASSWORD_FILE + value: "/opt/bitnami/redis/secrets/redis-password" + {{- else }} + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "redis.secretName" . }} + key: {{ template "redis.secretPasswordKey" . }} + {{- end }} + {{- else }} + - name: ALLOW_EMPTY_PASSWORD + value: "yes" + {{- end }} + - name: REDIS_SENTINEL_PORT + value: {{ .Values.sentinel.port | quote }} + ports: + - name: redis-sentinel + containerPort: {{ .Values.sentinel.port }} + {{- if .Values.sentinel.livenessProbe.enabled }} + livenessProbe: + initialDelaySeconds: {{ .Values.sentinel.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.sentinel.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.sentinel.livenessProbe.timeoutSeconds }} + successThreshold: {{ .Values.sentinel.livenessProbe.successThreshold }} + failureThreshold: {{ .Values.sentinel.livenessProbe.failureThreshold }} + exec: + command: + - sh + - -c + - /health/ping_sentinel.sh {{ .Values.sentinel.livenessProbe.timeoutSeconds }} + {{- end }} + {{- if .Values.sentinel.readinessProbe.enabled}} + readinessProbe: + initialDelaySeconds: {{ .Values.sentinel.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.sentinel.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.sentinel.readinessProbe.timeoutSeconds }} + successThreshold: {{ .Values.sentinel.readinessProbe.successThreshold }} + failureThreshold: {{ .Values.sentinel.readinessProbe.failureThreshold }} + exec: + command: + - sh + - -c + - /health/ping_sentinel.sh {{ .Values.sentinel.livenessProbe.timeoutSeconds }} + {{- end }} + resources: +{{ toYaml .Values.sentinel.resources | indent 10 }} + volumeMounts: + - name: health + mountPath: /health + {{- if .Values.usePasswordFile }} + - name: redis-password + mountPath: /opt/bitnami/redis/secrets/ + {{- end }} + - name: redis-data + mountPath: {{ .Values.master.persistence.path }} + subPath: {{ .Values.master.persistence.subPath }} + - name: config + mountPath: /opt/bitnami/redis-sentinel/mounted-etc + - name: sentinel-tmp-conf + mountPath: /opt/bitnami/redis-sentinel/etc/ + {{- end }} +{{- if .Values.metrics.enabled }} + - name: metrics + image: {{ template "redis.metrics.image" . }} + imagePullPolicy: {{ .Values.metrics.image.pullPolicy | quote }} + command: + - /bin/bash + - -c + - | + if [[ -f '/secrets/redis-password' ]]; then + export REDIS_PASSWORD=$(cat /secrets/redis-password) + fi + redis_exporter{{- range $key, $value := .Values.metrics.extraArgs }} --{{ $key }}={{ $value }}{{- end }} + env: + - name: REDIS_ALIAS + value: {{ template "redis.fullname" . }} + {{- if and .Values.usePassword (not .Values.usePasswordFile) }} + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "redis.secretName" . }} + key: {{ template "redis.secretPasswordKey" . }} + {{- end }} + volumeMounts: + {{- if .Values.usePasswordFile }} + - name: redis-password + mountPath: /secrets/ + {{- end }} + ports: + - name: metrics + containerPort: 9121 + resources: +{{ toYaml .Values.metrics.resources | indent 10 }} +{{- end }} + {{- $needsVolumePermissions := and .Values.volumePermissions.enabled (and ( and .Values.master.persistence.enabled (not .Values.persistence.existingClaim) ) .Values.securityContext.enabled) }} + {{- if or $needsVolumePermissions .Values.sysctlImage.enabled }} + initContainers: + {{- if $needsVolumePermissions }} + - name: volume-permissions + image: "{{ template "redis.volumePermissions.image" . }}" + imagePullPolicy: {{ .Values.volumePermissions.image.pullPolicy | quote }} + command: ["/bin/chown", "-R", "{{ .Values.securityContext.runAsUser }}:{{ .Values.securityContext.fsGroup }}", "{{ .Values.master.persistence.path }}"] + securityContext: + runAsUser: 0 + resources: +{{ toYaml .Values.volumePermissions.resources | indent 10 }} + volumeMounts: + - name: redis-data + mountPath: {{ .Values.master.persistence.path }} + subPath: {{ .Values.master.persistence.subPath }} + {{- end }} + {{- if .Values.sysctlImage.enabled }} + - name: init-sysctl + image: {{ template "redis.sysctl.image" . }} + imagePullPolicy: {{ default "" .Values.sysctlImage.pullPolicy | quote }} + resources: +{{ toYaml .Values.sysctlImage.resources | indent 10 }} + {{- if .Values.sysctlImage.mountHostSys }} + volumeMounts: + - name: host-sys + mountPath: /host-sys + {{- end }} + command: +{{ toYaml .Values.sysctlImage.command | indent 10 }} + securityContext: + privileged: true + runAsUser: 0 + {{- end }} + {{- end }} + volumes: + - name: health + configMap: + name: {{ template "redis.fullname" . }}-health + defaultMode: 0755 + {{- if .Values.usePasswordFile }} + - name: redis-password + secret: + secretName: {{ template "redis.secretName" . }} + items: + - key: {{ template "redis.secretPasswordKey" . }} + path: redis-password + {{- end }} + - name: config + configMap: + name: {{ template "redis.fullname" . }} + {{- if not .Values.master.persistence.enabled }} + - name: "redis-data" + emptyDir: {} + {{- else }} + {{- if .Values.persistence.existingClaim }} + - name: "redis-data" + persistentVolumeClaim: + claimName: {{ .Values.persistence.existingClaim }} + {{- end }} + {{- end }} + {{- if .Values.sysctlImage.mountHostSys }} + - name: host-sys + hostPath: + path: /sys + {{- end }} + - name: redis-tmp-conf + emptyDir: {} + {{- if and .Values.cluster.enabled .Values.sentinel.enabled }} + - name: sentinel-tmp-conf + emptyDir: {} + {{- end }} + {{- if and .Values.master.persistence.enabled (not .Values.persistence.existingClaim) }} + volumeClaimTemplates: + - metadata: + name: redis-data + labels: + app: {{ template "redis.name" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + component: master + spec: + accessModes: + {{- range .Values.master.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.master.persistence.size | quote }} + {{ include "redis.master.storageClass" . }} + selector: + {{- if .Values.master.persistence.matchLabels }} + matchLabels: +{{ toYaml .Values.master.persistence.matchLabels | indent 12 }} + {{- end -}} + {{- if .Values.master.persistence.matchExpressions }} + matchExpressions: +{{ toYaml .Values.master.persistence.matchExpressions | indent 12 }} + {{- end -}} + {{- end }} + updateStrategy: + type: {{ .Values.master.statefulset.updateStrategy }} + {{- if .Values.master.statefulset.rollingUpdatePartition }} + {{- if (eq "Recreate" .Values.master.statefulset.updateStrategy) }} + rollingUpdate: null + {{- else }} + rollingUpdate: + partition: {{ .Values.master.statefulset.rollingUpdatePartition }} + {{- end }} + {{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/charts/redis/templates/redis-master-svc.yaml b/charts/deps/charts/airflow-8.8.0/charts/redis/templates/redis-master-svc.yaml new file mode 100644 index 0000000..3a98e66 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/redis/templates/redis-master-svc.yaml @@ -0,0 +1,39 @@ +{{- if not .Values.sentinel.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "redis.fullname" . }}-master + labels: + app: {{ template "redis.name" . }} + chart: {{ template "redis.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + {{- if .Values.master.service.labels -}} + {{ toYaml .Values.master.service.labels | nindent 4 }} + {{- end -}} +{{- if .Values.master.service.annotations }} + annotations: {{ toYaml .Values.master.service.annotations | nindent 4 }} +{{- end }} +spec: + type: {{ .Values.master.service.type }} + {{- if and (eq .Values.master.service.type "LoadBalancer") .Values.master.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.master.service.loadBalancerIP }} + {{- end }} + {{- if and (eq .Values.master.service.type "LoadBalancer") .Values.master.service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: + {{- with .Values.master.service.loadBalancerSourceRanges }} +{{ toYaml . | indent 4 }} +{{- end }} + {{- end }} + ports: + - name: redis + port: {{ .Values.master.service.port }} + targetPort: redis + {{- if .Values.master.service.nodePort }} + nodePort: {{ .Values.master.service.nodePort }} + {{- end }} + selector: + app: {{ template "redis.name" . }} + release: {{ .Release.Name }} + role: master +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/charts/redis/templates/redis-role.yaml b/charts/deps/charts/airflow-8.8.0/charts/redis/templates/redis-role.yaml new file mode 100644 index 0000000..71f75ef --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/redis/templates/redis-role.yaml @@ -0,0 +1,21 @@ +{{- if .Values.rbac.create -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ template "redis.fullname" . }} + labels: + app: {{ template "redis.name" . }} + chart: {{ template "redis.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +rules: +{{- if .Values.podSecurityPolicy.create }} + - apiGroups: ['{{ template "podSecurityPolicy.apiGroup" . }}'] + resources: ['podsecuritypolicies'] + verbs: ['use'] + resourceNames: [{{ template "redis.fullname" . }}] +{{- end -}} +{{- if .Values.rbac.role.rules }} +{{ toYaml .Values.rbac.role.rules | indent 2 }} +{{- end -}} +{{- end -}} diff --git a/charts/deps/charts/airflow-8.8.0/charts/redis/templates/redis-rolebinding.yaml b/charts/deps/charts/airflow-8.8.0/charts/redis/templates/redis-rolebinding.yaml new file mode 100644 index 0000000..aceb258 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/redis/templates/redis-rolebinding.yaml @@ -0,0 +1,18 @@ +{{- if .Values.rbac.create -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ template "redis.fullname" . }} + labels: + app: {{ template "redis.name" . }} + chart: {{ template "redis.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ template "redis.fullname" . }} +subjects: +- kind: ServiceAccount + name: {{ template "redis.serviceAccountName" . }} +{{- end -}} diff --git a/charts/deps/charts/airflow-8.8.0/charts/redis/templates/redis-serviceaccount.yaml b/charts/deps/charts/airflow-8.8.0/charts/redis/templates/redis-serviceaccount.yaml new file mode 100644 index 0000000..f027176 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/redis/templates/redis-serviceaccount.yaml @@ -0,0 +1,11 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "redis.serviceAccountName" . }} + labels: + app: {{ template "redis.name" . }} + chart: {{ template "redis.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +{{- end -}} diff --git a/charts/deps/charts/airflow-8.8.0/charts/redis/templates/redis-slave-statefulset.yaml b/charts/deps/charts/airflow-8.8.0/charts/redis/templates/redis-slave-statefulset.yaml new file mode 100644 index 0000000..d5a8db5 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/redis/templates/redis-slave-statefulset.yaml @@ -0,0 +1,437 @@ +{{- if .Values.cluster.enabled }} +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ template "redis.fullname" . }}-slave + labels: + app: {{ template "redis.name" . }} + chart: {{ template "redis.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: +{{- if .Values.slave.updateStrategy }} + strategy: +{{ toYaml .Values.slave.updateStrategy | indent 4 }} +{{- end }} + replicas: {{ .Values.cluster.slaveCount }} + serviceName: {{ template "redis.fullname" . }}-headless + selector: + matchLabels: + app: {{ template "redis.name" . }} + release: {{ .Release.Name }} + role: slave + template: + metadata: + labels: + app: {{ template "redis.name" . }} + release: {{ .Release.Name }} + chart: {{ template "redis.chart" . }} + role: slave + {{- if .Values.slave.podLabels }} +{{ toYaml .Values.slave.podLabels | indent 8 }} + {{- end }} + {{- if and .Values.metrics.enabled .Values.metrics.podLabels }} +{{ toYaml .Values.metrics.podLabels | indent 8 }} + {{- end }} + annotations: + checksum/health: {{ include (print $.Template.BasePath "/health-configmap.yaml") . | sha256sum }} + checksum/configmap: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + checksum/secret: {{ include (print $.Template.BasePath "/secret.yaml") . | sha256sum }} + {{- if .Values.slave.podAnnotations }} +{{ toYaml .Values.slave.podAnnotations | indent 8 }} + {{- end }} + {{- if and .Values.metrics.enabled .Values.metrics.podAnnotations }} +{{ toYaml .Values.metrics.podAnnotations | indent 8 }} + {{- end }} + spec: +{{- include "redis.imagePullSecrets" . | indent 6 }} + {{- if .Values.securityContext.enabled }} + securityContext: + fsGroup: {{ .Values.securityContext.fsGroup }} + {{- if .Values.securityContext.sysctls }} + sysctls: +{{ toYaml .Values.securityContext.sysctls | indent 8 }} + {{- end }} + {{- end }} + serviceAccountName: "{{ template "redis.serviceAccountName" . }}" + {{- if .Values.slave.priorityClassName }} + priorityClassName: "{{ .Values.slave.priorityClassName }}" + {{- end }} + {{- if .Values.slave.nodeSelector }} + nodeSelector: +{{ toYaml .Values.slave.nodeSelector | indent 8 }} + {{- end }} + {{- if .Values.slave.tolerations }} + tolerations: +{{ toYaml .Values.slave.tolerations | indent 8 }} + {{- end }} + {{- if .Values.slave.schedulerName }} + schedulerName: "{{ .Values.slave.schedulerName }}" + {{- end }} + {{- with .Values.slave.affinity }} + affinity: +{{ tpl (toYaml .) $ | indent 8 }} + {{- end }} + containers: + - name: {{ template "redis.fullname" . }} + image: {{ template "redis.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy | quote }} + {{- if .Values.securityContext.enabled }} + securityContext: + runAsUser: {{ .Values.securityContext.runAsUser }} + {{- end }} + command: + - /bin/bash + - -c + - | + {{- if (eq (.Values.securityContext.runAsUser | int) 0) }} + useradd redis + chown -R redis {{ .Values.slave.persistence.path }} + {{- end }} + if [[ -n $REDIS_PASSWORD_FILE ]]; then + password_aux=`cat ${REDIS_PASSWORD_FILE}` + export REDIS_PASSWORD=$password_aux + fi + if [[ -n $REDIS_MASTER_PASSWORD_FILE ]]; then + password_aux=`cat ${REDIS_MASTER_PASSWORD_FILE}` + export REDIS_MASTER_PASSWORD=$password_aux + fi + if [[ ! -f /opt/bitnami/redis/etc/replica.conf ]];then + cp /opt/bitnami/redis/mounted-etc/replica.conf /opt/bitnami/redis/etc/replica.conf + fi + if [[ ! -f /opt/bitnami/redis/etc/redis.conf ]];then + cp /opt/bitnami/redis/mounted-etc/redis.conf /opt/bitnami/redis/etc/redis.conf + fi + ARGS=("--port" "${REDIS_PORT}") + ARGS+=("--slaveof" "${REDIS_MASTER_HOST}" "${REDIS_MASTER_PORT_NUMBER}") + {{- if .Values.usePassword }} + ARGS+=("--requirepass" "${REDIS_PASSWORD}") + ARGS+=("--masterauth" "${REDIS_MASTER_PASSWORD}") + {{- else }} + ARGS+=("--protected-mode" "no") + {{- end }} + ARGS+=("--include" "/opt/bitnami/redis/etc/redis.conf") + ARGS+=("--include" "/opt/bitnami/redis/etc/replica.conf") + {{- if .Values.slave.extraFlags }} + {{- range .Values.slave.extraFlags }} + ARGS+=({{ . | quote }}) + {{- end }} + {{- end }} + {{- if .Values.slave.command }} + {{ .Values.slave.command }} "${ARGS[@]}" + {{- else }} + redis-server "${ARGS[@]}" + {{- end }} + env: + - name: REDIS_REPLICATION_MODE + value: slave + - name: REDIS_MASTER_HOST + value: {{ template "redis.fullname" . }}-master-0.{{ template "redis.fullname" . }}-headless.{{ .Release.Namespace }}.svc.{{ .Values.clusterDomain }} + - name: REDIS_PORT + value: {{ .Values.redisPort | quote }} + - name: REDIS_MASTER_PORT_NUMBER + value: {{ .Values.redisPort | quote }} + {{- if .Values.usePassword }} + {{- if .Values.usePasswordFile }} + - name: REDIS_PASSWORD_FILE + value: "/opt/bitnami/redis/secrets/redis-password" + - name: REDIS_MASTER_PASSWORD_FILE + value: "/opt/bitnami/redis/secrets/redis-password" + {{- else }} + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "redis.secretName" . }} + key: {{ template "redis.secretPasswordKey" . }} + - name: REDIS_MASTER_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "redis.secretName" . }} + key: {{ template "redis.secretPasswordKey" . }} + {{- end }} + {{- else }} + - name: ALLOW_EMPTY_PASSWORD + value: "yes" + {{- end }} + ports: + - name: redis + containerPort: {{ .Values.redisPort }} + {{- if .Values.slave.livenessProbe.enabled }} + livenessProbe: + initialDelaySeconds: {{ .Values.slave.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.slave.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.slave.livenessProbe.timeoutSeconds }} + successThreshold: {{ .Values.slave.livenessProbe.successThreshold }} + failureThreshold: {{ .Values.slave.livenessProbe.failureThreshold}} + exec: + command: + - sh + - -c + {{- if .Values.sentinel.enabled }} + - /health/ping_liveness_local.sh {{ .Values.slave.livenessProbe.timeoutSeconds }} + {{- else }} + - /health/ping_liveness_local_and_master.sh {{ .Values.slave.livenessProbe.timeoutSeconds }} + {{- end }} + {{- end }} + + {{- if .Values.slave.readinessProbe.enabled }} + readinessProbe: + initialDelaySeconds: {{ .Values.slave.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.slave.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.slave.readinessProbe.timeoutSeconds }} + successThreshold: {{ .Values.slave.readinessProbe.successThreshold }} + failureThreshold: {{ .Values.slave.readinessProbe.failureThreshold }} + exec: + command: + - sh + - -c + {{- if .Values.sentinel.enabled }} + - /health/ping_readiness_local.sh {{ .Values.slave.livenessProbe.timeoutSeconds }} + {{- else }} + - /health/ping_readiness_local_and_master.sh {{ .Values.slave.livenessProbe.timeoutSeconds }} + {{- end }} + {{- end }} + resources: +{{ toYaml .Values.slave.resources | indent 10 }} + volumeMounts: + - name: health + mountPath: /health + {{- if .Values.usePasswordFile }} + - name: redis-password + mountPath: /opt/bitnami/redis/secrets/ + {{- end }} + - name: redis-data + mountPath: /data + - name: config + mountPath: /opt/bitnami/redis/mounted-etc + - name: redis-tmp-conf + mountPath: /opt/bitnami/redis/etc + {{- if and .Values.cluster.enabled .Values.sentinel.enabled }} + - name: sentinel + image: "{{ template "sentinel.image" . }}" + imagePullPolicy: {{ .Values.sentinel.image.pullPolicy | quote }} + {{- if .Values.securityContext.enabled }} + securityContext: + runAsUser: {{ .Values.securityContext.runAsUser }} + {{- end }} + command: + - /bin/bash + - -c + - | + if [[ -n $REDIS_PASSWORD_FILE ]]; then + password_aux=`cat ${REDIS_PASSWORD_FILE}` + export REDIS_PASSWORD=$password_aux + fi + if [[ ! -f /opt/bitnami/redis-sentinel/etc/sentinel.conf ]];then + cp /opt/bitnami/redis-sentinel/mounted-etc/sentinel.conf /opt/bitnami/redis-sentinel/etc/sentinel.conf + {{- if .Values.usePassword }} + printf "\nsentinel auth-pass {{ .Values.sentinel.masterSet }} $REDIS_PASSWORD" >> /opt/bitnami/redis-sentinel/etc/sentinel.conf + {{- if .Values.sentinel.usePassword }} + printf "\nrequirepass $REDIS_PASSWORD" >> /opt/bitnami/redis-sentinel/etc/sentinel.conf + {{- end }} + {{- end }} + {{- if .Values.sentinel.staticID }} + printf "\nsentinel myid $(echo $HOSTNAME | openssl sha1 | awk '{ print $2 }')" >> /opt/bitnami/redis-sentinel/etc/sentinel.conf + {{- end }} + fi + + redis-server /opt/bitnami/redis-sentinel/etc/sentinel.conf --sentinel + env: + {{- if .Values.usePassword }} + {{- if .Values.usePasswordFile }} + - name: REDIS_PASSWORD_FILE + value: "/opt/bitnami/redis/secrets/redis-password" + {{- else }} + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "redis.secretName" . }} + key: {{ template "redis.secretPasswordKey" . }} + {{- end }} + {{- else }} + - name: ALLOW_EMPTY_PASSWORD + value: "yes" + {{- end }} + - name: REDIS_SENTINEL_PORT + value: {{ .Values.sentinel.port | quote }} + ports: + - name: redis-sentinel + containerPort: {{ .Values.sentinel.port }} + {{- if .Values.sentinel.livenessProbe.enabled }} + livenessProbe: + initialDelaySeconds: {{ .Values.sentinel.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.sentinel.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.sentinel.livenessProbe.timeoutSeconds }} + successThreshold: {{ .Values.sentinel.livenessProbe.successThreshold }} + failureThreshold: {{ .Values.sentinel.livenessProbe.failureThreshold }} + exec: + command: + - sh + - -c + - /health/ping_sentinel.sh {{ .Values.sentinel.livenessProbe.timeoutSeconds }} + {{- end }} + {{- if .Values.sentinel.readinessProbe.enabled}} + readinessProbe: + initialDelaySeconds: {{ .Values.sentinel.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.sentinel.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.sentinel.readinessProbe.timeoutSeconds }} + successThreshold: {{ .Values.sentinel.readinessProbe.successThreshold }} + failureThreshold: {{ .Values.sentinel.readinessProbe.failureThreshold }} + exec: + command: + - sh + - -c + - /health/ping_sentinel.sh {{ .Values.sentinel.livenessProbe.timeoutSeconds }} + {{- end }} + resources: +{{ toYaml .Values.sentinel.resources | indent 10 }} + volumeMounts: + - name: health + mountPath: /health + {{- if .Values.usePasswordFile }} + - name: redis-password + mountPath: /opt/bitnami/redis/secrets/ + {{- end }} + - name: redis-data + mountPath: {{ .Values.master.persistence.path }} + subPath: {{ .Values.master.persistence.subPath }} + - name: config + mountPath: /opt/bitnami/redis-sentinel/mounted-etc + - name: sentinel-tmp-conf + mountPath: /opt/bitnami/redis-sentinel/etc + {{- end }} +{{- if .Values.metrics.enabled }} + - name: metrics + image: {{ template "redis.metrics.image" . }} + imagePullPolicy: {{ .Values.metrics.image.pullPolicy | quote }} + command: + - /bin/bash + - -c + - | + if [[ -f '/secrets/redis-password' ]]; then + export REDIS_PASSWORD=$(cat /secrets/redis-password) + fi + redis_exporter{{- range $key, $value := .Values.metrics.extraArgs }} --{{ $key }}={{ $value }}{{- end }} + env: + - name: REDIS_ALIAS + value: {{ template "redis.fullname" . }} + {{- if and .Values.usePassword (not .Values.usePasswordFile) }} + - name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "redis.secretName" . }} + key: {{ template "redis.secretPasswordKey" . }} + {{- end }} + volumeMounts: + {{- if .Values.usePasswordFile }} + - name: redis-password + mountPath: /secrets/ + {{- end }} + ports: + - name: metrics + containerPort: 9121 + resources: +{{ toYaml .Values.metrics.resources | indent 10 }} +{{- end }} + {{- $needsVolumePermissions := and .Values.volumePermissions.enabled (and .Values.slave.persistence.enabled .Values.securityContext.enabled) }} + {{- if or $needsVolumePermissions .Values.sysctlImage.enabled }} + initContainers: + {{- if $needsVolumePermissions }} + - name: volume-permissions + image: "{{ template "redis.volumePermissions.image" . }}" + imagePullPolicy: {{ .Values.volumePermissions.image.pullPolicy | quote }} + command: ["/bin/chown", "-R", "{{ .Values.securityContext.runAsUser }}:{{ .Values.securityContext.fsGroup }}", "{{ .Values.slave.persistence.path }}"] + securityContext: + runAsUser: 0 + resources: +{{ toYaml .Values.volumePermissions.resources | indent 10 }} + volumeMounts: + - name: redis-data + mountPath: {{ .Values.slave.persistence.path }} + subPath: {{ .Values.slave.persistence.subPath }} + {{- end }} + {{- if .Values.sysctlImage.enabled }} + - name: init-sysctl + image: {{ template "redis.sysctl.image" . }} + imagePullPolicy: {{ default "" .Values.sysctlImage.pullPolicy | quote }} + resources: +{{ toYaml .Values.sysctlImage.resources | indent 10 }} + {{- if .Values.sysctlImage.mountHostSys }} + volumeMounts: + - name: host-sys + mountPath: /host-sys + {{- end }} + command: +{{ toYaml .Values.sysctlImage.command | indent 10 }} + securityContext: + privileged: true + runAsUser: 0 + {{- end }} + {{- end }} + volumes: + - name: health + configMap: + name: {{ template "redis.fullname" . }}-health + defaultMode: 0755 + {{- if .Values.usePasswordFile }} + - name: redis-password + secret: + secretName: {{ template "redis.secretName" . }} + items: + - key: {{ template "redis.secretPasswordKey" . }} + path: redis-password + {{- end }} + - name: config + configMap: + name: {{ template "redis.fullname" . }} + {{- if .Values.sysctlImage.mountHostSys }} + - name: host-sys + hostPath: + path: /sys + {{- end }} + - name: sentinel-tmp-conf + emptyDir: {} + - name: redis-tmp-conf + emptyDir: {} + {{- if not .Values.slave.persistence.enabled }} + - name: redis-data + emptyDir: {} + {{- else }} + volumeClaimTemplates: + - metadata: + name: redis-data + labels: + app: {{ template "redis.name" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + component: slave + spec: + accessModes: + {{- range .Values.slave.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.slave.persistence.size | quote }} + {{ include "redis.slave.storageClass" . }} + selector: + {{- if .Values.slave.persistence.matchLabels }} + matchLabels: +{{ toYaml .Values.slave.persistence.matchLabels | indent 12 }} + {{- end -}} + {{- if .Values.slave.persistence.matchExpressions }} + matchExpressions: +{{ toYaml .Values.slave.persistence.matchExpressions | indent 12 }} + {{- end -}} + {{- end }} + updateStrategy: + type: {{ .Values.slave.statefulset.updateStrategy }} + {{- if .Values.slave.statefulset.rollingUpdatePartition }} + {{- if (eq "Recreate" .Values.slave.statefulset.updateStrategy) }} + rollingUpdate: null + {{- else }} + rollingUpdate: + partition: {{ .Values.slave.statefulset.rollingUpdatePartition }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/charts/redis/templates/redis-slave-svc.yaml b/charts/deps/charts/airflow-8.8.0/charts/redis/templates/redis-slave-svc.yaml new file mode 100644 index 0000000..052ecea --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/redis/templates/redis-slave-svc.yaml @@ -0,0 +1,40 @@ +{{- if and .Values.cluster.enabled (not .Values.sentinel.enabled) }} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "redis.fullname" . }}-slave + labels: + app: {{ template "redis.name" . }} + chart: {{ template "redis.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + {{- if .Values.slave.service.labels -}} + {{ toYaml .Values.slave.service.labels | nindent 4 }} + {{- end -}} +{{- if .Values.slave.service.annotations }} + annotations: +{{ toYaml .Values.slave.service.annotations | indent 4 }} +{{- end }} +spec: + type: {{ .Values.slave.service.type }} + {{- if and (eq .Values.slave.service.type "LoadBalancer") .Values.slave.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.slave.service.loadBalancerIP }} + {{- end }} + {{- if and (eq .Values.slave.service.type "LoadBalancer") .Values.slave.service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: + {{- with .Values.slave.service.loadBalancerSourceRanges }} +{{ toYaml . | indent 4 }} +{{- end }} + {{- end }} + ports: + - name: redis + port: {{ .Values.slave.service.port }} + targetPort: redis + {{- if .Values.slave.service.nodePort }} + nodePort: {{ .Values.slave.service.nodePort }} + {{- end }} + selector: + app: {{ template "redis.name" . }} + release: {{ .Release.Name }} + role: slave +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/charts/redis/templates/redis-with-sentinel-svc.yaml b/charts/deps/charts/airflow-8.8.0/charts/redis/templates/redis-with-sentinel-svc.yaml new file mode 100644 index 0000000..5017c22 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/redis/templates/redis-with-sentinel-svc.yaml @@ -0,0 +1,40 @@ +{{- if .Values.sentinel.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "redis.fullname" . }} + labels: + app: {{ template "redis.name" . }} + chart: {{ template "redis.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + {{- if .Values.sentinel.service.labels }} + {{ toYaml .Values.sentinel.service.labels | nindent 4 }} + {{- end }} +{{- if .Values.sentinel.service.annotations }} + annotations: +{{ toYaml .Values.sentinel.service.annotations | indent 4 }} +{{- end }} +spec: + type: {{ .Values.sentinel.service.type }} + {{ if eq .Values.sentinel.service.type "LoadBalancer" -}} {{ if .Values.sentinel.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.sentinel.service.loadBalancerIP }} + {{ end -}} + {{- end -}} + ports: + - name: redis + port: {{ .Values.sentinel.service.redisPort }} + targetPort: redis + {{- if .Values.sentinel.service.redisNodePort }} + nodePort: {{ .Values.sentinel.service.redisNodePort }} + {{- end }} + - name: redis-sentinel + port: {{ .Values.sentinel.service.sentinelPort }} + targetPort: redis-sentinel + {{- if .Values.sentinel.service.sentinelNodePort }} + nodePort: {{ .Values.sentinel.service.sentinelNodePort }} + {{- end }} + selector: + app: {{ template "redis.name" . }} + release: {{ .Release.Name }} +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/charts/redis/templates/secret.yaml b/charts/deps/charts/airflow-8.8.0/charts/redis/templates/secret.yaml new file mode 100644 index 0000000..ead9c61 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/redis/templates/secret.yaml @@ -0,0 +1,14 @@ +{{- if and .Values.usePassword (not .Values.existingSecret) -}} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "redis.fullname" . }} + labels: + app: {{ template "redis.name" . }} + chart: {{ template "redis.chart" . }} + release: "{{ .Release.Name }}" + heritage: "{{ .Release.Service }}" +type: Opaque +data: + redis-password: {{ include "redis.password" . | b64enc | quote }} +{{- end -}} diff --git a/charts/deps/charts/airflow-8.8.0/charts/redis/values-production.yaml b/charts/deps/charts/airflow-8.8.0/charts/redis/values-production.yaml new file mode 100644 index 0000000..cae2af1 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/redis/values-production.yaml @@ -0,0 +1,630 @@ +## Global Docker image parameters +## Please, note that this will override the image parameters, including dependencies, configured to use the global value +## Current available global Docker image parameters: imageRegistry and imagePullSecrets +## +global: +# imageRegistry: myRegistryName +# imagePullSecrets: +# - myRegistryKeySecretName +# storageClass: myStorageClass + redis: {} + +## Bitnami Redis image version +## ref: https://hub.docker.com/r/bitnami/redis/tags/ +## +image: + registry: docker.io + repository: bitnami/redis + ## Bitnami Redis image tag + ## ref: https://github.com/bitnami/bitnami-docker-redis#supported-tags-and-respective-dockerfile-links + ## + tag: 5.0.7-debian-10-r32 + ## Specify a imagePullPolicy + ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images + ## + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + +## String to partially override redis.fullname template (will maintain the release name) +## +# nameOverride: + +## String to fully override redis.fullname template +## +# fullnameOverride: + +## Cluster settings +cluster: + enabled: true + slaveCount: 3 + +## Use redis sentinel in the redis pod. This will disable the master and slave services and +## create one redis service with ports to the sentinel and the redis instances +sentinel: + enabled: false + ## Require password authentication on the sentinel itself + ## ref: https://redis.io/topics/sentinel + usePassword: true + ## Bitnami Redis Sentintel image version + ## ref: https://hub.docker.com/r/bitnami/redis-sentinel/tags/ + ## + image: + registry: docker.io + repository: bitnami/redis-sentinel + ## Bitnami Redis image tag + ## ref: https://github.com/bitnami/bitnami-docker-redis-sentinel#supported-tags-and-respective-dockerfile-links + ## + tag: 5.0.7-debian-10-r27 + ## Specify a imagePullPolicy + ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images + ## + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + masterSet: mymaster + initialCheckTimeout: 5 + quorum: 2 + downAfterMilliseconds: 60000 + failoverTimeout: 18000 + parallelSyncs: 1 + port: 26379 + ## Additional Redis configuration for the sentinel nodes + ## ref: https://redis.io/topics/config + ## + configmap: + ## Enable or disable static sentinel IDs for each replicas + ## If disabled each sentinel will generate a random id at startup + ## If enabled, each replicas will have a constant ID on each start-up + ## + staticID: false + ## Configure extra options for Redis Sentinel liveness and readiness probes + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes) + ## + livenessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + readinessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 5 + ## Redis Sentinel resource requests and limits + ## ref: http://kubernetes.io/docs/user-guide/compute-resources/ + # resources: + # requests: + # memory: 256Mi + # cpu: 100m + ## Redis Sentinel Service properties + service: + ## Redis Sentinel Service type + type: ClusterIP + sentinelPort: 26379 + redisPort: 6379 + + ## Specify the nodePort value for the LoadBalancer and NodePort service types. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport + ## + # sentinelNodePort: + # redisNodePort: + + ## Provide any additional annotations which may be required. This can be used to + ## set the LoadBalancer service type to internal only. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer + ## + annotations: {} + labels: {} + loadBalancerIP: + +## Specifies the Kubernetes Cluster's Domain Name. +## +clusterDomain: cluster.local + +networkPolicy: + ## Specifies whether a NetworkPolicy should be created + ## + enabled: true + + ## The Policy model to apply. When set to false, only pods with the correct + ## client label will have network access to the port Redis is listening + ## on. When true, Redis will accept connections from any source + ## (with the correct destination port). + ## + # allowExternal: true + + ## Allow connections from other namespacess. Just set label for namespace and set label for pods (optional). + ## + ingressNSMatchLabels: {} + ingressNSPodMatchLabels: {} + +serviceAccount: + ## Specifies whether a ServiceAccount should be created + ## + create: false + ## The name of the ServiceAccount to use. + ## If not set and create is true, a name is generated using the fullname template + name: + +rbac: + ## Specifies whether RBAC resources should be created + ## + create: false + + role: + ## Rules to create. It follows the role specification + # rules: + # - apiGroups: + # - extensions + # resources: + # - podsecuritypolicies + # verbs: + # - use + # resourceNames: + # - gce.unprivileged + rules: [] + +## Redis pod Security Context +securityContext: + enabled: true + fsGroup: 1001 + runAsUser: 1001 + ## sysctl settings for master and slave pods + ## + ## Uncomment the setting below to increase the net.core.somaxconn value + ## + # sysctls: + # - name: net.core.somaxconn + # value: "10000" + +## Use password authentication +usePassword: true +## Redis password (both master and slave) +## Defaults to a random 10-character alphanumeric string if not set and usePassword is true +## ref: https://github.com/bitnami/bitnami-docker-redis#setting-the-server-password-on-first-run +## +password: +## Use existing secret (ignores previous password) +# existingSecret: +## Password key to be retrieved from Redis secret +## +# existingSecretPasswordKey: + +## Mount secrets as files instead of environment variables +usePasswordFile: false + +## Persist data to a persistent volume (Redis Master) +persistence: {} + ## A manually managed Persistent Volume and Claim + ## Requires persistence.enabled: true + ## If defined, PVC must be created manually before volume will be bound + # existingClaim: + +# Redis port +redisPort: 6379 + +## +## Redis Master parameters +## +master: + ## Redis command arguments + ## + ## Can be used to specify command line arguments, for example: + ## + command: "/run.sh" + ## Additional Redis configuration for the master nodes + ## ref: https://redis.io/topics/config + ## + configmap: + ## Redis additional command line flags + ## + ## Can be used to specify command line flags, for example: + ## + ## extraFlags: + ## - "--maxmemory-policy volatile-ttl" + ## - "--repl-backlog-size 1024mb" + extraFlags: [] + ## Comma-separated list of Redis commands to disable + ## + ## Can be used to disable Redis commands for security reasons. + ## Commands will be completely disabled by renaming each to an empty string. + ## ref: https://redis.io/topics/security#disabling-of-specific-commands + ## + disableCommands: + - FLUSHDB + - FLUSHALL + + ## Redis Master additional pod labels and annotations + ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + podLabels: {} + podAnnotations: {} + + ## Redis Master resource requests and limits + ## ref: http://kubernetes.io/docs/user-guide/compute-resources/ + # resources: + # requests: + # memory: 256Mi + # cpu: 100m + ## Use an alternate scheduler, e.g. "stork". + ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ + ## + # schedulerName: + + ## Configure extra options for Redis Master liveness and readiness probes + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes) + ## + livenessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + readinessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 5 + + ## Redis Master Node selectors and tolerations for pod assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector + ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#taints-and-tolerations-beta-feature + ## + # nodeSelector: {"beta.kubernetes.io/arch": "amd64"} + # tolerations: [] + ## Redis Master pod/node affinity/anti-affinity + ## + affinity: {} + + ## Redis Master Service properties + service: + ## Redis Master Service type + type: ClusterIP + port: 6379 + + ## Specify the nodePort value for the LoadBalancer and NodePort service types. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport + ## + # nodePort: + + ## Provide any additional annotations which may be required. This can be used to + ## set the LoadBalancer service type to internal only. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer + ## + annotations: {} + labels: {} + loadBalancerIP: + # loadBalancerSourceRanges: ["10.0.0.0/8"] + + ## Enable persistence using Persistent Volume Claims + ## ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ + ## + persistence: + enabled: true + ## The path the volume will be mounted at, useful when using different + ## Redis images. + path: /data + ## The subdirectory of the volume to mount to, useful in dev environments + ## and one PV for multiple services. + subPath: "" + ## redis data Persistent Volume Storage Class + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + # storageClass: "-" + accessModes: + - ReadWriteOnce + size: 8Gi + ## Persistent Volume selectors + ## https://kubernetes.io/docs/concepts/storage/persistent-volumes/#selector + matchLabels: {} + matchExpressions: {} + + ## Update strategy, can be set to RollingUpdate or onDelete by default. + ## https://kubernetes.io/docs/tutorials/stateful-application/basic-stateful-set/#updating-statefulsets + statefulset: + updateStrategy: RollingUpdate + ## Partition update strategy + ## https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#partitions + # rollingUpdatePartition: + + ## Redis Master pod priorityClassName + # priorityClassName: {} + +## +## Redis Slave properties +## Note: service.type is a mandatory parameter +## The rest of the parameters are either optional or, if undefined, will inherit those declared in Redis Master +## +slave: + ## Slave Service properties + service: + ## Redis Slave Service type + type: ClusterIP + ## Redis port + port: 6379 + ## Specify the nodePort value for the LoadBalancer and NodePort service types. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport + ## + # nodePort: + + ## Provide any additional annotations which may be required. This can be used to + ## set the LoadBalancer service type to internal only. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer + ## + annotations: {} + labels: {} + loadBalancerIP: + # loadBalancerSourceRanges: ["10.0.0.0/8"] + + ## Redis slave port + port: 6379 + ## Can be used to specify command line arguments, for example: + ## + command: "/run.sh" + ## Additional Redis configuration for the slave nodes + ## ref: https://redis.io/topics/config + ## + configmap: + ## Redis extra flags + extraFlags: [] + ## List of Redis commands to disable + disableCommands: + - FLUSHDB + - FLUSHALL + + ## Redis Slave pod/node affinity/anti-affinity + ## + affinity: {} + + ## Configure extra options for Redis Slave liveness and readiness probes + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes) + ## + livenessProbe: + enabled: true + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + readinessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 10 + successThreshold: 1 + failureThreshold: 5 + + ## Redis slave Resource + # resources: + # requests: + # memory: 256Mi + # cpu: 100m + + ## Redis slave selectors and tolerations for pod assignment + # nodeSelector: {"beta.kubernetes.io/arch": "amd64"} + # tolerations: [] + + ## Use an alternate scheduler, e.g. "stork". + ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ + ## + # schedulerName: + + ## Redis slave pod Annotation and Labels + podLabels: {} + podAnnotations: {} + + ## Redis slave pod priorityClassName + # priorityClassName: {} + + ## Enable persistence using Persistent Volume Claims + ## ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ + ## + persistence: + enabled: true + ## The path the volume will be mounted at, useful when using different + ## Redis images. + path: /data + ## The subdirectory of the volume to mount to, useful in dev environments + ## and one PV for multiple services. + subPath: "" + ## redis data Persistent Volume Storage Class + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + # storageClass: "-" + accessModes: + - ReadWriteOnce + size: 8Gi + ## Persistent Volume selectors + ## https://kubernetes.io/docs/concepts/storage/persistent-volumes/#selector + matchLabels: {} + matchExpressions: {} + + ## Update strategy, can be set to RollingUpdate or onDelete by default. + ## https://kubernetes.io/docs/tutorials/stateful-application/basic-stateful-set/#updating-statefulsets + statefulset: + updateStrategy: RollingUpdate + ## Partition update strategy + ## https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#partitions + # rollingUpdatePartition: + +## Prometheus Exporter / Metrics +## +metrics: + enabled: true + + image: + registry: docker.io + repository: bitnami/redis-exporter + tag: 1.4.0-debian-10-r3 + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + + ## Metrics exporter resource requests and limits + ## ref: http://kubernetes.io/docs/user-guide/compute-resources/ + ## + # resources: {} + + ## Extra arguments for Metrics exporter, for example: + ## extraArgs: + ## check-keys: myKey,myOtherKey + # extraArgs: {} + + ## Metrics exporter pod Annotation and Labels + podAnnotations: + prometheus.io/scrape: "true" + prometheus.io/port: "9121" + # podLabels: {} + + # Enable this if you're using https://github.com/coreos/prometheus-operator + serviceMonitor: + enabled: false + ## Specify a namespace if needed + # namespace: monitoring + # fallback to the prometheus default unless specified + # interval: 10s + ## Defaults to what's used if you follow CoreOS [Prometheus Install Instructions](https://github.com/helm/charts/tree/master/stable/prometheus-operator#tldr) + ## [Prometheus Selector Label](https://github.com/helm/charts/tree/master/stable/prometheus-operator#prometheus-operator-1) + ## [Kube Prometheus Selector Label](https://github.com/helm/charts/tree/master/stable/prometheus-operator#exporters) + selector: + prometheus: kube-prometheus + + ## Metrics exporter pod priorityClassName + # priorityClassName: {} + service: + type: ClusterIP + ## Use serviceLoadBalancerIP to request a specific static IP, + ## otherwise leave blank + # loadBalancerIP: + annotations: {} + labels: {} + + ## Custom PrometheusRule to be defined + ## The value is evaluated as a template, so, for example, the value can depend on .Release or .Chart + ## ref: https://github.com/coreos/prometheus-operator#customresourcedefinitions + prometheusRule: + enabled: false + additionalLabels: {} + namespace: "" + rules: [] + ## These are just examples rules, please adapt them to your needs. + ## Make sure to constraint the rules to the current postgresql service. + # - alert: RedisDown + # expr: redis_up{service="{{ template "redis.fullname" . }}-metrics"} == 0 + # for: 2m + # labels: + # severity: error + # annotations: + # summary: Redis instance {{ "{{ $instance }}" }} down + # description: Redis instance {{ "{{ $instance }}" }} is down. + # - alert: RedisMemoryHigh + # expr: > + # redis_memory_used_bytes{service="{{ template "redis.fullname" . }}-metrics"} * 100 + # / + # redis_memory_max_bytes{service="{{ template "redis.fullname" . }}-metrics"} + # > 90 =< 100 + # for: 2m + # labels: + # severity: error + # annotations: + # summary: Redis instance {{ "{{ $instance }}" }} is using too much memory + # description: Redis instance {{ "{{ $instance }}" }} is using {{ "{{ $value }}" }}% of its available memory. + # - alert: RedisKeyEviction + # expr: increase(redis_evicted_keys_total{service="{{ template "redis.fullname" . }}-metrics"}[5m]) > 0 + # for: 1s + # labels: + # severity: error + # annotations: + # summary: Redis instance {{ "{{ $instance }}" }} has evicted keys + # description: Redis instance {{ "{{ $instance }}" }} has evicted {{ "{{ $value }}" }} keys in the last 5 minutes. + +## +## Init containers parameters: +## volumePermissions: Change the owner of the persist volume mountpoint to RunAsUser:fsGroup +## +volumePermissions: + enabled: false + image: + registry: docker.io + repository: bitnami/minideb + tag: buster + pullPolicy: Always + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + resources: {} + # resources: + # requests: + # memory: 128Mi + # cpu: 100m + +## Redis config file +## ref: https://redis.io/topics/config +## +configmap: |- + # Enable AOF https://redis.io/topics/persistence#append-only-file + appendonly yes + # Disable RDB persistence, AOF persistence already enabled. + save "" + +## Sysctl InitContainer +## used to perform sysctl operation to modify Kernel settings (needed sometimes to avoid warnings) +sysctlImage: + enabled: false + command: [] + registry: docker.io + repository: bitnami/minideb + tag: buster + pullPolicy: Always + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + mountHostSys: false + resources: {} + # resources: + # requests: + # memory: 128Mi + # cpu: 100m + +## PodSecurityPolicy configuration +## ref: https://kubernetes.io/docs/concepts/policy/pod-security-policy/ +## +podSecurityPolicy: + ## Specifies whether a PodSecurityPolicy should be created + ## + create: false diff --git a/charts/deps/charts/airflow-8.8.0/charts/redis/values.schema.json b/charts/deps/charts/airflow-8.8.0/charts/redis/values.schema.json new file mode 100644 index 0000000..2138e45 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/redis/values.schema.json @@ -0,0 +1,168 @@ +{ + "$schema": "http://json-schema.org/schema#", + "type": "object", + "properties": { + "usePassword": { + "type": "boolean", + "title": "Use password authentication", + "form": true + }, + "password": { + "type": "string", + "title": "Password", + "form": true, + "description": "Defaults to a random 10-character alphanumeric string if not set", + "hidden": { + "condition": false, + "value": "usePassword" + } + }, + "cluster": { + "type": "object", + "title": "Cluster Settings", + "form": true, + "properties": { + "enabled": { + "type": "boolean", + "form": true, + "title": "Enable master-slave", + "description": "Enable master-slave architecture" + }, + "slaveCount": { + "type": "integer", + "title": "Slave Replicas", + "form": true, + "hidden": { + "condition": false, + "value": "cluster.enabled" + } + } + } + }, + "master": { + "type": "object", + "title": "Master replicas settings", + "form": true, + "properties": { + "persistence": { + "type": "object", + "title": "Persistence for master replicas", + "form": true, + "properties": { + "enabled": { + "type": "boolean", + "form": true, + "title": "Enable persistence", + "description": "Enable persistence using Persistent Volume Claims" + }, + "size": { + "type": "string", + "title": "Persistent Volume Size", + "form": true, + "render": "slider", + "sliderMin": 1, + "sliderMax": 100, + "sliderUnit": "Gi", + "hidden": { + "condition": false, + "value": "master.persistence.enabled" + } + }, + "matchLabels": { + "type": "object", + "title": "Persistent Match Labels Selector" + }, + "matchExpressions": { + "type": "object", + "title": "Persistent Match Expressions Selector" + } + } + } + } + }, + "slave": { + "type": "object", + "title": "Slave replicas settings", + "form": true, + "hidden": { + "condition": false, + "value": "cluster.enabled" + }, + "properties": { + "persistence": { + "type": "object", + "title": "Persistence for slave replicas", + "form": true, + "properties": { + "enabled": { + "type": "boolean", + "form": true, + "title": "Enable persistence", + "description": "Enable persistence using Persistent Volume Claims" + }, + "size": { + "type": "string", + "title": "Persistent Volume Size", + "form": true, + "render": "slider", + "sliderMin": 1, + "sliderMax": 100, + "sliderUnit": "Gi", + "hidden": { + "condition": false, + "value": "slave.persistence.enabled" + } + }, + "matchLabels": { + "type": "object", + "title": "Persistent Match Labels Selector" + }, + "matchExpressions": { + "type": "object", + "title": "Persistent Match Expressions Selector" + } + } + } + } + }, + "volumePermissions": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "form": true, + "title": "Enable Init Containers", + "description": "Use an init container to set required folder permissions on the data volume before mounting it in the final destination" + } + } + }, + "metrics": { + "type": "object", + "form": true, + "title": "Prometheus metrics details", + "properties": { + "enabled": { + "type": "boolean", + "title": "Create Prometheus metrics exporter", + "description": "Create a side-car container to expose Prometheus metrics", + "form": true + }, + "serviceMonitor": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean", + "title": "Create Prometheus Operator ServiceMonitor", + "description": "Create a ServiceMonitor to track metrics using Prometheus Operator", + "form": true, + "hidden": { + "condition": false, + "value": "metrics.enabled" + } + } + } + } + } + } + } +} diff --git a/charts/deps/charts/airflow-8.8.0/charts/redis/values.yaml b/charts/deps/charts/airflow-8.8.0/charts/redis/values.yaml new file mode 100644 index 0000000..2649466 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/charts/redis/values.yaml @@ -0,0 +1,631 @@ +## Global Docker image parameters +## Please, note that this will override the image parameters, including dependencies, configured to use the global value +## Current available global Docker image parameters: imageRegistry and imagePullSecrets +## +global: +# imageRegistry: myRegistryName +# imagePullSecrets: +# - myRegistryKeySecretName +# storageClass: myStorageClass + redis: {} + +## Bitnami Redis image version +## ref: https://hub.docker.com/r/bitnami/redis/tags/ +## +image: + registry: docker.io + repository: bitnami/redis + ## Bitnami Redis image tag + ## ref: https://github.com/bitnami/bitnami-docker-redis#supported-tags-and-respective-dockerfile-links + ## + tag: 5.0.7-debian-10-r32 + ## Specify a imagePullPolicy + ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images + ## + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + +## String to partially override redis.fullname template (will maintain the release name) +## +# nameOverride: + +## String to fully override redis.fullname template +## +# fullnameOverride: + +## Cluster settings +cluster: + enabled: true + slaveCount: 2 + +## Use redis sentinel in the redis pod. This will disable the master and slave services and +## create one redis service with ports to the sentinel and the redis instances +sentinel: + enabled: false + ## Require password authentication on the sentinel itself + ## ref: https://redis.io/topics/sentinel + usePassword: true + ## Bitnami Redis Sentintel image version + ## ref: https://hub.docker.com/r/bitnami/redis-sentinel/tags/ + ## + image: + registry: docker.io + repository: bitnami/redis-sentinel + ## Bitnami Redis image tag + ## ref: https://github.com/bitnami/bitnami-docker-redis-sentinel#supported-tags-and-respective-dockerfile-links + ## + tag: 5.0.7-debian-10-r27 + ## Specify a imagePullPolicy + ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + ## ref: http://kubernetes.io/docs/user-guide/images/#pre-pulling-images + ## + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + masterSet: mymaster + initialCheckTimeout: 5 + quorum: 2 + downAfterMilliseconds: 60000 + failoverTimeout: 18000 + parallelSyncs: 1 + port: 26379 + ## Additional Redis configuration for the sentinel nodes + ## ref: https://redis.io/topics/config + ## + configmap: + ## Enable or disable static sentinel IDs for each replicas + ## If disabled each sentinel will generate a random id at startup + ## If enabled, each replicas will have a constant ID on each start-up + ## + staticID: false + ## Configure extra options for Redis Sentinel liveness and readiness probes + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes) + ## + livenessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + readinessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 5 + ## Redis Sentinel resource requests and limits + ## ref: http://kubernetes.io/docs/user-guide/compute-resources/ + # resources: + # requests: + # memory: 256Mi + # cpu: 100m + ## Redis Sentinel Service properties + service: + ## Redis Sentinel Service type + type: ClusterIP + sentinelPort: 26379 + redisPort: 6379 + + ## Specify the nodePort value for the LoadBalancer and NodePort service types. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport + ## + # sentinelNodePort: + # redisNodePort: + + ## Provide any additional annotations which may be required. This can be used to + ## set the LoadBalancer service type to internal only. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer + ## + annotations: {} + labels: {} + loadBalancerIP: + +## Specifies the Kubernetes Cluster's Domain Name. +## +clusterDomain: cluster.local + +networkPolicy: + ## Specifies whether a NetworkPolicy should be created + ## + enabled: false + + ## The Policy model to apply. When set to false, only pods with the correct + ## client label will have network access to the port Redis is listening + ## on. When true, Redis will accept connections from any source + ## (with the correct destination port). + ## + # allowExternal: true + + ## Allow connections from other namespacess. Just set label for namespace and set label for pods (optional). + ## + ingressNSMatchLabels: {} + ingressNSPodMatchLabels: {} + +serviceAccount: + ## Specifies whether a ServiceAccount should be created + ## + create: false + ## The name of the ServiceAccount to use. + ## If not set and create is true, a name is generated using the fullname template + name: + +rbac: + ## Specifies whether RBAC resources should be created + ## + create: false + + role: + ## Rules to create. It follows the role specification + # rules: + # - apiGroups: + # - extensions + # resources: + # - podsecuritypolicies + # verbs: + # - use + # resourceNames: + # - gce.unprivileged + rules: [] + +## Redis pod Security Context +securityContext: + enabled: true + fsGroup: 1001 + runAsUser: 1001 + ## sysctl settings for master and slave pods + ## + ## Uncomment the setting below to increase the net.core.somaxconn value + ## + # sysctls: + # - name: net.core.somaxconn + # value: "10000" + +## Use password authentication +usePassword: true +## Redis password (both master and slave) +## Defaults to a random 10-character alphanumeric string if not set and usePassword is true +## ref: https://github.com/bitnami/bitnami-docker-redis#setting-the-server-password-on-first-run +## +password: "" +## Use existing secret (ignores previous password) +# existingSecret: +## Password key to be retrieved from Redis secret +## +# existingSecretPasswordKey: + +## Mount secrets as files instead of environment variables +usePasswordFile: false + +## Persist data to a persistent volume (Redis Master) +persistence: {} + ## A manually managed Persistent Volume and Claim + ## Requires persistence.enabled: true + ## If defined, PVC must be created manually before volume will be bound + # existingClaim: + +# Redis port +redisPort: 6379 + +## +## Redis Master parameters +## +master: + ## Redis command arguments + ## + ## Can be used to specify command line arguments, for example: + ## + command: "/run.sh" + ## Additional Redis configuration for the master nodes + ## ref: https://redis.io/topics/config + ## + configmap: + ## Redis additional command line flags + ## + ## Can be used to specify command line flags, for example: + ## + ## extraFlags: + ## - "--maxmemory-policy volatile-ttl" + ## - "--repl-backlog-size 1024mb" + extraFlags: [] + ## Comma-separated list of Redis commands to disable + ## + ## Can be used to disable Redis commands for security reasons. + ## Commands will be completely disabled by renaming each to an empty string. + ## ref: https://redis.io/topics/security#disabling-of-specific-commands + ## + disableCommands: + - FLUSHDB + - FLUSHALL + + ## Redis Master additional pod labels and annotations + ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + podLabels: {} + podAnnotations: {} + + ## Redis Master resource requests and limits + ## ref: http://kubernetes.io/docs/user-guide/compute-resources/ + # resources: + # requests: + # memory: 256Mi + # cpu: 100m + ## Use an alternate scheduler, e.g. "stork". + ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ + ## + # schedulerName: + + ## Configure extra options for Redis Master liveness and readiness probes + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes) + ## + livenessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + readinessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 5 + + ## Redis Master Node selectors and tolerations for pod assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector + ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#taints-and-tolerations-beta-feature + ## + # nodeSelector: {"beta.kubernetes.io/arch": "amd64"} + # tolerations: [] + ## Redis Master pod/node affinity/anti-affinity + ## + affinity: {} + + ## Redis Master Service properties + service: + ## Redis Master Service type + type: ClusterIP + port: 6379 + + ## Specify the nodePort value for the LoadBalancer and NodePort service types. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport + ## + # nodePort: + + ## Provide any additional annotations which may be required. This can be used to + ## set the LoadBalancer service type to internal only. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer + ## + annotations: {} + labels: {} + loadBalancerIP: + # loadBalancerSourceRanges: ["10.0.0.0/8"] + + ## Enable persistence using Persistent Volume Claims + ## ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ + ## + persistence: + enabled: true + ## The path the volume will be mounted at, useful when using different + ## Redis images. + path: /data + ## The subdirectory of the volume to mount to, useful in dev environments + ## and one PV for multiple services. + subPath: "" + ## redis data Persistent Volume Storage Class + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + # storageClass: "-" + accessModes: + - ReadWriteOnce + size: 8Gi + ## Persistent Volume selectors + ## https://kubernetes.io/docs/concepts/storage/persistent-volumes/#selector + matchLabels: {} + matchExpressions: {} + + ## Update strategy, can be set to RollingUpdate or onDelete by default. + ## https://kubernetes.io/docs/tutorials/stateful-application/basic-stateful-set/#updating-statefulsets + statefulset: + updateStrategy: RollingUpdate + ## Partition update strategy + ## https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#partitions + # rollingUpdatePartition: + + ## Redis Master pod priorityClassName + # priorityClassName: {} + +## +## Redis Slave properties +## Note: service.type is a mandatory parameter +## The rest of the parameters are either optional or, if undefined, will inherit those declared in Redis Master +## +slave: + ## Slave Service properties + service: + ## Redis Slave Service type + type: ClusterIP + ## Redis port + port: 6379 + ## Specify the nodePort value for the LoadBalancer and NodePort service types. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport + ## + # nodePort: + + ## Provide any additional annotations which may be required. This can be used to + ## set the LoadBalancer service type to internal only. + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer + ## + annotations: {} + labels: {} + loadBalancerIP: + # loadBalancerSourceRanges: ["10.0.0.0/8"] + + ## Redis slave port + port: 6379 + ## Can be used to specify command line arguments, for example: + ## + command: "/run.sh" + ## Additional Redis configuration for the slave nodes + ## ref: https://redis.io/topics/config + ## + configmap: + ## Redis extra flags + extraFlags: [] + ## List of Redis commands to disable + disableCommands: + - FLUSHDB + - FLUSHALL + + ## Redis Slave pod/node affinity/anti-affinity + ## + affinity: {} + + ## Configure extra options for Redis Slave liveness and readiness probes + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes) + ## + livenessProbe: + enabled: true + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 5 + readinessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 10 + successThreshold: 1 + failureThreshold: 5 + + ## Redis slave Resource + # resources: + # requests: + # memory: 256Mi + # cpu: 100m + + ## Redis slave selectors and tolerations for pod assignment + # nodeSelector: {"beta.kubernetes.io/arch": "amd64"} + # tolerations: [] + + ## Use an alternate scheduler, e.g. "stork". + ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ + ## + # schedulerName: + + ## Redis slave pod Annotation and Labels + podLabels: {} + podAnnotations: {} + + ## Redis slave pod priorityClassName + # priorityClassName: {} + + ## Enable persistence using Persistent Volume Claims + ## ref: http://kubernetes.io/docs/user-guide/persistent-volumes/ + ## + persistence: + enabled: true + ## The path the volume will be mounted at, useful when using different + ## Redis images. + path: /data + ## The subdirectory of the volume to mount to, useful in dev environments + ## and one PV for multiple services. + subPath: "" + ## redis data Persistent Volume Storage Class + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + # storageClass: "-" + accessModes: + - ReadWriteOnce + size: 8Gi + ## Persistent Volume selectors + ## https://kubernetes.io/docs/concepts/storage/persistent-volumes/#selector + matchLabels: {} + matchExpressions: {} + + ## Update strategy, can be set to RollingUpdate or onDelete by default. + ## https://kubernetes.io/docs/tutorials/stateful-application/basic-stateful-set/#updating-statefulsets + statefulset: + updateStrategy: RollingUpdate + ## Partition update strategy + ## https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#partitions + # rollingUpdatePartition: + +## Prometheus Exporter / Metrics +## +metrics: + enabled: false + + image: + registry: docker.io + repository: bitnami/redis-exporter + tag: 1.4.0-debian-10-r3 + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + + ## Metrics exporter resource requests and limits + ## ref: http://kubernetes.io/docs/user-guide/compute-resources/ + ## + # resources: {} + + ## Extra arguments for Metrics exporter, for example: + ## extraArgs: + ## check-keys: myKey,myOtherKey + # extraArgs: {} + + ## Metrics exporter pod Annotation and Labels + podAnnotations: + prometheus.io/scrape: "true" + prometheus.io/port: "9121" + # podLabels: {} + + # Enable this if you're using https://github.com/coreos/prometheus-operator + serviceMonitor: + enabled: false + ## Specify a namespace if needed + # namespace: monitoring + # fallback to the prometheus default unless specified + # interval: 10s + ## Defaults to what's used if you follow CoreOS [Prometheus Install Instructions](https://github.com/helm/charts/tree/master/stable/prometheus-operator#tldr) + ## [Prometheus Selector Label](https://github.com/helm/charts/tree/master/stable/prometheus-operator#prometheus-operator-1) + ## [Kube Prometheus Selector Label](https://github.com/helm/charts/tree/master/stable/prometheus-operator#exporters) + selector: + prometheus: kube-prometheus + + ## Custom PrometheusRule to be defined + ## The value is evaluated as a template, so, for example, the value can depend on .Release or .Chart + ## ref: https://github.com/coreos/prometheus-operator#customresourcedefinitions + prometheusRule: + enabled: false + additionalLabels: {} + namespace: "" + rules: [] + ## These are just examples rules, please adapt them to your needs. + ## Make sure to constraint the rules to the current postgresql service. + # - alert: RedisDown + # expr: redis_up{service="{{ template "redis.fullname" . }}-metrics"} == 0 + # for: 2m + # labels: + # severity: error + # annotations: + # summary: Redis instance {{ "{{ $instance }}" }} down + # description: Redis instance {{ "{{ $instance }}" }} is down. + # - alert: RedisMemoryHigh + # expr: > + # redis_memory_used_bytes{service="{{ template "redis.fullname" . }}-metrics"} * 100 + # / + # redis_memory_max_bytes{service="{{ template "redis.fullname" . }}-metrics"} + # > 90 =< 100 + # for: 2m + # labels: + # severity: error + # annotations: + # summary: Redis instance {{ "{{ $instance }}" }} is using too much memory + # description: Redis instance {{ "{{ $instance }}" }} is using {{ "{{ $value }}" }}% of its available memory. + # - alert: RedisKeyEviction + # expr: increase(redis_evicted_keys_total{service="{{ template "redis.fullname" . }}-metrics"}[5m]) > 0 + # for: 1s + # labels: + # severity: error + # annotations: + # summary: Redis instance {{ "{{ $instance }}" }} has evicted keys + # description: Redis instance {{ "{{ $instance }}" }} has evicted {{ "{{ $value }}" }} keys in the last 5 minutes. + + + ## Metrics exporter pod priorityClassName + # priorityClassName: {} + service: + type: ClusterIP + ## Use serviceLoadBalancerIP to request a specific static IP, + ## otherwise leave blank + # loadBalancerIP: + annotations: {} + labels: {} + +## +## Init containers parameters: +## volumePermissions: Change the owner of the persist volume mountpoint to RunAsUser:fsGroup +## +volumePermissions: + enabled: false + image: + registry: docker.io + repository: bitnami/minideb + tag: buster + pullPolicy: Always + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + resources: {} + # resources: + # requests: + # memory: 128Mi + # cpu: 100m + +## Redis config file +## ref: https://redis.io/topics/config +## +configmap: |- + # Enable AOF https://redis.io/topics/persistence#append-only-file + appendonly yes + # Disable RDB persistence, AOF persistence already enabled. + save "" + +## Sysctl InitContainer +## used to perform sysctl operation to modify Kernel settings (needed sometimes to avoid warnings) +sysctlImage: + enabled: false + command: [] + registry: docker.io + repository: bitnami/minideb + tag: buster + pullPolicy: Always + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## + # pullSecrets: + # - myRegistryKeySecretName + mountHostSys: false + resources: {} + # resources: + # requests: + # memory: 128Mi + # cpu: 100m + +## PodSecurityPolicy configuration +## ref: https://kubernetes.io/docs/concepts/policy/pod-security-policy/ +## +podSecurityPolicy: + ## Specifies whether a PodSecurityPolicy should be created + ## + create: false diff --git a/charts/deps/charts/airflow-8.8.0/files/pod_template.kubernetes-helm-yaml b/charts/deps/charts/airflow-8.8.0/files/pod_template.kubernetes-helm-yaml new file mode 100644 index 0000000..b4c2325 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/files/pod_template.kubernetes-helm-yaml @@ -0,0 +1,87 @@ +{{- $podNodeSelector := include "airflow.podNodeSelector" (dict "Release" .Release "Values" .Values "nodeSelector" .Values.airflow.kubernetesPodTemplate.nodeSelector) }} +{{- $podAffinity := include "airflow.podAffinity" (dict "Release" .Release "Values" .Values "affinity" .Values.airflow.kubernetesPodTemplate.affinity) }} +{{- $podTolerations := include "airflow.podTolerations" (dict "Release" .Release "Values" .Values "tolerations" .Values.airflow.kubernetesPodTemplate.tolerations) }} +{{- $podSecurityContext := include "airflow.podSecurityContext" (dict "Release" .Release "Values" .Values "securityContext" .Values.airflow.kubernetesPodTemplate.securityContext) }} +{{- $extraPipPackages := .Values.airflow.kubernetesPodTemplate.extraPipPackages }} +{{- $extraVolumeMounts := .Values.airflow.kubernetesPodTemplate.extraVolumeMounts }} +{{- $volumeMounts := include "airflow.volumeMounts" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages "extraVolumeMounts" $extraVolumeMounts) }} +{{- $extraVolumes := .Values.airflow.kubernetesPodTemplate.extraVolumes }} +{{- $volumes := include "airflow.volumes" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages "extraVolumes" $extraVolumes "extraVolumeMounts" $extraVolumeMounts) }} +apiVersion: v1 +kind: Pod +metadata: + name: dummy-name + {{- if .Values.airflow.kubernetesPodTemplate.podAnnotations }} + annotations: + {{- toYaml .Values.airflow.kubernetesPodTemplate.podAnnotations | nindent 4 }} + {{- end }} + {{- if .Values.airflow.kubernetesPodTemplate.podLabels }} + labels: + {{- toYaml .Values.airflow.kubernetesPodTemplate.podLabels | nindent 4 }} + {{- end }} +spec: + restartPolicy: Never + {{- if .Values.airflow.image.pullSecret }} + imagePullSecrets: + - name: {{ .Values.airflow.image.pullSecret }} + {{- end }} + serviceAccountName: {{ include "airflow.serviceAccountName" . }} + shareProcessNamespace: {{ .Values.airflow.kubernetesPodTemplate.shareProcessNamespace }} + {{- if $podNodeSelector }} + nodeSelector: + {{- $podNodeSelector | nindent 4 }} + {{- end }} + {{- if $podAffinity }} + affinity: + {{- $podAffinity | nindent 4 }} + {{- end }} + {{- if $podTolerations }} + tolerations: + {{- $podTolerations | nindent 4 }} + {{- end }} + {{- if $podSecurityContext }} + securityContext: + {{- $podSecurityContext | nindent 4 }} + {{- end }} + {{- if or ($extraPipPackages) (.Values.dags.gitSync.enabled) (.Values.airflow.kubernetesPodTemplate.extraInitContainers) }} + initContainers: + {{- if $extraPipPackages }} + {{- include "airflow.init_container.install_pip_packages" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages) | indent 4 }} + {{- end }} + {{- if .Values.dags.gitSync.enabled }} + {{- include "airflow.container.git_sync" (dict "Release" .Release "Values" .Values "sync_one_time" "true") | indent 4 }} + {{- end }} + {{- if .Values.airflow.kubernetesPodTemplate.extraInitContainers }} + {{- toYaml .Values.airflow.kubernetesPodTemplate.extraInitContainers | nindent 4 }} + {{- end }} + {{- end }} + containers: + - name: base + {{- include "airflow.image" . | indent 6 }} + envFrom: + {{- include "airflow.envFrom" . | indent 8 }} + env: + ## KubernetesExecutor Pods use LocalExecutor internally + - name: AIRFLOW__CORE__EXECUTOR + value: LocalExecutor + {{- /* NOTE: the FIRST definition of an `env` takes precedence (so we include user-defined `env` LAST) */ -}} + {{- /* NOTE: we set `CONNECTION_CHECK_MAX_COUNT=20` to enable airflow's `/entrypoint` db connection check */ -}} + {{- include "airflow.env" (dict "Release" .Release "Values" .Values "CONNECTION_CHECK_MAX_COUNT" "20") | indent 8 }} + ports: [] + command: [] + args: [] + {{- if .Values.airflow.kubernetesPodTemplate.resources }} + resources: + {{- toYaml .Values.airflow.kubernetesPodTemplate.resources | nindent 8 }} + {{- end }} + {{- if $volumeMounts }} + volumeMounts: + {{- $volumeMounts | indent 8 }} + {{- end }} + {{- if .Values.airflow.kubernetesPodTemplate.extraContainers }} + {{- toYaml .Values.airflow.kubernetesPodTemplate.extraContainers | nindent 4 }} + {{- end }} + {{- if $volumes }} + volumes: + {{- $volumes | indent 4 }} + {{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/files/webserver_config.py b/charts/deps/charts/airflow-8.8.0/files/webserver_config.py new file mode 100644 index 0000000..8e91cad --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/files/webserver_config.py @@ -0,0 +1,10 @@ +from flask_appbuilder.security.manager import AUTH_DB + +{{- if .Values.airflow.legacyCommands }} +# only needed for airflow 1.10 +from airflow import configuration as conf +SQLALCHEMY_DATABASE_URI = conf.get("core", "SQL_ALCHEMY_CONN") +{{- end }} + +# use embedded DB for auth +AUTH_TYPE = AUTH_DB diff --git a/charts/deps/charts/airflow-8.8.0/sample-values-CeleryExecutor.yaml b/charts/deps/charts/airflow-8.8.0/sample-values-CeleryExecutor.yaml new file mode 100644 index 0000000..76be4e0 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/sample-values-CeleryExecutor.yaml @@ -0,0 +1,333 @@ +######################################## +## CONFIG | Airflow Configs +######################################## +airflow: + ## if we use legacy 1.10 airflow commands + legacyCommands: false + + ## configs for the airflow container image + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/configuration/airflow-version.md + image: + repository: apache/airflow + tag: 2.6.3-python3.9 + + ## the airflow executor type to use + executor: CeleryExecutor + + ## the fernet encryption key (sets `AIRFLOW__CORE__FERNET_KEY`) + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/security/set-fernet-key.md + ## [WARNING] change from default value to ensure security + fernetKey: "7T512UXSSmBOkpWimFHIVb8jK6lfmSAvx4mO6Arehnc=" + + ## the secret_key for flask (sets `AIRFLOW__WEBSERVER__SECRET_KEY`) + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/security/set-webserver-secret-key.md + ## [WARNING] change from default value to ensure security + webserverSecretKey: "THIS IS UNSAFE!" + + ## environment variables for airflow configs + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/configuration/airflow-configs.md + config: + AIRFLOW__WEBSERVER__EXPOSE_CONFIG: "False" + AIRFLOW__CORE__LOAD_EXAMPLES: "False" + + ## a list of users to create + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/security/airflow-users.md + users: + - username: admin + password: admin + role: Admin + email: admin@example.com + firstName: admin + lastName: admin + + ## a list airflow connections to create + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/dags/airflow-connections.md + connections: [] + + ## a list airflow variables to create + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/dags/airflow-variables.md + variables: [] + + ## a list airflow pools to create + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/dags/airflow-pools.md + pools: [] + + ## extra pip packages to install in airflow Pods + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/configuration/extra-python-packages.md + ## [WARNING] this feature is not recommended for production use, see docs + extraPipPackages: [] + + ## extra environment variables for the airflow Pods + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/kubernetes/mount-environment-variables.md + extraEnv: [] + + ## extra VolumeMounts for the airflow Pods + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/kubernetes/mount-persistent-volumes.md + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/kubernetes/mount-files.md + extraVolumeMounts: [] + + ## extra Volumes for the airflow Pods + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/kubernetes/mount-persistent-volumes.md + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/kubernetes/mount-files.md + extraVolumes: [] + +################################### +## COMPONENT | Airflow Scheduler +################################### +scheduler: + ## the number of scheduler Pods to run + replicas: 1 + + ## resource requests/limits for the scheduler Pods + ## [SPEC] https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core + resources: {} + + ## configs for the log-cleanup sidecar of the scheduler + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/monitoring/log-cleanup.md + logCleanup: + enabled: true + retentionMinutes: 21600 + + ## configs for the scheduler Pods' liveness probe + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/monitoring/scheduler-liveness-probe.md + livenessProbe: + enabled: true + + ## configs for an additional check that ensures tasks are being created by the scheduler + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/monitoring/scheduler-liveness-probe.md + taskCreationCheck: + enabled: false + thresholdSeconds: 300 + schedulerAgeBeforeCheck: 180 + +################################### +## COMPONENT | Airflow Webserver +################################### +web: + ## the number of web Pods to run + replicas: 1 + + ## resource requests/limits for the web Pods + ## [SPEC] https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core + resources: {} + + ## configs for the Service of the web Pods + service: + type: ClusterIP + externalPort: 8080 + + ## configs generating the `webserver_config.py` file + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/configuration/airflow-configs.md#webserver_configpy + webserverConfig: + ## the full content of the `webserver_config.py` file (as a string) + stringOverride: | + from airflow import configuration as conf + from flask_appbuilder.security.manager import AUTH_DB + + # the SQLAlchemy connection string + SQLALCHEMY_DATABASE_URI = conf.get("core", "SQL_ALCHEMY_CONN") + + # use embedded DB for auth + AUTH_TYPE = AUTH_DB + + ## the name of a Secret containing a `webserver_config.py` key + existingSecret: "" + +################################### +## COMPONENT | Airflow Workers +################################### +workers: + ## if the airflow workers StatefulSet should be deployed + enabled: true + + ## the number of worker Pods to run + replicas: 1 + + ## resource requests/limits for the worker Pods + ## [SPEC] https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core + resources: {} + + ## configs for the log-cleanup sidecar of the worker Pods + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/monitoring/log-cleanup.md + logCleanup: + enabled: true + retentionMinutes: 21600 + +################################### +## COMPONENT | Triggerer +################################### +triggerer: + ## if the airflow triggerer should be deployed + enabled: true + + ## the number of triggerer Pods to run + replicas: 1 + + ## resource requests/limits for the triggerer Pods + ## [SPEC] https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core + resources: {} + + ## maximum number of triggers each triggerer will run at once (sets `AIRFLOW__TRIGGERER__DEFAULT_CAPACITY`) + capacity: 1000 + +################################### +## COMPONENT | Flower +################################### +flower: + ## if the airflow flower UI should be deployed + enabled: true + + ## the number of flower Pods to run + replicas: 1 + + ## resource requests/limits for the flower Pod + ## [SPEC] https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core + resources: {} + + ## configs for the Service of the flower Pods + service: + type: ClusterIP + externalPort: 5555 + +################################### +## CONFIG | Airflow Logs +################################### +logs: + ## the airflow logs folder + path: /opt/airflow/logs + + ## configs for the logs PVC + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/monitoring/log-persistence.md + persistence: + enabled: false + +################################### +## CONFIG | Airflow DAGs +################################### +dags: + ## the airflow dags folder + path: /opt/airflow/dags + + ## configs for the dags PVC + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/dags/load-dag-definitions.md + persistence: + enabled: false + + ## configs for the git-sync sidecar + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/dags/load-dag-definitions.md + gitSync: + enabled: false + +################################### +## CONFIG | Kubernetes Ingress +################################### +ingress: + ## if we should deploy Ingress resources + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/kubernetes/ingress.md + enabled: false + +################################### +## CONFIG | Kubernetes ServiceAccount +################################### +serviceAccount: + ## if a Kubernetes ServiceAccount is created + create: true + + ## the name of the ServiceAccount + name: "" + + ## annotations for the ServiceAccount + annotations: {} + +################################### +## CONFIG | Kubernetes Extra Manifests +################################### + +## a list of extra Kubernetes manifests that will be deployed alongside the chart +## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/kubernetes/extra-manifests.md +extraManifests: [] + +################################### +## DATABASE | PgBouncer +################################### +pgbouncer: + ## if the pgbouncer Deployment is created + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/database/pgbouncer.md + enabled: true + + ## resource requests/limits for the pgbouncer Pods + ## [SPEC] https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core + resources: {} + + ## sets pgbouncer config: `auth_type` + authType: md5 + +################################### +## DATABASE | Embedded Postgres +################################### +postgresql: + ## if the `stable/postgresql` chart is used + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/database/embedded-database.md + ## [WARNING] the embedded Postgres is NOT SUITABLE for production deployments of Airflow + ## [WARNING] consider using an external database with `externalDatabase.*` + enabled: true + + ## configs for the PVC of postgresql + persistence: + enabled: true + storageClass: "" + size: 8Gi + +################################### +## DATABASE | External Database +################################### +externalDatabase: + ## the type of external database + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/database/external-database.md + type: postgres + +################################### +## DATABASE | Embedded Redis +################################### +redis: + ## if the `stable/redis` chart is used + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/database/embedded-redis.md + ## [WARNING] consider using an external database with `externalDatabase.*` + enabled: true + + ## configs for redis cluster mode + cluster: + enabled: false + slaveCount: 1 + + ## configs for the redis master StatefulSet + master: + ## resource requests/limits for the redis master Pods + ## [SPEC] https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core + resources: {} + + ## configs for the PVC of the redis master Pods + persistence: + enabled: false + storageClass: "" + size: 8Gi + + ## configs for the redis slave StatefulSet + slave: + ## resource requests/limits for the slave Pods + ## [SPEC] https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core + resources: {} + + ## configs for the PVC of the redis slave Pods + persistence: + enabled: false + storageClass: "" + size: 8Gi + +################################### +## DATABASE | External Redis +################################### +externalRedis: + ## the host of the external redis + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/database/external-redis.md + host: localhost \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/sample-values-CeleryKubernetesExecutor.yaml b/charts/deps/charts/airflow-8.8.0/sample-values-CeleryKubernetesExecutor.yaml new file mode 100644 index 0000000..b3775ea --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/sample-values-CeleryKubernetesExecutor.yaml @@ -0,0 +1,361 @@ +######################################## +## CONFIG | Airflow Configs +######################################## +airflow: + ## if we use legacy 1.10 airflow commands + legacyCommands: false + + ## configs for the airflow container image + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/configuration/airflow-version.md + image: + repository: apache/airflow + tag: 2.6.3-python3.9 + + ## the airflow executor type to use + executor: CeleryKubernetesExecutor + + ## the fernet encryption key (sets `AIRFLOW__CORE__FERNET_KEY`) + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/security/set-fernet-key.md + ## [WARNING] change from default value to ensure security + fernetKey: "7T512UXSSmBOkpWimFHIVb8jK6lfmSAvx4mO6Arehnc=" + + ## the secret_key for flask (sets `AIRFLOW__WEBSERVER__SECRET_KEY`) + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/security/set-webserver-secret-key.md + ## [WARNING] change from default value to ensure security + webserverSecretKey: "THIS IS UNSAFE!" + + ## environment variables for airflow configs + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/configuration/airflow-configs.md + config: + AIRFLOW__WEBSERVER__EXPOSE_CONFIG: "False" + AIRFLOW__CORE__LOAD_EXAMPLES: "False" + + ## a list of users to create + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/security/airflow-users.md + users: + - username: admin + password: admin + role: Admin + email: admin@example.com + firstName: admin + lastName: admin + + ## a list airflow connections to create + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/dags/airflow-connections.md + connections: [] + + ## a list airflow variables to create + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/dags/airflow-variables.md + variables: [] + + ## a list airflow pools to create + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/dags/airflow-pools.md + pools: [] + + ## extra pip packages to install in airflow Pods + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/configuration/extra-python-packages.md + ## [WARNING] this feature is not recommended for production use, see docs + extraPipPackages: [] + + ## extra environment variables for the airflow Pods + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/kubernetes/mount-environment-variables.md + extraEnv: [] + + ## extra VolumeMounts for the airflow Pods + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/kubernetes/mount-persistent-volumes.md + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/kubernetes/mount-files.md + extraVolumeMounts: [] + + ## extra Volumes for the airflow Pods + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/kubernetes/mount-persistent-volumes.md + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/kubernetes/mount-files.md + extraVolumes: [] + + ## configs generating the `pod_template.yaml` file for `AIRFLOW__KUBERNETES__POD_TEMPLATE_FILE` + ## [NOTE] the `dags.gitSync` values will create a git-sync init-container in the pod + ## [NOTE] the `airflow.extraPipPackages` will NOT be installed + kubernetesPodTemplate: + + ## the full content of the pod-template file (as a string) + ## [NOTE] all other `kubernetesPodTemplate.*` are disabled when this is set + stringOverride: "" + + ## resource requests/limits for the Pod template "base" container + ## [SPEC] https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core + resources: {} + + ## extra pip packages to install in the Pod template + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/configuration/extra-python-packages.md + ## [WARNING] this feature is not recommended for production use, see docs + extraPipPackages: [] + + ## extra VolumeMounts for the Pod template + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/kubernetes/mount-persistent-volumes.md + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/kubernetes/mount-files.md + extraVolumeMounts: [] + + ## extra Volumes for the Pod template + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/kubernetes/mount-persistent-volumes.md + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/kubernetes/mount-files.md + extraVolumes: [] + +################################### +## COMPONENT | Airflow Scheduler +################################### +scheduler: + ## the number of scheduler Pods to run + replicas: 1 + + ## resource requests/limits for the scheduler Pods + ## [SPEC] https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core + resources: {} + + ## configs for the log-cleanup sidecar of the scheduler + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/monitoring/log-cleanup.md + logCleanup: + enabled: true + retentionMinutes: 21600 + + ## configs for the scheduler Pods' liveness probe + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/monitoring/scheduler-liveness-probe.md + livenessProbe: + enabled: true + + ## configs for an additional check that ensures tasks are being created by the scheduler + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/monitoring/scheduler-liveness-probe.md + taskCreationCheck: + enabled: false + thresholdSeconds: 300 + schedulerAgeBeforeCheck: 180 + +################################### +## COMPONENT | Airflow Webserver +################################### +web: + ## the number of web Pods to run + replicas: 1 + + ## resource requests/limits for the web Pods + ## [SPEC] https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core + resources: {} + + ## configs for the Service of the web Pods + service: + type: ClusterIP + externalPort: 8080 + + ## configs generating the `webserver_config.py` file + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/configuration/airflow-configs.md#webserver_configpy + webserverConfig: + ## the full content of the `webserver_config.py` file (as a string) + stringOverride: | + from airflow import configuration as conf + from flask_appbuilder.security.manager import AUTH_DB + + # the SQLAlchemy connection string + SQLALCHEMY_DATABASE_URI = conf.get("core", "SQL_ALCHEMY_CONN") + + # use embedded DB for auth + AUTH_TYPE = AUTH_DB + + ## the name of a Secret containing a `webserver_config.py` key + existingSecret: "" + +################################### +## COMPONENT | Airflow Workers +################################### +workers: + ## if the airflow workers StatefulSet should be deployed + enabled: true + + ## the number of worker Pods to run + replicas: 1 + + ## resource requests/limits for the worker Pods + ## [SPEC] https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core + resources: {} + + ## configs for the log-cleanup sidecar of the worker Pods + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/monitoring/log-cleanup.md + logCleanup: + enabled: true + retentionMinutes: 21600 + +################################### +## COMPONENT | Triggerer +################################### +triggerer: + ## if the airflow triggerer should be deployed + enabled: true + + ## the number of triggerer Pods to run + replicas: 1 + + ## resource requests/limits for the triggerer Pods + ## [SPEC] https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core + resources: {} + + ## maximum number of triggers each triggerer will run at once (sets `AIRFLOW__TRIGGERER__DEFAULT_CAPACITY`) + capacity: 1000 + +################################### +## COMPONENT | Flower +################################### +flower: + ## if the airflow flower UI should be deployed + enabled: true + + ## the number of flower Pods to run + replicas: 1 + + ## resource requests/limits for the flower Pod + ## [SPEC] https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core + resources: {} + + ## configs for the Service of the flower Pods + service: + type: ClusterIP + externalPort: 5555 + +################################### +## CONFIG | Airflow Logs +################################### +logs: + ## the airflow logs folder + path: /opt/airflow/logs + + ## configs for the logs PVC + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/monitoring/log-persistence.md + persistence: + enabled: false + +################################### +## CONFIG | Airflow DAGs +################################### +dags: + ## the airflow dags folder + path: /opt/airflow/dags + + ## configs for the dags PVC + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/dags/load-dag-definitions.md + persistence: + enabled: false + + ## configs for the git-sync sidecar + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/dags/load-dag-definitions.md + gitSync: + enabled: false + +################################### +## CONFIG | Kubernetes Ingress +################################### +ingress: + ## if we should deploy Ingress resources + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/kubernetes/ingress.md + enabled: false + +################################### +## CONFIG | Kubernetes ServiceAccount +################################### +serviceAccount: + ## if a Kubernetes ServiceAccount is created + create: true + + ## the name of the ServiceAccount + name: "" + + ## annotations for the ServiceAccount + annotations: {} + +################################### +## CONFIG | Kubernetes Extra Manifests +################################### + +## a list of extra Kubernetes manifests that will be deployed alongside the chart +## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/kubernetes/extra-manifests.md +extraManifests: [] + +################################### +## DATABASE | PgBouncer +################################### +pgbouncer: + ## if the pgbouncer Deployment is created + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/database/pgbouncer.md + enabled: true + + ## resource requests/limits for the pgbouncer Pods + ## [SPEC] https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core + resources: {} + + ## sets pgbouncer config: `auth_type` + authType: md5 + +################################### +## DATABASE | Embedded Postgres +################################### +postgresql: + ## if the `stable/postgresql` chart is used + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/database/embedded-database.md + ## [WARNING] the embedded Postgres is NOT SUITABLE for production deployments of Airflow + ## [WARNING] consider using an external database with `externalDatabase.*` + enabled: true + + ## configs for the PVC of postgresql + persistence: + enabled: true + storageClass: "" + size: 8Gi + +################################### +## DATABASE | External Database +################################### +externalDatabase: + ## the type of external database + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/database/external-database.md + type: postgres + +################################### +## DATABASE | Embedded Redis +################################### +redis: + ## if the `stable/redis` chart is used + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/database/embedded-redis.md + ## [WARNING] consider using an external database with `externalDatabase.*` + enabled: true + + ## configs for redis cluster mode + cluster: + enabled: false + slaveCount: 1 + + ## configs for the redis master StatefulSet + master: + ## resource requests/limits for the redis master Pods + ## [SPEC] https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core + resources: {} + + ## configs for the PVC of the redis master Pods + persistence: + enabled: false + storageClass: "" + size: 8Gi + + ## configs for the redis slave StatefulSet + slave: + ## resource requests/limits for the slave Pods + ## [SPEC] https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core + resources: {} + + ## configs for the PVC of the redis slave Pods + persistence: + enabled: false + storageClass: "" + size: 8Gi + +################################### +## DATABASE | External Redis +################################### +externalRedis: + ## the host of the external redis + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/database/external-redis.md + host: localhost \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/sample-values-KubernetesExecutor.yaml b/charts/deps/charts/airflow-8.8.0/sample-values-KubernetesExecutor.yaml new file mode 100644 index 0000000..afe5c8c --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/sample-values-KubernetesExecutor.yaml @@ -0,0 +1,305 @@ +######################################## +## CONFIG | Airflow Configs +######################################## +airflow: + ## if we use legacy 1.10 airflow commands + legacyCommands: false + + ## configs for the airflow container image + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/configuration/airflow-version.md + image: + repository: apache/airflow + tag: 2.6.3-python3.9 + + ## the airflow executor type to use + executor: KubernetesExecutor + + ## the fernet encryption key (sets `AIRFLOW__CORE__FERNET_KEY`) + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/security/set-fernet-key.md + ## [WARNING] change from default value to ensure security + fernetKey: "7T512UXSSmBOkpWimFHIVb8jK6lfmSAvx4mO6Arehnc=" + + ## the secret_key for flask (sets `AIRFLOW__WEBSERVER__SECRET_KEY`) + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/security/set-webserver-secret-key.md + ## [WARNING] change from default value to ensure security + webserverSecretKey: "THIS IS UNSAFE!" + + ## environment variables for airflow configs + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/configuration/airflow-configs.md + config: + AIRFLOW__WEBSERVER__EXPOSE_CONFIG: "False" + AIRFLOW__CORE__LOAD_EXAMPLES: "False" + + ## a list of users to create + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/security/airflow-users.md + users: + - username: admin + password: admin + role: Admin + email: admin@example.com + firstName: admin + lastName: admin + + ## a list airflow connections to create + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/dags/airflow-connections.md + connections: [] + + ## a list airflow variables to create + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/dags/airflow-variables.md + variables: [] + + ## a list airflow pools to create + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/dags/airflow-pools.md + pools: [] + + ## extra pip packages to install in airflow Pods + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/configuration/extra-python-packages.md + ## [WARNING] this feature is not recommended for production use, see docs + extraPipPackages: [] + + ## extra environment variables for the airflow Pods + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/kubernetes/mount-environment-variables.md + extraEnv: [] + + ## extra VolumeMounts for the airflow Pods + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/kubernetes/mount-persistent-volumes.md + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/kubernetes/mount-files.md + extraVolumeMounts: [] + + ## extra Volumes for the airflow Pods + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/kubernetes/mount-persistent-volumes.md + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/kubernetes/mount-files.md + extraVolumes: [] + + ## configs generating the `pod_template.yaml` file for `AIRFLOW__KUBERNETES__POD_TEMPLATE_FILE` + ## [NOTE] the `dags.gitSync` values will create a git-sync init-container in the pod + ## [NOTE] the `airflow.extraPipPackages` will NOT be installed + kubernetesPodTemplate: + + ## the full content of the pod-template file (as a string) + ## [NOTE] all other `kubernetesPodTemplate.*` are disabled when this is set + stringOverride: "" + + ## resource requests/limits for the Pod template "base" container + ## [SPEC] https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core + resources: {} + + ## extra pip packages to install in the Pod template + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/configuration/extra-python-packages.md + ## [WARNING] this feature is not recommended for production use, see docs + extraPipPackages: [] + + ## extra VolumeMounts for the Pod template + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/kubernetes/mount-persistent-volumes.md + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/kubernetes/mount-files.md + extraVolumeMounts: [] + + ## extra Volumes for the Pod template + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/kubernetes/mount-persistent-volumes.md + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/kubernetes/mount-files.md + extraVolumes: [] + +################################### +## COMPONENT | Airflow Scheduler +################################### +scheduler: + ## the number of scheduler Pods to run + replicas: 1 + + ## resource requests/limits for the scheduler Pods + ## [SPEC] https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core + resources: {} + + ## configs for the log-cleanup sidecar of the scheduler + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/monitoring/log-cleanup.md + logCleanup: + enabled: true + retentionMinutes: 21600 + + ## configs for the scheduler Pods' liveness probe + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/monitoring/scheduler-liveness-probe.md + livenessProbe: + enabled: true + + ## configs for an additional check that ensures tasks are being created by the scheduler + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/monitoring/scheduler-liveness-probe.md + taskCreationCheck: + enabled: false + thresholdSeconds: 300 + schedulerAgeBeforeCheck: 180 + +################################### +## COMPONENT | Airflow Webserver +################################### +web: + ## the number of web Pods to run + replicas: 1 + + ## resource requests/limits for the web Pods + ## [SPEC] https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core + resources: {} + + ## configs for the Service of the web Pods + service: + type: ClusterIP + externalPort: 8080 + + ## configs generating the `webserver_config.py` file + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/configuration/airflow-configs.md#webserver_configpy + webserverConfig: + ## the full content of the `webserver_config.py` file (as a string) + stringOverride: | + from airflow import configuration as conf + from flask_appbuilder.security.manager import AUTH_DB + + # the SQLAlchemy connection string + SQLALCHEMY_DATABASE_URI = conf.get("core", "SQL_ALCHEMY_CONN") + + # use embedded DB for auth + AUTH_TYPE = AUTH_DB + + ## the name of a Secret containing a `webserver_config.py` key + existingSecret: "" + +################################### +## COMPONENT | Airflow Workers +################################### +workers: + ## if the airflow workers StatefulSet should be deployed + enabled: false + +################################### +## COMPONENT | Triggerer +################################### +triggerer: + ## if the airflow triggerer should be deployed + enabled: true + + ## the number of triggerer Pods to run + replicas: 1 + + ## resource requests/limits for the triggerer Pods + ## [SPEC] https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core + resources: {} + + ## maximum number of triggers each triggerer will run at once (sets `AIRFLOW__TRIGGERER__DEFAULT_CAPACITY`) + capacity: 1000 + +################################### +## COMPONENT | Flower +################################### +flower: + ## if the airflow flower UI should be deployed + enabled: false + +################################### +## CONFIG | Airflow Logs +################################### +logs: + ## the airflow logs folder + path: /opt/airflow/logs + + ## configs for the logs PVC + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/monitoring/log-persistence.md + persistence: + enabled: false + +################################### +## CONFIG | Airflow DAGs +################################### +dags: + ## the airflow dags folder + path: /opt/airflow/dags + + ## configs for the dags PVC + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/dags/load-dag-definitions.md + persistence: + enabled: false + + ## configs for the git-sync sidecar + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/dags/load-dag-definitions.md + gitSync: + enabled: false + +################################### +## CONFIG | Kubernetes Ingress +################################### +ingress: + ## if we should deploy Ingress resources + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/kubernetes/ingress.md + enabled: false + +################################### +## CONFIG | Kubernetes ServiceAccount +################################### +serviceAccount: + ## if a Kubernetes ServiceAccount is created + create: true + + ## the name of the ServiceAccount + name: "" + + ## annotations for the ServiceAccount + annotations: {} + +################################### +## CONFIG | Kubernetes Extra Manifests +################################### + +## a list of extra Kubernetes manifests that will be deployed alongside the chart +## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/kubernetes/extra-manifests.md +extraManifests: [] + +################################### +## DATABASE | PgBouncer +################################### +pgbouncer: + ## if the pgbouncer Deployment is created + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/database/pgbouncer.md + enabled: true + + ## resource requests/limits for the pgbouncer Pods + ## [SPEC] https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core + resources: {} + + ## sets pgbouncer config: `auth_type` + authType: md5 + +################################### +## DATABASE | Embedded Postgres +################################### +postgresql: + ## if the `stable/postgresql` chart is used + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/database/embedded-database.md + ## [WARNING] the embedded Postgres is NOT SUITABLE for production deployments of Airflow + ## [WARNING] consider using an external database with `externalDatabase.*` + enabled: true + + ## configs for the PVC of postgresql + persistence: + enabled: true + storageClass: "" + size: 8Gi + +################################### +## DATABASE | External Database +################################### +externalDatabase: + ## the type of external database + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/database/external-database.md + type: postgres + +################################### +## DATABASE | Embedded Redis +################################### +redis: + ## if the `stable/redis` chart is used + enabled: false + +################################### +## DATABASE | External Redis +################################### +externalRedis: + ## the host of the external redis + ## [FAQ] https://github.com/airflow-helm/charts/blob/main/charts/airflow/docs/faq/database/external-redis.md + host: localhost \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/templates/NOTES.txt b/charts/deps/charts/airflow-8.8.0/templates/NOTES.txt new file mode 100644 index 0000000..86ca996 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/NOTES.txt @@ -0,0 +1,229 @@ +{{- /* if remote_logging has been enabled by the user */ -}} +{{- $remote_logging_enabled := false }} +{{- $remote_logging_envvars := list "AIRFLOW__CORE__REMOTE_LOGGING" "AIRFLOW__LOGGING__REMOTE_LOGGING" }} +{{- range $key, $val := .Values.airflow.config }} + {{- if has $key $remote_logging_envvars }} + {{- $remote_logging_enabled = true }} + {{- end }} +{{- end }} +{{- range $env := .Values.airflow.extraEnv }} + {{- if has $env.name $remote_logging_envvars }} + {{- $remote_logging_enabled = true }} + {{- end }} +{{- end }} + +{{- /* if an extra volume has been mounted for worker logs */ -}} +{{- $extra_volumes_worker_logs := false }} +{{- if include "airflow.extraVolumeMounts.has_log_path" . }} +{{- $extra_volumes_worker_logs = true }} +{{- end }} +{{- if .Values.workers.enabled }} + {{- if include "airflow._has_logs_path" (dict "Values" .Values "volume_mounts" .Values.workers.extraVolumeMounts) }} + {{- $extra_volumes_worker_logs = true }} + {{- end }} +{{- end }} +{{- if and (include "airflow.executor.kubernetes_like" .) (not .Values.airflow.kubernetesPodTemplate.stringOverride) }} + {{- if include "airflow._has_logs_path" (dict "Values" .Values "volume_mounts" .Values.airflow.kubernetesPodTemplate.extraVolumeMounts) }} + {{- $extra_volumes_worker_logs = true }} + {{- end }} +{{- end }} + +{{- /* if we show the fernet_key warning */ -}} +{{- $fernet_key_warning := true }} +{{- $fernet_key_envvars := list "AIRFLOW__CORE__FERNET_KEY" "AIRFLOW__CORE__FERNET_KEY_CMD" "AIRFLOW__CORE__FERNET_KEY_SECRET" }} +{{- if not (eq .Values.airflow.fernetKey "7T512UXSSmBOkpWimFHIVb8jK6lfmSAvx4mO6Arehnc=") }} +{{- $fernet_key_warning = false }} +{{- end }} +{{- range $key, $val := .Values.airflow.config }} + {{- if has $key $fernet_key_envvars }} + {{- $fernet_key_warning = false }} + {{- end }} +{{- end }} +{{- range $env := .Values.airflow.extraEnv }} + {{- if has $env.name $fernet_key_envvars }} + {{- $fernet_key_warning = false }} + {{- end }} +{{- end }} + +{{- /* if we show the webserver secret_key warning */ -}} +{{- $web_secret_warning := true }} +{{- $web_secret_envvars := list "AIRFLOW__WEBSERVER__SECRET_KEY" "AIRFLOW__WEBSERVER__SECRET_KEY_CMD" "AIRFLOW__WEBSERVER__SECRET_KEY_SECRET" }} +{{- if not (eq .Values.airflow.webserverSecretKey "THIS IS UNSAFE!") }} +{{- $web_secret_warning = false }} +{{- end }} +{{- range $key, $val := .Values.airflow.config }} + {{- if has $key $web_secret_envvars }} + {{- $web_secret_warning = false }} + {{- end }} +{{- end }} +{{- range $env := .Values.airflow.extraEnv }} + {{- if has $env.name $web_secret_envvars }} + {{- $web_secret_warning = false }} + {{- end }} +{{- end }} + +{{- /* if we show the external database password warning */ -}} +{{- $external_database_password_warning := false }} +{{- if and (not .Values.postgresql.enabled) (not .Values.externalDatabase.passwordSecret) }} + {{- /* don't show a warning if the password is empty */ -}} + {{- if .Values.externalDatabase.password }} + {{- $external_database_password_warning = true }} + {{- end }} +{{- end }} + +{{- /* if we show the external redis password warning */ -}} +{{- $external_redis_password_warning := false }} +{{- if and (not .Values.redis.enabled) (not .Values.externalRedis.passwordSecret) }} + {{- /* don't show a warning if the password is empty */ -}} + {{- if .Values.externalRedis.password }} + {{- $external_redis_password_warning = true }} + {{- end }} +{{- end }} + +{{- /* if we show the extraPipPackages warning */ -}} +{{- $extra_pip_warning := false }} +{{- if .Values.airflow.extraPipPackages }} +{{- $extra_pip_warning = true }} +{{- else if .Values.airflow.kubernetesPodTemplate.extraPipPackages }} +{{- $extra_pip_warning = true }} +{{- else if .Values.scheduler.extraPipPackages }} +{{- $extra_pip_warning = true }} +{{- else if .Values.web.extraPipPackages }} +{{- $extra_pip_warning = true }} +{{- else if .Values.workers.extraPipPackages }} +{{- $extra_pip_warning = true }} +{{- else if .Values.flower.extraPipPackages }} +{{- $extra_pip_warning = true }} +{{- end }} + +{{- /* if we show the embedded postgres warning */ -}} +{{- $embedded_postgres_warning := false }} +{{- if .Values.postgresql.enabled }} +{{- $embedded_postgres_warning = true }} +{{- end }} + +{{- /* if we show the git-sync known_hosts warning */ -}} +{{- $known_host_warning := false }} +{{- if and (.Values.dags.gitSync.enabled) (.Values.dags.gitSync.sshSecret) (not .Values.dags.gitSync.sshKnownHosts) }} +{{- $known_host_warning = true }} +{{- end }} + +{{- /* if we show the scheduler livenessProbe warning */ -}} +{{- $scheduler_livenessProbe_warning := false }} +{{- if not .Values.scheduler.livenessProbe.enabled }} +{{- $scheduler_livenessProbe_warning = true }} +{{- end }} + +{{- /* if we show the scheduler livenessProbe taskCreationCheck warning */ -}} +{{- $scheduler_livenessProbe_taskCreationCheck_warning := false }} +{{- if and (.Values.scheduler.livenessProbe.enabled) (not .Values.scheduler.livenessProbe.taskCreationCheck.enabled) }} +{{- $scheduler_livenessProbe_taskCreationCheck_warning = true }} +{{- end }} + +======================================================================== +Thanks for deploying Apache Airflow with the User-Community Helm Chart! + +==================== + TIPS +==================== +{{- range $user := .Values.airflow.users }} +{{- if and (eq $user.username "admin") (eq $user.password "admin") }} +Default Airflow Webserver login: + * Username: admin + * Password: admin +{{- end }} +{{ end }} + +{{- if not (or .Values.logs.persistence.enabled $remote_logging_enabled $extra_volumes_worker_logs) }} +You have NOT set up persistence for worker task logs, do this by: + 1. Using a PersistentVolumeClaim with `logs.persistence.*` + 2. Using remote logging with `AIRFLOW__LOGGING__REMOTE_LOGGING` +{{ end }} + +{{- if and (not .Values.ingress.enabled) (eq .Values.web.service.type "ClusterIP") }} +It looks like you have NOT exposed the Airflow Webserver, do this by: + 1. Using a Kubernetes Ingress with `ingress.*` + 2. Using a Kubernetes LoadBalancer/NodePort type Service with `web.service.type` +{{ end }} + +{{- if .Values.ingress.enabled }} +Your Kubernetes Ingress endpoint URLs: + * Airflow Webserver: http{{ if .Values.ingress.web.tls.enabled }}s{{ end }}://{{ .Values.ingress.web.host }}{{ .Values.ingress.web.path }}/ +{{- if .Values.flower.enabled }} + * Flower Dashboard: http{{ if .Values.ingress.flower.tls.enabled }}s{{ end }}://{{ .Values.ingress.flower.host }}{{ .Values.ingress.flower.path }}/ +{{- end }} +{{ end }} + +{{- if eq .Values.web.service.type "LoadBalancer" }} +You deployed a "LoadBalancer" type Service for the Airflow Webserver: + * External IP: kubectl get svc/{{ include "airflow.fullname" . }}-web --namespace {{ .Release.Namespace }} -o jsonpath="{.status.loadBalancer.ingress[0].ip}" + * External Port: {{ .Values.web.service.externalPort }} + * NOTE: it may take a few minutes for the External IP to be provisioned by your cloud provider +{{ end }} + +{{- if eq .Values.web.service.type "NodePort" }} +You deployed a "NodePort" type Service for the Airflow Webserver: + * Node IP: kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}" + * Node Port: kubectl get svc/{{ include "airflow.fullname" . }}-web --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" + * NOTE: any Node's external IP will work +{{ end }} + +{{- if true }} +Use these commands to port-forward the Services to your localhost: + * Airflow Webserver: kubectl port-forward svc/{{ include "airflow.fullname" . }}-web 8080:8080 --namespace {{ .Release.Namespace }} +{{- if .Values.flower.enabled }} + * Flower Dashboard: kubectl port-forward svc/{{ include "airflow.fullname" . }}-flower 5555:5555 --namespace {{ .Release.Namespace }} +{{- end }} +{{ end }} + +{{- if or ($fernet_key_warning) ($web_secret_warning) ($extra_pip_warning) ($embedded_postgres_warning) ($known_host_warning) }} +==================== + WARNINGS +==================== +{{- if $fernet_key_warning }} +[CRITICAL] default fernet encryption key value! + * HELP: set a custom value with `airflow.fernetKey` or `AIRFLOW__CORE__FERNET_KEY` +{{ end }} + +{{- if $web_secret_warning }} +[CRITICAL] default webserver secret_key value! + * HELP: set a custom value with `airflow.webserverSecretKey` or `AIRFLOW__WEBSERVER__SECRET_KEY` +{{ end }} + +{{- if $external_database_password_warning }} +[CRITICAL] external database password is set using plain-text value! + * HELP: create a Kubernetes secret with your database password and configure `externalDatabase.passwordSecret` +{{ end }} + +{{- if $external_redis_password_warning }} +[CRITICAL] external redis password is set using plain-text value! + * HELP: create a Kubernetes secret with your redis password and configure `externalRedis.passwordSecret` +{{ end }} + +{{- if $extra_pip_warning }} +[HIGH] using `extraPipPackages` can cause unexpected runtime errors if external PyPi packages change between Pod restarts + * HELP: create a Docker image with your pip packages installed and use it with `airflow.image.*` +{{ end }} + +{{- if $embedded_postgres_warning }} +[HIGH] using the embedded postgres database is NOT suitable for production! + * HELP: use an external postgres/mysql database with `externalDatabase.*` +{{ end }} + +{{- if $known_host_warning }} +[HIGH] git-sync ssh known_hosts verification is disabled! + * HELP: set `dags.gitSync.sshKnownHosts` with the ssh fingerprint of your git host +{{ end }} + +{{- if $scheduler_livenessProbe_warning }} +[MEDIUM] the scheduler liveness probe is disabled, the scheduler may not be restarted if it becomes unhealthy! + * HELP: enable the probe with `scheduler.livenessProbe.enabled` +{{ end }} + +{{- if $scheduler_livenessProbe_taskCreationCheck_warning }} +[MEDIUM] the scheduler "task creation check" is disabled, the scheduler may not be restarted if it deadlocks! + * HELP: configure the check with `scheduler.livenessProbe.taskCreationCheck.*` +{{ end }} + +{{- end }} +======================================================================== \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/templates/_helpers/common.tpl b/charts/deps/charts/airflow-8.8.0/templates/_helpers/common.tpl new file mode 100644 index 0000000..df2d8ad --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/_helpers/common.tpl @@ -0,0 +1,186 @@ +{{/* +Construct the base name for all resources in this chart. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +*/}} +{{- define "airflow.fullname" -}} +{{- printf "%s" .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +The version of airflow being deployed. +- extracted from the image tag (only for images in airflow's official DockerHub repo) +- always in `XX.XX.XX` format (ignores any pre-release suffixes) +- empty if no version can be extracted +*/}} +{{- define "airflow.image.version" -}} +{{- if eq .Values.airflow.image.repository "apache/airflow" -}} +{{- regexFind `^[0-9]+\.[0-9]+\.[0-9]+` .Values.airflow.image.tag -}} +{{- end -}} +{{- end -}} + +{{/* +Construct the `labels.app` for used by all resources in this chart. +*/}} +{{- define "airflow.labels.app" -}} +{{- printf "%s" .Chart.Name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Construct the `labels.chart` for used by all resources in this chart. +*/}} +{{- define "airflow.labels.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Construct the name of the airflow ServiceAccount. +*/}} +{{- define "airflow.serviceAccountName" -}} +{{- if .Values.serviceAccount.create -}} +{{- .Values.serviceAccount.name | default (include "airflow.fullname" .) -}} +{{- else -}} +{{- .Values.serviceAccount.name | default "default" -}} +{{- end -}} +{{- end -}} + +{{/* +A flag indicating if a celery-like executor is selected (empty if false) +*/}} +{{- define "airflow.executor.celery_like" -}} +{{- if or (eq .Values.airflow.executor "CeleryExecutor") (eq .Values.airflow.executor "CeleryKubernetesExecutor") -}} +true +{{- end -}} +{{- end -}} + +{{/* +A flag indicating if a kubernetes-like executor is selected (empty if false) +*/}} +{{- define "airflow.executor.kubernetes_like" -}} +{{- if or (eq .Values.airflow.executor "KubernetesExecutor") (eq .Values.airflow.executor "CeleryKubernetesExecutor") -}} +true +{{- end -}} +{{- end -}} + +{{/* +The scheme (HTTP, HTTPS) used by the webserver +*/}} +{{- define "airflow.web.scheme" -}} +{{- if and (.Values.airflow.config.AIRFLOW__WEBSERVER__WEB_SERVER_SSL_CERT) (.Values.airflow.config.AIRFLOW__WEBSERVER__WEB_SERVER_SSL_KEY) -}} +HTTPS +{{- else -}} +HTTP +{{- end -}} +{{- end -}} + +{{/* +The path containing DAG files +*/}} +{{- define "airflow.dags.path" -}} +{{- if .Values.dags.gitSync.enabled -}} +{{- printf "%s/repo/%s" (.Values.dags.path | trimSuffix "/") (.Values.dags.gitSync.repoSubPath | trimAll "/") -}} +{{- else -}} +{{- printf .Values.dags.path -}} +{{- end -}} +{{- end -}} + +{{/* +Helper template which checks if `logs.path` is stored under any of the passed `volumeMounts`. +EXAMPLE USAGE: {{ include "airflow._has_logs_path" (dict "Values" .Values "volume_mounts" .Values.xxxx.extraVolumeMounts) }} +*/}} +{{- define "airflow._has_logs_path" -}} +{{- $has_logs_path := false -}} +{{- /* loop over `.volume_mounts`, checking if `mountPath` is a prefix of `logs.path` */ -}} +{{- range .volume_mounts -}} + {{- if .mountPath }} + {{- if hasPrefix (clean .mountPath) (clean $.Values.logs.path) -}} + {{- $has_logs_path = true -}} + {{- end -}} + {{- end -}} +{{- end -}} +{{- if $has_logs_path -}} +true +{{- end -}} +{{- end -}} + +{{/* +If `logs.path` is stored under any of the global `airflow.extraVolumeMounts` mounts. +*/}} +{{- define "airflow.extraVolumeMounts.has_log_path" -}} +{{- include "airflow._has_logs_path" (dict "Values" .Values "volume_mounts" .Values.airflow.extraVolumeMounts) -}} +{{- end -}} + +{{/* +If `logs.path` is stored under any of the `scheduler.extraVolumeMounts` mounts. +*/}} +{{- define "airflow.scheduler.extraVolumeMounts.has_log_path" -}} +{{- include "airflow._has_logs_path" (dict "Values" .Values "volume_mounts" .Values.scheduler.extraVolumeMounts) -}} +{{- end -}} + +{{/* +If `logs.path` is stored under any of the `workers.extraVolumeMounts` mounts. +*/}} +{{- define "airflow.workers.extraVolumeMounts.has_log_path" -}} +{{- include "airflow._has_logs_path" (dict "Values" .Values "volume_mounts" .Values.workers.extraVolumeMounts) -}} +{{- end -}} + +{{/* +If the airflow triggerer should be used. +*/}} +{{- define "airflow.triggerer.should_use" -}} +{{- if .Values.triggerer.enabled -}} +{{- if not .Values.airflow.legacyCommands -}} +{{- if include "airflow.image.version" . -}} +{{- if semverCompare ">=2.2.0" (include "airflow.image.version" .) -}} +true +{{- end -}} +{{- else -}} +true +{{- end -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +If PgBouncer should be used. +*/}} +{{- define "airflow.pgbouncer.should_use" -}} +{{- if .Values.pgbouncer.enabled -}} +{{- if or (.Values.postgresql.enabled) (eq .Values.externalDatabase.type "postgres") -}} +true +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Construct the `postgresql.fullname` of the postgresql sub-chat chart. +Used to discover the Service and Secret name created by the sub-chart. +*/}} +{{- define "airflow.postgresql.fullname" -}} +{{- if .Values.postgresql.fullnameOverride -}} +{{- .Values.postgresql.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default "postgresql" .Values.postgresql.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Construct the `redis.fullname` of the redis sub-chat chart. +Used to discover the master Service and Secret name created by the sub-chart. +*/}} +{{- define "airflow.redis.fullname" -}} +{{- if .Values.redis.fullnameOverride -}} +{{- .Values.redis.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default "redis" .Values.redis.nameOverride -}} +{{- if contains $name .Release.Name -}} +{{- .Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/deps/charts/airflow-8.8.0/templates/_helpers/pods.tpl b/charts/deps/charts/airflow-8.8.0/templates/_helpers/pods.tpl new file mode 100644 index 0000000..3cacda2 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/_helpers/pods.tpl @@ -0,0 +1,588 @@ +{{/* +Define the image configs for airflow containers +*/}} +{{- define "airflow.image" }} +image: {{ .Values.airflow.image.repository }}:{{ .Values.airflow.image.tag }} +imagePullPolicy: {{ .Values.airflow.image.pullPolicy }} +securityContext: + runAsUser: {{ .Values.airflow.image.uid }} + runAsGroup: {{ .Values.airflow.image.gid }} +{{- end }} + +{{/* +Define the command/entrypoint configs for airflow containers +*/}} +{{- define "airflow.command" }} +- "/usr/bin/dumb-init" +- "--" +{{- /* only use `/entrypoint` for airflow 2.0+ (older images dont pass "bash" & "python") */ -}} +{{- if not .Values.airflow.legacyCommands }} +- "/entrypoint" +{{- end }} +{{- end }} + +{{/* +Define the nodeSelector for airflow pods +EXAMPLE USAGE: {{ include "airflow.nodeSelector" (dict "Release" .Release "Values" .Values "nodeSelector" $nodeSelector) }} +*/}} +{{- define "airflow.podNodeSelector" }} +{{- .nodeSelector | default .Values.airflow.defaultNodeSelector | toYaml }} +{{- end }} + +{{/* +Define the Affinity for airflow pods +EXAMPLE USAGE: {{ include "airflow.podAffinity" (dict "Release" .Release "Values" .Values "affinity" $affinity) }} +*/}} +{{- define "airflow.podAffinity" }} +{{- .affinity | default .Values.airflow.defaultAffinity | toYaml }} +{{- end }} + +{{/* +Define the Tolerations for airflow pods +EXAMPLE USAGE: {{ include "airflow.podTolerations" (dict "Release" .Release "Values" .Values "tolerations" $tolerations) }} +*/}} +{{- define "airflow.podTolerations" }} +{{- .tolerations | default .Values.airflow.defaultTolerations | toYaml }} +{{- end }} + +{{/* +Define the PodSecurityContext for airflow pods +EXAMPLE USAGE: {{ include "airflow.podSecurityContext" (dict "Release" .Release "Values" .Values "securityContext" $securityContext) }} +*/}} +{{- define "airflow.podSecurityContext" }} +{{- .securityContext | default .Values.airflow.defaultSecurityContext | toYaml }} +{{- end }} + +{{/* +Define an init-container which checks the DB status +EXAMPLE USAGE: {{ include "airflow.init_container.check_db" (dict "Release" .Release "Values" .Values "volumeMounts" $volumeMounts) }} +*/}} +{{- define "airflow.init_container.check_db" }} +- name: check-db + {{- include "airflow.image" . | indent 2 }} + envFrom: + {{- include "airflow.envFrom" . | indent 4 }} + env: + {{- include "airflow.env" . | indent 4 }} + command: + {{- include "airflow.command" . | indent 4 }} + args: + - "bash" + - "-c" + {{- if .Values.airflow.legacyCommands }} + - "exec timeout 60s airflow checkdb" + {{- else }} + - "exec timeout 60s airflow db check" + {{- end }} + {{- if .volumeMounts }} + volumeMounts: + {{- .volumeMounts | indent 4 }} + {{- end }} +{{- end }} + +{{/* +Define an init-container which waits for DB migrations +EXAMPLE USAGE: {{ include "airflow.init_container.wait_for_db_migrations" (dict "Release" .Release "Values" .Values "volumeMounts" $volumeMounts) }} +*/}} +{{- define "airflow.init_container.wait_for_db_migrations" }} +- name: wait-for-db-migrations + {{- include "airflow.image" . | indent 2 }} + envFrom: + {{- include "airflow.envFrom" . | indent 4 }} + env: + {{- include "airflow.env" . | indent 4 }} + command: + {{- include "airflow.command" . | indent 4 }} + args: + {{- if .Values.airflow.legacyCommands }} + - "python" + - "-c" + - | + import logging + import os + import time + + import airflow + from airflow import settings + + # modified from https://github.com/apache/airflow/blob/2.1.0/airflow/utils/db.py#L583-L592 + def _get_alembic_config(): + from alembic.config import Config + + package_dir = os.path.abspath(os.path.dirname(airflow.__file__)) + directory = os.path.join(package_dir, 'migrations') + config = Config(os.path.join(package_dir, 'alembic.ini')) + config.set_main_option('script_location', directory.replace('%', '%%')) + config.set_main_option('sqlalchemy.url', settings.SQL_ALCHEMY_CONN.replace('%', '%%')) + return config + + # copied from https://github.com/apache/airflow/blob/2.1.0/airflow/utils/db.py#L595-L622 + def check_migrations(timeout): + """ + Function to wait for all airflow migrations to complete. + :param timeout: Timeout for the migration in seconds + :return: None + """ + from alembic.runtime.migration import MigrationContext + from alembic.script import ScriptDirectory + + config = _get_alembic_config() + script_ = ScriptDirectory.from_config(config) + with settings.engine.connect() as connection: + context = MigrationContext.configure(connection) + ticker = 0 + while True: + source_heads = set(script_.get_heads()) + db_heads = set(context.get_current_heads()) + if source_heads == db_heads: + break + if ticker >= timeout: + raise TimeoutError( + f"There are still unapplied migrations after {ticker} seconds. " + f"Migration Head(s) in DB: {db_heads} | Migration Head(s) in Source Code: {source_heads}" + ) + ticker += 1 + time.sleep(1) + logging.info('Waiting for migrations... %s second(s)', ticker) + + check_migrations(60) + {{- else }} + - "bash" + - "-c" + - "exec airflow db check-migrations -t 60" + {{- end }} + {{- if .volumeMounts }} + volumeMounts: + {{- .volumeMounts | indent 4 }} + {{- end }} +{{- end }} + +{{/* +Define an init-container which installs a list of pip packages +EXAMPLE USAGE: {{ include "airflow.init_container.install_pip_packages" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages) }} +*/}} +{{- define "airflow.init_container.install_pip_packages" }} +- name: install-pip-packages + {{- include "airflow.image" . | indent 2 }} + envFrom: + {{- include "airflow.envFrom" . | indent 4 }} + env: + {{- include "airflow.env" . | indent 4 }} + command: + {{- include "airflow.command" . | indent 4 }} + args: + - "bash" + - "-c" + - | + unset PYTHONUSERBASE && \ + pip freeze | grep -i {{ range .Values.airflow.protectedPipPackages }}-e {{ printf "%s==" . | quote }} {{ end }} > protected-packages.txt && \ + pip install --constraint ./protected-packages.txt --user {{ range .extraPipPackages }}{{ . | quote }} {{ end }} && \ + echo "copying '/home/airflow/.local/' to '/opt/home-airflow-local/.local/'..." && \ + rsync -ah --stats --delete /home/airflow/.local/ /opt/home-airflow-local/.local/ + volumeMounts: + - name: home-airflow-local + mountPath: /opt/home-airflow-local +{{- end }} + +{{/* +Define a container which regularly syncs a git-repo +EXAMPLE USAGE: {{ include "airflow.container.git_sync" (dict "Release" .Release "Values" .Values "sync_one_time" "true") }} +*/}} +{{- define "airflow.container.git_sync" }} +{{- if .sync_one_time }} +- name: dags-git-clone +{{- else }} +- name: dags-git-sync +{{- end }} + image: {{ .Values.dags.gitSync.image.repository }}:{{ .Values.dags.gitSync.image.tag }} + imagePullPolicy: {{ .Values.dags.gitSync.image.pullPolicy }} + securityContext: + runAsUser: {{ .Values.dags.gitSync.image.uid }} + runAsGroup: {{ .Values.dags.gitSync.image.gid }} + resources: + {{- toYaml .Values.dags.gitSync.resources | nindent 4 }} + envFrom: + {{- include "airflow.envFrom" . | indent 4 }} + env: + {{- if .sync_one_time }} + - name: GIT_SYNC_ONE_TIME + value: "true" + {{- end }} + - name: GIT_SYNC_ROOT + value: "/dags" + - name: GIT_SYNC_DEST + value: "repo" + - name: GIT_SYNC_REPO + value: {{ .Values.dags.gitSync.repo | quote }} + - name: GIT_SYNC_BRANCH + value: {{ .Values.dags.gitSync.branch | quote }} + - name: GIT_SYNC_REV + value: {{ .Values.dags.gitSync.revision | quote }} + - name: GIT_SYNC_DEPTH + value: {{ .Values.dags.gitSync.depth | quote }} + - name: GIT_SYNC_WAIT + value: {{ .Values.dags.gitSync.syncWait | quote }} + - name: GIT_SYNC_TIMEOUT + value: {{ .Values.dags.gitSync.syncTimeout | quote }} + - name: GIT_SYNC_ADD_USER + value: "true" + - name: GIT_SYNC_MAX_SYNC_FAILURES + value: {{ .Values.dags.gitSync.maxFailures | quote }} + - name: GIT_SYNC_SUBMODULES + value: {{ .Values.dags.gitSync.submodules | quote }} + {{- if .Values.dags.gitSync.sshSecret }} + - name: GIT_SYNC_SSH + value: "true" + - name: GIT_SSH_KEY_FILE + value: "/etc/git-secret/id_rsa" + {{- end }} + {{- if .Values.dags.gitSync.sshKnownHosts }} + - name: GIT_KNOWN_HOSTS + value: "true" + - name: GIT_SSH_KNOWN_HOSTS_FILE + value: "/etc/git-secret/known_hosts" + {{- else }} + - name: GIT_KNOWN_HOSTS + value: "false" + {{- end }} + {{- if .Values.dags.gitSync.httpSecret }} + - name: GIT_SYNC_USERNAME + valueFrom: + secretKeyRef: + name: {{ .Values.dags.gitSync.httpSecret }} + key: {{ .Values.dags.gitSync.httpSecretUsernameKey }} + - name: GIT_SYNC_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .Values.dags.gitSync.httpSecret }} + key: {{ .Values.dags.gitSync.httpSecretPasswordKey }} + {{- end }} + {{- /* this has user-defined variables, so must be included BELOW (so the ABOVE `env` take precedence) */ -}} + {{- include "airflow.env" . | indent 4 }} + volumeMounts: + - name: dags-data + mountPath: /dags + {{- if .Values.dags.gitSync.sshSecret }} + - name: git-secret + mountPath: /etc/git-secret/id_rsa + readOnly: true + subPath: {{ .Values.dags.gitSync.sshSecretKey }} + {{- end }} + {{- if .Values.dags.gitSync.sshKnownHosts }} + - name: git-known-hosts + mountPath: /etc/git-secret/known_hosts + readOnly: true + subPath: known_hosts + {{- end }} +{{- end }} + +{{/* +Define a container which regularly deletes airflow logs older than a retention period. +EXAMPLE USAGE: {{ include "airflow.container.log_cleanup" (dict "Release" .Release "Values" .Values "resources" $lc_resources "retention_min" $lc_retention_min "interval_sec" $lc_interval_sec) }} +*/}} +{{- define "airflow.container.log_cleanup" }} +- name: log-cleanup + {{- include "airflow.image" . | indent 2 }} + resources: + {{- toYaml .resources | nindent 4 }} + envFrom: + {{- include "airflow.envFrom" . | indent 4 }} + env: + - name: LOG_PATH + value: {{ .Values.logs.path | quote }} + - name: RETENTION_MINUTES + value: {{ .retention_min | quote }} + - name: INTERVAL_SECONDS + value: {{ .interval_sec | quote }} + {{- /* this has user-defined variables, so must be included BELOW (so the ABOVE `env` take precedence) */ -}} + {{- include "airflow.env" . | indent 4 }} + command: + {{- include "airflow.command" . | indent 4 }} + args: + - "bash" + - "-c" + - | + set -euo pipefail + + # break the infinite loop when we receive SIGINT or SIGTERM + trap "exit 0" SIGINT SIGTERM + + while true; do + START_EPOCH=$(date --utc +%s) + echo "[$(date --utc +%FT%T.%3N)] deleting log files older than $RETENTION_MINUTES minutes..." + + # delete all writable files ending in ".log" with modified-time older than $RETENTION_MINUTES + # NOTE: `-printf "."` prints a "." for each deleted file, which we count the bytes of with `wc -c` + DELETED_COUNT=$( + find "$LOG_PATH" \ + -type f \ + -name "*.log" \ + -mmin +"$RETENTION_MINUTES" \ + -writable \ + -delete \ + -printf "." \ + | wc -c + ) + + END_EPOCH=$(date --utc +%s) + LOOP_DURATION=$((END_EPOCH - START_EPOCH)) + echo "[$(date --utc +%FT%T.%3N)] deleted $DELETED_COUNT files in $LOOP_DURATION seconds" + + SECONDS_TO_SLEEP=$((INTERVAL_SECONDS - LOOP_DURATION)) + if (( SECONDS_TO_SLEEP > 0 )); then + echo "[$(date --utc +%FT%T.%3N)] waiting $SECONDS_TO_SLEEP seconds..." + sleep $SECONDS_TO_SLEEP + fi + done + volumeMounts: + - name: logs-data + mountPath: {{ .Values.logs.path }} +{{- end }} + +{{/* +The list of `volumeMounts` for web/scheduler/worker/flower container +EXAMPLE USAGE: {{ include "airflow.volumeMounts" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages "extraVolumeMounts" $extraVolumeMounts) }} +*/}} +{{- define "airflow.volumeMounts" }} +{{- /* airflow_local_settings.py */ -}} +{{- if or (.Values.airflow.localSettings.stringOverride) (.Values.airflow.localSettings.existingSecret) }} +- name: airflow-local-settings + mountPath: /opt/airflow/config/airflow_local_settings.py + subPath: airflow_local_settings.py + readOnly: true +{{- end }} + +{{- /* dags */ -}} +{{- if .Values.dags.persistence.enabled }} +- name: dags-data + mountPath: {{ .Values.dags.path }} + subPath: {{ .Values.dags.persistence.subPath }} + {{- if eq .Values.dags.persistence.accessMode "ReadOnlyMany" }} + readOnly: true + {{- end }} +{{- else if .Values.dags.gitSync.enabled }} +- name: dags-data + mountPath: {{ .Values.dags.path }} +{{- end }} + +{{- /* logs */ -}} +{{- if include "airflow.extraVolumeMounts.has_log_path" . }} +{{- /* when `airflow.extraVolumeMounts` has `logs.path`, we dont need a "logs-data" volume mount */ -}} +{{- else if include "airflow._has_logs_path" (dict "Values" .Values "volume_mounts" .extraVolumeMounts) }} +{{- /* when `.extraVolumeMounts` has `logs.path`, we dont need a "logs-data" volume mount */ -}} +{{- else if .Values.logs.persistence.enabled }} +- name: logs-data + mountPath: {{ .Values.logs.path }} + subPath: {{ .Values.logs.persistence.subPath }} +{{- else }} +- name: logs-data + mountPath: {{ .Values.logs.path }} +{{- end }} + +{{- /* pip-packages */ -}} +{{- if .extraPipPackages }} +- name: home-airflow-local + mountPath: /home/airflow/.local + subPath: .local +{{- end }} + +{{- /* user-defined (global) */ -}} +{{- if .Values.airflow.extraVolumeMounts }} +{{ toYaml .Values.airflow.extraVolumeMounts }} +{{- end }} + +{{- /* user-defined */ -}} +{{- if .extraVolumeMounts }} +{{ toYaml .extraVolumeMounts }} +{{- end }} +{{- end }} + +{{/* +The list of `volumes` for web/scheduler/worker/flower Pods +EXAMPLE USAGE: {{ include "airflow.volumes" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages "extraVolumes" $extraVolumes "extraVolumeMounts" $extraVolumeMounts) }} +*/}} +{{- define "airflow.volumes" }} +{{- /* airflow_local_settings.py */ -}} +{{- if or (.Values.airflow.localSettings.stringOverride) (.Values.airflow.localSettings.existingSecret) }} +- name: airflow-local-settings + secret: + {{- if .Values.airflow.localSettings.existingSecret }} + secretName: {{ .Values.airflow.localSettings.existingSecret }} + {{- else }} + secretName: {{ include "airflow.fullname" . }}-local-settings + {{- end }} + defaultMode: 0644 +{{- end }} + +{{- /* dags */ -}} +{{- if .Values.dags.persistence.enabled }} +- name: dags-data + persistentVolumeClaim: + {{- if .Values.dags.persistence.existingClaim }} + claimName: {{ .Values.dags.persistence.existingClaim }} + {{- else }} + claimName: {{ printf "%s-dags" (include "airflow.fullname" . | trunc 58) }} + {{- end }} +{{- else if .Values.dags.gitSync.enabled }} +- name: dags-data + emptyDir: {} +{{- end }} + +{{- /* logs */ -}} +{{- if include "airflow.extraVolumeMounts.has_log_path" . }} +{{- /* when `airflow.extraVolumeMounts` has `logs.path`, we dont need a "logs-data" volume */ -}} +{{- else if include "airflow._has_logs_path" (dict "Values" .Values "volume_mounts" .extraVolumeMounts) }} +{{- /* when `.extraVolumeMounts` has `logs.path`, we dont need a "logs-data" volume */ -}} +{{- else if .Values.logs.persistence.enabled }} +- name: logs-data + persistentVolumeClaim: + {{- if .Values.logs.persistence.existingClaim }} + claimName: {{ .Values.logs.persistence.existingClaim }} + {{- else }} + claimName: {{ printf "%s-logs" (include "airflow.fullname" . | trunc 58) }} + {{- end }} +{{- else }} +- name: logs-data + emptyDir: {} +{{- end }} + +{{- /* git-sync */ -}} +{{- if .Values.dags.gitSync.enabled }} +{{- if .Values.dags.gitSync.sshSecret }} +- name: git-secret + secret: + secretName: {{ .Values.dags.gitSync.sshSecret }} + defaultMode: 0644 +{{- end }} +{{- if .Values.dags.gitSync.sshKnownHosts }} +- name: git-known-hosts + secret: + secretName: {{ include "airflow.fullname" . }}-known-hosts + defaultMode: 0644 +{{- end }} +{{- end }} + +{{- /* pip-packages */ -}} +{{- if .extraPipPackages }} +- name: home-airflow-local + emptyDir: {} +{{- end }} + +{{- /* user-defined (global) */ -}} +{{- if .Values.airflow.extraVolumes }} +{{ toYaml .Values.airflow.extraVolumes }} +{{- end }} + +{{- /* user-defined */ -}} +{{- if .extraVolumes }} +{{ toYaml .extraVolumes }} +{{- end }} +{{- end }} + +{{/* +The list of `envFrom` for web/scheduler/worker/flower Pods +*/}} +{{- define "airflow.envFrom" }} +- secretRef: + name: {{ include "airflow.fullname" . }}-config-envs +{{- end }} + +{{/* +The list of `env` for web/scheduler/worker/flower Pods +EXAMPLE USAGE: {{ include "airflow.env" (dict "Release" .Release "Values" .Values "CONNECTION_CHECK_MAX_COUNT" "0") }} +*/}} +{{- define "airflow.env" }} +{{- /* set DATABASE_USER */ -}} +{{- if .Values.postgresql.enabled }} +- name: DATABASE_USER + value: {{ .Values.postgresql.postgresqlUsername | quote }} +{{- else }} +{{- if .Values.externalDatabase.userSecret }} +- name: DATABASE_USER + valueFrom: + secretKeyRef: + name: {{ .Values.externalDatabase.userSecret }} + key: {{ .Values.externalDatabase.userSecretKey }} +{{- else }} +{{- /* in this case, DATABASE_USER is set in the `-config-envs` Secret */ -}} +{{- end }} +{{- end }} + +{{- /* set DATABASE_PASSWORD */ -}} +{{- if .Values.postgresql.enabled }} +{{- if .Values.postgresql.existingSecret }} +- name: DATABASE_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .Values.postgresql.existingSecret }} + key: {{ .Values.postgresql.existingSecretKey }} +{{- else }} +- name: DATABASE_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "airflow.postgresql.fullname" . }} + key: postgresql-password +{{- end }} +{{- else }} +{{- if .Values.externalDatabase.passwordSecret }} +- name: DATABASE_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .Values.externalDatabase.passwordSecret }} + key: {{ .Values.externalDatabase.passwordSecretKey }} +{{- else }} +{{- /* in this case, DATABASE_PASSWORD is set in the `-config-envs` Secret */ -}} +{{- end }} +{{- end }} + +{{- /* set REDIS_PASSWORD */ -}} +{{- if .Values.redis.enabled }} +{{- if .Values.redis.existingSecret }} +- name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .Values.redis.existingSecret }} + key: {{ .Values.redis.existingSecretPasswordKey }} +{{- else }} +- name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "airflow.redis.fullname" . }} + key: redis-password +{{- end }} +{{- else }} +{{- if .Values.externalRedis.passwordSecret }} +- name: REDIS_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .Values.externalRedis.passwordSecret }} + key: {{ .Values.externalRedis.passwordSecretKey }} +{{- else }} +{{- /* in this case, REDIS_PASSWORD is set in the `-config-envs` Secret */ -}} +{{- end }} +{{- end }} + +{{- /* disable the `/entrypoint` db connection check */ -}} +{{- if not .Values.airflow.legacyCommands }} +- name: CONNECTION_CHECK_MAX_COUNT + {{- if .CONNECTION_CHECK_MAX_COUNT }} + value: {{ .CONNECTION_CHECK_MAX_COUNT | quote }} + {{- else }} + value: "0" + {{- end }} +{{- end }} + +{{- /* set AIRFLOW__CELERY__FLOWER_BASIC_AUTH */ -}} +{{- if .Values.flower.basicAuthSecret }} +- name: AIRFLOW__CELERY__FLOWER_BASIC_AUTH + valueFrom: + secretKeyRef: + name: {{ .Values.flower.basicAuthSecret }} + key: {{ .Values.flower.basicAuthSecretKey }} +{{- end }} + +{{- /* user-defined environment variables */ -}} +{{- if .Values.airflow.extraEnv }} +{{ toYaml .Values.airflow.extraEnv }} +{{- end }} +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/templates/_helpers/validate-values.tpl b/charts/deps/charts/airflow-8.8.0/templates/_helpers/validate-values.tpl new file mode 100644 index 0000000..9ccbd7f --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/_helpers/validate-values.tpl @@ -0,0 +1,223 @@ +{{/* Checks for `.Release.name` */}} +{{/* NOTE: `allowLongReleaseName` was added when the max length dropped from 43 to 40, and is NOT intended for new deployments */}} +{{- if .Values.allowLongReleaseName }} + {{- if gt (len .Release.Name) 43 }} + {{ required "The `.Release.name` must be <= 43 characters (due to the 63 character limit for names in Kubernetes)!" nil }} + {{- end }} +{{- else }} + {{- if gt (len .Release.Name) 40 }} + {{ required "The `.Release.name` must be <= 40 characters (due to the 63 character limit for names in Kubernetes)!" nil }} + {{- end }} +{{- end }} + +{{/* Checks for `airflow.legacyCommands` */}} +{{- if .Values.airflow.legacyCommands }} + {{- if not (eq "1" (.Values.scheduler.replicas | toString)) }} + {{ required "If `airflow.legacyCommands=true`, then `scheduler.replicas` must be set to `1`!" nil }} + {{- end }} +{{- end }} + +{{/* Checks for `airflow.image` */}} +{{- if eq .Values.airflow.image.repository "apache/airflow" }} + {{- if hasPrefix "1." .Values.airflow.image.tag }} + {{- if not .Values.airflow.legacyCommands }} + {{ required "When using airflow 1.10.X, `airflow.legacyCommands` must be `true`!" nil }} + {{- end }} + {{ end }} + {{- if hasPrefix "2." .Values.airflow.image.tag }} + {{- if .Values.airflow.legacyCommands }} + {{ required "When using airflow 2.X.X, `airflow.legacyCommands` must be `false`!" nil }} + {{- end }} + {{ end }} +{{- end }} + +{{/* Checks for `airflow.executor` */}} +{{- if not (has .Values.airflow.executor (list "CeleryExecutor" "CeleryKubernetesExecutor" "KubernetesExecutor")) }} + {{ required "The `airflow.executor` must be one of: [CeleryExecutor, CeleryKubernetesExecutor, KubernetesExecutor]!" nil }} +{{- end }} +{{- if eq .Values.airflow.executor "CeleryExecutor" }} + {{- if not .Values.workers.enabled }} + {{ required "If `airflow.executor=CeleryExecutor`, then `workers.enabled` should be `true`!" nil }} + {{- end }} +{{- end }} +{{- if eq .Values.airflow.executor "CeleryKubernetesExecutor" }} + {{- if not .Values.workers.enabled }} + {{ required "If `airflow.executor=CeleryKubernetesExecutor`, then `workers.enabled` should be `true`!" nil }} + {{- end }} +{{- end }} +{{- if eq .Values.airflow.executor "KubernetesExecutor" }} + {{- if or (.Values.workers.enabled) (.Values.flower.enabled) (.Values.redis.enabled) }} + {{ required "If `airflow.executor=KubernetesExecutor`, then all of [`workers.enabled`, `flower.enabled`, `redis.enabled`] should be `false`!" nil }} + {{- end }} +{{- end }} + +{{/* Checks for `airflow.config` */}} +{{- if .Values.airflow.config.AIRFLOW__CORE__EXECUTOR }} + {{ required "Don't define `airflow.config.AIRFLOW__CORE__EXECUTOR`, it will be automatically set from `airflow.executor`!" nil }} +{{- end }} +{{- if or .Values.airflow.config.AIRFLOW__CORE__DAGS_FOLDER }} + {{ required "Don't define `airflow.config.AIRFLOW__CORE__DAGS_FOLDER`, it will be automatically set from `dags.path`!" nil }} +{{- end }} +{{- if or (.Values.airflow.config.AIRFLOW__CELERY__BROKER_URL) (.Values.airflow.config.AIRFLOW__CELERY__BROKER_URL_CMD) }} + {{ required "Don't define `airflow.config.AIRFLOW__CELERY__BROKER_URL`, it will be automatically set by the chart!" nil }} +{{- end }} +{{- if or (.Values.airflow.config.AIRFLOW__CELERY__RESULT_BACKEND) (.Values.airflow.config.AIRFLOW__CELERY__RESULT_BACKEND_CMD) }} + {{ required "Don't define `airflow.config.AIRFLOW__CELERY__RESULT_BACKEND`, it will be automatically set by the chart!" nil }} +{{- end }} +{{- if or (.Values.airflow.config.AIRFLOW__CORE__SQL_ALCHEMY_CONN) (.Values.airflow.config.AIRFLOW__CORE__SQL_ALCHEMY_CONN_CMD) }} + {{ required "Don't define `airflow.config.AIRFLOW__CORE__SQL_ALCHEMY_CONN`, it will be automatically set by the chart!" nil }} +{{- end }} +{{- if or (.Values.airflow.config.AIRFLOW__DATABASE__SQL_ALCHEMY_CONN) (.Values.airflow.config.AIRFLOW__DATABASE__SQL_ALCHEMY_CONN_CMD) }} + {{ required "Don't define `airflow.config.AIRFLOW__DATABASE__SQL_ALCHEMY_CONN`, it will be automatically set by the chart!" nil }} +{{- end }} + +{{/* Checks for `scheduler.logCleanup` */}} +{{- if .Values.scheduler.logCleanup.enabled }} + {{- if .Values.logs.persistence.enabled }} + {{ required "If `logs.persistence.enabled=true`, then `scheduler.logCleanup.enabled` must be `false`!" nil }} + {{- end }} + {{- if include "airflow.extraVolumeMounts.has_log_path" . }} + {{ required "If `logs.path` is under any `airflow.extraVolumeMounts`, then `scheduler.logCleanup.enabled` must be `false`!" nil }} + {{- end }} + {{- if include "airflow.scheduler.extraVolumeMounts.has_log_path" . }} + {{ required "If `logs.path` is under any `scheduler.extraVolumeMounts`, then `scheduler.logCleanup.enabled` must be `false`!" nil }} + {{- end }} +{{- end }} + +{{/* Checks for `workers.logCleanup` */}} +{{- if .Values.workers.enabled }} + {{- if .Values.workers.logCleanup.enabled }} + {{- if .Values.logs.persistence.enabled }} + {{ required "If `logs.persistence.enabled=true`, then `workers.logCleanup.enabled` must be `false`!" nil }} + {{- end }} + {{- if include "airflow.extraVolumeMounts.has_log_path" . }} + {{ required "If `logs.path` is under any `airflow.extraVolumeMounts`, then `workers.logCleanup.enabled` must be `false`!" nil }} + {{- end }} + {{- if include "airflow.workers.extraVolumeMounts.has_log_path" . }} + {{ required "If `logs.path` is under any `workers.extraVolumeMounts`, then `workers.logCleanup.enabled` must be `false`!" nil }} + {{- end }} + {{- end }} +{{- end }} + +{{/* Checks for `logs.persistence` */}} +{{- if .Values.logs.persistence.enabled }} + {{- if not (eq .Values.logs.persistence.accessMode "ReadWriteMany") }} + {{ required "The `logs.persistence.accessMode` must be `ReadWriteMany`!" nil }} + {{- end }} + {{- if include "airflow.extraVolumeMounts.has_log_path" . }} + {{ required "If `logs.path` is under any `airflow.extraVolumeMounts`, then `logs.persistence.enabled` must be `false`!" nil }} + {{- end }} +{{- end }} + +{{/* Checks for `dags.persistence` */}} +{{- if .Values.dags.persistence.enabled }} + {{- if not (has .Values.dags.persistence.accessMode (list "ReadOnlyMany" "ReadWriteMany")) }} + {{ required "The `dags.persistence.accessMode` must be one of: [ReadOnlyMany, ReadWriteMany]!" nil }} + {{- end }} +{{- end }} + +{{/* Checks for `dags.gitSync` */}} +{{- if .Values.dags.gitSync.enabled }} + {{- if .Values.dags.persistence.enabled }} + {{ required "If `dags.gitSync.enabled=true`, then `persistence.enabled` must be disabled!" nil }} + {{- end }} + {{- if not .Values.dags.gitSync.repo }} + {{ required "If `dags.gitSync.enabled=true`, then `dags.gitSync.repo` must be non-empty!" nil }} + {{- end }} + {{- if and (.Values.dags.gitSync.sshSecret) (.Values.dags.gitSync.httpSecret) }} + {{ required "At most, one of `dags.gitSync.sshSecret` and `dags.gitSync.httpSecret` can be defined!" nil }} + {{- end }} + {{- if and (.Values.dags.gitSync.repo | lower | hasPrefix "git@github.com") (not .Values.dags.gitSync.sshSecret) }} + {{ required "You must define `dags.gitSync.sshSecret` when using GitHub with SSH for `dags.gitSync.repo`!" nil }} + {{- end }} +{{- end }} + +{{/* Checks for `ingress` */}} +{{- if .Values.ingress.enabled }} + {{/* Checks for `ingress.apiVersion` */}} + {{- if not (has .Values.ingress.apiVersion (list "networking.k8s.io/v1" "networking.k8s.io/v1beta1")) }} + {{ required "The `ingress.apiVersion` must be one of: [networking.k8s.io/v1, networking.k8s.io/v1beta1]!" nil }} + {{- end }} + + {{/* Checks for `ingress.web.path` */}} + {{- if .Values.ingress.web.path }} + {{- if not (.Values.ingress.web.path | hasPrefix "/") }} + {{ required "The `ingress.web.path` should start with a '/'!" nil }} + {{- end }} + {{- if .Values.ingress.web.path | hasSuffix "/" }} + {{ required "The `ingress.web.path` should NOT include a trailing '/'!" nil }} + {{- end }} + {{- if .Values.airflow.config.AIRFLOW__WEBSERVER__BASE_URL }} + {{- $webUrl := .Values.airflow.config.AIRFLOW__WEBSERVER__BASE_URL | urlParse }} + {{- if not (eq (.Values.ingress.web.path | trimSuffix "/*") (get $webUrl "path")) }} + {{ required (printf "The `ingress.web.path` must be compatible with `airflow.config.AIRFLOW__WEBSERVER__BASE_URL`! (try setting AIRFLOW__WEBSERVER__BASE_URL to 'http://{HOSTNAME}%s', rather than '%s')" (.Values.ingress.web.path | trimSuffix "/*") .Values.airflow.config.AIRFLOW__WEBSERVER__BASE_URL) nil }} + {{- end }} + {{- else }} + {{ required (printf "If `ingress.web.path` is set, then `airflow.config.AIRFLOW__WEBSERVER__BASE_URL` must be set! (try setting AIRFLOW__WEBSERVER__BASE_URL to 'http://{HOSTNAME}%s')" (.Values.ingress.web.path | trimSuffix "/*")) nil }} + {{- end }} + {{- end }} + + {{/* Checks for `ingress.flower.path` */}} + {{- if .Values.ingress.flower.path }} + {{- if not (.Values.ingress.flower.path | hasPrefix "/") }} + {{ required "The `ingress.flower.path` should start with a '/'!" nil }} + {{- end }} + {{- if .Values.ingress.flower.path | hasSuffix "/" }} + {{ required "The `ingress.flower.path` should NOT include a trailing '/'!" nil }} + {{- end }} + {{- if .Values.airflow.config.AIRFLOW__CELERY__FLOWER_URL_PREFIX }} + {{- if not (eq (.Values.ingress.flower.path | trimSuffix "/*") .Values.airflow.config.AIRFLOW__CELERY__FLOWER_URL_PREFIX) }} + {{ required (printf "The `ingress.flower.path` must be compatible with `airflow.config.AIRFLOW__CELERY__FLOWER_URL_PREFIX`! (try setting AIRFLOW__CELERY__FLOWER_URL_PREFIX to '%s', rather than '%s')" (.Values.ingress.flower.path | trimSuffix "/*") .Values.airflow.config.AIRFLOW__CELERY__FLOWER_URL_PREFIX) nil }} + {{- end }} + {{- else }} + {{ required (printf "If `ingress.flower.path` is set, then `airflow.config.AIRFLOW__CELERY__FLOWER_URL_PREFIX` must be set! (try setting AIRFLOW__CELERY__FLOWER_URL_PREFIX to '%s')" (.Values.ingress.flower.path | trimSuffix "/*")) nil }} + {{- end }} + {{- end }} +{{- end }} + +{{/* Checks for `pgbouncer` */}} +{{- if include "airflow.pgbouncer.should_use" . }} + {{- if .Values.pgbouncer.clientSSL.keyFile.existingSecret }} + {{- if not .Values.pgbouncer.clientSSL.certFile.existingSecret }} + {{ required "If `pgbouncer.clientSSL.keyFile.existingSecret` is set, then `pgbouncer.clientSSL.certFile.existingSecret` must also be!" nil }} + {{- end }} + {{- end }} + {{- if .Values.pgbouncer.clientSSL.certFile.existingSecret }} + {{- if not .Values.pgbouncer.clientSSL.keyFile.existingSecret }} + {{ required "If `pgbouncer.clientSSL.certFile.existingSecret` is set, then `pgbouncer.clientSSL.keyFile.existingSecret` must also be!" nil }} + {{- end }} + {{- end }} + {{- if .Values.pgbouncer.serverSSL.keyFile.existingSecret }} + {{- if not .Values.pgbouncer.serverSSL.certFile.existingSecret }} + {{ required "If `pgbouncer.serverSSL.keyFile.existingSecret` is set, then `pgbouncer.serverSSL.certFile.existingSecret` must also be!" nil }} + {{- end }} + {{- end }} + {{- if .Values.pgbouncer.serverSSL.certFile.existingSecret }} + {{- if not .Values.pgbouncer.serverSSL.keyFile.existingSecret }} + {{ required "If `pgbouncer.serverSSL.certFile.existingSecret` is set, then `pgbouncer.serverSSL.keyFile.existingSecret` must also be!" nil }} + {{- end }} + {{- end }} +{{- end }} + +{{/* Checks for `externalDatabase` */}} +{{- if .Values.externalDatabase.host }} + {{/* check if they are using externalDatabase (the default value for `externalDatabase.host` is "localhost") */}} + {{- if not (eq .Values.externalDatabase.host "localhost") }} + {{- if .Values.postgresql.enabled }} + {{ required "If `externalDatabase.host` is set, then `postgresql.enabled` should be `false`!" nil }} + {{- end }} + {{- if not (has .Values.externalDatabase.type (list "mysql" "postgres")) }} + {{ required "The `externalDatabase.type` must be one of: [mysql, postgres]!" nil }} + {{- end }} + {{- end }} +{{- end }} + +{{/* Checks for `externalRedis` */}} +{{- if .Values.externalRedis.host }} + {{/* check if they are using externalRedis (the default value for `externalRedis.host` is "localhost") */}} + {{- if not (eq .Values.externalRedis.host "localhost") }} + {{- if .Values.redis.enabled }} + {{ required "If `externalRedis.host` is set, then `redis.enabled` should be `false`!" nil }} + {{- end }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/templates/config/configmap-pod-template.yaml b/charts/deps/charts/airflow-8.8.0/templates/config/configmap-pod-template.yaml new file mode 100644 index 0000000..186f177 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/config/configmap-pod-template.yaml @@ -0,0 +1,18 @@ +{{- if include "airflow.executor.kubernetes_like" . }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "airflow.fullname" . }}-pod-template + labels: + app: {{ include "airflow.labels.app" . }} + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +data: + pod_template.yaml: |- + {{- if .Values.airflow.kubernetesPodTemplate.stringOverride }} + {{- .Values.airflow.kubernetesPodTemplate.stringOverride | nindent 4 }} + {{- else }} + {{- tpl (.Files.Get "files/pod_template.kubernetes-helm-yaml") . | nindent 4 }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/templates/config/secret-config-envs.yaml b/charts/deps/charts/airflow-8.8.0/templates/config/secret-config-envs.yaml new file mode 100644 index 0000000..e78a1f2 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/config/secret-config-envs.yaml @@ -0,0 +1,217 @@ +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "airflow.fullname" . }}-config-envs + labels: + app: {{ include "airflow.labels.app" . }} + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +## we must use `data` rather than `stringData` (see: https://github.com/helm/helm/issues/10010) +data: + ## ================ + ## Linux Configs + ## ================ + TZ: {{ "Etc/UTC" | b64enc | quote }} + + ## ================ + ## Database Configs + ## ================ + ## database host/port + {{- if include "airflow.pgbouncer.should_use" . }} + DATABASE_HOST: {{ printf "%s-pgbouncer.%s.svc.%s" (include "airflow.fullname" .) (.Release.Namespace) (.Values.airflow.clusterDomain) | b64enc | quote }} + DATABASE_PORT: {{ "6432" | b64enc | quote }} + {{- else if .Values.postgresql.enabled }} + DATABASE_HOST: {{ printf "%s.%s.svc.%s" (include "airflow.postgresql.fullname" .) (.Release.Namespace) (.Values.airflow.clusterDomain) | b64enc | quote }} + DATABASE_PORT: {{ "5432" | b64enc | quote }} + {{- else }} + DATABASE_HOST: {{ .Values.externalDatabase.host | toString | b64enc | quote }} + DATABASE_PORT: {{ .Values.externalDatabase.port | toString | b64enc | quote }} + {{- end }} + + ## database configs + {{- if .Values.postgresql.enabled }} + DATABASE_DB: {{ .Values.postgresql.postgresqlDatabase | toString | b64enc | quote }} + {{- else }} + DATABASE_DB: {{ .Values.externalDatabase.database | toString | b64enc | quote }} + DATABASE_PROPERTIES: {{ .Values.externalDatabase.properties | toString | b64enc | quote }} + {{- end }} + + {{- /* in other cases, these variables are set in the "airflow.env" template */ -}} + {{- if not .Values.postgresql.enabled }} + {{- if or (not .Values.externalDatabase.userSecret ) (not .Values.externalDatabase.passwordSecret) }} + + ## database credentials (from plain-text helm values) + {{- if not .Values.externalDatabase.userSecret }} + DATABASE_USER: {{ .Values.externalDatabase.user | toString | b64enc | quote }} + {{- end }} + {{- if not .Values.externalDatabase.passwordSecret }} + DATABASE_PASSWORD: {{ .Values.externalDatabase.password | toString | b64enc | quote }} + {{- end }} + {{- end }} + {{- end }} + + ## bash command which echos the URL encoded value of $DATABASE_USER + DATABASE_USER_CMD: {{ `echo "${DATABASE_USER}" | python3 -c "import urllib.parse; encoded_user = urllib.parse.quote(input()); print(encoded_user)"` | b64enc | quote }} + + ## bash command which echos the URL encoded value of $DATABASE_PASSWORD + DATABASE_PASSWORD_CMD: {{ `echo "${DATABASE_PASSWORD}" | python3 -c "import urllib.parse; encoded_pass = urllib.parse.quote(input()); print(encoded_pass)"` | b64enc | quote }} + + ## bash command which echos the DB connection string in SQLAlchemy format + {{- if .Values.postgresql.enabled }} + DATABASE_SQLALCHEMY_CMD: {{ `echo -n "postgresql+psycopg2://$(eval $DATABASE_USER_CMD):$(eval $DATABASE_PASSWORD_CMD)@${DATABASE_HOST}:${DATABASE_PORT}/${DATABASE_DB}"` | b64enc | quote }} + {{- else if eq "postgres" .Values.externalDatabase.type }} + DATABASE_SQLALCHEMY_CMD: {{ `echo -n "postgresql+psycopg2://$(eval $DATABASE_USER_CMD):$(eval $DATABASE_PASSWORD_CMD)@${DATABASE_HOST}:${DATABASE_PORT}/${DATABASE_DB}${DATABASE_PROPERTIES}"` | b64enc | quote }} + {{- else if eq "mysql" .Values.externalDatabase.type }} + DATABASE_SQLALCHEMY_CMD: {{ `echo -n "mysql+mysqldb://$(eval $DATABASE_USER_CMD):$(eval $DATABASE_PASSWORD_CMD)@${DATABASE_HOST}:${DATABASE_PORT}/${DATABASE_DB}${DATABASE_PROPERTIES}"` | b64enc | quote }} + {{- end }} + + ## bash command which echos the DB connection string in Celery result_backend format + {{- if .Values.postgresql.enabled }} + DATABASE_CELERY_CMD: {{ `echo -n "db+postgresql://$(eval $DATABASE_USER_CMD):$(eval $DATABASE_PASSWORD_CMD)@${DATABASE_HOST}:${DATABASE_PORT}/${DATABASE_DB}"` | b64enc | quote }} + {{- else if eq "postgres" .Values.externalDatabase.type }} + DATABASE_CELERY_CMD: {{ `echo -n "db+postgresql://$(eval $DATABASE_USER_CMD):$(eval $DATABASE_PASSWORD_CMD)@${DATABASE_HOST}:${DATABASE_PORT}/${DATABASE_DB}${DATABASE_PROPERTIES}"` | b64enc | quote }} + {{- else if eq "mysql" .Values.externalDatabase.type }} + DATABASE_CELERY_CMD: {{ `echo -n "db+mysql://$(eval $DATABASE_USER_CMD):$(eval $DATABASE_PASSWORD_CMD)@${DATABASE_HOST}:${DATABASE_PORT}/${DATABASE_DB}${DATABASE_PROPERTIES}"` | b64enc | quote }} + {{- end }} + + {{- if include "airflow.pgbouncer.should_use" . }} + ## bash command which echos the DB connection string in `psql` cli format + ## NOTE: uses `127.0.0.1` as the host because this is only used in the pgbouncer liveness probe + ## and minikube does not allow pods to access their own `cluster.local` service so would otherwise fail + DATABASE_PSQL_CMD: {{ `echo -n "postgresql://$(eval $DATABASE_USER_CMD):$(eval $DATABASE_PASSWORD_CMD)@127.0.0.1:${DATABASE_PORT}/${DATABASE_DB}${DATABASE_PROPERTIES}"` | b64enc | quote }} + {{- end }} + + ## ================ + ## Redis Configs + ## ================ + {{- if include "airflow.executor.celery_like" . }} + ## connection string components + {{- if .Values.redis.enabled }} + REDIS_HOST: {{ printf "%s-master.%s.svc.%s" (include "airflow.redis.fullname" .) (.Release.Namespace) (.Values.airflow.clusterDomain) | b64enc | quote }} + REDIS_PORT: {{ "6379" | b64enc | quote }} + REDIS_DBNUM: {{ "1" | b64enc | quote }} + {{- else }} + REDIS_HOST: {{ .Values.externalRedis.host | toString | b64enc | quote }} + REDIS_PORT: {{ .Values.externalRedis.port | toString | b64enc | quote }} + REDIS_DBNUM: {{ .Values.externalRedis.databaseNumber | toString | b64enc | quote }} + REDIS_PROPERTIES: {{.Values.externalRedis.properties | toString | b64enc | quote }} + {{- end }} + + {{- /* in other cases, these variables are set in the "airflow.env" template */ -}} + {{- if not .Values.redis.enabled }} + {{- if not .Values.externalRedis.passwordSecret }} + + ## credentials (from plain-text helm values) + REDIS_PASSWORD: {{ .Values.externalRedis.password | toString | b64enc | quote }} + {{- end }} + {{- end }} + + ## bash command which echos the URL encoded value of $REDIS_PASSWORD + ## NOTE: if $REDIS_PASSWORD is non-empty, prints `:${REDIS_PASSWORD}@`, else `` + REDIS_PASSWORD_CMD: {{ `echo "${REDIS_PASSWORD}" | python3 -c "import urllib.parse; encoded_pass = urllib.parse.quote(input()); print(f\":{encoded_pass}@\") if len(encoded_pass) > 0 else None"` | b64enc | quote }} + + ## bash command which echos the Redis connection string + REDIS_CONNECTION_CMD: {{ `echo -n "redis://$(eval $REDIS_PASSWORD_CMD)${REDIS_HOST}:${REDIS_PORT}/${REDIS_DBNUM}${REDIS_PROPERTIES}"` | b64enc | quote }} + {{- end }} + + ## ================ + ## Airflow Configs (General) + ## ================ + AIRFLOW__CORE__DAGS_FOLDER: {{ include "airflow.dags.path" . | b64enc | quote }} + AIRFLOW__CORE__EXECUTOR: {{ .Values.airflow.executor | toString | b64enc | quote }} + {{- if .Values.airflow.fernetKey }} + AIRFLOW__CORE__FERNET_KEY: {{ .Values.airflow.fernetKey | toString | b64enc | quote }} + {{- end }} + {{- if .Values.airflow.webserverSecretKey }} + AIRFLOW__WEBSERVER__SECRET_KEY: {{ .Values.airflow.webserverSecretKey | toString | b64enc | quote }} + {{- end }} + AIRFLOW__WEBSERVER__WEB_SERVER_PORT: {{ "8080" | b64enc | quote }} + AIRFLOW__CELERY__FLOWER_PORT: {{ "5555" | b64enc | quote }} + + {{- if and (.Values.dags.gitSync.enabled) (not .Values.airflow.config.AIRFLOW__SCHEDULER__DAG_DIR_LIST_INTERVAL) }} + ## refresh the dags folder at the same frequency as git-sync + AIRFLOW__SCHEDULER__DAG_DIR_LIST_INTERVAL: {{ .Values.dags.gitSync.syncWait | toString | b64enc | quote }} + {{- end }} + + {{- if and (.Values.airflow.legacyCommands) (not .Values.airflow.config.AIRFLOW__WEBSERVER__RBAC) }} + ## default to the RBAC UI when in legacy mode + AIRFLOW__WEBSERVER__RBAC: {{ "true" | b64enc | quote }} + {{- end }} + + ## ================ + ## Airflow Configs (Database) + ## ================ + AIRFLOW__CORE__SQL_ALCHEMY_CONN_CMD: {{ `bash -c 'eval "$DATABASE_SQLALCHEMY_CMD"'` | b64enc | quote }} + ## `core.sql_alchemy_conn` moved to `database.sql_alchemy_conn` in airflow 2.3.0 + AIRFLOW__DATABASE__SQL_ALCHEMY_CONN_CMD: {{ `bash -c 'eval "$DATABASE_SQLALCHEMY_CMD"'` | b64enc | quote }} + + ## ================ + ## Airflow Configs (Triggerer) + ## ================ + {{- if include "airflow.triggerer.should_use" . }} + {{- if not .Values.airflow.config.AIRFLOW__TRIGGERER__DEFAULT_CAPACITY }} + AIRFLOW__TRIGGERER__DEFAULT_CAPACITY: {{ .Values.triggerer.capacity | toString | b64enc | quote }} + {{- end }} + {{- end }} + + ## ================ + ## Airflow Configs (Logging) + ## ================ + {{- if .Values.airflow.legacyCommands }} + AIRFLOW__CORE__BASE_LOG_FOLDER: {{ .Values.logs.path | toString | b64enc | quote }} + AIRFLOW__CORE__DAG_PROCESSOR_MANAGER_LOG_LOCATION: {{ printf "%s/dag_processor_manager/dag_processor_manager.log" .Values.logs.path | b64enc | quote }} + {{- else }} + AIRFLOW__LOGGING__BASE_LOG_FOLDER: {{ .Values.logs.path | toString | b64enc | quote }} + AIRFLOW__LOGGING__DAG_PROCESSOR_MANAGER_LOG_LOCATION: {{ printf "%s/dag_processor_manager/dag_processor_manager.log" .Values.logs.path | b64enc | quote }} + {{- end }} + AIRFLOW__SCHEDULER__CHILD_PROCESS_LOG_DIRECTORY: {{ printf "%s/scheduler" .Values.logs.path | b64enc | quote }} + + ## ================ + ## Airflow Configs (Celery) + ## ================ + {{- if include "airflow.executor.celery_like" . }} + AIRFLOW__LOGGING__WORKER_LOG_SERVER_PORT: {{ "8793" | b64enc | quote }} + # `logging.worker_log_server_port` replaced `celery.worker_log_server_port` in airflow 2.2.0 + AIRFLOW__CELERY__WORKER_LOG_SERVER_PORT: {{ "8793" | b64enc | quote }} + AIRFLOW__CELERY__BROKER_URL_CMD: {{ `bash -c 'eval "$REDIS_CONNECTION_CMD"'` | b64enc | quote }} + AIRFLOW__CELERY__RESULT_BACKEND_CMD: {{ `bash -c 'eval "$DATABASE_CELERY_CMD"'` | b64enc | quote }} + {{- end }} + + ## ================ + ## Airflow Configs (Kubernetes) + ## ================ + {{- if include "airflow.executor.kubernetes_like" . }} + {{- if not (or .Values.airflow.config.AIRFLOW__KUBERNETES__NAMESPACE .Values.airflow.config.AIRFLOW__KUBERNETES_EXECUTOR__NAMESPACE) }} + AIRFLOW__KUBERNETES__NAMESPACE: {{ .Release.Namespace | toString | b64enc | quote }} + AIRFLOW__KUBERNETES_EXECUTOR__NAMESPACE: {{ .Release.Namespace | toString | b64enc | quote }} + {{- end }} + {{- if not (or .Values.airflow.config.AIRFLOW__KUBERNETES__WORKER_CONTAINER_REPOSITORY .Values.airflow.config.AIRFLOW__KUBERNETES_EXECUTOR__WORKER_CONTAINER_REPOSITORY) }} + AIRFLOW__KUBERNETES__WORKER_CONTAINER_REPOSITORY: {{ .Values.airflow.image.repository | toString | b64enc | quote }} + AIRFLOW__KUBERNETES_EXECUTOR__WORKER_CONTAINER_REPOSITORY: {{ .Values.airflow.image.repository | toString | b64enc | quote }} + {{- end }} + {{- if not (or .Values.airflow.config.AIRFLOW__KUBERNETES__WORKER_CONTAINER_TAG .Values.airflow.config.AIRFLOW__KUBERNETES_EXECUTOR__WORKER_CONTAINER_TAG) }} + AIRFLOW__KUBERNETES__WORKER_CONTAINER_TAG: {{ .Values.airflow.image.tag | toString | b64enc | quote }} + AIRFLOW__KUBERNETES_EXECUTOR__WORKER_CONTAINER_TAG: {{ .Values.airflow.image.tag | toString | b64enc | quote }} + {{- end }} + {{- if not (or .Values.airflow.config.AIRFLOW__KUBERNETES__POD_TEMPLATE_FILE .Values.airflow.config.AIRFLOW__KUBERNETES_EXECUTOR__POD_TEMPLATE_FILE) }} + AIRFLOW__KUBERNETES__POD_TEMPLATE_FILE: {{ "/opt/airflow/pod_templates/pod_template.yaml" | b64enc | quote }} + AIRFLOW__KUBERNETES_EXECUTOR__POD_TEMPLATE_FILE: {{ "/opt/airflow/pod_templates/pod_template.yaml" | b64enc | quote }} + {{- end }} + + {{- if .Values.airflow.legacyCommands }} + {{- if not .Values.airflow.config.AIRFLOW__KUBERNETES__ENV_FROM_CONFIGMAP_REF }} + AIRFLOW__KUBERNETES__ENV_FROM_SECRET_REF: {{ printf "%s-config" (include "airflow.fullname" .) | b64enc | quote }} + {{- end }} + {{- if (not .Values.airflow.config.AIRFLOW__KUBERNETES__WORKER_SERVICE_ACCOUNT_NAME) }} + AIRFLOW__KUBERNETES__WORKER_SERVICE_ACCOUNT_NAME: {{ include "airflow.serviceAccountName" . | b64enc | quote }} + {{- end }} + {{- end }} + {{- end }} + + ## ================ + ## User Configs + ## ================ + {{- range $k, $v := .Values.airflow.config }} + {{ $k | quote }}: {{ $v | toString | b64enc | quote }} + {{- end }} \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/templates/config/secret-known-hosts.yaml b/charts/deps/charts/airflow-8.8.0/templates/config/secret-known-hosts.yaml new file mode 100644 index 0000000..564ba3c --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/config/secret-known-hosts.yaml @@ -0,0 +1,13 @@ +{{- if and (.Values.dags.gitSync.enabled) (.Values.dags.gitSync.sshKnownHosts) }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "airflow.fullname" . }}-known-hosts + labels: + app: {{ include "airflow.labels.app" . }} + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +data: + known_hosts: {{ .Values.dags.gitSync.sshKnownHosts | b64enc | quote }} +{{- end }} \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/templates/config/secret-local-settings.yaml b/charts/deps/charts/airflow-8.8.0/templates/config/secret-local-settings.yaml new file mode 100644 index 0000000..9ab0189 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/config/secret-local-settings.yaml @@ -0,0 +1,13 @@ +{{- if and (.Values.airflow.localSettings.stringOverride) (not .Values.airflow.localSettings.existingSecret) }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "airflow.fullname" . }}-local-settings + labels: + app: {{ include "airflow.labels.app" . }} + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +data: + airflow_local_settings.py: {{ .Values.airflow.localSettings.stringOverride | b64enc | quote }} +{{- end }} \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/templates/config/secret-webserver-config.yaml b/charts/deps/charts/airflow-8.8.0/templates/config/secret-webserver-config.yaml new file mode 100644 index 0000000..c1aaa6b --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/config/secret-webserver-config.yaml @@ -0,0 +1,17 @@ +{{- if and (.Values.web.webserverConfig.enabled) (not .Values.web.webserverConfig.existingSecret) }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "airflow.fullname" . }}-webserver-config + labels: + app: {{ include "airflow.labels.app" . }} + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +data: + {{- if .Values.web.webserverConfig.stringOverride }} + webserver_config.py: {{ .Values.web.webserverConfig.stringOverride | b64enc | quote }} + {{- else }} + webserver_config.py: {{ tpl (.Files.Get "files/webserver_config.py") . | b64enc | quote }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/templates/db-migrations/_helpers/code.tpl b/charts/deps/charts/airflow-8.8.0/templates/db-migrations/_helpers/code.tpl new file mode 100644 index 0000000..779527d --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/db-migrations/_helpers/code.tpl @@ -0,0 +1,136 @@ +{{/* +The python script to apply airflow database migrations. +*/}} +{{- define "airflow.db_migrations.db_migrations.py" }} +############# +## Imports ## +############# +import logging +import time +from airflow.utils.db import upgradedb + + +############# +## Configs ## +############# +log = logging.getLogger(__file__) +log.setLevel("INFO") + +# how frequently to check for unapplied migrations +CONF__CHECK_MIGRATIONS_INTERVAL = {{ .Values.airflow.dbMigrations.checkInterval }} + + +############### +## Functions ## +############### +{{- if .Values.airflow.legacyCommands }} +# imports required for the following functions +import os +import airflow +from airflow import settings + +# modified from https://github.com/apache/airflow/blob/2.1.0/airflow/utils/db.py#L583-L592 +def _get_alembic_config(): + from alembic.config import Config + + package_dir = os.path.abspath(os.path.dirname(airflow.__file__)) + directory = os.path.join(package_dir, 'migrations') + config = Config(os.path.join(package_dir, 'alembic.ini')) + config.set_main_option('script_location', directory.replace('%', '%%')) + config.set_main_option('sqlalchemy.url', settings.SQL_ALCHEMY_CONN.replace('%', '%%')) + return config + + +# copied from https://github.com/apache/airflow/blob/2.1.0/airflow/utils/db.py#L595-L622 +def check_migrations(timeout): + """ + Function to wait for all airflow migrations to complete. + :param timeout: Timeout for the migration in seconds + :return: None + """ + from alembic.runtime.migration import MigrationContext + from alembic.script import ScriptDirectory + + config = _get_alembic_config() + script_ = ScriptDirectory.from_config(config) + with settings.engine.connect() as connection: + context = MigrationContext.configure(connection) + ticker = 0 + while True: + source_heads = set(script_.get_heads()) + db_heads = set(context.get_current_heads()) + if source_heads == db_heads: + break + if ticker >= timeout: + raise TimeoutError( + f"There are still unapplied migrations after {ticker} seconds. " + f"Migration Head(s) in DB: {db_heads} | Migration Head(s) in Source Code: {source_heads}" + ) + ticker += 1 + time.sleep(1) + log.info('Waiting for migrations... %s second(s)', ticker) +{{- else }} +from airflow.utils.db import check_migrations +{{- end }} + + +def needs_db_migrations() -> bool: + """ + Return a boolean representing if the database has unapplied migrations. + """ + log_alembic = logging.getLogger("alembic.runtime.migration") + log_alembic_level = log_alembic.level + try: + log_alembic.setLevel("WARN") + check_migrations(1) + log_alembic.setLevel(log_alembic_level) + return False + except TimeoutError: + return True + + +def apply_db_migrations() -> None: + """ + Apply any pending DB migrations. + """ + log.info("-------- START - APPLY DB MIGRATIONS --------") + upgradedb() + log.info("-------- FINISH - APPLY DB MIGRATIONS --------") + + +def main(sync_forever: bool): + # initial check & apply + if needs_db_migrations(): + log.warning("there are unapplied db migrations, triggering apply...") + apply_db_migrations() + else: + log.info("there are no unapplied db migrations, continuing...") + + if sync_forever: + # define variable to track how long since last migrations check + migrations_check_epoch = time.time() + + # main loop + while True: + if (time.time() - migrations_check_epoch) > CONF__CHECK_MIGRATIONS_INTERVAL: + log.debug(f"check interval reached, checking for unapplied db migrations...") + if needs_db_migrations(): + log.warning("there are unapplied db migrations, triggering apply...") + apply_db_migrations() + migrations_check_epoch = time.time() + + # ensure we dont loop too fast + time.sleep(0.5) + + +############## +## Run Main ## +############## +{{- /* if running as a Job, only run the initial check & apply */ -}} +{{- if .Values.airflow.dbMigrations.runAsJob }} +main(sync_forever=False) +{{- else }} +main(sync_forever=True) +{{- end }} + +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/templates/db-migrations/db-migrations-deployment.yaml b/charts/deps/charts/airflow-8.8.0/templates/db-migrations/db-migrations-deployment.yaml new file mode 100644 index 0000000..1ad6034 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/db-migrations/db-migrations-deployment.yaml @@ -0,0 +1,119 @@ +{{- if and (.Values.airflow.dbMigrations.enabled) (not .Values.airflow.dbMigrations.runAsJob) }} +{{- $podNodeSelector := include "airflow.podNodeSelector" (dict "Release" .Release "Values" .Values "nodeSelector" .Values.airflow.dbMigrations.nodeSelector) }} +{{- $podAffinity := include "airflow.podAffinity" (dict "Release" .Release "Values" .Values "affinity" .Values.airflow.dbMigrations.affinity) }} +{{- $podTolerations := include "airflow.podTolerations" (dict "Release" .Release "Values" .Values "tolerations" .Values.airflow.dbMigrations.tolerations) }} +{{- $podSecurityContext := include "airflow.podSecurityContext" (dict "Release" .Release "Values" .Values "securityContext" .Values.airflow.dbMigrations.securityContext) }} +{{- $extraPipPackages := .Values.airflow.extraPipPackages }} +{{- $volumeMounts := include "airflow.volumeMounts" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages) }} +{{- $volumes := include "airflow.volumes" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages) }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "airflow.fullname" . }}-db-migrations + labels: + app: {{ include "airflow.labels.app" . }} + component: db-migrations + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + {{- if .Values.airflow.dbMigrations.labels }} + {{- toYaml .Values.airflow.dbMigrations.labels | nindent 4 }} + {{- end }} + {{- if .Values.airflow.dbMigrations.annotations }} + annotations: + {{- toYaml .Values.airflow.dbMigrations.annotations | nindent 4 }} + {{- end }} +spec: + replicas: 1 + strategy: + ## only 1 replica should run at a time + type: Recreate + selector: + matchLabels: + app: {{ include "airflow.labels.app" . }} + component: db-migrations + release: {{ .Release.Name }} + template: + metadata: + annotations: + checksum/secret-config-envs: {{ include (print $.Template.BasePath "/config/secret-config-envs.yaml") . | sha256sum }} + checksum/secret-local-settings: {{ include (print $.Template.BasePath "/config/secret-local-settings.yaml") . | sha256sum }} + checksum/db-migrations-script: {{ include "airflow.db_migrations.db_migrations.py" . | sha256sum }} + {{- if .Values.airflow.podAnnotations }} + {{- toYaml .Values.airflow.podAnnotations | nindent 8 }} + {{- end }} + {{- if .Values.airflow.dbMigrations.podAnnotations }} + {{- toYaml .Values.airflow.dbMigrations.podAnnotations | nindent 8 }} + {{- end }} + {{- if .Values.airflow.dbMigrations.safeToEvict }} + cluster-autoscaler.kubernetes.io/safe-to-evict: "true" + {{- end }} + labels: + app: {{ include "airflow.labels.app" . }} + component: db-migrations + release: {{ .Release.Name }} + {{- if .Values.airflow.dbMigrations.podLabels }} + {{- toYaml .Values.airflow.dbMigrations.podLabels | nindent 8 }} + {{- end }} + spec: + restartPolicy: Always + {{- if .Values.airflow.image.pullSecret }} + imagePullSecrets: + - name: {{ .Values.airflow.image.pullSecret }} + {{- end }} + {{- if $podNodeSelector }} + nodeSelector: + {{- $podNodeSelector | nindent 8 }} + {{- end }} + {{- if $podAffinity }} + affinity: + {{- $podAffinity | nindent 8 }} + {{- end }} + {{- if $podTolerations }} + tolerations: + {{- $podTolerations | nindent 8 }} + {{- end }} + {{- if $podSecurityContext }} + securityContext: + {{- $podSecurityContext | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "airflow.serviceAccountName" . }} + initContainers: + {{- if $extraPipPackages }} + {{- include "airflow.init_container.install_pip_packages" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages) | indent 8 }} + {{- end }} + {{- if .Values.dags.gitSync.enabled }} + ## git-sync is included so "airflow plugins" & "python packages" can be stored in the dags repo + {{- include "airflow.container.git_sync" (dict "Release" .Release "Values" .Values "sync_one_time" "true") | indent 8 }} + {{- end }} + {{- include "airflow.init_container.check_db" (dict "Release" .Release "Values" .Values "volumeMounts" $volumeMounts) | indent 8 }} + containers: + - name: db-migrations + {{- include "airflow.image" . | indent 10 }} + resources: + {{- toYaml .Values.airflow.dbMigrations.resources | nindent 12 }} + envFrom: + {{- include "airflow.envFrom" . | indent 12 }} + env: + {{- include "airflow.env" . | indent 12 }} + command: + {{- include "airflow.command" . | indent 12 }} + args: + - "python" + - "-u" + - "/mnt/scripts/db_migrations.py" + volumeMounts: + {{- $volumeMounts | indent 12 }} + - name: scripts + mountPath: /mnt/scripts + readOnly: true + {{- if .Values.dags.gitSync.enabled }} + ## git-sync is included so "airflow plugins" & "python packages" can be stored in the dags repo + {{- include "airflow.container.git_sync" . | indent 8 }} + {{- end }} + volumes: + {{- $volumes | indent 8 }} + - name: scripts + secret: + secretName: {{ include "airflow.fullname" . }}-db-migrations +{{- end }} \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/templates/db-migrations/db-migrations-job.yaml b/charts/deps/charts/airflow-8.8.0/templates/db-migrations/db-migrations-job.yaml new file mode 100644 index 0000000..2e729af --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/db-migrations/db-migrations-job.yaml @@ -0,0 +1,109 @@ +{{- if and (.Values.airflow.dbMigrations.enabled) (.Values.airflow.dbMigrations.runAsJob) }} +{{- $podNodeSelector := include "airflow.podNodeSelector" (dict "Release" .Release "Values" .Values "nodeSelector" .Values.airflow.dbMigrations.nodeSelector) }} +{{- $podAffinity := include "airflow.podAffinity" (dict "Release" .Release "Values" .Values "affinity" .Values.airflow.dbMigrations.affinity) }} +{{- $podTolerations := include "airflow.podTolerations" (dict "Release" .Release "Values" .Values "tolerations" .Values.airflow.dbMigrations.tolerations) }} +{{- $podSecurityContext := include "airflow.podSecurityContext" (dict "Release" .Release "Values" .Values "securityContext" .Values.airflow.dbMigrations.securityContext) }} +{{- $extraPipPackages := .Values.airflow.extraPipPackages }} +{{- $volumeMounts := include "airflow.volumeMounts" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages) }} +{{- $volumes := include "airflow.volumes" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages) }} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "airflow.fullname" . }}-db-migrations + labels: + app: {{ include "airflow.labels.app" . }} + component: db-migrations + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + {{- if .Values.airflow.dbMigrations.labels }} + {{- toYaml .Values.airflow.dbMigrations.labels | nindent 4 }} + {{- end }} + annotations: + helm.sh/hook: post-install,post-upgrade + helm.sh/hook-weight: "-100" + helm.sh/hook-delete-policy: before-hook-creation + {{- if .Values.airflow.dbMigrations.annotations }} + {{- toYaml .Values.airflow.dbMigrations.annotations | nindent 4 }} + {{- end }} +spec: + template: + metadata: + annotations: + checksum/secret-config-envs: {{ include (print $.Template.BasePath "/config/secret-config-envs.yaml") . | sha256sum }} + checksum/secret-local-settings: {{ include (print $.Template.BasePath "/config/secret-local-settings.yaml") . | sha256sum }} + checksum/db-migrations-script: {{ include "airflow.db_migrations.db_migrations.py" . | sha256sum }} + {{- if .Values.airflow.podAnnotations }} + {{- toYaml .Values.airflow.podAnnotations | nindent 8 }} + {{- end }} + {{- if .Values.airflow.dbMigrations.podAnnotations }} + {{- toYaml .Values.airflow.dbMigrations.podAnnotations | nindent 8 }} + {{- end }} + {{- if .Values.airflow.dbMigrations.safeToEvict }} + cluster-autoscaler.kubernetes.io/safe-to-evict: "true" + {{- end }} + labels: + app: {{ include "airflow.labels.app" . }} + component: db-migrations + release: {{ .Release.Name }} + {{- if .Values.airflow.dbMigrations.podLabels }} + {{- toYaml .Values.airflow.dbMigrations.podLabels | nindent 8 }} + {{- end }} + spec: + restartPolicy: OnFailure + {{- if .Values.airflow.image.pullSecret }} + imagePullSecrets: + - name: {{ .Values.airflow.image.pullSecret }} + {{- end }} + {{- if $podNodeSelector }} + nodeSelector: + {{- $podNodeSelector | nindent 8 }} + {{- end }} + {{- if $podAffinity }} + affinity: + {{- $podAffinity | nindent 8 }} + {{- end }} + {{- if $podTolerations }} + tolerations: + {{- $podTolerations | nindent 8 }} + {{- end }} + {{- if $podSecurityContext }} + securityContext: + {{- $podSecurityContext | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "airflow.serviceAccountName" . }} + initContainers: + {{- if $extraPipPackages }} + {{- include "airflow.init_container.install_pip_packages" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages) | indent 8 }} + {{- end }} + {{- if .Values.dags.gitSync.enabled }} + ## git-sync is included so "airflow plugins" & "python packages" can be stored in the dags repo + {{- include "airflow.container.git_sync" (dict "Release" .Release "Values" .Values "sync_one_time" "true") | indent 8 }} + {{- end }} + {{- include "airflow.init_container.check_db" (dict "Release" .Release "Values" .Values "volumeMounts" $volumeMounts) | indent 8 }} + containers: + - name: db-migrations + {{- include "airflow.image" . | indent 10 }} + resources: + {{- toYaml .Values.airflow.dbMigrations.resources | nindent 12 }} + envFrom: + {{- include "airflow.envFrom" . | indent 12 }} + env: + {{- include "airflow.env" . | indent 12 }} + command: + {{- include "airflow.command" . | indent 12 }} + args: + - "python" + - "-u" + - "/mnt/scripts/db_migrations.py" + volumeMounts: + {{- $volumeMounts | indent 12 }} + - name: scripts + mountPath: /mnt/scripts + readOnly: true + volumes: + {{- $volumes | indent 8 }} + - name: scripts + secret: + secretName: {{ include "airflow.fullname" . }}-db-migrations +{{- end }} \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/templates/db-migrations/db-migrations-secret.yaml b/charts/deps/charts/airflow-8.8.0/templates/db-migrations/db-migrations-secret.yaml new file mode 100644 index 0000000..c13ef85 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/db-migrations/db-migrations-secret.yaml @@ -0,0 +1,14 @@ +{{- if .Values.airflow.dbMigrations.enabled }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "airflow.fullname" . }}-db-migrations + labels: + app: {{ include "airflow.labels.app" . }} + component: db-migrations + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +data: + db_migrations.py: {{ include "airflow.db_migrations.db_migrations.py" . | b64enc | quote }} +{{- end }} \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/templates/extra-manifests.yaml b/charts/deps/charts/airflow-8.8.0/templates/extra-manifests.yaml new file mode 100644 index 0000000..c338acc --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/extra-manifests.yaml @@ -0,0 +1,9 @@ +{{- range $manifest := .Values.extraManifests }} +--- +{{- if typeIs "string" $manifest }} +{{ tpl $manifest $ }} +{{- else }} +{{- /* support non-string manifests for backwards compatibility with previous chart versions */ -}} +{{ tpl (toYaml $manifest) $ }} +{{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/templates/flower/flower-deployment.yaml b/charts/deps/charts/airflow-8.8.0/templates/flower/flower-deployment.yaml new file mode 100644 index 0000000..436f7de --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/flower/flower-deployment.yaml @@ -0,0 +1,165 @@ +{{- if .Values.flower.enabled }} +{{- $podNodeSelector := include "airflow.podNodeSelector" (dict "Release" .Release "Values" .Values "nodeSelector" .Values.flower.nodeSelector) }} +{{- $podAffinity := include "airflow.podAffinity" (dict "Release" .Release "Values" .Values "affinity" .Values.flower.affinity) }} +{{- $podTolerations := include "airflow.podTolerations" (dict "Release" .Release "Values" .Values "tolerations" .Values.flower.tolerations) }} +{{- $podSecurityContext := include "airflow.podSecurityContext" (dict "Release" .Release "Values" .Values "securityContext" .Values.flower.securityContext) }} +{{- $extraPipPackages := concat .Values.airflow.extraPipPackages .Values.flower.extraPipPackages }} +{{- $extraVolumeMounts := .Values.flower.extraVolumeMounts }} +{{- $volumeMounts := include "airflow.volumeMounts" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages "extraVolumeMounts" $extraVolumeMounts) }} +{{- $extraVolumes := .Values.flower.extraVolumes }} +{{- $volumes := include "airflow.volumes" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages "extraVolumes" $extraVolumes "extraVolumeMounts" $extraVolumeMounts) }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "airflow.fullname" . }}-flower + {{- if .Values.flower.annotations }} + annotations: + {{- toYaml .Values.flower.annotations | nindent 4 }} + {{- end }} + labels: + app: {{ include "airflow.labels.app" . }} + component: flower + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + {{- if .Values.flower.labels }} + {{- toYaml .Values.flower.labels | nindent 4 }} + {{- end }} +spec: + replicas: {{ .Values.flower.replicas }} + strategy: + type: RollingUpdate + rollingUpdate: + ## multiple flower pods can safely run concurrently + maxSurge: 25% + maxUnavailable: 0 + selector: + matchLabels: + app: {{ include "airflow.labels.app" . }} + component: flower + release: {{ .Release.Name }} + template: + metadata: + annotations: + checksum/secret-config-envs: {{ include (print $.Template.BasePath "/config/secret-config-envs.yaml") . | sha256sum }} + checksum/secret-local-settings: {{ include (print $.Template.BasePath "/config/secret-local-settings.yaml") . | sha256sum }} + {{- if .Values.airflow.podAnnotations }} + {{- toYaml .Values.airflow.podAnnotations | nindent 8 }} + {{- end }} + {{- if .Values.flower.podAnnotations }} + {{- toYaml .Values.flower.podAnnotations | nindent 8 }} + {{- end }} + {{- if .Values.flower.safeToEvict }} + cluster-autoscaler.kubernetes.io/safe-to-evict: "true" + {{- end }} + labels: + app: {{ include "airflow.labels.app" . }} + component: flower + release: {{ .Release.Name }} + {{- if .Values.flower.podLabels }} + {{- toYaml .Values.flower.podLabels | nindent 8 }} + {{- end }} + spec: + restartPolicy: Always + {{- if .Values.airflow.image.pullSecret }} + imagePullSecrets: + - name: {{ .Values.airflow.image.pullSecret }} + {{- end }} + {{- if $podNodeSelector }} + nodeSelector: + {{- $podNodeSelector | nindent 8 }} + {{- end }} + {{- if $podAffinity }} + affinity: + {{- $podAffinity | nindent 8 }} + {{- end }} + {{- if $podTolerations }} + tolerations: + {{- $podTolerations | nindent 8 }} + {{- end }} + {{- if $podSecurityContext }} + securityContext: + {{- $podSecurityContext | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "airflow.serviceAccountName" . }} + initContainers: + {{- if $extraPipPackages }} + {{- include "airflow.init_container.install_pip_packages" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages) | indent 8 }} + {{- end }} + {{- if .Values.dags.gitSync.enabled }} + ## git-sync is included so "airflow plugins" & "python packages" can be stored in the dags repo + {{- include "airflow.container.git_sync" (dict "Release" .Release "Values" .Values "sync_one_time" "true") | indent 8 }} + {{- end }} + {{- include "airflow.init_container.check_db" (dict "Release" .Release "Values" .Values "volumeMounts" $volumeMounts) | indent 8 }} + {{- include "airflow.init_container.wait_for_db_migrations" (dict "Release" .Release "Values" .Values "volumeMounts" $volumeMounts) | indent 8 }} + containers: + - name: airflow-flower + {{- include "airflow.image" . | indent 10 }} + resources: + {{- toYaml .Values.flower.resources | nindent 12 }} + envFrom: + {{- include "airflow.envFrom" . | indent 12 }} + env: + {{- include "airflow.env" . | indent 12 }} + ports: + - name: flower + containerPort: 5555 + protocol: TCP + command: + {{- include "airflow.command" . | indent 12 }} + args: + - "bash" + - "-c" + {{- if .Values.airflow.legacyCommands }} + - "exec airflow flower" + {{- else }} + - "exec airflow celery flower" + {{- end }} + {{- if .Values.flower.readinessProbe.enabled }} + readinessProbe: + initialDelaySeconds: {{ .Values.flower.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.flower.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.flower.readinessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.flower.readinessProbe.failureThreshold }} + exec: + command: + - "bash" + - "-c" + {{- if or (.Values.flower.basicAuthSecret) (.Values.airflow.config.AIRFLOW__CELERY__FLOWER_BASIC_AUTH) }} + - "exec curl --user $AIRFLOW__CELERY__FLOWER_BASIC_AUTH 'http://localhost:5555{{ .Values.airflow.config.AIRFLOW__CELERY__FLOWER_URL_PREFIX }}'" + {{- else }} + - "exec curl 'http://localhost:5555{{ .Values.airflow.config.AIRFLOW__CELERY__FLOWER_URL_PREFIX }}'" + {{- end }} + {{- end }} + {{- if .Values.flower.livenessProbe.enabled }} + livenessProbe: + initialDelaySeconds: {{ .Values.flower.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.flower.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.flower.livenessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.flower.livenessProbe.failureThreshold }} + exec: + command: + - "bash" + - "-c" + {{- if or (.Values.flower.basicAuthSecret) (.Values.airflow.config.AIRFLOW__CELERY__FLOWER_BASIC_AUTH) }} + - "exec curl --user $AIRFLOW__CELERY__FLOWER_BASIC_AUTH 'http://localhost:5555{{ .Values.airflow.config.AIRFLOW__CELERY__FLOWER_URL_PREFIX }}'" + {{- else }} + - "exec curl 'http://localhost:5555{{ .Values.airflow.config.AIRFLOW__CELERY__FLOWER_URL_PREFIX }}'" + {{- end }} + {{- end }} + {{- if $volumeMounts }} + volumeMounts: + {{- $volumeMounts | indent 12 }} + {{- end }} + {{- if .Values.dags.gitSync.enabled }} + ## git-sync is included so "airflow plugins" & "python packages" can be stored in the dags repo + {{- include "airflow.container.git_sync" . | indent 8 }} + {{- end }} + {{- if .Values.airflow.extraContainers }} + {{- toYaml .Values.airflow.extraContainers | nindent 8 }} + {{- end }} + {{- if $volumes }} + volumes: + {{- $volumes | indent 8 }} + {{- end }} +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/templates/flower/flower-ingress-v1beta1.yaml b/charts/deps/charts/airflow-8.8.0/templates/flower/flower-ingress-v1beta1.yaml new file mode 100644 index 0000000..2b85b26 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/flower/flower-ingress-v1beta1.yaml @@ -0,0 +1,51 @@ +{{- if and (.Values.flower.enabled) (.Values.ingress.enabled) (eq .Values.ingress.apiVersion "networking.k8s.io/v1beta1") }} +apiVersion: networking.k8s.io/v1beta1 +kind: Ingress +metadata: + name: {{ include "airflow.fullname" . }}-flower + {{- if .Values.ingress.flower.annotations }} + annotations: + {{- toYaml .Values.ingress.flower.annotations | nindent 4 }} + {{- end }} + labels: + app: {{ include "airflow.labels.app" . }} + component: flower + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + {{- if .Values.ingress.flower.labels }} + {{- toYaml .Values.ingress.flower.labels | nindent 4 }} + {{- end }} +spec: + {{- if .Values.ingress.flower.tls.enabled }} + tls: + - hosts: + - {{ .Values.ingress.flower.host }} + {{- if .Values.ingress.flower.tls.secretName }} + secretName: {{ .Values.ingress.flower.tls.secretName }} + {{- end }} + {{- end }} + {{- if .Values.ingress.flower.ingressClassName }} + ingressClassName: {{ .Values.ingress.flower.ingressClassName }} + {{- end }} + rules: + - host: {{ .Values.ingress.flower.host }} + http: + paths: + {{- range .Values.ingress.flower.precedingPaths }} + - path: {{ .path }} + backend: + serviceName: {{ .serviceName }} + servicePort: {{ .servicePort }} + {{- end }} + - path: {{ .Values.ingress.flower.path }} + backend: + serviceName: {{ include "airflow.fullname" . }}-flower + servicePort: flower + {{- range .Values.ingress.flower.succeedingPaths }} + - path: {{ .path }} + backend: + serviceName: {{ .serviceName }} + servicePort: {{ .servicePort }} + {{- end }} +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/templates/flower/flower-ingress.yaml b/charts/deps/charts/airflow-8.8.0/templates/flower/flower-ingress.yaml new file mode 100644 index 0000000..62a5355 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/flower/flower-ingress.yaml @@ -0,0 +1,68 @@ +{{- if and (.Values.flower.enabled) (.Values.ingress.enabled) (eq .Values.ingress.apiVersion "networking.k8s.io/v1") }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "airflow.fullname" . }}-flower + {{- if .Values.ingress.flower.annotations }} + annotations: + {{- toYaml .Values.ingress.flower.annotations | nindent 4 }} + {{- end }} + labels: + app: {{ include "airflow.labels.app" . }} + component: flower + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + {{- if .Values.ingress.flower.labels }} + {{- toYaml .Values.ingress.flower.labels | nindent 4 }} + {{- end }} +spec: + {{- if .Values.ingress.flower.tls.enabled }} + tls: + - hosts: + - {{ .Values.ingress.flower.host }} + {{- if .Values.ingress.flower.tls.secretName }} + secretName: {{ .Values.ingress.flower.tls.secretName }} + {{- end }} + {{- end }} + {{- if .Values.ingress.flower.ingressClassName }} + ingressClassName: {{ .Values.ingress.flower.ingressClassName }} + {{- end }} + rules: + - host: {{ .Values.ingress.flower.host }} + http: + paths: + {{- range .Values.ingress.flower.precedingPaths }} + - path: {{ .path }} + pathType: ImplementationSpecific + backend: + service: + name: {{ .serviceName }} + port: + {{- if kindIs "string" .servicePort }} + name: {{ .servicePort }} + {{- else }} + number: {{ .servicePort }} + {{- end }} + {{- end }} + - path: {{ .Values.ingress.flower.path }} + pathType: ImplementationSpecific + backend: + service: + name: {{ include "airflow.fullname" . }}-flower + port: + name: flower + {{- range .Values.ingress.flower.succeedingPaths }} + - path: {{ .path }} + pathType: ImplementationSpecific + backend: + service: + name: {{ .serviceName }} + port: + {{- if kindIs "string" .servicePort }} + name: {{ .servicePort }} + {{- else }} + number: {{ .servicePort }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/templates/flower/flower-pdb.yaml b/charts/deps/charts/airflow-8.8.0/templates/flower/flower-pdb.yaml new file mode 100644 index 0000000..e920d1d --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/flower/flower-pdb.yaml @@ -0,0 +1,24 @@ +{{- if and (.Values.flower.enabled) (.Values.flower.podDisruptionBudget.enabled) }} +apiVersion: {{ .Values.flower.podDisruptionBudget.apiVersion }} +kind: PodDisruptionBudget +metadata: + name: {{ include "airflow.fullname" . }}-flower + labels: + app: {{ include "airflow.labels.app" . }} + component: flower + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + {{- if .Values.flower.podDisruptionBudget.maxUnavailable }} + maxUnavailable: {{ .Values.flower.podDisruptionBudget.maxUnavailable }} + {{- end }} + {{- if .Values.flower.podDisruptionBudget.minAvailable }} + minAvailable: {{ .Values.flower.podDisruptionBudget.minAvailable }} + {{- end }} + selector: + matchLabels: + app: {{ include "airflow.labels.app" . }} + component: flower + release: {{ .Release.Name }} +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/templates/flower/flower-service.yaml b/charts/deps/charts/airflow-8.8.0/templates/flower/flower-service.yaml new file mode 100644 index 0000000..013e648 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/flower/flower-service.yaml @@ -0,0 +1,39 @@ +{{- if .Values.flower.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "airflow.fullname" . }}-flower + {{- if .Values.flower.service.annotations }} + annotations: + {{- toYaml .Values.flower.service.annotations | nindent 4 }} + {{- end }} + labels: + app: {{ include "airflow.labels.app" . }} + component: flower + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + type: {{ .Values.flower.service.type }} + selector: + app: {{ include "airflow.labels.app" . }} + component: flower + release: {{ .Release.Name }} + ports: + - name: flower + protocol: TCP + port: {{ .Values.flower.service.externalPort }} + {{- if and (eq .Values.flower.service.type "NodePort") (.Values.flower.service.nodePort.http) }} + nodePort: {{ .Values.flower.service.nodePort.http }} + {{- end }} + targetPort: 5555 + {{- if eq .Values.flower.service.type "LoadBalancer" }} + {{- if .Values.flower.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.flower.service.loadBalancerIP | quote }} + {{- end }} + {{- if .Values.flower.service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: + {{- toYaml .Values.flower.service.loadBalancerSourceRanges | nindent 4 }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/templates/pgbouncer/_helpers/pgbouncer.tpl b/charts/deps/charts/airflow-8.8.0/templates/pgbouncer/_helpers/pgbouncer.tpl new file mode 100644 index 0000000..f4959a1 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/pgbouncer/_helpers/pgbouncer.tpl @@ -0,0 +1,118 @@ +{{/* +Define the content of the `pgbouncer.ini` config file. +*/}} +{{- define "airflow.pgbouncer.pgbouncer.ini" }} +[databases] +{{- if .Values.postgresql.enabled }} +* = host={{ printf "%s.%s.svc.%s" (include "airflow.postgresql.fullname" .) (.Release.Namespace) (.Values.airflow.clusterDomain) }} port=5432 +{{- else }} +* = host={{ .Values.externalDatabase.host }} port={{ .Values.externalDatabase.port }} +{{- end }} + +[pgbouncer] +pool_mode = transaction +max_client_conn = {{ .Values.pgbouncer.maxClientConnections }} +default_pool_size = {{ .Values.pgbouncer.poolSize }} +ignore_startup_parameters = extra_float_digits + +listen_port = 6432 +listen_addr = * + +auth_type = {{ .Values.pgbouncer.authType }} +auth_file = /home/pgbouncer/users.txt + +log_disconnections = {{ .Values.pgbouncer.logDisconnections }} +log_connections = {{ .Values.pgbouncer.logConnections }} + +# locks will never be released when `pool_mode=transaction` (airflow initdb/upgradedb scripts create locks) +server_reset_query = SELECT pg_advisory_unlock_all() +server_reset_query_always = 1 + +## CLIENT TLS SETTINGS ## +client_tls_sslmode = {{ .Values.pgbouncer.clientSSL.mode }} +client_tls_ciphers = {{ .Values.pgbouncer.clientSSL.ciphers }} +{{- if .Values.pgbouncer.clientSSL.caFile.existingSecret }} +client_tls_ca_file = /home/pgbouncer/certs/client-ca.crt +{{- end }} +{{- if .Values.pgbouncer.clientSSL.keyFile.existingSecret }} +client_tls_key_file = /home/pgbouncer/certs/client.key +{{- else }} +client_tls_key_file = /home/pgbouncer/generated-certs/client.key +{{- end }} +{{- if .Values.pgbouncer.clientSSL.certFile.existingSecret }} +client_tls_cert_file = /home/pgbouncer/certs/client.crt +{{- else }} +client_tls_cert_file = /home/pgbouncer/generated-certs/client.crt +{{- end }} + +## SERVER TLS SETTINGS ## +server_tls_sslmode = {{ .Values.pgbouncer.serverSSL.mode }} +server_tls_ciphers = {{ .Values.pgbouncer.serverSSL.ciphers }} +{{- if .Values.pgbouncer.serverSSL.caFile.existingSecret }} +server_tls_ca_file = /home/pgbouncer/certs/server-ca.crt +{{- end }} +{{- if .Values.pgbouncer.serverSSL.keyFile.existingSecret }} +server_tls_key_file = /home/pgbouncer/certs/server.key +{{- end }} +{{- if .Values.pgbouncer.serverSSL.certFile.existingSecret }} +server_tls_cert_file = /home/pgbouncer/certs/server.crt +{{- end }} + +{{- end }} + +{{/* +Define the content of the `gen_self_signed_cert.sh` script. +*/}} +{{- define "airflow.pgbouncer.gen_self_signed_cert.sh" }} +#!/bin/sh -e + +CERT_DIR="/home/pgbouncer/generated-certs" +KEY_FILE="$CERT_DIR/client.key" +CERT_FILE="$CERT_DIR/client.crt" + +# create the directory for the self-signed certificate +mkdir -p "$CERT_DIR" + +# variables for certificate generation +COMMON_NAME="localhost" +DAYS_VALID=365 + +# generate the self-signed certificate and a private key +openssl req -x509 \ + -newkey rsa:4096 \ + -keyout "$KEY_FILE" \ + -out "$CERT_FILE" \ + -days "$DAYS_VALID" \ + -subj "/CN=$COMMON_NAME" \ + -nodes + +# set permissions for the private key file +chmod 600 "$KEY_FILE" + +echo "Successfully generated self-signed certificate: $CERT_FILE" +echo "Successfully generated self-signed certificate key: $KEY_FILE" +{{- end }} + +{{/* +Define the content of the `gen_auth_file.sh` script. +*/}} +{{- define "airflow.pgbouncer.gen_auth_file.sh" }} +#!/bin/sh -e + +# DESCRIPTION: +# - updates the pgbouncer `auth_file` from environment variables +# - called in main pgbouncer container start-command so that `auth_file` is updated each restart, +# for example, when the livenessProbe fails due to a DATABASE_PASSWORD secret update + +# variables to increase clarity of pattern matching +ONE_QUOTE='"' +TWO_QUOTE='""' + +# pgbouncer requires `"` to be escaped as `""` +ESCAPED_DATABASE_USER="${DATABASE_USER/$ONE_QUOTE/$TWO_QUOTE}" +ESCAPED_DATABASE_PASSWORD="${DATABASE_PASSWORD/$ONE_QUOTE/$TWO_QUOTE}" + +# pgbouncer requires auth_file in format `"my-username" "my-password"` +echo \"$ESCAPED_DATABASE_USER\" \"$ESCAPED_DATABASE_PASSWORD\" > /home/pgbouncer/users.txt +echo "Successfully generated auth_file: /home/pgbouncer/users.txt" +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/templates/pgbouncer/pgbouncer-deployment.yaml b/charts/deps/charts/airflow-8.8.0/templates/pgbouncer/pgbouncer-deployment.yaml new file mode 100644 index 0000000..e766c19 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/pgbouncer/pgbouncer-deployment.yaml @@ -0,0 +1,218 @@ +{{- define "airflow.pgbouncer.certs_volume_sources" }} +{{- if or (.Values.pgbouncer.clientSSL.caFile.existingSecret) (.Values.pgbouncer.clientSSL.keyFile.existingSecret) (.Values.pgbouncer.clientSSL.certFile.existingSecret) }} +## CLIENT TLS FILES (USER PROVIDED) +{{- if .Values.pgbouncer.clientSSL.caFile.existingSecret }} +- secret: + name: {{ .Values.pgbouncer.clientSSL.caFile.existingSecret }} + items: + - key: {{ .Values.pgbouncer.clientSSL.caFile.existingSecretKey }} + path: client-ca.crt +{{- end }} +{{- if .Values.pgbouncer.clientSSL.keyFile.existingSecret }} +- secret: + name: {{ .Values.pgbouncer.clientSSL.keyFile.existingSecret }} + items: + - key: {{ .Values.pgbouncer.clientSSL.keyFile.existingSecretKey }} + path: client.key +{{- end }} +{{- if .Values.pgbouncer.clientSSL.certFile.existingSecret }} +- secret: + name: {{ .Values.pgbouncer.clientSSL.certFile.existingSecret }} + items: + - key: {{ .Values.pgbouncer.clientSSL.certFile.existingSecretKey }} + path: client.crt +{{- end }} +{{- end }} + +{{- if or (.Values.pgbouncer.serverSSL.caFile.existingSecret) (.Values.pgbouncer.serverSSL.keyFile.existingSecret) (.Values.pgbouncer.serverSSL.certFile.existingSecret) }} +## SERVER TLS FILES (USER PROVIDED) +{{- if .Values.pgbouncer.serverSSL.caFile.existingSecret }} +- secret: + name: {{ .Values.pgbouncer.serverSSL.caFile.existingSecret }} + items: + - key: {{ .Values.pgbouncer.serverSSL.caFile.existingSecretKey }} + path: server-ca.crt +{{- end }} +{{- if .Values.pgbouncer.serverSSL.keyFile.existingSecret }} +- secret: + name: {{ .Values.pgbouncer.serverSSL.keyFile.existingSecret }} + items: + - key: {{ .Values.pgbouncer.serverSSL.keyFile.existingSecretKey }} + path: server.key +{{- end }} +{{- if .Values.pgbouncer.serverSSL.certFile.existingSecret }} +- secret: + name: {{ .Values.pgbouncer.serverSSL.certFile.existingSecret }} + items: + - key: {{ .Values.pgbouncer.serverSSL.certFile.existingSecretKey }} + path: server.crt +{{- end }} +{{- end }} +{{- end }} + +{{- if include "airflow.pgbouncer.should_use" . }} +{{- $podNodeSelector := include "airflow.podNodeSelector" (dict "Release" .Release "Values" .Values "nodeSelector" .Values.pgbouncer.nodeSelector) }} +{{- $podAffinity := include "airflow.podAffinity" (dict "Release" .Release "Values" .Values "affinity" .Values.pgbouncer.affinity) }} +{{- $podTolerations := include "airflow.podTolerations" (dict "Release" .Release "Values" .Values "tolerations" .Values.pgbouncer.tolerations) }} +{{- $podSecurityContext := include "airflow.podSecurityContext" (dict "Release" .Release "Values" .Values "securityContext" .Values.pgbouncer.securityContext) }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "airflow.fullname" . }}-pgbouncer + labels: + app: {{ include "airflow.labels.app" . }} + component: pgbouncer + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + {{- if .Values.pgbouncer.labels }} + {{- toYaml .Values.pgbouncer.labels | nindent 4 }} + {{- end }} + {{- if .Values.pgbouncer.annotations }} + annotations: + {{- toYaml .Values.pgbouncer.annotations | nindent 4 }} + {{- end }} +spec: + replicas: 1 + strategy: + rollingUpdate: + ## multiple pgbouncer pods can safely run concurrently + maxSurge: 1 + maxUnavailable: 0 + selector: + matchLabels: + app: {{ include "airflow.labels.app" . }} + component: pgbouncer + release: {{ .Release.Name }} + template: + metadata: + annotations: + checksum/secret-config-envs: {{ include (print $.Template.BasePath "/config/secret-config-envs.yaml") . | sha256sum }} + checksum/secret-pgbouncer: {{ include (print $.Template.BasePath "/pgbouncer/pgbouncer-secret.yaml") . | sha256sum }} + {{- if .Values.airflow.podAnnotations }} + {{- toYaml .Values.airflow.podAnnotations | nindent 8 }} + {{- end }} + {{- if .Values.pgbouncer.podAnnotations }} + {{- toYaml .Values.pgbouncer.podAnnotations | nindent 8 }} + {{- end }} + {{- if .Values.pgbouncer.safeToEvict }} + cluster-autoscaler.kubernetes.io/safe-to-evict: "true" + {{- end }} + labels: + app: {{ include "airflow.labels.app" . }} + component: pgbouncer + release: {{ .Release.Name }} + {{- if .Values.pgbouncer.podLabels }} + {{- toYaml .Values.pgbouncer.podLabels | nindent 8 }} + {{- end }} + spec: + restartPolicy: Always + {{- if .Values.airflow.image.pullSecret }} + imagePullSecrets: + - name: {{ .Values.airflow.image.pullSecret }} + {{- end }} + {{- if $podNodeSelector }} + nodeSelector: + {{- $podNodeSelector | nindent 8 }} + {{- end }} + {{- if $podAffinity }} + affinity: + {{- $podAffinity | nindent 8 }} + {{- end }} + {{- if $podTolerations }} + tolerations: + {{- $podTolerations | nindent 8 }} + {{- end }} + {{- if $podSecurityContext }} + securityContext: + {{- $podSecurityContext | nindent 8 }} + {{- end }} + terminationGracePeriodSeconds: {{ .Values.pgbouncer.terminationGracePeriodSeconds }} + serviceAccountName: {{ include "airflow.serviceAccountName" . }} + containers: + - name: pgbouncer + image: {{ .Values.pgbouncer.image.repository }}:{{ .Values.pgbouncer.image.tag }} + imagePullPolicy: {{ .Values.pgbouncer.image.pullPolicy }} + securityContext: + runAsUser: {{ .Values.pgbouncer.image.uid }} + runAsGroup: {{ .Values.pgbouncer.image.gid }} + resources: + {{- toYaml .Values.pgbouncer.resources | nindent 12 }} + envFrom: + {{- include "airflow.envFrom" . | indent 12 }} + env: + {{- include "airflow.env" . | indent 12 }} + ports: + - name: pgbouncer + containerPort: 6432 + protocol: TCP + command: + - "/usr/bin/dumb-init" + ## rewrite SIGTERM as SIGINT, so pgbouncer does a safe shutdown + - "--rewrite=15:2" + - "--" + args: + - "/bin/sh" + - "-c" + ## we generate users.txt on startup, because DATABASE_PASSWORD is defined from a Secret, + ## and we want to pickup the new values on container restart (possibly due to livenessProbe failure) + - |- + {{- if or (not .Values.pgbouncer.clientSSL.keyFile.existingSecret) (not .Values.pgbouncer.clientSSL.certFile.existingSecret) }} + /home/pgbouncer/config/gen_self_signed_cert.sh && \ + {{- end }} + /home/pgbouncer/config/gen_auth_file.sh && \ + exec pgbouncer /home/pgbouncer/config/pgbouncer.ini + {{- if .Values.pgbouncer.livenessProbe.enabled }} + livenessProbe: + initialDelaySeconds: {{ .Values.pgbouncer.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.pgbouncer.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.pgbouncer.livenessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.pgbouncer.livenessProbe.failureThreshold }} + exec: + command: + - "/bin/sh" + - "-c" + ## this check is intended to fail when the DATABASE_PASSWORD secret is updated, + ## which would cause `gen_auth_file.sh` to run again on container start + - psql $(eval $DATABASE_PSQL_CMD) --tuples-only --command="SELECT 1;" | grep -q "1" + {{- end }} + {{- if .Values.pgbouncer.startupProbe.enabled }} + startupProbe: + initialDelaySeconds: {{ .Values.pgbouncer.startupProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.pgbouncer.startupProbe.periodSeconds }} + timeoutSeconds: {{ .Values.pgbouncer.startupProbe.timeoutSeconds }} + failureThreshold: {{ .Values.pgbouncer.startupProbe.failureThreshold }} + tcpSocket: + port: 6432 + {{- end }} + volumeMounts: + - name: pgbouncer-config + mountPath: /home/pgbouncer/config + readOnly: true + {{- if include "airflow.pgbouncer.certs_volume_sources" . }} + - name: pgbouncer-certs + mountPath: /home/pgbouncer/certs + readOnly: true + {{- end }} + volumes: + - name: pgbouncer-config + secret: + secretName: {{ include "airflow.fullname" . }}-pgbouncer + items: + - key: gen_auth_file.sh + path: gen_auth_file.sh + mode: 0755 + {{- if or (not .Values.pgbouncer.clientSSL.keyFile.existingSecret) (not .Values.pgbouncer.clientSSL.certFile.existingSecret) }} + - key: gen_self_signed_cert.sh + path: gen_self_signed_cert.sh + mode: 0755 + {{- end }} + - key: pgbouncer.ini + path: pgbouncer.ini + {{- if include "airflow.pgbouncer.certs_volume_sources" . }} + - name: pgbouncer-certs + projected: + sources: + {{- include "airflow.pgbouncer.certs_volume_sources" . | indent 14 }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/templates/pgbouncer/pgbouncer-pdb.yaml b/charts/deps/charts/airflow-8.8.0/templates/pgbouncer/pgbouncer-pdb.yaml new file mode 100644 index 0000000..cc5af7a --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/pgbouncer/pgbouncer-pdb.yaml @@ -0,0 +1,24 @@ +{{- if and (include "airflow.pgbouncer.should_use" .) (.Values.pgbouncer.podDisruptionBudget.enabled) }} +apiVersion: {{ .Values.pgbouncer.podDisruptionBudget.apiVersion }} +kind: PodDisruptionBudget +metadata: + name: {{ include "airflow.fullname" . }}-pgbouncer + labels: + app: {{ include "airflow.labels.app" . }} + component: pgbouncer + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + {{- if .Values.pgbouncer.podDisruptionBudget.maxUnavailable }} + maxUnavailable: {{ .Values.pgbouncer.podDisruptionBudget.maxUnavailable }} + {{- end }} + {{- if .Values.pgbouncer.podDisruptionBudget.minAvailable }} + minAvailable: {{ .Values.pgbouncer.podDisruptionBudget.minAvailable }} + {{- end }} + selector: + matchLabels: + app: {{ include "airflow.labels.app" . }} + component: pgbouncer + release: {{ .Release.Name }} +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/templates/pgbouncer/pgbouncer-secret.yaml b/charts/deps/charts/airflow-8.8.0/templates/pgbouncer/pgbouncer-secret.yaml new file mode 100644 index 0000000..4217c29 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/pgbouncer/pgbouncer-secret.yaml @@ -0,0 +1,18 @@ +{{- if include "airflow.pgbouncer.should_use" . }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "airflow.fullname" . }}-pgbouncer + labels: + app: {{ include "airflow.labels.app" . }} + component: pgbouncer + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +data: + pgbouncer.ini: {{ include "airflow.pgbouncer.pgbouncer.ini" . | b64enc | quote }} + gen_auth_file.sh: {{ include "airflow.pgbouncer.gen_auth_file.sh" . | b64enc | quote }} + {{- if or (not .Values.pgbouncer.clientSSL.keyFile.existingSecret) (not .Values.pgbouncer.clientSSL.certFile.existingSecret) }} + gen_self_signed_cert.sh: {{ include "airflow.pgbouncer.gen_self_signed_cert.sh" . | b64enc | quote }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/templates/pgbouncer/pgbouncer-service.yaml b/charts/deps/charts/airflow-8.8.0/templates/pgbouncer/pgbouncer-service.yaml new file mode 100644 index 0000000..dda2895 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/pgbouncer/pgbouncer-service.yaml @@ -0,0 +1,22 @@ +{{- if include "airflow.pgbouncer.should_use" . }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "airflow.fullname" . }}-pgbouncer + labels: + app: {{ include "airflow.labels.app" . }} + component: pgbouncer + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + type: ClusterIP + selector: + app: {{ include "airflow.labels.app" . }} + component: pgbouncer + release: {{ .Release.Name }} + ports: + - name: pgbouncer + protocol: TCP + port: 6432 +{{- end }} \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/templates/pvc-dags.yaml b/charts/deps/charts/airflow-8.8.0/templates/pvc-dags.yaml new file mode 100644 index 0000000..c37685b --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/pvc-dags.yaml @@ -0,0 +1,24 @@ +{{- if and (.Values.dags.persistence.enabled) (not .Values.dags.persistence.existingClaim) }} +kind: PersistentVolumeClaim +apiVersion: v1 +metadata: + name: {{ printf "%s-dags" (include "airflow.fullname" . | trunc 58) }} + labels: + app: {{ include "airflow.labels.app" . }} + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + accessModes: + - {{ .Values.dags.persistence.accessMode | quote }} + resources: + requests: + storage: {{ .Values.dags.persistence.size | quote }} + {{- if .Values.dags.persistence.storageClass }} + {{- if (eq "-" .Values.dags.persistence.storageClass) }} + storageClassName: "" + {{- else }} + storageClassName: "{{ .Values.dags.persistence.storageClass }}" + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/templates/pvc-logs.yaml b/charts/deps/charts/airflow-8.8.0/templates/pvc-logs.yaml new file mode 100644 index 0000000..b093832 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/pvc-logs.yaml @@ -0,0 +1,24 @@ +{{- if and (.Values.logs.persistence.enabled) (not .Values.logs.persistence.existingClaim) }} +kind: PersistentVolumeClaim +apiVersion: v1 +metadata: + name: {{ printf "%s-logs" (include "airflow.fullname" . | trunc 58) }} + labels: + app: {{ include "airflow.labels.app" . }} + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + accessModes: + - {{ .Values.logs.persistence.accessMode | quote }} + resources: + requests: + storage: {{ .Values.logs.persistence.size | quote }} + {{- if .Values.logs.persistence.storageClass }} + {{- if (eq "-" .Values.logs.persistence.storageClass) }} + storageClassName: "" + {{- else }} + storageClassName: "{{ .Values.logs.persistence.storageClass }}" + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/templates/rbac/airflow-role.yaml b/charts/deps/charts/airflow-8.8.0/templates/rbac/airflow-role.yaml new file mode 100644 index 0000000..3676f42 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/rbac/airflow-role.yaml @@ -0,0 +1,46 @@ +{{- if .Values.rbac.create }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "airflow.fullname" . }} + labels: + app: {{ include "airflow.labels.app" . }} + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +rules: +{{- if .Values.rbac.events }} +- apiGroups: + - "" + resources: + - events + verbs: + - "get" + - "list" +{{- end }} +- apiGroups: + - "" + resources: + - pods + verbs: + - "create" + - "get" + - "delete" + - "list" + - "patch" + - "watch" +- apiGroups: + - "" + resources: + - "pods/log" + verbs: + - "get" + - "list" +- apiGroups: + - "" + resources: + - "pods/exec" + verbs: + - "create" + - "get" +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/templates/rbac/airflow-rolebinding.yaml b/charts/deps/charts/airflow-8.8.0/templates/rbac/airflow-rolebinding.yaml new file mode 100644 index 0000000..ba6d6e1 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/rbac/airflow-rolebinding.yaml @@ -0,0 +1,19 @@ +{{- if .Values.rbac.create }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "airflow.fullname" . }} + labels: + app: {{ include "airflow.labels.app" . }} + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "airflow.fullname" . }} +subjects: +- kind: ServiceAccount + name: {{ include "airflow.serviceAccountName" . }} + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/templates/rbac/airflow-serviceaccount.yaml b/charts/deps/charts/airflow-8.8.0/templates/rbac/airflow-serviceaccount.yaml new file mode 100644 index 0000000..9ec7056 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/rbac/airflow-serviceaccount.yaml @@ -0,0 +1,15 @@ +{{- if .Values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "airflow.serviceAccountName" . }} + {{- if .Values.serviceAccount.annotations }} + annotations: + {{- toYaml .Values.serviceAccount.annotations | nindent 4 }} + {{- end }} + labels: + app: {{ include "airflow.labels.app" . }} + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/templates/scheduler/scheduler-deployment.yaml b/charts/deps/charts/airflow-8.8.0/templates/scheduler/scheduler-deployment.yaml new file mode 100644 index 0000000..9660f83 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/scheduler/scheduler-deployment.yaml @@ -0,0 +1,251 @@ +{{- $podNodeSelector := include "airflow.podNodeSelector" (dict "Release" .Release "Values" .Values "nodeSelector" .Values.scheduler.nodeSelector) }} +{{- $podAffinity := include "airflow.podAffinity" (dict "Release" .Release "Values" .Values "affinity" .Values.scheduler.affinity) }} +{{- $podTolerations := include "airflow.podTolerations" (dict "Release" .Release "Values" .Values "tolerations" .Values.scheduler.tolerations) }} +{{- $podSecurityContext := include "airflow.podSecurityContext" (dict "Release" .Release "Values" .Values "securityContext" .Values.scheduler.securityContext) }} +{{- $extraPipPackages := concat .Values.airflow.extraPipPackages .Values.scheduler.extraPipPackages }} +{{- $extraVolumeMounts := .Values.scheduler.extraVolumeMounts }} +{{- $volumeMounts := include "airflow.volumeMounts" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages "extraVolumeMounts" $extraVolumeMounts) }} +{{- $extraVolumes := .Values.scheduler.extraVolumes }} +{{- $volumes := include "airflow.volumes" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages "extraVolumes" $extraVolumes "extraVolumeMounts" $extraVolumeMounts) }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "airflow.fullname" . }}-scheduler + {{- if .Values.scheduler.annotations }} + annotations: + {{- toYaml .Values.scheduler.annotations | nindent 4 }} + {{- end }} + labels: + app: {{ include "airflow.labels.app" . }} + component: scheduler + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + {{- if .Values.scheduler.labels }} + {{- toYaml .Values.scheduler.labels | nindent 4 }} + {{- end }} +spec: + replicas: {{ .Values.scheduler.replicas }} + strategy: + type: RollingUpdate + rollingUpdate: + {{- if .Values.airflow.legacyCommands }} + ## only one scheduler can run concurrently (Airflow 1.10) + maxSurge: 0 + maxUnavailable: 100% + {{- else }} + ## multiple schedulers can run concurrently (Airflow 2.0) + maxSurge: 25% + maxUnavailable: 0 + {{- end }} + selector: + matchLabels: + app: {{ include "airflow.labels.app" . }} + component: scheduler + release: {{ .Release.Name }} + template: + metadata: + annotations: + checksum/secret-config-envs: {{ include (print $.Template.BasePath "/config/secret-config-envs.yaml") . | sha256sum }} + checksum/secret-local-settings: {{ include (print $.Template.BasePath "/config/secret-local-settings.yaml") . | sha256sum }} + {{- if include "airflow.executor.kubernetes_like" . }} + checksum/config-pod-template: {{ include (print $.Template.BasePath "/config/configmap-pod-template.yaml") . | sha256sum }} + {{- end }} + {{- if .Values.airflow.podAnnotations }} + {{- toYaml .Values.airflow.podAnnotations | nindent 8 }} + {{- end }} + {{- if .Values.scheduler.podAnnotations }} + {{- toYaml .Values.scheduler.podAnnotations | nindent 8 }} + {{- end }} + {{- if .Values.scheduler.safeToEvict }} + cluster-autoscaler.kubernetes.io/safe-to-evict: "true" + {{- end }} + labels: + app: {{ include "airflow.labels.app" . }} + component: scheduler + release: {{ .Release.Name }} + {{- if .Values.scheduler.podLabels }} + {{- toYaml .Values.scheduler.podLabels | nindent 8 }} + {{- end }} + spec: + restartPolicy: Always + {{- if .Values.airflow.image.pullSecret }} + imagePullSecrets: + - name: {{ .Values.airflow.image.pullSecret }} + {{- end }} + {{- if $podNodeSelector }} + nodeSelector: + {{- $podNodeSelector | nindent 8 }} + {{- end }} + {{- if $podAffinity }} + affinity: + {{- $podAffinity | nindent 8 }} + {{- end }} + {{- if $podTolerations }} + tolerations: + {{- $podTolerations | nindent 8 }} + {{- end }} + {{- if $podSecurityContext }} + securityContext: + {{- $podSecurityContext | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "airflow.serviceAccountName" . }} + initContainers: + {{- if $extraPipPackages }} + {{- include "airflow.init_container.install_pip_packages" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages) | indent 8 }} + {{- end }} + {{- if .Values.dags.gitSync.enabled }} + {{- include "airflow.container.git_sync" (dict "Release" .Release "Values" .Values "sync_one_time" "true") | indent 8 }} + {{- end }} + {{- include "airflow.init_container.check_db" (dict "Release" .Release "Values" .Values "volumeMounts" $volumeMounts) | indent 8 }} + {{- include "airflow.init_container.wait_for_db_migrations" (dict "Release" .Release "Values" .Values "volumeMounts" $volumeMounts) | indent 8 }} + {{- if .Values.scheduler.extraInitContainers }} + {{- toYaml .Values.scheduler.extraInitContainers | nindent 8 }} + {{- end }} + containers: + - name: airflow-scheduler + {{- include "airflow.image" . | indent 10 }} + resources: + {{- toYaml .Values.scheduler.resources | nindent 12 }} + envFrom: + {{- include "airflow.envFrom" . | indent 12 }} + env: + {{- include "airflow.env" . | indent 12 }} + command: + {{- include "airflow.command" . | indent 12 }} + args: + - "bash" + - "-c" + - "exec airflow scheduler -n {{ .Values.scheduler.numRuns }}" + {{- if .Values.scheduler.livenessProbe.enabled }} + livenessProbe: + initialDelaySeconds: {{ .Values.scheduler.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.scheduler.livenessProbe.periodSeconds }} + failureThreshold: {{ .Values.scheduler.livenessProbe.failureThreshold }} + timeoutSeconds: {{ .Values.scheduler.livenessProbe.timeoutSeconds }} + exec: + command: + {{- include "airflow.command" . | indent 16 }} + - "python" + - "-Wignore" + - "-c" + - | + import os + import sys + + # suppress logs triggered from importing airflow packages + {{- if .Values.airflow.legacyCommands }} + os.environ["AIRFLOW__CORE__LOGGING_LEVEL"] = "ERROR" + {{- else }} + os.environ["AIRFLOW__LOGGING__LOGGING_LEVEL"] = "ERROR" + {{- end }} + + # shared imports + try: + from airflow.jobs.job import Job + except ImportError: + # `BaseJob` was renamed to `Job` in airflow 2.6.0 + from airflow.jobs.base_job import BaseJob as Job + from airflow.utils.db import create_session + from airflow.utils.net import get_hostname + + # heartbeat check imports + try: + from airflow.jobs.scheduler_job_runner import SchedulerJobRunner + except ImportError: + # `SchedulerJob` is wrapped by `SchedulerJobRunner` since airflow 2.6.0 + from airflow.jobs.scheduler_job import SchedulerJob as SchedulerJobRunner + + {{- if .Values.scheduler.livenessProbe.taskCreationCheck.enabled }} + {{ "" }} + # task creation check imports + try: + from airflow.jobs.local_task_job_runner import LocalTaskJobRunner + except ImportError: + # `LocalTaskJob` is wrapped by `LocalTaskJobRunner` since airflow 2.6.0 + from airflow.jobs.local_task_job import LocalTaskJob as LocalTaskJobRunner + from airflow.utils import timezone + {{- end }} + + with create_session() as session: + ######################## + # heartbeat check + ######################## + # ensure the SchedulerJob with most recent heartbeat for this `hostname` is alive + hostname = get_hostname() + scheduler_job = session \ + .query(Job) \ + .filter_by(job_type=SchedulerJobRunner.job_type) \ + .filter_by(hostname=hostname) \ + .order_by(Job.latest_heartbeat.desc()) \ + .limit(1) \ + .first() + if (scheduler_job is not None) and scheduler_job.is_alive(): + pass + else: + sys.exit(f"The SchedulerJob (id={scheduler_job.id}) for hostname '{hostname}' is not alive") + {{- if .Values.scheduler.livenessProbe.taskCreationCheck.enabled }} + {{- $min_scheduler_age := .Values.scheduler.livenessProbe.taskCreationCheck.schedulerAgeBeforeCheck }} + {{- if not (or (typeIs "float64" $min_scheduler_age) (typeIs "int64" $min_scheduler_age)) }} + {{- /* the type of a number could be float64 or int64 depending on how it was set (values.yaml, or --set) */ -}} + {{ required (printf "`scheduler.livenessProbe.taskCreationCheck.schedulerAgeBeforeCheck` must be int-type, but got %s!" (typeOf $min_scheduler_age)) nil }} + {{- end }} + {{- $task_job_threshold := .Values.scheduler.livenessProbe.taskCreationCheck.thresholdSeconds }} + {{- if not (or (typeIs "float64" $task_job_threshold) (typeIs "int64" $task_job_threshold)) }} + {{- /* the type of a number could be float64 or int64 depending on how it was set (values.yaml, or --set) */ -}} + {{ required (printf "`scheduler.livenessProbe.taskCreationCheck.thresholdSeconds` must be int-type, but got %s!" (typeOf $task_job_threshold)) nil }} + {{- end }} + ######################## + # task creation check + ######################## + min_scheduler_age = {{ $min_scheduler_age }} + if (timezone.utcnow() - scheduler_job.start_date).total_seconds() > min_scheduler_age: + # ensure the most recent LocalTaskJob had a start_date in the last `task_job_threshold` seconds + task_job_threshold = {{ $task_job_threshold }} + task_job = session \ + .query(Job) \ + .filter_by(job_type=LocalTaskJobRunner.job_type) \ + .order_by(Job.id.desc()) \ + .limit(1) \ + .first() + if task_job is not None: + if (timezone.utcnow() - task_job.start_date).total_seconds() < task_job_threshold: + pass + else: + sys.exit( + f"The most recent LocalTaskJob (id={task_job.id}, dag_id={task_job.dag_id}) " + f"started over {task_job_threshold} seconds ago" + ) + {{- end }} + {{- end }} + {{- if or ($volumeMounts) (include "airflow.executor.kubernetes_like" .) }} + volumeMounts: + {{- $volumeMounts | indent 12 }} + {{- if include "airflow.executor.kubernetes_like" . }} + - name: pod-template + mountPath: /opt/airflow/pod_templates/pod_template.yaml + subPath: pod_template.yaml + readOnly: true + {{- end }} + {{- end }} + {{- if .Values.dags.gitSync.enabled }} + {{- include "airflow.container.git_sync" . | indent 8 }} + {{- end }} + {{- if .Values.scheduler.logCleanup.enabled }} + {{- $lc_resources := .Values.scheduler.logCleanup.resources }} + {{- $lc_retention_min := .Values.scheduler.logCleanup.retentionMinutes }} + {{- $lc_interval_sec := .Values.scheduler.logCleanup.intervalSeconds }} + {{- include "airflow.container.log_cleanup" (dict "Release" .Release "Values" .Values "resources" $lc_resources "retention_min" $lc_retention_min "interval_sec" $lc_interval_sec) | indent 8 }} + {{- end }} + {{- if .Values.airflow.extraContainers }} + {{- toYaml .Values.airflow.extraContainers | nindent 8 }} + {{- end }} + {{- if or ($volumes) (include "airflow.executor.kubernetes_like" .) }} + volumes: + {{- $volumes | indent 8 }} + {{- if include "airflow.executor.kubernetes_like" . }} + - name: pod-template + configMap: + name: {{ include "airflow.fullname" . }}-pod-template + {{- end }} + {{- end }} \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/templates/scheduler/scheduler-pdb.yaml b/charts/deps/charts/airflow-8.8.0/templates/scheduler/scheduler-pdb.yaml new file mode 100644 index 0000000..ddd7d82 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/scheduler/scheduler-pdb.yaml @@ -0,0 +1,24 @@ +{{- if .Values.scheduler.podDisruptionBudget.enabled }} +apiVersion: {{ .Values.scheduler.podDisruptionBudget.apiVersion }} +kind: PodDisruptionBudget +metadata: + name: {{ include "airflow.fullname" . }}-scheduler + labels: + app: {{ include "airflow.labels.app" . }} + component: scheduler + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + {{- if .Values.scheduler.podDisruptionBudget.maxUnavailable }} + maxUnavailable: {{ .Values.scheduler.podDisruptionBudget.maxUnavailable }} + {{- end }} + {{- if .Values.scheduler.podDisruptionBudget.minAvailable }} + minAvailable: {{ .Values.scheduler.podDisruptionBudget.minAvailable }} + {{- end }} + selector: + matchLabels: + app: {{ include "airflow.labels.app" . }} + component: scheduler + release: {{ .Release.Name }} +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/templates/sync/_helpers/global_code.tpl b/charts/deps/charts/airflow-8.8.0/templates/sync/_helpers/global_code.tpl new file mode 100644 index 0000000..34addbf --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/sync/_helpers/global_code.tpl @@ -0,0 +1,130 @@ +{{/* +Code which is included in all python sync scripts. +*/}} +{{- define "airflow.sync.global_code" }} +#################### +## Global Imports ## +#################### +import logging +import os +import time +from string import Template +from typing import List, Dict, Optional + + +#################### +## Global Configs ## +#################### +# the path which Secret/ConfigMap are mounted to +CONF__TEMPLATES_PATH = "/mnt/templates" + +# how frequently to check for Secret/ConfigMap updates +CONF__TEMPLATES_SYNC_INTERVAL = 10 + +# how frequently to re-sync objects (Connections, Pools, Users, Variables) +CONF__OBJECTS_SYNC_INTERVAL = 60 + + +###################### +## Global Functions ## +###################### +def string_substitution(raw_string: Optional[str], substitution_map: Dict[str, str]) -> str: + """ + Apply bash-like substitutions to a raw string. + + Example: + - string_substitution("Hello!", None) -> "Hello!" + - string_substitution("Hello ${NAME}!", {"NAME": "Airflow"}) -> "Hello Airflow!" + """ + if raw_string and len(substitution_map) > 0: + tpl = Template(raw_string) + return tpl.safe_substitute(substitution_map) + else: + return raw_string + + +def template_mtime(template_name: str) -> float: + """ + Return the modification-time of the file storing `template_name` + """ + file_path = f"{CONF__TEMPLATES_PATH}/{template_name}" + return os.stat(file_path).st_mtime + + +def template_value(template_name: str) -> str: + """ + Return the contents of the file storing `template_name` + """ + file_path = f"{CONF__TEMPLATES_PATH}/{template_name}" + with open(file_path, "r") as f: + return f.read() + + +def refresh_template_cache(template_names: List[str], + template_mtime_cache: Dict[str, float], + template_value_cache: Dict[str, str]) -> List[str]: + """ + Refresh the provided dictionary caches of template values & mtimes. + + :param template_names: the names of all templates to refresh + :param template_mtime_cache: the dictionary cache of template file modification-times + :param template_value_cache: the dictionary cache of template values + :return: the names of templates which changed + """ + changed_templates = [] + for template_name in template_names: + old_mtime = template_mtime_cache.get(template_name, None) + new_mtime = template_mtime(template_name) + # first, check if the files were modified + if old_mtime != new_mtime: + old_value = template_value_cache.get(template_name, None) + new_value = template_value(template_name) + # second, check if the value actually changed + if old_value != new_value: + template_value_cache[template_name] = new_value + changed_templates += [template_name] + template_mtime_cache[template_name] = new_mtime + return changed_templates + + +def main(sync_forever: bool): + # initial sync of template cache + refresh_template_cache( + template_names=VAR__TEMPLATE_NAMES, + template_mtime_cache=VAR__TEMPLATE_MTIME_CACHE, + template_value_cache=VAR__TEMPLATE_VALUE_CACHE + ) + + # initial sync of objects into Airflow DB + sync_with_airflow() + + if sync_forever: + # define variables used to track how long since last refresh/sync + templates_sync_epoch = time.time() + objects_sync_epoch = time.time() + + # main loop + while True: + # monitor for template secret/configmap updates + if (time.time() - templates_sync_epoch) > CONF__TEMPLATES_SYNC_INTERVAL: + logging.debug(f"template sync interval reached, re-syncing all templates...") + changed_templates = refresh_template_cache( + template_names=VAR__TEMPLATE_NAMES, + template_mtime_cache=VAR__TEMPLATE_MTIME_CACHE, + template_value_cache=VAR__TEMPLATE_VALUE_CACHE + ) + templates_sync_epoch = time.time() + if changed_templates: + logging.info(f"template values have changed: [{','.join(changed_templates)}]") + sync_with_airflow() + objects_sync_epoch = time.time() + + # monitor for external changes to objects (like from UI) + if (time.time() - objects_sync_epoch) > CONF__OBJECTS_SYNC_INTERVAL: + logging.debug(f"sync interval reached, re-syncing all objects...") + sync_with_airflow() + objects_sync_epoch = time.time() + + # ensure we dont loop too fast + time.sleep(0.5) +{{- end }} \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/templates/sync/_helpers/sync_connections.tpl b/charts/deps/charts/airflow-8.8.0/templates/sync/_helpers/sync_connections.tpl new file mode 100644 index 0000000..2ce2399 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/sync/_helpers/sync_connections.tpl @@ -0,0 +1,219 @@ +{{/* +The python sync script for connections. +*/}} +{{- define "airflow.sync.sync_connections.py" }} +############################ +#### BEGIN: GLOBAL CODE #### +############################ +{{- include "airflow.sync.global_code" . }} +########################## +#### END: GLOBAL CODE #### +########################## + + +############# +## Imports ## +############# +from airflow.models import Connection +from airflow.utils.db import create_session + + +############# +## Classes ## +############# +class ConnectionWrapper(object): + def __init__( + self, + conn_id: str, + conn_type: str, + {{- if not .Values.airflow.legacyCommands }} + description: Optional[str] = None, + {{- end }} + host: Optional[str] = None, + login: Optional[str] = None, + password: Optional[str] = None, + schema: Optional[str] = None, + port: Optional[int] = None, + extra: Optional[str] = None, + ): + self.conn_id = conn_id + self.conn_type = conn_type + {{- if not .Values.airflow.legacyCommands }} + self.description = description + {{- end }} + self._host = host + self._login = login + self._password = password + self._schema = schema + self.port = port + self._extra = extra + + @property + def host(self) -> str: + return string_substitution(self._host, VAR__TEMPLATE_VALUE_CACHE) + + @property + def login(self) -> str: + return string_substitution(self._login, VAR__TEMPLATE_VALUE_CACHE) + + @property + def password(self) -> str: + return string_substitution(self._password, VAR__TEMPLATE_VALUE_CACHE) + + @property + def schema(self) -> str: + return string_substitution(self._schema, VAR__TEMPLATE_VALUE_CACHE) + + @property + def extra(self) -> str: + return string_substitution(self._extra, VAR__TEMPLATE_VALUE_CACHE) + + def as_connection(self) -> Connection: + return Connection( + conn_id=self.conn_id, + conn_type=self.conn_type, + {{- if not .Values.airflow.legacyCommands }} + description=self.description, + {{- end }} + host=self.host, + login=self.login, + password=self.password, + schema=self.schema, + port=self.port, + extra=self.extra, + ) + + +############### +## Variables ## +############### +VAR__TEMPLATE_NAMES = [ + {{- range $k, $v := .Values.airflow.connectionsTemplates }} + {{ $k | quote }}, + {{- end }} +] +VAR__TEMPLATE_MTIME_CACHE = {} +VAR__TEMPLATE_VALUE_CACHE = {} +VAR__CONNECTION_WRAPPERS = { + {{- range .Values.airflow.connections }} + {{ .id | quote }}: ConnectionWrapper( + conn_id={{ (required "each `id` in `airflow.connections` must be non-empty!" .id) | quote }}, + conn_type={{ (required "each `type` in `airflow.connections` must be non-empty!" .type) | quote }}, + {{- if and (.description) (not $.Values.airflow.legacyCommands) }} + description={{ .description | quote }}, + {{- end }} + {{- if .host }} + host={{ .host | quote }}, + {{- end }} + {{- if .login }} + login={{ .login | quote }}, + {{- end }} + {{- if .password }} + password={{ .password | quote }}, + {{- end }} + {{- if .schema }} + schema={{ .schema | quote }}, + {{- end }} + {{- if .port }} + {{- if not (or (typeIs "float64" .port) (typeIs "int64" .port)) }} + {{- /* the type of a number could be float64 or int64 depending on how it was set */ -}} + {{ required "each `port` in `airflow.connections` must be int-type!" nil }} + {{- end }} + port={{ .port }}, + {{- end }} + {{- if .extra }} + extra={{ .extra | quote }}, + {{- end }} + ), + {{- end }} +} + + +############### +## Functions ## +############### +def compare_connections(c1: Connection, c2: Connection) -> bool: + """ + Check if two Connection objects are identical. + """ + return ( + c1.conn_id == c2.conn_id + and c1.conn_type == c2.conn_type + {{- if not .Values.airflow.legacyCommands }} + and c1.description == c2.description + {{- end }} + and c1.host == c2.host + and c1.login == c2.login + and c1.password == c2.password + and c1.schema == c2.schema + and c1.port == c2.port + and c1.extra == c2.extra + ) + + +def sync_connection(connection_wrapper: ConnectionWrapper) -> None: + """ + Sync the Connection defined by a provided ConnectionWrapper into the airflow DB. + """ + c_id = connection_wrapper.conn_id + c_new = connection_wrapper.as_connection() + + connection_added = False + connection_updated = False + + with create_session() as session: + c_old = session.query(Connection).filter(Connection.conn_id == c_id).first() + if not c_old: + logging.info(f"Connection=`{c_id}` is missing, adding...") + session.add(c_new) + connection_added = True + else: + if compare_connections(c_new, c_old): + pass + else: + logging.info(f"Connection=`{c_id}` exists but has changed, updating...") + c_old.conn_type = c_new.conn_type + {{- if not .Values.airflow.legacyCommands }} + c_old.description = c_new.description + {{- end }} + c_old.host = c_new.host + c_old.login = c_new.login + c_old.password = c_new.password + c_old.schema = c_new.schema + c_old.port = c_new.port + c_old.extra = c_new.extra + connection_updated = True + + if connection_added: + logging.info(f"Connection=`{c_id}` was successfully added.") + if connection_updated: + logging.info(f"Connection=`{c_id}` was successfully updated.") + + +def sync_all_connections(connection_wrappers: Dict[str, ConnectionWrapper]) -> None: + """ + Sync all connections in provided `connection_wrappers`. + """ + logging.info("BEGIN: airflow connections sync") + for connection_wrapper in connection_wrappers.values(): + sync_connection(connection_wrapper) + logging.info("END: airflow connections sync") + + +def sync_with_airflow() -> None: + """ + Preform a sync of all objects with airflow (note, `sync_with_airflow()` is called in `main()` template). + """ + sync_all_connections(connection_wrappers=VAR__CONNECTION_WRAPPERS) + + +############## +## Run Main ## +############## +{{- if .Values.airflow.connectionsUpdate }} +main(sync_forever=True) +{{- else }} +main(sync_forever=False) +{{- end }} + +{{- end }} \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/templates/sync/_helpers/sync_pools.tpl b/charts/deps/charts/airflow-8.8.0/templates/sync/_helpers/sync_pools.tpl new file mode 100644 index 0000000..505b205 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/sync/_helpers/sync_pools.tpl @@ -0,0 +1,206 @@ +{{/* +The python sync script for pools. +*/}} +{{- define "airflow.sync.sync_pools.py" }} +############################ +#### BEGIN: GLOBAL CODE #### +############################ +{{- include "airflow.sync.global_code" . }} +########################## +#### END: GLOBAL CODE #### +########################## + +############# +## Imports ## +############# +from airflow.models import Pool +from airflow.utils.db import create_session +from croniter import croniter +from datetime import datetime +from typing import Tuple + + +############# +## Classes ## +############# +class ScheduledPolicy(object): + def __init__( + self, + name: str, + recurrence: str, + slots: int, + ): + if not croniter.is_valid(recurrence): + raise ValueError(f"Invalid recurrence '{recurrence}' for schedule '{name}'") + + self.name = name + self.recurrence = recurrence + self.slots = slots + + def last_match_time(self, now: datetime) -> datetime: + return croniter(expr_format=self.recurrence, start_time=now).get_prev(ret_type=datetime) + + +class PoolWrapper(object): + def __init__( + self, + name: str, + description: str, + slots: int, + include_deferred: bool, + policies: List[ScheduledPolicy], + enable_policies: bool, + ): + self.name = name + self.description = description + self.slots = slots + self.include_deferred = include_deferred + self.policies = policies + self.enable_policies = enable_policies + + def as_pool(self) -> Pool: + pool = Pool() + pool.pool = self.name + # NOTE: include_deferred is only available in Airflow 2.7.0+ + if hasattr(Pool, "include_deferred"): + pool.include_deferred = self.include_deferred + if self._has_policies(): + most_recent_policy = self._most_recent_policy() + pool.slots = most_recent_policy.slots + pool.description = f"{self.description} - MOST_RECENT_POLICY='{most_recent_policy.name}'" + else: + pool.slots = self.slots + pool.description = self.description + return pool + + def _has_policies(self) -> bool: + return self.enable_policies and len(self.policies) > 0 + + def _most_recent_policy(self) -> ScheduledPolicy: + now = datetime.utcnow() + return max(self.policies, key=lambda policy: policy.last_match_time(now)) + + +############### +## Variables ## +############### +VAR__TEMPLATE_NAMES = [] +VAR__TEMPLATE_MTIME_CACHE = {} +VAR__TEMPLATE_VALUE_CACHE = {} +VAR__POOL_WRAPPERS = { + {{- range .Values.airflow.pools }} + {{ .name | quote }}: PoolWrapper( + name={{ (required "the `name` in each `airflow.pools[]` must be non-empty!" .name) | quote }}, + description={{ (required "the `description` in each `airflow.pools[]` must be non-empty!" .description) | quote }}, + {{- if not (or (typeIs "float64" .slots) (typeIs "int64" .slots)) }} + {{- /* the type of a number could be float64 or int64 depending on how it was set (values.yaml, or --set) */ -}} + {{ required "the `slots` in each `airflow.pools[]` must be int-type!" nil }} + {{- end }} + slots={{ (required "the `slots` in each `airflow.pools[]` must be non-empty!" .slots) }}, + {{- $include_deferred := dig "include_deferred" nil . }} + {{- if not (or (typeIs "bool" $include_deferred) (eq $include_deferred nil)) }} + {{ required "if specified, the `include_deferred` in each `airflow.pools[]` must be bool-type!" nil }} + {{- end }} + {{- if $include_deferred }} + include_deferred=True, + {{- else }} + include_deferred=False, + {{- end }} + policies=[ + {{- range .policies }} + ScheduledPolicy( + name={{ (required "the `name` in each `airflow.pools[].policies[]` must be non-empty!" .name) | quote }}, + recurrence={{ (required "the `recurrence` in each `airflow.pools[].policies[]` must be non-empty!" .recurrence) | quote }}, + {{- if not (or (typeIs "float64" .slots) (typeIs "int64" .slots)) }} + {{- /* the type of a number could be float64 or int64 depending on how it was set (values.yaml, or --set) */ -}} + {{ required "the `slots` in each `airflow.pools[].policies[]` must be int-type!" nil }} + {{- end }} + slots={{ (required "the `slots` in each `airflow.pools[].policies[]` must be non-empty!" .slots) }}, + ), + {{- end }} + ], + {{- if $.Values.airflow.poolsUpdate }} + enable_policies=True, + {{- else }} + enable_policies=False, + {{- end }} + ), + {{- end }} +} + + +############### +## Functions ## +############### +def compare_pools(p1: Pool, p2: Pool) -> bool: + """ + Check if two Pool objects are identical. + """ + return ( + p1.pool == p1.pool + and p1.description == p2.description + and p1.slots == p2.slots + and getattr(p1, "include_deferred", False) == getattr(p2, "include_deferred", False) + ) + + +def sync_pool(pool_wrapper: PoolWrapper) -> None: + """ + Sync the Pool defined by a provided PoolWrapper into the airflow DB. + """ + p_name = pool_wrapper.name + p_new = pool_wrapper.as_pool() + + pool_added = False + pool_updated = False + + with create_session() as session: + p_old = session.query(Pool).filter(Pool.pool == p_name).first() + if not p_old: + logging.info(f"Pool=`{p_name}` is missing, adding...") + session.add(p_new) + pool_added = True + else: + if compare_pools(p_new, p_old): + pass + else: + logging.info(f"Pool=`{p_name}` exists but has changed, updating...") + p_old.description = p_new.description + p_old.slots = p_new.slots + if hasattr(Pool, "include_deferred"): + p_old.include_deferred = p_new.include_deferred + pool_updated = True + + if pool_added: + logging.info(f"Pool=`{p_name}` was successfully added.") + if pool_updated: + logging.info(f"Pool=`{p_name}` was successfully updated.") + + +def sync_all_pools(pool_wrappers: Dict[str, PoolWrapper]) -> None: + """ + Sync all pools in provided `pool_wrappers`. + """ + logging.info("BEGIN: airflow pools sync") + for pool_wrapper in pool_wrappers.values(): + sync_pool(pool_wrapper) + logging.info("END: airflow pools sync") + + +def sync_with_airflow() -> None: + """ + Preform a sync of all objects with airflow (note, `sync_with_airflow()` is called in `main()` template). + """ + sync_all_pools(pool_wrappers=VAR__POOL_WRAPPERS) + + +############## +## Run Main ## +############## +{{- if .Values.airflow.poolsUpdate }} +main(sync_forever=True) +{{- else }} +main(sync_forever=False) +{{- end }} + +{{- end }} \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/templates/sync/_helpers/sync_users.tpl b/charts/deps/charts/airflow-8.8.0/templates/sync/_helpers/sync_users.tpl new file mode 100644 index 0000000..9f75c6d --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/sync/_helpers/sync_users.tpl @@ -0,0 +1,240 @@ +{{/* +The python sync script for users. +*/}} +{{- define "airflow.sync.sync_users.py" }} +############################ +#### BEGIN: GLOBAL CODE #### +############################ +{{- include "airflow.sync.global_code" . }} +########################## +#### END: GLOBAL CODE #### +########################## + + +############# +## Imports ## +############# +import sys +from werkzeug.security import check_password_hash, generate_password_hash +{{- if .Values.airflow.legacyCommands }} +import airflow.www_rbac.app as www_app +flask_app, flask_appbuilder = www_app.create_app() +{{- else }} +import airflow.www.app as www_app +flask_app = www_app.create_app() +flask_appbuilder = flask_app.appbuilder +{{- end }} + +# airflow moves the User and Role models around in different versions +try: + # since 2.7.0 + from airflow.auth.managers.fab.models import User, Role +except ModuleNotFoundError: + try: + # from 2.3.0 to 2.6.3 + from airflow.www.fab_security.sqla.models import User, Role + except ModuleNotFoundError: + # before 2.3.0 + from flask_appbuilder.security.sqla.models import User, Role + + +############# +## Classes ## +############# +class UserWrapper(object): + def __init__( + self, + username: str, + first_name: Optional[str] = None, + last_name: Optional[str] = None, + email: Optional[str] = None, + roles: Optional[List[str]] = None, + password: Optional[str] = None + ): + self.username = username + self._first_name = first_name + self._last_name = last_name + self._email = email + self.roles = roles + self._password = password + + @property + def first_name(self) -> str: + return string_substitution(self._first_name, VAR__TEMPLATE_VALUE_CACHE) + + @property + def last_name(self) -> str: + return string_substitution(self._last_name, VAR__TEMPLATE_VALUE_CACHE) + + @property + def email(self) -> str: + return string_substitution(self._email, VAR__TEMPLATE_VALUE_CACHE) + + @property + def password(self) -> str: + return string_substitution(self._password, VAR__TEMPLATE_VALUE_CACHE) + + def as_dict(self) -> Dict[str, str]: + return { + "username": self.username, + "first_name": self.first_name, + "last_name": self.last_name, + "email": self.email, + "roles": [find_role(role_name=role_name) for role_name in self.roles], + "password": self.password + } + + +############### +## Variables ## +############### +VAR__TEMPLATE_NAMES = [ + {{- range $k, $v := .Values.airflow.usersTemplates }} + {{ $k | quote }}, + {{- end }} +] +VAR__TEMPLATE_MTIME_CACHE = {} +VAR__TEMPLATE_VALUE_CACHE = {} +VAR__USER_WRAPPERS = { + {{- range .Values.airflow.users }} + {{ .username | quote }}: UserWrapper( + username={{ (required "each `username` in `airflow.users` must be non-empty!" .username) | quote }}, + first_name={{ (required "each `firstName` in `airflow.users` must be non-empty!" .firstName) | quote }}, + last_name={{ (required "each `lastName` in `airflow.users` must be non-empty!" .lastName) | quote }}, + email={{ (required "each `email` in `airflow.users` must be non-empty!" .email) | quote }}, + roles=[ + {{- if kindIs "string" .role }} + {{- (required "each string-type `role` in `airflow.users` must be non-empty!" .role) | quote | indent 8 }}, + {{- else if kindIs "slice" .role }} + {{- if eq (len .role) 0 }} + {{ required "each list-type `role` in `airflow.users` must contain at least one element!" nil }} + {{- end }} + {{- range .role }} + {{- (required "each list-type `role` in `airflow.users` must not contain any empty elements!" .) | quote | indent 8 }}, + {{- end }} + {{- else }} + {{ required (printf "each `role` in `airflow.users` must be string-type or list-type, but got '%s'!" (kindOf .role)) nil }} + {{- end }} + ], + password={{ (required "each `password` in `airflow.users` must be non-empty!" .password) | quote }}, + ), + {{- end }} +} + + +############### +## Functions ## +############### +def find_role(role_name: str) -> Role: + """ + Get the FAB Role model associated with a `role_name`. + """ + found_role = flask_appbuilder.sm.find_role(role_name) + if found_role: + return found_role + else: + valid_roles = flask_appbuilder.sm.get_all_roles() + logging.error(f"Failed to find role=`{role_name}`, valid roles are: {valid_roles}") + sys.exit(1) + + +def compare_role_lists(role_list_1: List[Role], role_list_2: List[Role]) -> bool: + """ + Check if two lists of FAB Roles contain the same roles (ignores duplicates and order). + """ + name_set_1 = set(role.name for role in role_list_1) + name_set_2 = set(role.name for role in role_list_2) + return name_set_1 == name_set_2 + + + +def compare_users(user_dict: Dict, user_model: User) -> bool: + """ + Check if user info (stored in dict) is identical to a FAB User model. + """ + return ( + user_dict["username"] == user_model.username + and user_dict["first_name"] == user_model.first_name + and user_dict["last_name"] == user_model.last_name + and user_dict["email"] == user_model.email + and compare_role_lists(user_dict["roles"], user_model.roles) + and check_password_hash(pwhash=user_model.password, password=user_dict["password"]) + ) + + +def sync_user(user_wrapper: UserWrapper) -> None: + """ + Sync the User defined by a provided UserWrapper into the FAB DB. + """ + username = user_wrapper.username + u_new = user_wrapper.as_dict() + u_old = flask_appbuilder.sm.find_user(username=username) + + if not u_old: + logging.info(f"User=`{username}` is missing, adding...") + created_user = flask_appbuilder.sm.add_user( + username=u_new["username"], + first_name=u_new["first_name"], + last_name=u_new["last_name"], + email=u_new["email"], + # in old versions of flask_appbuilder `add_user(role=` can only add exactly one role + # (unchecked 0 index is safe because we require at least one role using helm values validation) + role=u_new["roles"][0], + password=u_new["password"] + ) + if created_user: + # add the full list of roles (we only added the first one above) + created_user.roles = u_new["roles"] + logging.info(f"User=`{username}` was successfully added.") + else: + logging.error(f"Failed to add User=`{username}`") + sys.exit(1) + else: + if compare_users(u_new, u_old): + pass + else: + logging.info(f"User=`{username}` exists but has changed, updating...") + u_old.first_name = u_new["first_name"] + u_old.last_name = u_new["last_name"] + u_old.email = u_new["email"] + u_old.roles = u_new["roles"] + u_old.password = generate_password_hash(u_new["password"]) + # strange check for False is because update_user() returns None for success + # but in future might return the User model + if not (flask_appbuilder.sm.update_user(u_old) is False): + logging.info(f"User=`{username}` was successfully updated.") + else: + logging.error(f"Failed to update User=`{username}`") + sys.exit(1) + + +def sync_all_users(user_wrappers: Dict[str, UserWrapper]) -> None: + """ + Sync all users in provided `user_wrappers`. + """ + logging.info("BEGIN: airflow users sync") + for user_wrapper in user_wrappers.values(): + sync_user(user_wrapper) + logging.info("END: airflow users sync") + + # ensures than any SQLAlchemy sessions are closed (so we don't hold a connection to the database) + flask_app.do_teardown_appcontext() + + +def sync_with_airflow() -> None: + """ + Preform a sync of all objects with airflow (note, `sync_with_airflow()` is called in `main()` template). + """ + sync_all_users(user_wrappers=VAR__USER_WRAPPERS) + + +############## +## Run Main ## +############## +{{- if .Values.airflow.usersUpdate }} +main(sync_forever=True) +{{- else }} +main(sync_forever=False) +{{- end }} + +{{- end }} \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/templates/sync/_helpers/sync_variables.tpl b/charts/deps/charts/airflow-8.8.0/templates/sync/_helpers/sync_variables.tpl new file mode 100644 index 0000000..74636b5 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/sync/_helpers/sync_variables.tpl @@ -0,0 +1,129 @@ +{{/* +The python sync script for variables. +*/}} +{{- define "airflow.sync.sync_variables.py" }} +############################ +#### BEGIN: GLOBAL CODE #### +############################ +{{- include "airflow.sync.global_code" . }} +########################## +#### END: GLOBAL CODE #### +########################## + +############# +## Imports ## +############# +from airflow.models import Variable +from airflow.utils.db import create_session + + +############# +## Classes ## +############# +class VariableWrapper(object): + def __init__( + self, + key: str, + val: str, + ): + self.key = key + self._val = val + + @property + def val(self) -> str: + return string_substitution(self._val, VAR__TEMPLATE_VALUE_CACHE) + + def as_variable(self) -> Variable: + return Variable( + key=self.key, + val=self.val + ) + + +############### +## Variables ## +############### +VAR__TEMPLATE_NAMES = [ + {{- range $k, $v := .Values.airflow.variablesTemplates }} + {{ $k | quote }}, + {{- end }} +] +VAR__TEMPLATE_MTIME_CACHE = {} +VAR__TEMPLATE_VALUE_CACHE = {} +VAR__VARIABLE_WRAPPERS = { + {{- range .Values.airflow.variables }} + {{ .key | quote }}: VariableWrapper( + key={{ (required "each `key` in `airflow.variables` must be non-empty!" .key) | quote }}, + val={{ (required "each `value` in `airflow.variables` must be non-empty!" .value) | quote }}, + ), + {{- end }} +} + + +############### +## Functions ## +############### +def compare_variables(v1: Variable, v2: Variable) -> bool: + """ + Check if two Variable objects are identical. + """ + return v1.key == v2.key and v1.val == v2.val + + +def sync_variable(variable_wrapper: VariableWrapper) -> None: + """ + Sync the Variable defined by a provided VariableWrapper into the airflow DB. + """ + v_key = variable_wrapper.key + v_new = variable_wrapper.as_variable() + + variable_added = False + variable_updated = False + + with create_session() as session: + v_old = session.query(Variable).filter(Variable.key == v_key).first() + if not v_old: + logging.info(f"Variable=`{v_key}` is missing, adding...") + session.add(v_new) + variable_added = True + else: + if compare_variables(v_new, v_old): + pass + else: + logging.info(f"Variable=`{v_key}` exists but has changed, updating...") + v_old.val = v_new.val + variable_updated = True + + if variable_added: + logging.info(f"Variable=`{v_key}` was successfully added.") + if variable_updated: + logging.info(f"Variable=`{v_key}` was successfully updated.") + + +def sync_all_variables(variable_wrappers: Dict[str, VariableWrapper]) -> None: + """ + Sync all variables in provided `variable_wrappers`. + """ + logging.info("BEGIN: airflow variables sync") + for variable_wrapper in variable_wrappers.values(): + sync_variable(variable_wrapper) + logging.info("END: airflow variables sync") + + +def sync_with_airflow() -> None: + """ + Preform a sync of all objects with airflow (note, `sync_with_airflow()` is called in `main()` template). + """ + sync_all_variables(variable_wrappers=VAR__VARIABLE_WRAPPERS) + + +############## +## Run Main ## +############## +{{- if .Values.airflow.variablesUpdate }} +main(sync_forever=True) +{{- else }} +main(sync_forever=False) +{{- end }} + +{{- end }} \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/templates/sync/sync-connections-deployment.yaml b/charts/deps/charts/airflow-8.8.0/templates/sync/sync-connections-deployment.yaml new file mode 100644 index 0000000..8273430 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/sync/sync-connections-deployment.yaml @@ -0,0 +1,150 @@ +{{- if and (.Values.airflow.connections) (.Values.airflow.connectionsUpdate) }} +{{- $podNodeSelector := include "airflow.podNodeSelector" (dict "Release" .Release "Values" .Values "nodeSelector" .Values.airflow.sync.nodeSelector) }} +{{- $podAffinity := include "airflow.podAffinity" (dict "Release" .Release "Values" .Values "affinity" .Values.airflow.sync.affinity) }} +{{- $podTolerations := include "airflow.podTolerations" (dict "Release" .Release "Values" .Values "tolerations" .Values.airflow.sync.tolerations) }} +{{- $podSecurityContext := include "airflow.podSecurityContext" (dict "Release" .Release "Values" .Values "securityContext" .Values.airflow.sync.securityContext) }} +{{- $extraPipPackages := .Values.airflow.extraPipPackages }} +{{- $volumeMounts := include "airflow.volumeMounts" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages) }} +{{- $volumes := include "airflow.volumes" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages) }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "airflow.fullname" . }}-sync-connections + labels: + app: {{ include "airflow.labels.app" . }} + component: sync-connections + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + {{- if .Values.airflow.sync.labels }} + {{- toYaml .Values.airflow.sync.labels | nindent 4 }} + {{- end }} + {{- if .Values.airflow.sync.annotations }} + annotations: + {{- toYaml .Values.airflow.sync.annotations | nindent 4 }} + {{- end }} +spec: + replicas: 1 + strategy: + ## only 1 replica should run at a time + type: Recreate + selector: + matchLabels: + app: {{ include "airflow.labels.app" . }} + component: sync-connections + release: {{ .Release.Name }} + template: + metadata: + annotations: + checksum/secret-config-envs: {{ include (print $.Template.BasePath "/config/secret-config-envs.yaml") . | sha256sum }} + checksum/secret-local-settings: {{ include (print $.Template.BasePath "/config/secret-local-settings.yaml") . | sha256sum }} + checksum/sync-connections-script: {{ include "airflow.sync.sync_connections.py" . | sha256sum }} + {{- if .Values.airflow.podAnnotations }} + {{- toYaml .Values.airflow.podAnnotations | nindent 8 }} + {{- end }} + {{- if .Values.airflow.sync.podAnnotations }} + {{- toYaml .Values.airflow.sync.podAnnotations | nindent 8 }} + {{- end }} + {{- if .Values.airflow.sync.safeToEvict }} + cluster-autoscaler.kubernetes.io/safe-to-evict: "true" + {{- end }} + labels: + app: {{ include "airflow.labels.app" . }} + component: sync-connections + release: {{ .Release.Name }} + {{- if .Values.airflow.sync.podLabels }} + {{- toYaml .Values.airflow.sync.podLabels | nindent 8 }} + {{- end }} + spec: + restartPolicy: Always + {{- if .Values.airflow.image.pullSecret }} + imagePullSecrets: + - name: {{ .Values.airflow.image.pullSecret }} + {{- end }} + {{- if $podNodeSelector }} + nodeSelector: + {{- $podNodeSelector | nindent 8 }} + {{- end }} + {{- if $podAffinity }} + affinity: + {{- $podAffinity | nindent 8 }} + {{- end }} + {{- if $podTolerations }} + tolerations: + {{- $podTolerations | nindent 8 }} + {{- end }} + {{- if $podSecurityContext }} + securityContext: + {{- $podSecurityContext | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "airflow.serviceAccountName" . }} + initContainers: + {{- if $extraPipPackages }} + {{- include "airflow.init_container.install_pip_packages" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages) | indent 8 }} + {{- end }} + {{- if .Values.dags.gitSync.enabled }} + ## git-sync is included so "airflow plugins" & "python packages" can be stored in the dags repo + {{- include "airflow.container.git_sync" (dict "Release" .Release "Values" .Values "sync_one_time" "true") | indent 8 }} + {{- end }} + {{- include "airflow.init_container.check_db" (dict "Release" .Release "Values" .Values "volumeMounts" $volumeMounts) | indent 8 }} + {{- include "airflow.init_container.wait_for_db_migrations" (dict "Release" .Release "Values" .Values "volumeMounts" $volumeMounts) | indent 8 }} + containers: + - name: sync-airflow-connections + {{- include "airflow.image" . | indent 10 }} + resources: + {{- toYaml .Values.airflow.sync.resources | nindent 12 }} + envFrom: + {{- include "airflow.envFrom" . | indent 12 }} + env: + {{- include "airflow.env" . | indent 12 }} + command: + {{- include "airflow.command" . | indent 12 }} + args: + - "python" + - "-u" + - "/mnt/scripts/sync_connections.py" + volumeMounts: + {{- $volumeMounts | indent 12 }} + - name: scripts + mountPath: /mnt/scripts + readOnly: true + {{- if .Values.airflow.connectionsTemplates }} + - name: templates + mountPath: "/mnt/templates" + readOnly: true + {{- end }} + {{- if .Values.dags.gitSync.enabled }} + ## git-sync is included so "airflow plugins" & "python packages" can be stored in the dags repo + {{- include "airflow.container.git_sync" . | indent 8 }} + {{- end }} + volumes: + {{- $volumes | indent 8 }} + - name: scripts + secret: + secretName: {{ include "airflow.fullname" . }}-sync-connections + {{- if .Values.airflow.connectionsTemplates }} + - name: templates + projected: + sources: + {{- range $k, $v := .Values.airflow.connectionsTemplates }} + {{- if not (regexMatch "^[a-zA-Z_][a-zA-Z0-9_]*$" $k) }} + {{ required "each key in `airflow.connectionsTemplates` must match the regex: ^[a-zA-Z_][a-zA-Z0-9_]*$" nil }} + {{- end }} + {{- if eq ($v.kind | lower) "configmap" }} + - configMap: + name: {{ $v.name | quote }} + items: + - key: {{ $v.key | quote }} + path: {{ $k | quote }} + {{- else if eq ($v.kind | lower) "secret" }} + - secret: + name: {{ $v.name | quote }} + items: + - key: {{ $v.key | quote }} + path: {{ $k | quote }} + {{- else }} + {{ required "each `kind` in `airflow.connectionsTemplates` must be one of ['configmap', 'secret']!" nil }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/templates/sync/sync-connections-job.yaml b/charts/deps/charts/airflow-8.8.0/templates/sync/sync-connections-job.yaml new file mode 100644 index 0000000..c77002a --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/sync/sync-connections-job.yaml @@ -0,0 +1,140 @@ +{{- if and (.Values.airflow.connections) (not .Values.airflow.connectionsUpdate) }} +{{- $podNodeSelector := include "airflow.podNodeSelector" (dict "Release" .Release "Values" .Values "nodeSelector" .Values.scheduler.nodeSelector) }} +{{- $podAffinity := include "airflow.podAffinity" (dict "Release" .Release "Values" .Values "affinity" .Values.scheduler.affinity) }} +{{- $podTolerations := include "airflow.podTolerations" (dict "Release" .Release "Values" .Values "tolerations" .Values.scheduler.tolerations) }} +{{- $podSecurityContext := include "airflow.podSecurityContext" (dict "Release" .Release "Values" .Values "securityContext" .Values.airflow.sync.securityContext) }} +{{- $extraPipPackages := .Values.airflow.extraPipPackages }} +{{- $volumeMounts := include "airflow.volumeMounts" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages) }} +{{- $volumes := include "airflow.volumes" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages) }} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "airflow.fullname" . }}-sync-connections + labels: + app: {{ include "airflow.labels.app" . }} + component: sync-connections + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + {{- if .Values.airflow.sync.labels }} + {{- toYaml .Values.airflow.sync.labels | nindent 4 }} + {{- end }} + annotations: + helm.sh/hook: post-install,post-upgrade + helm.sh/hook-weight: "0" + helm.sh/hook-delete-policy: before-hook-creation + {{- if .Values.airflow.sync.annotations }} + {{- toYaml .Values.airflow.sync.annotations | nindent 4 }} + {{- end }} +spec: + template: + metadata: + annotations: + checksum/secret-config-envs: {{ include (print $.Template.BasePath "/config/secret-config-envs.yaml") . | sha256sum }} + checksum/secret-local-settings: {{ include (print $.Template.BasePath "/config/secret-local-settings.yaml") . | sha256sum }} + checksum/sync-connections-script: {{ include "airflow.sync.sync_connections.py" . | sha256sum }} + {{- if .Values.airflow.podAnnotations }} + {{- toYaml .Values.airflow.podAnnotations | nindent 8 }} + {{- end }} + {{- if .Values.airflow.sync.podAnnotations }} + {{- toYaml .Values.airflow.sync.podAnnotations | nindent 8 }} + {{- end }} + {{- if .Values.airflow.sync.safeToEvict }} + cluster-autoscaler.kubernetes.io/safe-to-evict: "true" + {{- end }} + labels: + app: {{ include "airflow.labels.app" . }} + component: sync-connections + release: {{ .Release.Name }} + {{- if .Values.airflow.sync.podLabels }} + {{- toYaml .Values.airflow.sync.podLabels | nindent 8 }} + {{- end }} + spec: + restartPolicy: OnFailure + {{- if .Values.airflow.image.pullSecret }} + imagePullSecrets: + - name: {{ .Values.airflow.image.pullSecret }} + {{- end }} + {{- if $podNodeSelector }} + nodeSelector: + {{- $podNodeSelector | nindent 8 }} + {{- end }} + {{- if $podAffinity }} + affinity: + {{- $podAffinity | nindent 8 }} + {{- end }} + {{- if $podTolerations }} + tolerations: + {{- $podTolerations | nindent 8 }} + {{- end }} + {{- if $podSecurityContext }} + securityContext: + {{- $podSecurityContext | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "airflow.serviceAccountName" . }} + initContainers: + {{- if $extraPipPackages }} + {{- include "airflow.init_container.install_pip_packages" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages) | indent 8 }} + {{- end }} + {{- if .Values.dags.gitSync.enabled }} + ## git-sync is included so "airflow plugins" & "python packages" can be stored in the dags repo + {{- include "airflow.container.git_sync" (dict "Release" .Release "Values" .Values "sync_one_time" "true") | indent 8 }} + {{- end }} + {{- include "airflow.init_container.check_db" (dict "Release" .Release "Values" .Values "volumeMounts" $volumeMounts) | indent 8 }} + {{- include "airflow.init_container.wait_for_db_migrations" (dict "Release" .Release "Values" .Values "volumeMounts" $volumeMounts) | indent 8 }} + containers: + - name: sync-airflow-connections + {{- include "airflow.image" . | indent 10 }} + resources: + {{- toYaml .Values.airflow.sync.resources | nindent 12 }} + envFrom: + {{- include "airflow.envFrom" . | indent 12 }} + env: + {{- include "airflow.env" . | indent 12 }} + command: + {{- include "airflow.command" . | indent 12 }} + args: + - "python" + - "-u" + - "/mnt/scripts/sync_connections.py" + volumeMounts: + {{- $volumeMounts | indent 12 }} + - name: scripts + mountPath: /mnt/scripts + readOnly: true + {{- if .Values.airflow.connectionsTemplates }} + - name: templates + mountPath: "/mnt/templates" + readOnly: true + {{- end }} + volumes: + {{- $volumes | indent 8 }} + - name: scripts + secret: + secretName: {{ include "airflow.fullname" . }}-sync-connections + {{- if .Values.airflow.connectionsTemplates }} + - name: templates + projected: + sources: + {{- range $k, $v := .Values.airflow.connectionsTemplates }} + {{- if not (regexMatch "^[a-zA-Z_][a-zA-Z0-9_]*$" $k) }} + {{ required "each key in `airflow.connectionsTemplates` must match the regex: ^[a-zA-Z_][a-zA-Z0-9_]*$" nil }} + {{- end }} + {{- if eq ($v.kind | lower) "configmap" }} + - configMap: + name: {{ $v.name | quote }} + items: + - key: {{ $v.key | quote }} + path: {{ $k | quote }} + {{- else if eq ($v.kind | lower) "secret" }} + - secret: + name: {{ $v.name | quote }} + items: + - key: {{ $v.key | quote }} + path: {{ $k | quote }} + {{- else }} + {{ required "each `kind` in `airflow.connectionsTemplates` must be one of ['configmap', 'secret']!" nil }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/templates/sync/sync-connections-secret.yaml b/charts/deps/charts/airflow-8.8.0/templates/sync/sync-connections-secret.yaml new file mode 100644 index 0000000..e70040a --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/sync/sync-connections-secret.yaml @@ -0,0 +1,14 @@ +{{- if .Values.airflow.connections }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "airflow.fullname" . }}-sync-connections + labels: + app: {{ include "airflow.labels.app" . }} + component: sync-connections + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +data: + sync_connections.py: {{ include "airflow.sync.sync_connections.py" . | b64enc | quote }} +{{- end }} \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/templates/sync/sync-pools-deployment.yaml b/charts/deps/charts/airflow-8.8.0/templates/sync/sync-pools-deployment.yaml new file mode 100644 index 0000000..1635a98 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/sync/sync-pools-deployment.yaml @@ -0,0 +1,120 @@ +{{- if and (.Values.airflow.pools) (.Values.airflow.poolsUpdate) }} +{{- $podNodeSelector := include "airflow.podNodeSelector" (dict "Release" .Release "Values" .Values "nodeSelector" .Values.airflow.sync.nodeSelector) }} +{{- $podAffinity := include "airflow.podAffinity" (dict "Release" .Release "Values" .Values "affinity" .Values.airflow.sync.affinity) }} +{{- $podTolerations := include "airflow.podTolerations" (dict "Release" .Release "Values" .Values "tolerations" .Values.airflow.sync.tolerations) }} +{{- $podSecurityContext := include "airflow.podSecurityContext" (dict "Release" .Release "Values" .Values "securityContext" .Values.airflow.sync.securityContext) }} +{{- $extraPipPackages := .Values.airflow.extraPipPackages }} +{{- $volumeMounts := include "airflow.volumeMounts" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages) }} +{{- $volumes := include "airflow.volumes" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages) }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "airflow.fullname" . }}-sync-pools + labels: + app: {{ include "airflow.labels.app" . }} + component: sync-pools + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + {{- if .Values.airflow.sync.labels }} + {{- toYaml .Values.airflow.sync.labels | nindent 4 }} + {{- end }} + {{- if .Values.airflow.sync.annotations }} + annotations: + {{- toYaml .Values.airflow.sync.annotations | nindent 4 }} + {{- end }} +spec: + replicas: 1 + strategy: + ## only 1 replica should run at a time + type: Recreate + selector: + matchLabels: + app: {{ include "airflow.labels.app" . }} + component: sync-pools + release: {{ .Release.Name }} + template: + metadata: + annotations: + checksum/secret-config-envs: {{ include (print $.Template.BasePath "/config/secret-config-envs.yaml") . | sha256sum }} + checksum/secret-local-settings: {{ include (print $.Template.BasePath "/config/secret-local-settings.yaml") . | sha256sum }} + checksum/sync-pools-script: {{ include "airflow.sync.sync_pools.py" . | sha256sum }} + {{- if .Values.airflow.podAnnotations }} + {{- toYaml .Values.airflow.podAnnotations | nindent 8 }} + {{- end }} + {{- if .Values.airflow.sync.podAnnotations }} + {{- toYaml .Values.airflow.sync.podAnnotations | nindent 8 }} + {{- end }} + {{- if .Values.airflow.sync.safeToEvict }} + cluster-autoscaler.kubernetes.io/safe-to-evict: "true" + {{- end }} + labels: + app: {{ include "airflow.labels.app" . }} + component: sync-pools + release: {{ .Release.Name }} + {{- if .Values.airflow.sync.podLabels }} + {{- toYaml .Values.airflow.sync.podLabels | nindent 8 }} + {{- end }} + spec: + restartPolicy: Always + {{- if .Values.airflow.image.pullSecret }} + imagePullSecrets: + - name: {{ .Values.airflow.image.pullSecret }} + {{- end }} + {{- if $podNodeSelector }} + nodeSelector: + {{- $podNodeSelector | nindent 8 }} + {{- end }} + {{- if $podAffinity }} + affinity: + {{- $podAffinity | nindent 8 }} + {{- end }} + {{- if $podTolerations }} + tolerations: + {{- $podTolerations | nindent 8 }} + {{- end }} + {{- if $podSecurityContext }} + securityContext: + {{- $podSecurityContext | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "airflow.serviceAccountName" . }} + initContainers: + {{- if $extraPipPackages }} + {{- include "airflow.init_container.install_pip_packages" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages) | indent 8 }} + {{- end }} + {{- if .Values.dags.gitSync.enabled }} + ## git-sync is included so "airflow plugins" & "python packages" can be stored in the dags repo + {{- include "airflow.container.git_sync" (dict "Release" .Release "Values" .Values "sync_one_time" "true") | indent 8 }} + {{- end }} + {{- include "airflow.init_container.check_db" (dict "Release" .Release "Values" .Values "volumeMounts" $volumeMounts) | indent 8 }} + {{- include "airflow.init_container.wait_for_db_migrations" (dict "Release" .Release "Values" .Values "volumeMounts" $volumeMounts) | indent 8 }} + containers: + - name: sync-airflow-pools + {{- include "airflow.image" . | indent 10 }} + resources: + {{- toYaml .Values.airflow.sync.resources | nindent 12 }} + envFrom: + {{- include "airflow.envFrom" . | indent 12 }} + env: + {{- include "airflow.env" . | indent 12 }} + command: + {{- include "airflow.command" . | indent 12 }} + args: + - "python" + - "-u" + - "/mnt/scripts/sync_pools.py" + volumeMounts: + {{- $volumeMounts | indent 12 }} + - name: scripts + mountPath: /mnt/scripts + readOnly: true + {{- if .Values.dags.gitSync.enabled }} + ## git-sync is included so "airflow plugins" & "python packages" can be stored in the dags repo + {{- include "airflow.container.git_sync" . | indent 8 }} + {{- end }} + volumes: + {{- $volumes | indent 8 }} + - name: scripts + secret: + secretName: {{ include "airflow.fullname" . }}-sync-pools +{{- end }} \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/templates/sync/sync-pools-job.yaml b/charts/deps/charts/airflow-8.8.0/templates/sync/sync-pools-job.yaml new file mode 100644 index 0000000..cda0f7c --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/sync/sync-pools-job.yaml @@ -0,0 +1,110 @@ +{{- if and (.Values.airflow.pools) (not .Values.airflow.poolsUpdate) }} +{{- $podNodeSelector := include "airflow.podNodeSelector" (dict "Release" .Release "Values" .Values "nodeSelector" .Values.airflow.sync.nodeSelector) }} +{{- $podAffinity := include "airflow.podAffinity" (dict "Release" .Release "Values" .Values "affinity" .Values.airflow.sync.affinity) }} +{{- $podTolerations := include "airflow.podTolerations" (dict "Release" .Release "Values" .Values "tolerations" .Values.airflow.sync.tolerations) }} +{{- $podSecurityContext := include "airflow.podSecurityContext" (dict "Release" .Release "Values" .Values "securityContext" .Values.airflow.sync.securityContext) }} +{{- $extraPipPackages := .Values.airflow.extraPipPackages }} +{{- $volumeMounts := include "airflow.volumeMounts" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages) }} +{{- $volumes := include "airflow.volumes" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages) }} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "airflow.fullname" . }}-sync-pools + labels: + app: {{ include "airflow.labels.app" . }} + component: sync-pools + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + {{- if .Values.airflow.sync.labels }} + {{- toYaml .Values.airflow.sync.labels | nindent 4 }} + {{- end }} + annotations: + helm.sh/hook: post-install,post-upgrade + helm.sh/hook-weight: "0" + helm.sh/hook-delete-policy: before-hook-creation + {{- if .Values.airflow.sync.annotations }} + {{- toYaml .Values.airflow.sync.annotations | nindent 4 }} + {{- end }} +spec: + template: + metadata: + annotations: + checksum/secret-config-envs: {{ include (print $.Template.BasePath "/config/secret-config-envs.yaml") . | sha256sum }} + checksum/secret-local-settings: {{ include (print $.Template.BasePath "/config/secret-local-settings.yaml") . | sha256sum }} + checksum/sync-pools-script: {{ include "airflow.sync.sync_pools.py" . | sha256sum }} + {{- if .Values.airflow.podAnnotations }} + {{- toYaml .Values.airflow.podAnnotations | nindent 8 }} + {{- end }} + {{- if .Values.airflow.sync.podAnnotations }} + {{- toYaml .Values.airflow.sync.podAnnotations | nindent 8 }} + {{- end }} + {{- if .Values.airflow.sync.safeToEvict }} + cluster-autoscaler.kubernetes.io/safe-to-evict: "true" + {{- end }} + labels: + app: {{ include "airflow.labels.app" . }} + component: sync-pools + release: {{ .Release.Name }} + {{- if .Values.airflow.sync.podLabels }} + {{- toYaml .Values.airflow.sync.podLabels | nindent 8 }} + {{- end }} + spec: + restartPolicy: OnFailure + {{- if .Values.airflow.image.pullSecret }} + imagePullSecrets: + - name: {{ .Values.airflow.image.pullSecret }} + {{- end }} + {{- if $podNodeSelector }} + nodeSelector: + {{- $podNodeSelector | nindent 8 }} + {{- end }} + {{- if $podAffinity }} + affinity: + {{- $podAffinity | nindent 8 }} + {{- end }} + {{- if $podTolerations }} + tolerations: + {{- $podTolerations | nindent 8 }} + {{- end }} + {{- if $podSecurityContext }} + securityContext: + {{- $podSecurityContext | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "airflow.serviceAccountName" . }} + initContainers: + {{- if $extraPipPackages }} + {{- include "airflow.init_container.install_pip_packages" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages) | indent 8 }} + {{- end }} + {{- if .Values.dags.gitSync.enabled }} + ## git-sync is included so "airflow plugins" & "python packages" can be stored in the dags repo + {{- include "airflow.container.git_sync" (dict "Release" .Release "Values" .Values "sync_one_time" "true") | indent 8 }} + {{- end }} + {{- include "airflow.init_container.check_db" (dict "Release" .Release "Values" .Values "volumeMounts" $volumeMounts) | indent 8 }} + {{- include "airflow.init_container.wait_for_db_migrations" (dict "Release" .Release "Values" .Values "volumeMounts" $volumeMounts) | indent 8 }} + containers: + - name: sync-airflow-pools + {{- include "airflow.image" . | indent 10 }} + resources: + {{- toYaml .Values.airflow.sync.resources | nindent 12 }} + envFrom: + {{- include "airflow.envFrom" . | indent 12 }} + env: + {{- include "airflow.env" . | indent 12 }} + command: + {{- include "airflow.command" . | indent 12 }} + args: + - "python" + - "-u" + - "/mnt/scripts/sync_pools.py" + volumeMounts: + {{- $volumeMounts | indent 12 }} + - name: scripts + mountPath: /mnt/scripts + readOnly: true + volumes: + {{- $volumes | indent 8 }} + - name: scripts + secret: + secretName: {{ include "airflow.fullname" . }}-sync-pools +{{- end }} \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/templates/sync/sync-pools-secret.yaml b/charts/deps/charts/airflow-8.8.0/templates/sync/sync-pools-secret.yaml new file mode 100644 index 0000000..5fec98f --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/sync/sync-pools-secret.yaml @@ -0,0 +1,14 @@ +{{- if .Values.airflow.pools }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "airflow.fullname" . }}-sync-pools + labels: + app: {{ include "airflow.labels.app" . }} + component: sync-pools + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +data: + sync_pools.py: {{ include "airflow.sync.sync_pools.py" . | b64enc | quote }} +{{- end }} \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/templates/sync/sync-users-deployment.yaml b/charts/deps/charts/airflow-8.8.0/templates/sync/sync-users-deployment.yaml new file mode 100644 index 0000000..d61d894 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/sync/sync-users-deployment.yaml @@ -0,0 +1,150 @@ +{{- if and (.Values.airflow.users) (.Values.airflow.usersUpdate) }} +{{- $podNodeSelector := include "airflow.podNodeSelector" (dict "Release" .Release "Values" .Values "nodeSelector" .Values.airflow.sync.nodeSelector) }} +{{- $podAffinity := include "airflow.podAffinity" (dict "Release" .Release "Values" .Values "affinity" .Values.airflow.sync.affinity) }} +{{- $podTolerations := include "airflow.podTolerations" (dict "Release" .Release "Values" .Values "tolerations" .Values.airflow.sync.tolerations) }} +{{- $podSecurityContext := include "airflow.podSecurityContext" (dict "Release" .Release "Values" .Values "securityContext" .Values.airflow.sync.securityContext) }} +{{- $extraPipPackages := .Values.airflow.extraPipPackages }} +{{- $volumeMounts := include "airflow.volumeMounts" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages) }} +{{- $volumes := include "airflow.volumes" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages) }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "airflow.fullname" . }}-sync-users + labels: + app: {{ include "airflow.labels.app" . }} + component: sync-users + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + {{- if .Values.airflow.sync.labels }} + {{- toYaml .Values.airflow.sync.labels | nindent 4 }} + {{- end }} + {{- if .Values.airflow.sync.annotations }} + annotations: + {{- toYaml .Values.airflow.sync.annotations | nindent 4 }} + {{- end }} +spec: + replicas: 1 + strategy: + ## only 1 replica should run at a time + type: Recreate + selector: + matchLabels: + app: {{ include "airflow.labels.app" . }} + component: sync-users + release: {{ .Release.Name }} + template: + metadata: + annotations: + checksum/secret-config-envs: {{ include (print $.Template.BasePath "/config/secret-config-envs.yaml") . | sha256sum }} + checksum/secret-local-settings: {{ include (print $.Template.BasePath "/config/secret-local-settings.yaml") . | sha256sum }} + checksum/sync-users-script: {{ include "airflow.sync.sync_users.py" . | sha256sum }} + {{- if .Values.airflow.podAnnotations }} + {{- toYaml .Values.airflow.podAnnotations | nindent 8 }} + {{- end }} + {{- if .Values.airflow.sync.podAnnotations }} + {{- toYaml .Values.airflow.sync.podAnnotations | nindent 8 }} + {{- end }} + {{- if .Values.airflow.sync.safeToEvict }} + cluster-autoscaler.kubernetes.io/safe-to-evict: "true" + {{- end }} + labels: + app: {{ include "airflow.labels.app" . }} + component: sync-users + release: {{ .Release.Name }} + {{- if .Values.airflow.sync.podLabels }} + {{- toYaml .Values.airflow.sync.podLabels | nindent 8 }} + {{- end }} + spec: + restartPolicy: Always + {{- if .Values.airflow.image.pullSecret }} + imagePullSecrets: + - name: {{ .Values.airflow.image.pullSecret }} + {{- end }} + {{- if $podNodeSelector }} + nodeSelector: + {{- $podNodeSelector | nindent 8 }} + {{- end }} + {{- if $podAffinity }} + affinity: + {{- $podAffinity | nindent 8 }} + {{- end }} + {{- if $podTolerations }} + tolerations: + {{- $podTolerations | nindent 8 }} + {{- end }} + {{- if $podSecurityContext }} + securityContext: + {{- $podSecurityContext | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "airflow.serviceAccountName" . }} + initContainers: + {{- if $extraPipPackages }} + {{- include "airflow.init_container.install_pip_packages" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages) | indent 8 }} + {{- end }} + {{- if .Values.dags.gitSync.enabled }} + ## git-sync is included so "airflow plugins" & "python packages" can be stored in the dags repo + {{- include "airflow.container.git_sync" (dict "Release" .Release "Values" .Values "sync_one_time" "true") | indent 8 }} + {{- end }} + {{- include "airflow.init_container.check_db" (dict "Release" .Release "Values" .Values "volumeMounts" $volumeMounts) | indent 8 }} + {{- include "airflow.init_container.wait_for_db_migrations" (dict "Release" .Release "Values" .Values "volumeMounts" $volumeMounts) | indent 8 }} + containers: + - name: sync-airflow-users + {{- include "airflow.image" . | indent 10 }} + resources: + {{- toYaml .Values.airflow.sync.resources | nindent 12 }} + envFrom: + {{- include "airflow.envFrom" . | indent 12 }} + env: + {{- include "airflow.env" . | indent 12 }} + command: + {{- include "airflow.command" . | indent 12 }} + args: + - "python" + - "-u" + - "/mnt/scripts/sync_users.py" + volumeMounts: + {{- $volumeMounts | indent 12 }} + - name: scripts + mountPath: /mnt/scripts + readOnly: true + {{- if .Values.airflow.usersTemplates }} + - name: templates + mountPath: "/mnt/templates" + readOnly: true + {{- end }} + {{- if .Values.dags.gitSync.enabled }} + ## git-sync is included so "airflow plugins" & "python packages" can be stored in the dags repo + {{- include "airflow.container.git_sync" . | indent 8 }} + {{- end }} + volumes: + {{- $volumes | indent 8 }} + - name: scripts + secret: + secretName: {{ include "airflow.fullname" . }}-sync-users + {{- if .Values.airflow.usersTemplates }} + - name: templates + projected: + sources: + {{- range $k, $v := .Values.airflow.usersTemplates }} + {{- if not (regexMatch "^[a-zA-Z_][a-zA-Z0-9_]*$" $k) }} + {{ required "each key in `airflow.usersTemplates` must match the regex: ^[a-zA-Z_][a-zA-Z0-9_]*$" nil }} + {{- end }} + {{- if eq ($v.kind | lower) "configmap" }} + - configMap: + name: {{ $v.name | quote }} + items: + - key: {{ $v.key | quote }} + path: {{ $k | quote }} + {{- else if eq ($v.kind | lower) "secret" }} + - secret: + name: {{ $v.name | quote }} + items: + - key: {{ $v.key | quote }} + path: {{ $k | quote }} + {{- else }} + {{ required "each `kind` in `airflow.usersTemplates` must be one of ['configmap', 'secret']!" nil }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/templates/sync/sync-users-job.yaml b/charts/deps/charts/airflow-8.8.0/templates/sync/sync-users-job.yaml new file mode 100644 index 0000000..d96c8da --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/sync/sync-users-job.yaml @@ -0,0 +1,140 @@ +{{- if and (.Values.airflow.users) (not .Values.airflow.usersUpdate) }} +{{- $podNodeSelector := include "airflow.podNodeSelector" (dict "Release" .Release "Values" .Values "nodeSelector" .Values.airflow.sync.nodeSelector) }} +{{- $podAffinity := include "airflow.podAffinity" (dict "Release" .Release "Values" .Values "affinity" .Values.airflow.sync.affinity) }} +{{- $podTolerations := include "airflow.podTolerations" (dict "Release" .Release "Values" .Values "tolerations" .Values.airflow.sync.tolerations) }} +{{- $podSecurityContext := include "airflow.podSecurityContext" (dict "Release" .Release "Values" .Values "securityContext" .Values.airflow.sync.securityContext) }} +{{- $extraPipPackages := .Values.airflow.extraPipPackages }} +{{- $volumeMounts := include "airflow.volumeMounts" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages) }} +{{- $volumes := include "airflow.volumes" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages) }} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "airflow.fullname" . }}-sync-users + labels: + app: {{ include "airflow.labels.app" . }} + component: sync-users + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + {{- if .Values.airflow.sync.labels }} + {{- toYaml .Values.airflow.sync.labels | nindent 4 }} + {{- end }} + annotations: + helm.sh/hook: post-install,post-upgrade + helm.sh/hook-weight: "0" + helm.sh/hook-delete-policy: before-hook-creation + {{- if .Values.airflow.sync.annotations }} + {{- toYaml .Values.airflow.sync.annotations | nindent 4 }} + {{- end }} +spec: + template: + metadata: + annotations: + checksum/secret-config-envs: {{ include (print $.Template.BasePath "/config/secret-config-envs.yaml") . | sha256sum }} + checksum/secret-local-settings: {{ include (print $.Template.BasePath "/config/secret-local-settings.yaml") . | sha256sum }} + checksum/sync-users-script: {{ include "airflow.sync.sync_users.py" . | sha256sum }} + {{- if .Values.airflow.podAnnotations }} + {{- toYaml .Values.airflow.podAnnotations | nindent 8 }} + {{- end }} + {{- if .Values.airflow.sync.podAnnotations }} + {{- toYaml .Values.airflow.sync.podAnnotations | nindent 8 }} + {{- end }} + {{- if .Values.airflow.sync.safeToEvict }} + cluster-autoscaler.kubernetes.io/safe-to-evict: "true" + {{- end }} + labels: + app: {{ include "airflow.labels.app" . }} + component: sync-users + release: {{ .Release.Name }} + {{- if .Values.airflow.sync.podLabels }} + {{- toYaml .Values.airflow.sync.podLabels | nindent 8 }} + {{- end }} + spec: + restartPolicy: OnFailure + {{- if .Values.airflow.image.pullSecret }} + imagePullSecrets: + - name: {{ .Values.airflow.image.pullSecret }} + {{- end }} + {{- if $podNodeSelector }} + nodeSelector: + {{- $podNodeSelector | nindent 8 }} + {{- end }} + {{- if $podAffinity }} + affinity: + {{- $podAffinity | nindent 8 }} + {{- end }} + {{- if $podTolerations }} + tolerations: + {{- $podTolerations | nindent 8 }} + {{- end }} + {{- if $podSecurityContext }} + securityContext: + {{- $podSecurityContext | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "airflow.serviceAccountName" . }} + initContainers: + {{- if $extraPipPackages }} + {{- include "airflow.init_container.install_pip_packages" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages) | indent 8 }} + {{- end }} + {{- if .Values.dags.gitSync.enabled }} + ## git-sync is included so "airflow plugins" & "python packages" can be stored in the dags repo + {{- include "airflow.container.git_sync" (dict "Release" .Release "Values" .Values "sync_one_time" "true") | indent 8 }} + {{- end }} + {{- include "airflow.init_container.check_db" (dict "Release" .Release "Values" .Values "volumeMounts" $volumeMounts) | indent 8 }} + {{- include "airflow.init_container.wait_for_db_migrations" (dict "Release" .Release "Values" .Values "volumeMounts" $volumeMounts) | indent 8 }} + containers: + - name: sync-airflow-users + {{- include "airflow.image" . | indent 10 }} + resources: + {{- toYaml .Values.airflow.sync.resources | nindent 12 }} + envFrom: + {{- include "airflow.envFrom" . | indent 12 }} + env: + {{- include "airflow.env" . | indent 12 }} + command: + {{- include "airflow.command" . | indent 12 }} + args: + - "python" + - "-u" + - "/mnt/scripts/sync_users.py" + volumeMounts: + {{- $volumeMounts | indent 12 }} + - name: scripts + mountPath: /mnt/scripts + readOnly: true + {{- if .Values.airflow.usersTemplates }} + - name: templates + mountPath: "/mnt/templates" + readOnly: true + {{- end }} + volumes: + {{- $volumes | indent 8 }} + - name: scripts + secret: + secretName: {{ include "airflow.fullname" . }}-sync-users + {{- if .Values.airflow.usersTemplates }} + - name: templates + projected: + sources: + {{- range $k, $v := .Values.airflow.usersTemplates }} + {{- if not (regexMatch "^[a-zA-Z_][a-zA-Z0-9_]*$" $k) }} + {{ required "each key in `airflow.usersTemplates` must match the regex: ^[a-zA-Z_][a-zA-Z0-9_]*$" nil }} + {{- end }} + {{- if eq ($v.kind | lower) "configmap" }} + - configMap: + name: {{ $v.name | quote }} + items: + - key: {{ $v.key | quote }} + path: {{ $k | quote }} + {{- else if eq ($v.kind | lower) "secret" }} + - secret: + name: {{ $v.name | quote }} + items: + - key: {{ $v.key | quote }} + path: {{ $k | quote }} + {{- else }} + {{ required "each `kind` in `airflow.usersTemplates` must be one of ['configmap', 'secret']!" nil }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/templates/sync/sync-users-secret.yaml b/charts/deps/charts/airflow-8.8.0/templates/sync/sync-users-secret.yaml new file mode 100644 index 0000000..1d1d425 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/sync/sync-users-secret.yaml @@ -0,0 +1,14 @@ +{{- if .Values.airflow.users }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "airflow.fullname" . }}-sync-users + labels: + app: {{ include "airflow.labels.app" . }} + component: sync-users + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +data: + sync_users.py: {{ include "airflow.sync.sync_users.py" . | b64enc | quote }} +{{- end }} \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/templates/sync/sync-variables-deployment.yaml b/charts/deps/charts/airflow-8.8.0/templates/sync/sync-variables-deployment.yaml new file mode 100644 index 0000000..2192619 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/sync/sync-variables-deployment.yaml @@ -0,0 +1,150 @@ +{{- if and (.Values.airflow.variables) (.Values.airflow.variablesUpdate) }} +{{- $podNodeSelector := include "airflow.podNodeSelector" (dict "Release" .Release "Values" .Values "nodeSelector" .Values.airflow.sync.nodeSelector) }} +{{- $podAffinity := include "airflow.podAffinity" (dict "Release" .Release "Values" .Values "affinity" .Values.airflow.sync.affinity) }} +{{- $podTolerations := include "airflow.podTolerations" (dict "Release" .Release "Values" .Values "tolerations" .Values.airflow.sync.tolerations) }} +{{- $podSecurityContext := include "airflow.podSecurityContext" (dict "Release" .Release "Values" .Values "securityContext" .Values.airflow.sync.securityContext) }} +{{- $extraPipPackages := .Values.airflow.extraPipPackages }} +{{- $volumeMounts := include "airflow.volumeMounts" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages) }} +{{- $volumes := include "airflow.volumes" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages) }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "airflow.fullname" . }}-sync-variables + labels: + app: {{ include "airflow.labels.app" . }} + component: sync-variables + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + {{- if .Values.airflow.sync.labels }} + {{- toYaml .Values.airflow.sync.labels | nindent 4 }} + {{- end }} + {{- if .Values.airflow.sync.annotations }} + annotations: + {{- toYaml .Values.airflow.sync.annotations | nindent 4 }} + {{- end }} +spec: + replicas: 1 + strategy: + ## only 1 replica should run at a time + type: Recreate + selector: + matchLabels: + app: {{ include "airflow.labels.app" . }} + component: sync-variables + release: {{ .Release.Name }} + template: + metadata: + annotations: + checksum/secret-config-envs: {{ include (print $.Template.BasePath "/config/secret-config-envs.yaml") . | sha256sum }} + checksum/secret-local-settings: {{ include (print $.Template.BasePath "/config/secret-local-settings.yaml") . | sha256sum }} + checksum/sync-variables-script: {{ include "airflow.sync.sync_variables.py" . | sha256sum }} + {{- if .Values.airflow.podAnnotations }} + {{- toYaml .Values.airflow.podAnnotations | nindent 8 }} + {{- end }} + {{- if .Values.airflow.sync.podAnnotations }} + {{- toYaml .Values.airflow.sync.podAnnotations | nindent 8 }} + {{- end }} + {{- if .Values.airflow.sync.safeToEvict }} + cluster-autoscaler.kubernetes.io/safe-to-evict: "true" + {{- end }} + labels: + app: {{ include "airflow.labels.app" . }} + component: sync-variables + release: {{ .Release.Name }} + {{- if .Values.airflow.sync.podLabels }} + {{- toYaml .Values.airflow.sync.podLabels | nindent 8 }} + {{- end }} + spec: + restartPolicy: Always + {{- if .Values.airflow.image.pullSecret }} + imagePullSecrets: + - name: {{ .Values.airflow.image.pullSecret }} + {{- end }} + {{- if $podNodeSelector }} + nodeSelector: + {{- $podNodeSelector | nindent 8 }} + {{- end }} + {{- if $podAffinity }} + affinity: + {{- $podAffinity | nindent 8 }} + {{- end }} + {{- if $podTolerations }} + tolerations: + {{- $podTolerations | nindent 8 }} + {{- end }} + {{- if $podSecurityContext }} + securityContext: + {{- $podSecurityContext | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "airflow.serviceAccountName" . }} + initContainers: + {{- if $extraPipPackages }} + {{- include "airflow.init_container.install_pip_packages" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages) | indent 8 }} + {{- end }} + {{- if .Values.dags.gitSync.enabled }} + ## git-sync is included so "airflow plugins" & "python packages" can be stored in the dags repo + {{- include "airflow.container.git_sync" (dict "Release" .Release "Values" .Values "sync_one_time" "true") | indent 8 }} + {{- end }} + {{- include "airflow.init_container.check_db" (dict "Release" .Release "Values" .Values "volumeMounts" $volumeMounts) | indent 8 }} + {{- include "airflow.init_container.wait_for_db_migrations" (dict "Release" .Release "Values" .Values "volumeMounts" $volumeMounts) | indent 8 }} + containers: + - name: sync-airflow-variables + {{- include "airflow.image" . | indent 10 }} + resources: + {{- toYaml .Values.airflow.sync.resources | nindent 12 }} + envFrom: + {{- include "airflow.envFrom" . | indent 12 }} + env: + {{- include "airflow.env" . | indent 12 }} + command: + {{- include "airflow.command" . | indent 12 }} + args: + - "python" + - "-u" + - "/mnt/scripts/sync_variables.py" + volumeMounts: + {{- $volumeMounts | indent 12 }} + - name: scripts + mountPath: /mnt/scripts + readOnly: true + {{- if .Values.airflow.variablesTemplates }} + - name: templates + mountPath: "/mnt/templates" + readOnly: true + {{- end }} + {{- if .Values.dags.gitSync.enabled }} + ## git-sync is included so "airflow plugins" & "python packages" can be stored in the dags repo + {{- include "airflow.container.git_sync" . | indent 8 }} + {{- end }} + volumes: + {{- $volumes | indent 8 }} + - name: scripts + secret: + secretName: {{ include "airflow.fullname" . }}-sync-variables + {{- if .Values.airflow.variablesTemplates }} + - name: templates + projected: + sources: + {{- range $k, $v := .Values.airflow.variablesTemplates }} + {{- if not (regexMatch "^[a-zA-Z_][a-zA-Z0-9_]*$" $k) }} + {{ required "each key in `airflow.variablesTemplates` must match the regex: ^[a-zA-Z_][a-zA-Z0-9_]*$" nil }} + {{- end }} + {{- if eq ($v.kind | lower) "configmap" }} + - configMap: + name: {{ $v.name | quote }} + items: + - key: {{ $v.key | quote }} + path: {{ $k | quote }} + {{- else if eq ($v.kind | lower) "secret" }} + - secret: + name: {{ $v.name | quote }} + items: + - key: {{ $v.key | quote }} + path: {{ $k | quote }} + {{- else }} + {{ required "each `kind` in `airflow.variablesTemplates` must be one of ['configmap', 'secret']!" nil }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/templates/sync/sync-variables-job.yaml b/charts/deps/charts/airflow-8.8.0/templates/sync/sync-variables-job.yaml new file mode 100644 index 0000000..bd50349 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/sync/sync-variables-job.yaml @@ -0,0 +1,140 @@ +{{- if and (.Values.airflow.variables) (not .Values.airflow.variablesUpdate) }} +{{- $podNodeSelector := include "airflow.podNodeSelector" (dict "Release" .Release "Values" .Values "nodeSelector" .Values.airflow.sync.nodeSelector) }} +{{- $podAffinity := include "airflow.podAffinity" (dict "Release" .Release "Values" .Values "affinity" .Values.airflow.sync.affinity) }} +{{- $podTolerations := include "airflow.podTolerations" (dict "Release" .Release "Values" .Values "tolerations" .Values.airflow.sync.tolerations) }} +{{- $podSecurityContext := include "airflow.podSecurityContext" (dict "Release" .Release "Values" .Values "securityContext" .Values.airflow.sync.securityContext) }} +{{- $extraPipPackages := .Values.airflow.extraPipPackages }} +{{- $volumeMounts := include "airflow.volumeMounts" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages) }} +{{- $volumes := include "airflow.volumes" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages) }} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "airflow.fullname" . }}-sync-variables + labels: + app: {{ include "airflow.labels.app" . }} + component: sync-variables + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + {{- if .Values.airflow.sync.labels }} + {{- toYaml .Values.airflow.sync.labels | nindent 4 }} + {{- end }} + annotations: + helm.sh/hook: post-install,post-upgrade + helm.sh/hook-weight: "0" + helm.sh/hook-delete-policy: before-hook-creation + {{- if .Values.airflow.sync.annotations }} + {{- toYaml .Values.airflow.sync.annotations | nindent 4 }} + {{- end }} +spec: + template: + metadata: + annotations: + checksum/secret-config-envs: {{ include (print $.Template.BasePath "/config/secret-config-envs.yaml") . | sha256sum }} + checksum/secret-local-settings: {{ include (print $.Template.BasePath "/config/secret-local-settings.yaml") . | sha256sum }} + checksum/sync-variables-script: {{ include "airflow.sync.sync_variables.py" . | sha256sum }} + {{- if .Values.airflow.podAnnotations }} + {{- toYaml .Values.airflow.podAnnotations | nindent 8 }} + {{- end }} + {{- if .Values.airflow.sync.podAnnotations }} + {{- toYaml .Values.airflow.sync.podAnnotations | nindent 8 }} + {{- end }} + {{- if .Values.airflow.sync.safeToEvict }} + cluster-autoscaler.kubernetes.io/safe-to-evict: "true" + {{- end }} + labels: + app: {{ include "airflow.labels.app" . }} + component: sync-variables + release: {{ .Release.Name }} + {{- if .Values.airflow.sync.podLabels }} + {{- toYaml .Values.airflow.sync.podLabels | nindent 8 }} + {{- end }} + spec: + restartPolicy: OnFailure + {{- if .Values.airflow.image.pullSecret }} + imagePullSecrets: + - name: {{ .Values.airflow.image.pullSecret }} + {{- end }} + {{- if $podNodeSelector }} + nodeSelector: + {{- $podNodeSelector | nindent 8 }} + {{- end }} + {{- if $podAffinity }} + affinity: + {{- $podAffinity | nindent 8 }} + {{- end }} + {{- if $podTolerations }} + tolerations: + {{- $podTolerations | nindent 8 }} + {{- end }} + {{- if $podSecurityContext }} + securityContext: + {{- $podSecurityContext | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "airflow.serviceAccountName" . }} + initContainers: + {{- if $extraPipPackages }} + {{- include "airflow.init_container.install_pip_packages" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages) | indent 8 }} + {{- end }} + {{- if .Values.dags.gitSync.enabled }} + ## git-sync is included so "airflow plugins" & "python packages" can be stored in the dags repo + {{- include "airflow.container.git_sync" (dict "Release" .Release "Values" .Values "sync_one_time" "true") | indent 8 }} + {{- end }} + {{- include "airflow.init_container.check_db" (dict "Release" .Release "Values" .Values "volumeMounts" $volumeMounts) | indent 8 }} + {{- include "airflow.init_container.wait_for_db_migrations" (dict "Release" .Release "Values" .Values "volumeMounts" $volumeMounts) | indent 8 }} + containers: + - name: sync-airflow-variables + {{- include "airflow.image" . | indent 10 }} + resources: + {{- toYaml .Values.airflow.sync.resources | nindent 12 }} + envFrom: + {{- include "airflow.envFrom" . | indent 12 }} + env: + {{- include "airflow.env" . | indent 12 }} + command: + {{- include "airflow.command" . | indent 12 }} + args: + - "python" + - "-u" + - "/mnt/scripts/sync_variables.py" + volumeMounts: + {{- $volumeMounts | indent 12 }} + - name: scripts + mountPath: /mnt/scripts + readOnly: true + {{- if .Values.airflow.variablesTemplates }} + - name: templates + mountPath: "/mnt/templates" + readOnly: true + {{- end }} + volumes: + {{- $volumes | indent 8 }} + - name: scripts + secret: + secretName: {{ include "airflow.fullname" . }}-sync-variables + {{- if .Values.airflow.variablesTemplates }} + - name: templates + projected: + sources: + {{- range $k, $v := .Values.airflow.variablesTemplates }} + {{- if not (regexMatch "^[a-zA-Z_][a-zA-Z0-9_]*$" $k) }} + {{ required "each key in `airflow.variablesTemplates` must match the regex: ^[a-zA-Z_][a-zA-Z0-9_]*$" nil }} + {{- end }} + {{- if eq ($v.kind | lower) "configmap" }} + - configMap: + name: {{ $v.name | quote }} + items: + - key: {{ $v.key | quote }} + path: {{ $k | quote }} + {{- else if eq ($v.kind | lower) "secret" }} + - secret: + name: {{ $v.name | quote }} + items: + - key: {{ $v.key | quote }} + path: {{ $k | quote }} + {{- else }} + {{ required "each `kind` in `airflow.variablesTemplates` must be one of ['configmap', 'secret']!" nil }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/templates/sync/sync-variables-secret.yaml b/charts/deps/charts/airflow-8.8.0/templates/sync/sync-variables-secret.yaml new file mode 100644 index 0000000..25fa45b --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/sync/sync-variables-secret.yaml @@ -0,0 +1,14 @@ +{{- if .Values.airflow.variables }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "airflow.fullname" . }}-sync-variables + labels: + app: {{ include "airflow.labels.app" . }} + component: sync-variables + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +data: + sync_variables.py: {{ include "airflow.sync.sync_variables.py" . | b64enc | quote }} +{{- end }} \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/templates/triggerer/triggerer-deployment.yaml b/charts/deps/charts/airflow-8.8.0/templates/triggerer/triggerer-deployment.yaml new file mode 100644 index 0000000..2497f68 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/triggerer/triggerer-deployment.yaml @@ -0,0 +1,173 @@ +{{- if include "airflow.triggerer.should_use" . }} +{{- $podNodeSelector := include "airflow.podNodeSelector" (dict "Release" .Release "Values" .Values "nodeSelector" .Values.triggerer.nodeSelector) }} +{{- $podAffinity := include "airflow.podAffinity" (dict "Release" .Release "Values" .Values "affinity" .Values.triggerer.affinity) }} +{{- $podTolerations := include "airflow.podTolerations" (dict "Release" .Release "Values" .Values "tolerations" .Values.triggerer.tolerations) }} +{{- $podSecurityContext := include "airflow.podSecurityContext" (dict "Release" .Release "Values" .Values "securityContext" .Values.triggerer.securityContext) }} +{{- $extraPipPackages := concat .Values.airflow.extraPipPackages .Values.triggerer.extraPipPackages }} +{{- $extraVolumeMounts := .Values.triggerer.extraVolumeMounts }} +{{- $volumeMounts := include "airflow.volumeMounts" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages "extraVolumeMounts" $extraVolumeMounts) }} +{{- $extraVolumes := .Values.triggerer.extraVolumes }} +{{- $volumes := include "airflow.volumes" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages "extraVolumes" $extraVolumes "extraVolumeMounts" $extraVolumeMounts) }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "airflow.fullname" . }}-triggerer + {{- if .Values.triggerer.annotations }} + annotations: + {{- toYaml .Values.triggerer.annotations | nindent 4 }} + {{- end }} + labels: + app: {{ include "airflow.labels.app" . }} + component: triggerer + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + {{- if .Values.triggerer.labels }} + {{- toYaml .Values.triggerer.labels | nindent 4 }} + {{- end }} +spec: + replicas: {{ .Values.triggerer.replicas }} + strategy: + type: RollingUpdate + rollingUpdate: + ## multiple triggerer pods can safely run concurrently + maxSurge: 25% + maxUnavailable: 0 + selector: + matchLabels: + app: {{ include "airflow.labels.app" . }} + component: triggerer + release: {{ .Release.Name }} + template: + metadata: + annotations: + checksum/secret-config-envs: {{ include (print $.Template.BasePath "/config/secret-config-envs.yaml") . | sha256sum }} + checksum/secret-local-settings: {{ include (print $.Template.BasePath "/config/secret-local-settings.yaml") . | sha256sum }} + {{- if .Values.airflow.podAnnotations }} + {{- toYaml .Values.airflow.podAnnotations | nindent 8 }} + {{- end }} + {{- if .Values.triggerer.podAnnotations }} + {{- toYaml .Values.triggerer.podAnnotations | nindent 8 }} + {{- end }} + {{- if .Values.triggerer.safeToEvict }} + cluster-autoscaler.kubernetes.io/safe-to-evict: "true" + {{- end }} + labels: + app: {{ include "airflow.labels.app" . }} + component: triggerer + release: {{ .Release.Name }} + {{- if .Values.triggerer.podLabels }} + {{- toYaml .Values.triggerer.podLabels | nindent 8 }} + {{- end }} + spec: + restartPolicy: Always + {{- if .Values.airflow.image.pullSecret }} + imagePullSecrets: + - name: {{ .Values.airflow.image.pullSecret }} + {{- end }} + {{- if $podNodeSelector }} + nodeSelector: + {{- $podNodeSelector | nindent 8 }} + {{- end }} + {{- if $podAffinity }} + affinity: + {{- $podAffinity | nindent 8 }} + {{- end }} + {{- if $podTolerations }} + tolerations: + {{- $podTolerations | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "airflow.serviceAccountName" . }} + {{- if $podSecurityContext }} + securityContext: + {{- $podSecurityContext | nindent 8 }} + {{- end }} + initContainers: + {{- if $extraPipPackages }} + {{- include "airflow.init_container.install_pip_packages" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages) | indent 8 }} + {{- end }} + {{- if .Values.dags.gitSync.enabled }} + {{- include "airflow.container.git_sync" (dict "Release" .Release "Values" .Values "sync_one_time" "true") | indent 8 }} + {{- end }} + {{- include "airflow.init_container.check_db" (dict "Release" .Release "Values" .Values "volumeMounts" $volumeMounts) | indent 8 }} + {{- include "airflow.init_container.wait_for_db_migrations" (dict "Release" .Release "Values" .Values "volumeMounts" $volumeMounts) | indent 8 }} + containers: + - name: airflow-triggerer + {{- include "airflow.image" . | indent 10 }} + resources: + {{- toYaml .Values.triggerer.resources | nindent 12 }} + envFrom: + {{- include "airflow.envFrom" . | indent 12 }} + env: + {{- include "airflow.env" . | indent 12 }} + command: + {{- include "airflow.command" . | indent 12 }} + args: + - "bash" + - "-c" + - "exec airflow triggerer" + {{- if .Values.triggerer.livenessProbe.enabled }} + livenessProbe: + initialDelaySeconds: {{ .Values.triggerer.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.triggerer.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.triggerer.livenessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.triggerer.livenessProbe.failureThreshold }} + exec: + command: + {{- include "airflow.command" . | indent 16 }} + - "python" + - "-Wignore" + - "-c" + - | + import os + import sys + + # suppress logs triggered from importing airflow packages + os.environ["AIRFLOW__LOGGING__LOGGING_LEVEL"] = "ERROR" + + # shared imports + try: + from airflow.jobs.job import Job + except ImportError: + # `BaseJob` was renamed to `Job` in airflow 2.6.0 + from airflow.jobs.base_job import BaseJob as Job + from airflow.utils.db import create_session + from airflow.utils.net import get_hostname + + # heartbeat check imports + try: + from airflow.jobs.triggerer_job_runner import TriggererJobRunner + except ImportError: + # `TriggererJob` is wrapped by `TriggererJobRunner` since airflow 2.6.0 + from airflow.jobs.triggerer_job import TriggererJob as TriggererJobRunner + + with create_session() as session: + # ensure the TriggererJob with most recent heartbeat for this `hostname` is alive + hostname = get_hostname() + triggerer_job = session \ + .query(Job) \ + .filter_by(job_type=TriggererJobRunner.job_type) \ + .filter_by(hostname=hostname) \ + .order_by(Job.latest_heartbeat.desc()) \ + .limit(1) \ + .first() + if (triggerer_job is not None) and triggerer_job.is_alive(): + pass + else: + sys.exit(f"The TriggererJob (id={triggerer_job.id}) for hostname '{hostname}' is not alive") + {{- end }} + {{- if $volumeMounts }} + volumeMounts: + {{- $volumeMounts | indent 12 }} + {{- end }} + {{- if .Values.dags.gitSync.enabled }} + {{- include "airflow.container.git_sync" . | indent 8 }} + {{- end }} + {{- if .Values.airflow.extraContainers }} + {{- toYaml .Values.airflow.extraContainers | nindent 8 }} + {{- end }} + {{- if $volumes }} + volumes: + {{- $volumes | indent 8 }} + {{- end }} +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/templates/triggerer/triggerer-pdb.yaml b/charts/deps/charts/airflow-8.8.0/templates/triggerer/triggerer-pdb.yaml new file mode 100644 index 0000000..9080348 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/triggerer/triggerer-pdb.yaml @@ -0,0 +1,24 @@ +{{- if and (include "airflow.triggerer.should_use" .) (.Values.triggerer.podDisruptionBudget.enabled) }} +apiVersion: {{ .Values.triggerer.podDisruptionBudget.apiVersion }} +kind: PodDisruptionBudget +metadata: + name: {{ include "airflow.fullname" . }}-triggerer + labels: + app: {{ include "airflow.labels.app" . }} + component: triggerer + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + {{- if .Values.triggerer.podDisruptionBudget.maxUnavailable }} + maxUnavailable: {{ .Values.triggerer.podDisruptionBudget.maxUnavailable }} + {{- end }} + {{- if .Values.triggerer.podDisruptionBudget.minAvailable }} + minAvailable: {{ .Values.triggerer.podDisruptionBudget.minAvailable }} + {{- end }} + selector: + matchLabels: + app: {{ include "airflow.labels.app" . }} + component: triggerer + release: {{ .Release.Name }} +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/templates/webserver/webserver-deployment.yaml b/charts/deps/charts/airflow-8.8.0/templates/webserver/webserver-deployment.yaml new file mode 100644 index 0000000..605b012 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/webserver/webserver-deployment.yaml @@ -0,0 +1,164 @@ +{{- $podNodeSelector := include "airflow.podNodeSelector" (dict "Release" .Release "Values" .Values "nodeSelector" .Values.web.nodeSelector) }} +{{- $podAffinity := include "airflow.podAffinity" (dict "Release" .Release "Values" .Values "affinity" .Values.web.affinity) }} +{{- $podTolerations := include "airflow.podTolerations" (dict "Release" .Release "Values" .Values "tolerations" .Values.web.tolerations) }} +{{- $podSecurityContext := include "airflow.podSecurityContext" (dict "Release" .Release "Values" .Values "securityContext" .Values.web.securityContext) }} +{{- $extraPipPackages := concat .Values.airflow.extraPipPackages .Values.web.extraPipPackages }} +{{- $extraVolumeMounts := .Values.web.extraVolumeMounts }} +{{- $volumeMounts := include "airflow.volumeMounts" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages "extraVolumeMounts" $extraVolumeMounts) }} +{{- $extraVolumes := .Values.web.extraVolumes }} +{{- $volumes := include "airflow.volumes" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages "extraVolumes" $extraVolumes "extraVolumeMounts" $extraVolumeMounts) }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "airflow.fullname" . }}-web + {{- if .Values.web.annotations }} + annotations: + {{- toYaml .Values.web.annotations | nindent 4 }} + {{- end }} + labels: + app: {{ include "airflow.labels.app" . }} + component: web + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + {{- if .Values.web.labels }} + {{- toYaml .Values.web.labels | nindent 4 }} + {{- end }} +spec: + replicas: {{ .Values.web.replicas }} + strategy: + type: RollingUpdate + rollingUpdate: + ## multiple web pods can safely run concurrently + maxSurge: 25% + maxUnavailable: 0 + selector: + matchLabels: + app: {{ include "airflow.labels.app" . }} + component: web + release: {{ .Release.Name }} + template: + metadata: + annotations: + checksum/secret-config-envs: {{ include (print $.Template.BasePath "/config/secret-config-envs.yaml") . | sha256sum }} + checksum/secret-local-settings: {{ include (print $.Template.BasePath "/config/secret-local-settings.yaml") . | sha256sum }} + {{- if and (.Values.web.webserverConfig.enabled) (not .Values.web.webserverConfig.existingSecret) }} + checksum/config-webserver-config: {{ include (print $.Template.BasePath "/config/secret-webserver-config.yaml") . | sha256sum }} + {{- end }} + {{- if .Values.airflow.podAnnotations }} + {{- toYaml .Values.airflow.podAnnotations | nindent 8 }} + {{- end }} + {{- if .Values.web.podAnnotations }} + {{- toYaml .Values.web.podAnnotations | nindent 8 }} + {{- end }} + {{- if .Values.web.safeToEvict }} + cluster-autoscaler.kubernetes.io/safe-to-evict: "true" + {{- end }} + labels: + app: {{ include "airflow.labels.app" . }} + component: web + release: {{ .Release.Name }} + {{- if .Values.web.podLabels }} + {{- toYaml .Values.web.podLabels | nindent 8 }} + {{- end }} + spec: + restartPolicy: Always + {{- if .Values.airflow.image.pullSecret }} + imagePullSecrets: + - name: {{ .Values.airflow.image.pullSecret }} + {{- end }} + {{- if $podNodeSelector }} + nodeSelector: + {{- $podNodeSelector | nindent 8 }} + {{- end }} + {{- if $podAffinity }} + affinity: + {{- $podAffinity | nindent 8 }} + {{- end }} + {{- if $podTolerations }} + tolerations: + {{- $podTolerations | nindent 8 }} + {{- end }} + serviceAccountName: {{ include "airflow.serviceAccountName" . }} + {{- if $podSecurityContext }} + securityContext: + {{- $podSecurityContext | nindent 8 }} + {{- end }} + initContainers: + {{- if $extraPipPackages }} + {{- include "airflow.init_container.install_pip_packages" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages) | indent 8 }} + {{- end }} + {{- if .Values.dags.gitSync.enabled }} + {{- include "airflow.container.git_sync" (dict "Release" .Release "Values" .Values "sync_one_time" "true") | indent 8 }} + {{- end }} + {{- include "airflow.init_container.check_db" (dict "Release" .Release "Values" .Values "volumeMounts" $volumeMounts) | indent 8 }} + {{- include "airflow.init_container.wait_for_db_migrations" (dict "Release" .Release "Values" .Values "volumeMounts" $volumeMounts) | indent 8 }} + containers: + - name: airflow-web + {{- include "airflow.image" . | indent 10 }} + resources: + {{- toYaml .Values.web.resources | nindent 12 }} + ports: + - name: web + containerPort: 8080 + protocol: TCP + envFrom: + {{- include "airflow.envFrom" . | indent 12 }} + env: + {{- include "airflow.env" . | indent 12 }} + command: + {{- include "airflow.command" . | indent 12 }} + args: + - "bash" + - "-c" + - "exec airflow webserver" + {{- if .Values.web.livenessProbe.enabled }} + livenessProbe: + initialDelaySeconds: {{ .Values.web.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.web.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.web.livenessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.web.livenessProbe.failureThreshold }} + httpGet: + scheme: {{ include "airflow.web.scheme" . }} + {{- $airflowUrl := .Values.airflow.config.AIRFLOW__WEBSERVER__BASE_URL | default "" | printf "%s/health" | urlParse }} + path: {{ get $airflowUrl "path" }} + port: web + {{- end }} + {{- if .Values.web.readinessProbe.enabled }} + readinessProbe: + initialDelaySeconds: {{ .Values.web.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.web.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.web.readinessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.web.readinessProbe.failureThreshold }} + httpGet: + scheme: {{ include "airflow.web.scheme" . }} + {{- $airflowUrl := .Values.airflow.config.AIRFLOW__WEBSERVER__BASE_URL | default "" | printf "%s/health" | urlParse }} + path: {{ get $airflowUrl "path" }} + port: web + {{- end }} + volumeMounts: + {{- $volumeMounts | indent 12 }} + {{- if .Values.web.webserverConfig.enabled }} + - name: webserver-config + mountPath: /opt/airflow/webserver_config.py + subPath: webserver_config.py + readOnly: true + {{- end }} + {{- if .Values.dags.gitSync.enabled }} + {{- include "airflow.container.git_sync" . | indent 8 }} + {{- end }} + {{- if .Values.airflow.extraContainers }} + {{- toYaml .Values.airflow.extraContainers | nindent 8 }} + {{- end }} + volumes: + {{- $volumes | indent 8 }} + {{- if .Values.web.webserverConfig.enabled }} + - name: webserver-config + secret: + {{- if .Values.web.webserverConfig.existingSecret }} + secretName: {{ .Values.web.webserverConfig.existingSecret }} + {{- else }} + secretName: {{ include "airflow.fullname" . }}-webserver-config + {{- end }} + defaultMode: 0644 + {{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/templates/webserver/webserver-ingress-v1beta1.yaml b/charts/deps/charts/airflow-8.8.0/templates/webserver/webserver-ingress-v1beta1.yaml new file mode 100644 index 0000000..a5f7616 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/webserver/webserver-ingress-v1beta1.yaml @@ -0,0 +1,51 @@ +{{- if and (.Values.ingress.enabled) (eq .Values.ingress.apiVersion "networking.k8s.io/v1beta1") }} +apiVersion: networking.k8s.io/v1beta1 +kind: Ingress +metadata: + name: {{ include "airflow.fullname" . }}-web + {{- if .Values.ingress.web.annotations }} + annotations: + {{- toYaml .Values.ingress.web.annotations | nindent 4 }} + {{- end }} + labels: + app: {{ include "airflow.labels.app" . }} + component: web + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + {{- if .Values.ingress.web.labels }} + {{- toYaml .Values.ingress.web.labels | nindent 4 }} + {{- end }} +spec: + {{- if .Values.ingress.web.tls.enabled }} + tls: + - hosts: + - {{ .Values.ingress.web.host }} + {{- if .Values.ingress.web.tls.secretName }} + secretName: {{ .Values.ingress.web.tls.secretName }} + {{- end }} + {{- end }} + {{- if .Values.ingress.web.ingressClassName }} + ingressClassName: {{ .Values.ingress.web.ingressClassName }} + {{- end }} + rules: + - host: {{ .Values.ingress.web.host }} + http: + paths: + {{- range .Values.ingress.web.precedingPaths }} + - path: {{ .path }} + backend: + serviceName: {{ .serviceName }} + servicePort: {{ .servicePort }} + {{- end }} + - path: {{ .Values.ingress.web.path }} + backend: + serviceName: {{ include "airflow.fullname" . }}-web + servicePort: web + {{- range .Values.ingress.web.succeedingPaths }} + - path: {{ .path }} + backend: + serviceName: {{ .serviceName }} + servicePort: {{ .servicePort }} + {{- end }} +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/templates/webserver/webserver-ingress.yaml b/charts/deps/charts/airflow-8.8.0/templates/webserver/webserver-ingress.yaml new file mode 100644 index 0000000..a3f2531 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/webserver/webserver-ingress.yaml @@ -0,0 +1,69 @@ +{{- if and (.Values.ingress.enabled) (eq .Values.ingress.apiVersion "networking.k8s.io/v1") }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "airflow.fullname" . }}-web + {{- if .Values.ingress.web.annotations }} + annotations: + {{- toYaml .Values.ingress.web.annotations | nindent 4 }} + {{- end }} + labels: + app: {{ include "airflow.labels.app" . }} + component: web + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + {{- if .Values.ingress.web.labels }} + {{- toYaml .Values.ingress.web.labels | nindent 4 }} + {{- end }} +spec: + {{- if .Values.ingress.web.tls.enabled }} + tls: + - hosts: + - {{ .Values.ingress.web.host }} + {{- if .Values.ingress.web.tls.secretName }} + secretName: {{ .Values.ingress.web.tls.secretName }} + {{- end }} + {{- end }} + {{- if .Values.ingress.web.ingressClassName }} + ingressClassName: {{ .Values.ingress.web.ingressClassName }} + {{- end }} + rules: + - host: {{ .Values.ingress.web.host }} + http: + paths: + {{- range .Values.ingress.web.precedingPaths }} + - path: {{ .path }} + pathType: ImplementationSpecific + backend: + service: + name: {{ .serviceName }} + port: + {{- if kindIs "string" .servicePort }} + name: {{ .servicePort }} + {{- else }} + number: {{ .servicePort }} + {{- end }} + {{- end }} + - path: {{ .Values.ingress.web.path }} + pathType: ImplementationSpecific + backend: + service: + name: {{ include "airflow.fullname" . }}-web + port: + name: web + {{- range .Values.ingress.web.succeedingPaths }} + - path: {{ .path }} + pathType: ImplementationSpecific + backend: + service: + name: {{ .serviceName }} + port: + {{- if kindIs "string" .servicePort }} + name: {{ .servicePort }} + {{- else }} + number: {{ .servicePort }} + {{- end }} + {{- end }} + +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/templates/webserver/webserver-pdb.yaml b/charts/deps/charts/airflow-8.8.0/templates/webserver/webserver-pdb.yaml new file mode 100644 index 0000000..88d769c --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/webserver/webserver-pdb.yaml @@ -0,0 +1,24 @@ +{{- if .Values.web.podDisruptionBudget.enabled }} +apiVersion: {{ .Values.web.podDisruptionBudget.apiVersion }} +kind: PodDisruptionBudget +metadata: + name: {{ include "airflow.fullname" . }}-web + labels: + app: {{ include "airflow.labels.app" . }} + component: web + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + {{- if .Values.web.podDisruptionBudget.maxUnavailable }} + maxUnavailable: {{ .Values.web.podDisruptionBudget.maxUnavailable }} + {{- end }} + {{- if .Values.web.podDisruptionBudget.minAvailable }} + minAvailable: {{ .Values.web.podDisruptionBudget.minAvailable }} + {{- end }} + selector: + matchLabels: + app: {{ include "airflow.labels.app" . }} + component: web + release: {{ .Release.Name }} +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/templates/webserver/webserver-prometheus-rule.yaml b/charts/deps/charts/airflow-8.8.0/templates/webserver/webserver-prometheus-rule.yaml new file mode 100644 index 0000000..87167dc --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/webserver/webserver-prometheus-rule.yaml @@ -0,0 +1,18 @@ +{{- if .Values.prometheusRule.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: PrometheusRule +metadata: + name: {{ include "airflow.fullname" . }} + labels: + app: {{ include "airflow.labels.app" . }} + component: web + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + {{- if .Values.prometheusRule.additionalLabels }} + {{- toYaml .Values.prometheusRule.additionalLabels | nindent 4 }} + {{- end }} +spec: + groups: + {{- toYaml .Values.prometheusRule.groups | nindent 4 }} +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/templates/webserver/webserver-service-monitor.yaml b/charts/deps/charts/airflow-8.8.0/templates/webserver/webserver-service-monitor.yaml new file mode 100644 index 0000000..65fd0e5 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/webserver/webserver-service-monitor.yaml @@ -0,0 +1,25 @@ +{{- if .Values.serviceMonitor.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ include "airflow.fullname" . }} + labels: + app: {{ include "airflow.labels.app" . }} + component: web + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + {{- if .Values.serviceMonitor.selector }} + {{- toYaml .Values.serviceMonitor.selector | nindent 4 }} + {{- end }} +spec: + selector: + matchLabels: + app: {{ include "airflow.labels.app" . }} + component: web + release: {{ .Release.Name }} + endpoints: + - port: web + path: {{ .Values.serviceMonitor.path }} + interval: {{ .Values.serviceMonitor.interval }} +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/templates/webserver/webserver-service.yaml b/charts/deps/charts/airflow-8.8.0/templates/webserver/webserver-service.yaml new file mode 100644 index 0000000..f922c30 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/webserver/webserver-service.yaml @@ -0,0 +1,42 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "airflow.fullname" . }}-web + {{- if .Values.web.service.annotations }} + annotations: + {{- toYaml .Values.web.service.annotations | nindent 4 }} + {{- end }} + labels: + app: {{ include "airflow.labels.app" . }} + component: web + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + type: {{ .Values.web.service.type }} + selector: + app: {{ include "airflow.labels.app" . }} + component: web + release: {{ .Release.Name }} + sessionAffinity: {{ .Values.web.service.sessionAffinity }} + {{- if .Values.web.service.sessionAffinityConfig }} + sessionAffinityConfig: + {{- toYaml .Values.web.service.sessionAffinityConfig | nindent 4 }} + {{- end }} + ports: + - name: web + protocol: TCP + port: {{ .Values.web.service.externalPort | default 8080 }} + {{- if and (eq .Values.web.service.type "NodePort") (.Values.web.service.nodePort.http) }} + nodePort: {{ .Values.web.service.nodePort.http }} + {{- end }} + targetPort: 8080 + {{- if eq .Values.web.service.type "LoadBalancer" }} + {{- if .Values.web.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.web.service.loadBalancerIP | quote }} + {{- end }} + {{- if .Values.web.service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: + {{- toYaml .Values.web.service.loadBalancerSourceRanges | nindent 4 }} + {{- end }} +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/templates/worker/worker-hpa.yaml b/charts/deps/charts/airflow-8.8.0/templates/worker/worker-hpa.yaml new file mode 100644 index 0000000..c4b09a0 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/worker/worker-hpa.yaml @@ -0,0 +1,21 @@ +{{- if and (.Values.workers.enabled) (.Values.workers.autoscaling.enabled) }} +apiVersion: {{ .Values.workers.autoscaling.apiVersion }} +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "airflow.fullname" . }}-worker + labels: + app: {{ include "airflow.labels.app" . }} + component: worker + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: StatefulSet + name: {{ include "airflow.fullname" . }}-worker + minReplicas: {{ .Values.workers.replicas }} + maxReplicas: {{ .Values.workers.autoscaling.maxReplicas }} + metrics: + {{- toYaml .Values.workers.autoscaling.metrics | nindent 4 }} +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/templates/worker/worker-pdb.yaml b/charts/deps/charts/airflow-8.8.0/templates/worker/worker-pdb.yaml new file mode 100644 index 0000000..b0812bf --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/worker/worker-pdb.yaml @@ -0,0 +1,24 @@ +{{- if and (.Values.workers.enabled) (.Values.workers.podDisruptionBudget.enabled) }} +apiVersion: {{ .Values.workers.podDisruptionBudget.apiVersion }} +kind: PodDisruptionBudget +metadata: + name: {{ include "airflow.fullname" . }}-worker + labels: + app: {{ include "airflow.labels.app" . }} + component: worker + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + {{- if .Values.workers.podDisruptionBudget.maxUnavailable }} + maxUnavailable: {{ .Values.workers.podDisruptionBudget.maxUnavailable }} + {{- end }} + {{- if .Values.workers.podDisruptionBudget.minAvailable }} + minAvailable: {{ .Values.workers.podDisruptionBudget.minAvailable }} + {{- end }} + selector: + matchLabels: + app: {{ include "airflow.labels.app" . }} + component: worker + release: {{ .Release.Name }} +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/templates/worker/worker-service.yaml b/charts/deps/charts/airflow-8.8.0/templates/worker/worker-service.yaml new file mode 100644 index 0000000..bccabe7 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/worker/worker-service.yaml @@ -0,0 +1,23 @@ +{{- if .Values.workers.enabled }} +apiVersion: v1 +## this Service gives stable DNS entries for workers, used by webserver for logs +kind: Service +metadata: + name: {{ include "airflow.fullname" . }}-worker + labels: + app: {{ include "airflow.labels.app" . }} + component: worker + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} +spec: + ports: + - name: worker + protocol: TCP + port: 8793 + clusterIP: None + selector: + app: {{ include "airflow.labels.app" . }} + component: worker + release: {{ .Release.Name }} +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/templates/worker/worker-statefulset.yaml b/charts/deps/charts/airflow-8.8.0/templates/worker/worker-statefulset.yaml new file mode 100644 index 0000000..d8e5e94 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/templates/worker/worker-statefulset.yaml @@ -0,0 +1,225 @@ +{{- if .Values.workers.enabled }} +{{- $podNodeSelector := include "airflow.podNodeSelector" (dict "Release" .Release "Values" .Values "nodeSelector" .Values.workers.nodeSelector) }} +{{- $podAffinity := include "airflow.podAffinity" (dict "Release" .Release "Values" .Values "affinity" .Values.workers.affinity) }} +{{- $podTolerations := include "airflow.podTolerations" (dict "Release" .Release "Values" .Values "tolerations" .Values.workers.tolerations) }} +{{- $podSecurityContext := include "airflow.podSecurityContext" (dict "Release" .Release "Values" .Values "securityContext" .Values.workers.securityContext) }} +{{- $extraPipPackages := concat .Values.airflow.extraPipPackages .Values.workers.extraPipPackages }} +{{- $extraVolumeMounts := .Values.workers.extraVolumeMounts }} +{{- $volumeMounts := include "airflow.volumeMounts" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages "extraVolumeMounts" $extraVolumeMounts) }} +{{- $extraVolumes := .Values.workers.extraVolumes }} +{{- $volumes := include "airflow.volumes" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages "extraVolumes" $extraVolumes "extraVolumeMounts" $extraVolumeMounts) }} +apiVersion: apps/v1 +## StatefulSet gives workers consistent DNS names, allowing webserver access to log files +kind: StatefulSet +metadata: + name: {{ include "airflow.fullname" . }}-worker + {{- if .Values.workers.annotations }} + annotations: + {{- toYaml .Values.workers.annotations | nindent 4 }} + {{- end }} + labels: + app: {{ include "airflow.labels.app" . }} + component: worker + chart: {{ include "airflow.labels.chart" . }} + release: {{ .Release.Name }} + heritage: {{ .Release.Service }} + {{- if .Values.workers.labels }} + {{- toYaml .Values.workers.labels | nindent 4 }} + {{- end }} +spec: + serviceName: "{{ include "airflow.fullname" . }}-worker" + replicas: {{ .Values.workers.replicas }} + updateStrategy: + type: RollingUpdate + ## we do not need to guarantee the order in which workers are scaled + podManagementPolicy: Parallel + selector: + matchLabels: + app: {{ include "airflow.labels.app" . }} + component: worker + release: {{ .Release.Name }} + template: + metadata: + annotations: + checksum/secret-config-envs: {{ include (print $.Template.BasePath "/config/secret-config-envs.yaml") . | sha256sum }} + checksum/secret-local-settings: {{ include (print $.Template.BasePath "/config/secret-local-settings.yaml") . | sha256sum }} + {{- if .Values.airflow.podAnnotations }} + {{- toYaml .Values.airflow.podAnnotations | nindent 8 }} + {{- end }} + {{- if .Values.workers.podAnnotations }} + {{- toYaml .Values.workers.podAnnotations | nindent 8 }} + {{- end }} + {{- if .Values.workers.safeToEvict }} + cluster-autoscaler.kubernetes.io/safe-to-evict: "true" + {{- end }} + labels: + app: {{ include "airflow.labels.app" . }} + component: worker + release: {{ .Release.Name }} + {{- if .Values.workers.podLabels }} + {{- toYaml .Values.workers.podLabels | nindent 8 }} + {{- end }} + spec: + restartPolicy: Always + {{- if .Values.airflow.image.pullSecret }} + imagePullSecrets: + - name: {{ .Values.airflow.image.pullSecret }} + {{- end }} + {{- if .Values.workers.celery.gracefullTermination }} + terminationGracePeriodSeconds: {{ add .Values.workers.terminationPeriod .Values.workers.celery.gracefullTerminationPeriod }} + {{- else }} + terminationGracePeriodSeconds: {{ .Values.workers.terminationPeriod }} + {{- end }} + serviceAccountName: {{ include "airflow.serviceAccountName" . }} + {{- if $podNodeSelector }} + nodeSelector: + {{- $podNodeSelector | nindent 8 }} + {{- end }} + {{- if $podAffinity }} + affinity: + {{- $podAffinity | nindent 8 }} + {{- end }} + {{- if $podTolerations }} + tolerations: + {{- $podTolerations | nindent 8 }} + {{- end }} + {{- if $podSecurityContext }} + securityContext: + {{- $podSecurityContext | nindent 8 }} + {{- end }} + initContainers: + {{- if $extraPipPackages }} + {{- include "airflow.init_container.install_pip_packages" (dict "Release" .Release "Values" .Values "extraPipPackages" $extraPipPackages) | indent 8 }} + {{- end }} + {{- if .Values.dags.gitSync.enabled }} + {{- include "airflow.container.git_sync" (dict "Release" .Release "Values" .Values "sync_one_time" "true") | indent 8 }} + {{- end }} + {{- include "airflow.init_container.check_db" (dict "Release" .Release "Values" .Values "volumeMounts" $volumeMounts) | indent 8 }} + {{- include "airflow.init_container.wait_for_db_migrations" (dict "Release" .Release "Values" .Values "volumeMounts" $volumeMounts) | indent 8 }} + containers: + - name: airflow-worker + {{- include "airflow.image" . | indent 10 }} + resources: + {{- toYaml .Values.workers.resources | nindent 12 }} + envFrom: + {{- include "airflow.envFrom" . | indent 12 }} + env: + {{- include "airflow.env" . | indent 12 }} + # have dumb-init only send signals to direct child process (needed for celery workers to warm shutdown) + - name: DUMB_INIT_SETSID + value: "0" + {{- if .Values.workers.celery.gracefullTermination }} + lifecycle: + preStop: + exec: + command: + - "timeout" + - "{{ .Values.workers.celery.gracefullTerminationPeriod }}s" + - "python" + - "-Wignore" + - "-c" + - | + import os + import time + import subprocess + from celery import Celery + from celery.app.control import Inspect + from typing import List + + def run_command(cmd: List[str]) -> str: + process = subprocess.Popen(cmd, stdout=subprocess.PIPE) + output, error = process.communicate() + if error is not None: + raise Exception(error) + else: + return output.decode(encoding="utf-8") + + broker_url = run_command(["bash", "-c", "eval $AIRFLOW__CELERY__BROKER_URL_CMD"]) + local_celery_host = f"celery@{os.environ['HOSTNAME']}" + app = Celery(broker=broker_url) + + # prevent the worker accepting new tasks + print(f"canceling celery consumer for {local_celery_host}...") + app.control.cancel_consumer("default", destination=[local_celery_host]) + + # wait until the worker finishes its current tasks + i = Inspect(app=app, destination=[local_celery_host]) + active_tasks = i.active()[local_celery_host] + while len(active_tasks) > 0: + print(f"waiting [10 sec] for remaining tasks to finish: {[task.get('name') for task in active_tasks]}") + time.sleep(10) + active_tasks = i.active()[local_celery_host] + {{- end }} + {{- if .Values.workers.livenessProbe.enabled }} + livenessProbe: + initialDelaySeconds: {{ .Values.workers.livenessProbe.initialDelaySeconds }} + timeoutSeconds: {{ .Values.workers.livenessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.workers.livenessProbe.failureThreshold }} + periodSeconds: {{ .Values.workers.livenessProbe.periodSeconds }} + exec: + command: + {{- include "airflow.command" . | indent 16 }} + - "python" + - "-Wignore" + - "-c" + - | + import os + import sys + import subprocess + from celery import Celery + from celery.app.control import Inspect + from typing import List + + def run_command(cmd: List[str]) -> str: + process = subprocess.Popen(cmd, stdout=subprocess.PIPE) + output, error = process.communicate() + if error is not None: + raise Exception(error) + else: + return output.decode(encoding="utf-8") + + broker_url = run_command(["bash", "-c", "eval $AIRFLOW__CELERY__BROKER_URL_CMD"]) + local_celery_host = f"celery@{os.environ['HOSTNAME']}" + app = Celery(broker=broker_url) + + # ping the local celery worker to see if it's ok + i = Inspect(app=app, destination=[local_celery_host], timeout=5.0) + ping_responses = i.ping() + if local_celery_host not in ping_responses: + sys.exit(f"celery worker '{local_celery_host}' did not respond to ping") + {{- end }} + ports: + - name: wlog + containerPort: 8793 + protocol: TCP + command: + {{- include "airflow.command" . | indent 12 }} + args: + - "bash" + - "-c" + {{- if .Values.airflow.legacyCommands }} + - "exec airflow worker" + {{- else }} + - "exec airflow celery worker" + {{- end }} + {{- if $volumeMounts }} + volumeMounts: + {{- $volumeMounts | indent 12 }} + {{- end }} + {{- if .Values.dags.gitSync.enabled }} + {{- include "airflow.container.git_sync" . | indent 8 }} + {{- end }} + {{- if .Values.workers.logCleanup.enabled }} + {{- $lc_resources := .Values.workers.logCleanup.resources }} + {{- $lc_retention_min := .Values.workers.logCleanup.retentionMinutes }} + {{- $lc_interval_sec := .Values.workers.logCleanup.intervalSeconds }} + {{- include "airflow.container.log_cleanup" (dict "Release" .Release "Values" .Values "resources" $lc_resources "retention_min" $lc_retention_min "interval_sec" $lc_interval_sec) | indent 8 }} + {{- end }} + {{- if .Values.airflow.extraContainers }} + {{- toYaml .Values.airflow.extraContainers | nindent 8 }} + {{- end }} + {{- if $volumes }} + volumes: + {{- $volumes | indent 8 }} + {{- end }} +{{- end }} diff --git a/charts/deps/charts/airflow-8.8.0/values.yaml b/charts/deps/charts/airflow-8.8.0/values.yaml new file mode 100644 index 0000000..3712eb0 --- /dev/null +++ b/charts/deps/charts/airflow-8.8.0/values.yaml @@ -0,0 +1,2260 @@ +######################################## +## CONFIG | Airflow Configs +######################################## +airflow: + ## if we use legacy 1.10 airflow commands + ## + legacyCommands: false + + ## configs for the airflow container image + ## + image: + repository: apache/airflow + tag: 2.6.3-python3.9 + pullPolicy: IfNotPresent + pullSecret: "" + uid: 50000 + gid: 0 + + ## the airflow executor type to use + ## - allowed values: "CeleryExecutor", "KubernetesExecutor", "CeleryKubernetesExecutor" + ## - customize the "KubernetesExecutor" pod-template with `airflow.kubernetesPodTemplate.*` + ## + executor: CeleryExecutor + + ## the fernet encryption key (sets `AIRFLOW__CORE__FERNET_KEY`) + ## - [WARNING] you must change this value to ensure the security of your airflow + ## - set `AIRFLOW__CORE__FERNET_KEY` with `airflow.extraEnv` from a Secret to avoid storing this in your values + ## - use this command to generate your own fernet key: + ## python -c "from cryptography.fernet import Fernet; FERNET_KEY = Fernet.generate_key().decode(); print(FERNET_KEY)" + ## + fernetKey: "7T512UXSSmBOkpWimFHIVb8jK6lfmSAvx4mO6Arehnc=" + + ## the secret_key for flask (sets `AIRFLOW__WEBSERVER__SECRET_KEY`) + ## - [WARNING] you must change this value to ensure the security of your airflow + ## - set `AIRFLOW__WEBSERVER__SECRET_KEY` with `airflow.extraEnv` from a Secret to avoid storing this in your values + ## + webserverSecretKey: "THIS IS UNSAFE!" + + ## environment variables for airflow configs + ## - airflow env-vars are structured: "AIRFLOW__{config_section}__{config_name}" + ## - airflow configuration reference: + ## https://airflow.apache.org/docs/apache-airflow/stable/configurations-ref.html + ## + ## ____ EXAMPLE _______________ + ## config: + ## # dag configs + ## AIRFLOW__CORE__LOAD_EXAMPLES: "False" + ## AIRFLOW__SCHEDULER__DAG_DIR_LIST_INTERVAL: "30" + ## + ## # email configs + ## AIRFLOW__EMAIL__EMAIL_BACKEND: "airflow.utils.email.send_email_smtp" + ## AIRFLOW__SMTP__SMTP_HOST: "smtpmail.example.com" + ## AIRFLOW__SMTP__SMTP_MAIL_FROM: "admin@example.com" + ## AIRFLOW__SMTP__SMTP_PORT: "25" + ## AIRFLOW__SMTP__SMTP_SSL: "False" + ## AIRFLOW__SMTP__SMTP_STARTTLS: "False" + ## + ## # domain used in airflow emails + ## AIRFLOW__WEBSERVER__BASE_URL: "http://airflow.example.com" + ## + ## # ether environment variables + ## HTTP_PROXY: "http://proxy.example.com:8080" + ## + config: {} + + ## a list of users to create + ## - templates can ONLY be used in: `password`, `email`, `firstName`, `lastName` + ## - templates used a bash-like syntax: ${MY_USERNAME}, $MY_USERNAME + ## - templates are defined in `usersTemplates` + ## - `role` can be a single role or a list of roles + ## + users: + - username: admin + password: admin + role: Admin + email: admin@example.com + firstName: admin + lastName: admin + + ## bash-like templates to be used in `airflow.users` + ## - [WARNING] if a Secret or ConfigMap is missing, the sync Pod will crash + ## - [WARNING] all keys must match the regex: ^[a-zA-Z_][a-zA-Z0-9_]*$ + ## + ## ____ EXAMPLE _______________ + ## usersTemplates + ## MY_USERNAME: + ## kind: configmap + ## name: my-configmap + ## key: username + ## MY_PASSWORD: + ## kind: secret + ## name: my-secret + ## key: password + ## + usersTemplates: {} + + ## if we create a Deployment to perpetually sync `airflow.users` + ## - when `true`, users are updated in real-time, as ConfigMaps/Secrets change + ## - when `true`, users changes from the WebUI will be reverted automatically + ## - when `false`, users will only update one-time, after each `helm upgrade` + ## + usersUpdate: true + + ## a list airflow connections to create + ## - templates can ONLY be used in: `host`, `login`, `password`, `schema`, `extra` + ## - templates used a bash-like syntax: ${AWS_ACCESS_KEY} or $AWS_ACCESS_KEY + ## - templates are defined in `connectionsTemplates` + ## + ## ____ EXAMPLE _______________ + ## connections: + ## - id: my_aws + ## type: aws + ## description: my AWS connection + ## extra: |- + ## { "aws_access_key_id": "${AWS_KEY_ID}", + ## "aws_secret_access_key": "${AWS_ACCESS_KEY}", + ## "region_name":"eu-central-1" } + ## + connections: [] + + ## bash-like templates to be used in `airflow.connections` + ## - see docs for `airflow.usersTemplates` + ## + connectionsTemplates: {} + + ## if we create a Deployment to perpetually sync `airflow.connections` + ## - see docs for `airflow.usersUpdate` + ## + connectionsUpdate: true + + ## a list airflow variables to create + ## - templates can ONLY be used in: `value` + ## - templates used a bash-like syntax: ${MY_VALUE} or $MY_VALUE + ## - templates are defined in `connectionsTemplates` + ## + ## ____ EXAMPLE _______________ + ## variables: + ## - key: "var_1" + ## value: "my_value_1" + ## - key: "var_2" + ## value: "my_value_2" + ## + variables: [] + + ## bash-like templates to be used in `airflow.variables` + ## - see docs for `airflow.usersTemplates` + ## + variablesTemplates: {} + + ## if we create a Deployment to perpetually sync `airflow.variables` + ## - see docs for `airflow.usersUpdate` + ## + variablesUpdate: true + + ## a list airflow pools to create + ## + ## ____ EXAMPLE _______________ + ## pools: + ## - name: "pool_1" + ## description: "example pool with 5 slots" + ## slots: 5 + ## - name: "pool_2" + ## description: "example pool with 2 cron policies" + ## slots: 0 + ## ## if deferred tasks count towards the slot limit, requires airflow 2.7.0+ (default: false) + ## include_deferred: false + ## ## at each sync interval, the policy with the most recently past `recurrence` is applied + ## policies: + ## - name: "scale up at 7pm UTC" + ## slots: 50 + ## recurrence: "0 19 * * *" + ## - name: "scale down at 6am UTC" + ## slots: 10 + ## recurrence: "0 6 * * *" + ## + pools: [] + + ## if we create a Deployment to perpetually sync `airflow.pools` + ## - see docs for `airflow.usersUpdate` + ## + poolsUpdate: true + + ## default nodeSelector for airflow Pods (is overridden by pod-specific values) + ## - docs for nodeSelector: + ## https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector + ## + defaultNodeSelector: {} + + ## default affinity configs for airflow Pods (is overridden by pod-specific values) + ## - spec for Affinity: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#affinity-v1-core + ## + defaultAffinity: {} + + ## default toleration configs for airflow Pods (is overridden by pod-specific values) + ## - spec for Toleration: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#toleration-v1-core + ## + defaultTolerations: [] + + ## default securityContext configs for airflow Pods (is overridden by pod-specific values) + ## - spec for PodSecurityContext: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#podsecuritycontext-v1-core + ## + defaultSecurityContext: + ## sets the filesystem owner group of files/folders in mounted volumes + ## this does NOT give root permissions to Pods, only the "root" group + fsGroup: 0 + + ## extra annotations for airflow Pods + ## + podAnnotations: {} + + ## extra pip packages to install in airflow Pods + ## + ## ____ EXAMPLE _______________ + ## extraPipPackages: + ## - "SomeProject==1.0.0" + ## + extraPipPackages: [] + + ## pip packages that are protected from upgrade/downgrade by `extraPipPackages` + ## - [WARNING] Pods will fail to start if `extraPipPackages` would cause these packages to change versions + ## + protectedPipPackages: + - "apache-airflow" + + ## extra environment variables for the airflow Pods + ## - spec for EnvVar: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#envvar-v1-core + ## + extraEnv: [] + + ## extra containers for the airflow Pods + ## - spec for Container: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#container-v1-core + ## + extraContainers: [] + + ## extra VolumeMounts for the airflow Pods + ## - spec for VolumeMount: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#volumemount-v1-core + ## + extraVolumeMounts: [] + + ## extra Volumes for the airflow Pods + ## - spec for Volume: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#volume-v1-core + ## + extraVolumes: [] + + ## kubernetes cluster domain name + ## - configured in the kubelet with `--cluster-domain` flag (deprecated): + ## https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/ + ## - or configured in the kubelet with configuration file `clusterDomain` option: + ## https://kubernetes.io/docs/tasks/administer-cluster/kubelet-config-file/ + ## + clusterDomain: "cluster.local" + + ######################################## + ## FILE | airflow_local_settings.py + ######################################## + ## + localSettings: + ## the full content of the `airflow_local_settings.py` file (as a string) + ## - docs for airflow cluster policies: + ## https://airflow.apache.org/docs/apache-airflow/stable/concepts/cluster-policies.html + ## + ## ____ EXAMPLE _______________ + ## stringOverride: | + ## # use a custom `xcom_sidecar` image for KubernetesPodOperator() + ## from airflow.kubernetes.pod_generator import PodDefaults + ## PodDefaults.SIDECAR_CONTAINER.image = "gcr.io/PROJECT-ID/custom-sidecar-image" + ## + stringOverride: "" + + ## the name of a Secret containing a `airflow_local_settings.py` key + ## - if set, this disables `airflow.localSettings.stringOverride` + ## + existingSecret: "" + + ######################################## + ## FILE | pod_template.yaml + ######################################## + ## - generates a file for `AIRFLOW__KUBERNETES__POD_TEMPLATE_FILE` + ## - the `dags.gitSync` values will create a git-sync init-container in the pod + ## - the `airflow.extraPipPackages` will NOT be installed + ## + kubernetesPodTemplate: + ## the full content of the pod-template file (as a string) + ## - [WARNING] all other `kubernetesPodTemplate.*` are disabled when this is set + ## - docs for pod-template file: + ## https://airflow.apache.org/docs/apache-airflow/stable/executor/kubernetes.html#pod-template-file + ## + ## ____ EXAMPLE _______________ + ## stringOverride: |- + ## apiVersion: v1 + ## kind: Pod + ## spec: ... + ## + stringOverride: "" + + ## resource requests/limits for the Pod template "base" container + ## - spec for ResourceRequirements: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core + ## + resources: {} + + ## the nodeSelector configs for the Pod template + ## - docs for nodeSelector: + ## https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector + ## + nodeSelector: {} + + ## the affinity configs for the Pod template + ## - spec for Affinity: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#affinity-v1-core + ## + affinity: {} + + ## the toleration configs for the Pod template + ## - spec for Toleration: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#toleration-v1-core + ## + tolerations: [] + + ## labels for the Pod template + ## + podLabels: {} + + ## annotations for the Pod template + ## + podAnnotations: {} + + ## the security context for the Pod template + ## - spec for PodSecurityContext: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#podsecuritycontext-v1-core + ## + securityContext: {} + + ## the shareProcessNamespace config for the Pod template + ## - docs for shareProcessNamespace: + ## https://kubernetes.io/docs/tasks/configure-pod-container/share-process-namespace/ + ## + shareProcessNamespace: false + + ## extra pip packages to install in the Pod template + ## + ## ____ EXAMPLE _______________ + ## extraPipPackages: + ## - "SomeProject==1.0.0" + ## + extraPipPackages: [] + + ## extra containers for the pod template + ## - spec for Container: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#container-v1-core + ## + extraContainers: [] + + ## extra init-containers for the Pod template + ## - spec of Container: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#container-v1-core + ## + extraInitContainers: [] + + ## extra VolumeMounts for the Pod template + ## - spec for VolumeMount: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#volumemount-v1-core + ## + extraVolumeMounts: [] + + ## extra Volumes for the Pod template + ## - spec for Volume: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#volume-v1-core + ## + extraVolumes: [] + + ######################################## + ## COMPONENT | db-migrations Deployment + ######################################## + dbMigrations: + ## if the db-migrations Deployment/Job is created + ## - [WARNING] if `false`, you have to MANUALLY run `airflow db upgrade` when required + ## + enabled: true + + ## if a post-install helm Job should be used (instead of a Deployment) + ## - [WARNING] setting `true` will NOT work with the helm `--wait` flag, + ## this is because post-install helm Jobs run AFTER the main resources become Ready, + ## which will cause a deadlock, as other resources require db-migrations to become Ready + ## + runAsJob: false + + ## resource requests/limits for the db-migrations Pods + ## - spec for ResourceRequirements: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core + ## + resources: {} + + ## the nodeSelector configs for the db-migrations Pods + ## - docs for nodeSelector: + ## https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector + ## + nodeSelector: {} + + ## the affinity configs for the db-migrations Pods + ## - spec for Affinity: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#affinity-v1-core + ## + affinity: {} + + ## the toleration configs for the db-migrations Pods + ## - spec for Toleration: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#toleration-v1-core + ## + tolerations: [] + + ## the security context for the db-migrations Pods + ## - spec for PodSecurityContext: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#podsecuritycontext-v1-core + ## + securityContext: {} + + ## Labels for the db-migrations Deployment + ## + labels: {} + + ## Pod labels for the db-migrations Deployment + ## + podLabels: {} + + ## annotations for the db-migrations Deployment/Job + ## + annotations: {} + + ## Pod annotations for the db-migrations Deployment/Job + ## + podAnnotations: {} + + ## if we add the annotation: "cluster-autoscaler.kubernetes.io/safe-to-evict" = "true" + ## + safeToEvict: true + + ## the number of seconds between checks for unapplied db migrations + ## - only applies if `airflow.dbMigrations.runAsJob` is `false` + ## + checkInterval: 300 + + ######################################## + ## COMPONENT | Sync Deployments + ######################################## + ## - used by the Deployments/Jobs used by `airflow.{connections,pools,users,variables}` + ## + sync: + ## resource requests/limits for the sync Pods + ## - spec for ResourceRequirements: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core + ## + resources: {} + + ## the nodeSelector configs for the sync Pods + ## - docs for nodeSelector: + ## https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector + ## + nodeSelector: {} + + ## the affinity configs for the sync Pods + ## - spec for Affinity: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#affinity-v1-core + ## + affinity: {} + + ## the toleration configs for the sync Pods + ## - spec for Toleration: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#toleration-v1-core + ## + tolerations: [] + + ## the security context for the sync Pods + ## - spec for PodSecurityContext: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#podsecuritycontext-v1-core + ## + securityContext: {} + + ## Labels for the sync Deployments/Jobs + ## + labels: {} + + ## Pod labels for the sync Deployments/Jobs + ## + podLabels: {} + + ## annotations for the sync Deployments/Jobs + ## + annotations: {} + + ## Pod annotations for the sync Deployments/Jobs + ## + podAnnotations: {} + + ## if we add the annotation: "cluster-autoscaler.kubernetes.io/safe-to-evict" = "true" + ## + safeToEvict: true + +################################### +## COMPONENT | Airflow Scheduler +################################### +scheduler: + ## the number of scheduler Pods to run + ## - if you set this >1 we recommend defining a `scheduler.podDisruptionBudget` + ## + replicas: 1 + + ## resource requests/limits for the scheduler Pod + ## - spec of ResourceRequirements: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core + ## + resources: {} + + ## the nodeSelector configs for the scheduler Pods + ## - docs for nodeSelector: + ## https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector + ## + nodeSelector: {} + + ## the affinity configs for the scheduler Pods + ## - spec of Affinity: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#affinity-v1-core + ## + affinity: {} + + ## the toleration configs for the scheduler Pods + ## - spec of Toleration: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#toleration-v1-core + ## + tolerations: [] + + ## the security context for the scheduler Pods + ## - spec of PodSecurityContext: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#podsecuritycontext-v1-core + ## + securityContext: {} + + ## labels for the scheduler Deployment + ## + labels: {} + + ## Pod labels for the scheduler Deployment + ## + podLabels: {} + + ## annotations for the scheduler Deployment + ## + annotations: {} + + ## Pod annotations for the scheduler Deployment + ## + podAnnotations: {} + + ## if we add the annotation: "cluster-autoscaler.kubernetes.io/safe-to-evict" = "true" + ## + safeToEvict: true + + ## configs for the PodDisruptionBudget of the scheduler + ## + podDisruptionBudget: + ## if a PodDisruptionBudget resource is created for the scheduler + ## + enabled: false + + ## the `apiVersion` to use for PodDisruptionBudget resources + ## - for Kubernetes 1.21 and later: "policy/v1" + ## - for Kubernetes 1.20 and before: "policy/v1beta1" + ## + apiVersion: policy/v1 + + ## the maximum unavailable pods/percentage for the scheduler + ## + maxUnavailable: "" + + ## the minimum available pods/percentage for the scheduler + ## + minAvailable: "" + + ## configs for the log-cleanup sidecar of the scheduler + ## - helps prevent excessive log buildup by regularly deleting old files + ## + logCleanup: + ## if the log-cleanup sidecar is enabled + ## - [WARNING] must be disabled if `logs.persistence.enabled` is `true` + ## + enabled: true + + ## resource requests/limits for the log-cleanup container + ## - spec of ResourceRequirements: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core + ## + resources: {} + + ## the number of minutes to retain log files (by last-modified time) + ## + retentionMinutes: 21600 + + ## the number of seconds between each check for files to delete + ## + intervalSeconds: 900 + + ## sets `airflow --num_runs` parameter used to run the airflow scheduler + ## + numRuns: -1 + + ## configs for the scheduler Pods' liveness probe + ## - "unhealthy" means the SchedulerJob has not had a heartbeat for + ## AIRFLOW__SCHEDULER__SCHEDULER_HEALTH_CHECK_THRESHOLD seconds + ## - `periodSeconds` x `failureThreshold` = max seconds a scheduler can be in an "unhealthy" state + ## + livenessProbe: + enabled: true + initialDelaySeconds: 10 + periodSeconds: 30 + timeoutSeconds: 60 + failureThreshold: 5 + + ## configs for an additional check that ensures tasks are being created by the scheduler + ## - this check works by ensuring that the most recent LocalTaskJob had a `start_date` no more than + ## `taskCreationCheck.thresholdSeconds` seconds ago + ## - this check is useful because the scheduler can deadlock with a heartbeat, but not be scheduling new tasks: + ## https://github.com/apache/airflow/issues/7935 - patched in airflow `2.0.2` + ## https://github.com/apache/airflow/issues/15938 - patched in airflow `2.1.1` + ## + taskCreationCheck: + ## if the task creation check is enabled + ## + enabled: false + + ## the maximum number of seconds since the start_date of the most recent LocalTaskJob + ## - [WARNING] must be AT LEAST equal to your shortest DAG schedule_interval + ## - [WARNING] DummyOperator tasks will NOT be seen by this probe + ## + thresholdSeconds: 300 + + ## minimum number of seconds the scheduler must have run before the task creation check begins + ## - [WARNING] must be long enough for the scheduler to boot and create a task + ## + schedulerAgeBeforeCheck: 180 + + ## extra pip packages to install in the scheduler Pods + ## + ## ____ EXAMPLE _______________ + ## extraPipPackages: + ## - "SomeProject==1.0.0" + ## + extraPipPackages: [] + + ## extra VolumeMounts for the scheduler Pods + ## - spec of VolumeMount: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#volumemount-v1-core + ## + extraVolumeMounts: [] + + ## extra Volumes for the scheduler Pods + ## - spec of Volume: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#volume-v1-core + ## + extraVolumes: [] + + ## extra init containers to run in the scheduler Pods + ## - spec of Container: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#container-v1-core + ## + extraInitContainers: [] + +################################### +## COMPONENT | Airflow Webserver +################################### +web: + ######################################## + ## FILE | webserver_config.py + ######################################## + ## + webserverConfig: + ## if the `webserver_config.py` file is mounted + ## - set to false if you wish to mount your own `webserver_config.py` file + ## + enabled: true + + ## the full content of the `webserver_config.py` file (as a string) + ## - docs for Flask-AppBuilder security configs: + ## https://flask-appbuilder.readthedocs.io/en/latest/security.html + ## + ## ____ EXAMPLE _______________ + ## stringOverride: | + ## from airflow import configuration as conf + ## from flask_appbuilder.security.manager import AUTH_DB + ## + ## # the SQLAlchemy connection string + ## SQLALCHEMY_DATABASE_URI = conf.get('core', 'SQL_ALCHEMY_CONN') + ## + ## # use embedded DB for auth + ## AUTH_TYPE = AUTH_DB + ## + stringOverride: "" + + ## the name of a Secret containing a `webserver_config.py` key + ## + existingSecret: "" + + ## the number of web Pods to run + ## - if you set this >1 we recommend defining a `web.podDisruptionBudget` + ## + replicas: 1 + + ## resource requests/limits for the web Pod + ## - spec for ResourceRequirements: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core + ## + resources: {} + + ## the nodeSelector configs for the web Pods + ## - docs for nodeSelector: + ## https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector + ## + nodeSelector: {} + + ## the affinity configs for the web Pods + ## - spec for Affinity: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#affinity-v1-core + ## + affinity: {} + + ## the toleration configs for the web Pods + ## - spec for Toleration: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#toleration-v1-core + ## + tolerations: [] + + ## the security context for the web Pods + ## - spec for PodSecurityContext: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#podsecuritycontext-v1-core + ## + securityContext: {} + + ## labels for the web Deployment + ## + labels: {} + + ## Pod labels for the web Deployment + ## + podLabels: {} + + ## annotations for the web Deployment + ## + annotations: {} + + ## Pod annotations for the web Deployment + ## + podAnnotations: {} + + ## if we add the annotation: "cluster-autoscaler.kubernetes.io/safe-to-evict" = "true" + ## + safeToEvict: true + + ## configs for the PodDisruptionBudget of the web Deployment + ## + podDisruptionBudget: + ## if a PodDisruptionBudget resource is created for the web Deployment + ## + enabled: false + + ## the `apiVersion` to use for PodDisruptionBudget resources + ## - for Kubernetes 1.21 and later: "policy/v1" + ## - for Kubernetes 1.20 and before: "policy/v1beta1" + ## + apiVersion: policy/v1 + + ## the maximum unavailable pods/percentage for the web Deployment + ## + maxUnavailable: "" + + ## the minimum available pods/percentage for the web Deployment + ## + minAvailable: "" + + ## configs for the Service of the web Pods + ## + service: + annotations: {} + sessionAffinity: "None" + sessionAffinityConfig: {} + type: ClusterIP + externalPort: 8080 + loadBalancerIP: "" + loadBalancerSourceRanges: [] + nodePort: + http: "" + + ## configs for the web Pods' readiness probe + ## + readinessProbe: + enabled: true + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + + ## configs for the web Pods' liveness probe + ## + livenessProbe: + enabled: true + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + + ## extra pip packages to install in the web Pods + ## + ## ____ EXAMPLE _______________ + ## extraPipPackages: + ## - "SomeProject==1.0.0" + ## + extraPipPackages: [] + + ## extra VolumeMounts for the web Pods + ## - spec for VolumeMount: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#volumemount-v1-core + ## + extraVolumeMounts: [] + + ## extra Volumes for the web Pods + ## - spec for Volume: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#volume-v1-core + ## + extraVolumes: [] + +################################### +## COMPONENT | Airflow Workers +################################### +workers: + ## if the airflow workers StatefulSet should be deployed + ## + enabled: true + + ## the number of worker Pods to run + ## - if you set this >1 we recommend defining a `workers.podDisruptionBudget` + ## - this is the minimum when `workers.autoscaling.enabled` is true + ## + replicas: 1 + + ## resource requests/limits for the worker Pod + ## - spec for ResourceRequirements: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core + ## + resources: {} + + ## the nodeSelector configs for the worker Pods + ## - docs for nodeSelector: + ## https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector + ## + nodeSelector: {} + + ## the affinity configs for the worker Pods + ## - spec for Affinity: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#affinity-v1-core + ## + affinity: {} + + ## the toleration configs for the worker Pods + ## - spec for Toleration: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#toleration-v1-core + ## + tolerations: [] + + ## the security context for the worker Pods + ## - spec for PodSecurityContext: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#podsecuritycontext-v1-core + ## + securityContext: {} + + ## labels for the worker StatefulSet + ## + labels: {} + + ## Pod labels for the worker StatefulSet + ## + podLabels: {} + + ## annotations for the worker StatefulSet + ## + annotations: {} + + ## Pod annotations for the worker StatefulSet + ## + podAnnotations: {} + + ## if we add the annotation: "cluster-autoscaler.kubernetes.io/safe-to-evict" = "true" + ## + safeToEvict: true + + ## configs for the PodDisruptionBudget of the worker StatefulSet + ## + podDisruptionBudget: + ## if a PodDisruptionBudget resource is created for the worker StatefulSet + ## + enabled: false + + ## the `apiVersion` to use for PodDisruptionBudget resources + ## - for Kubernetes 1.21 and later: "policy/v1" + ## - for Kubernetes 1.20 and before: "policy/v1beta1" + ## + apiVersion: policy/v1 + + ## the maximum unavailable pods/percentage for the worker StatefulSet + ## + maxUnavailable: "" + + ## the minimum available pods/percentage for the worker StatefulSet + ## + minAvailable: "" + + ## configs for the HorizontalPodAutoscaler of the worker Pods + ## - [WARNING] if using git-sync, ensure `dags.gitSync.resources` is set + ## - [WARNING] if using worker log-cleanup, ensure `workers.logCleanup.resources` is set + ## + ## ____ EXAMPLE _______________ + ## autoscaling: + ## enabled: true + ## maxReplicas: 16 + ## metrics: + ## - type: Resource + ## resource: + ## name: memory + ## target: + ## type: Utilization + ## averageUtilization: 80 + ## + autoscaling: + enabled: false + maxReplicas: 2 + metrics: [] + + ## the `apiVersion` to use for HorizontalPodAutoscaler resources + ## - for Kubernetes 1.23 and later: "autoscaling/v2" + ## - for Kubernetes 1.22 and before: "autoscaling/v2beta2" + ## + apiVersion: autoscaling/v2 + + ## configs for the celery worker Pods + ## + celery: + ## if celery worker Pods are gracefully terminated + ## - consider defining a `workers.podDisruptionBudget` to prevent there not being + ## enough available workers during graceful termination waiting periods + ## + ## graceful termination process: + ## 1. prevent worker accepting new tasks + ## 2. wait AT MOST `workers.celery.gracefullTerminationPeriod` for tasks to finish + ## 3. send SIGTERM to worker + ## 4. wait AT MOST `workers.terminationPeriod` for kill to finish + ## 5. send SIGKILL to worker + ## + gracefullTermination: false + + ## how many seconds to wait for tasks to finish before SIGTERM of the celery worker + ## + gracefullTerminationPeriod: 600 + + ## how many seconds to wait after SIGTERM before SIGKILL of the celery worker + ## - [WARNING] tasks that are still running during SIGKILL will be orphaned, this is important + ## to understand with KubernetesPodOperator(), as Pods may continue running + ## + terminationPeriod: 60 + + ## configs for the log-cleanup sidecar of the worker Pods + ## - helps prevent excessive log buildup by regularly deleting old files + ## + logCleanup: + ## if the log-cleanup sidecar is enabled + ## - [WARNING] must be disabled if `logs.persistence.enabled` is `true` + ## + enabled: true + + ## resource requests/limits for the log-cleanup container + ## - spec of ResourceRequirements: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core + ## + resources: {} + + ## the number of minutes to retain log files (by last-modified time) + ## + retentionMinutes: 21600 + + ## the number of seconds between each check for files to delete + ## + intervalSeconds: 900 + + ## configs for the worker Pods' liveness probe + ## + livenessProbe: + enabled: true + initialDelaySeconds: 10 + periodSeconds: 30 + timeoutSeconds: 60 + failureThreshold: 5 + + ## extra pip packages to install in the worker Pod + ## + ## ____ EXAMPLE _______________ + ## extraPipPackages: + ## - "SomeProject==1.0.0" + ## + extraPipPackages: [] + + ## extra VolumeMounts for the worker Pods + ## - spec for VolumeMount: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#volumemount-v1-core + ## + extraVolumeMounts: [] + + ## extra Volumes for the worker Pods + ## - spec for Volume: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#volume-v1-core + ## + extraVolumes: [] + +################################### +## COMPONENT | Triggerer +################################### +triggerer: + ## if the airflow triggerer should be deployed + ## - [WARNING] the triggerer component was added in airflow 2.2.0 + ## - [WARNING] if `airflow.legacyCommands` is `true` the triggerer will NOT be deployed + ## + enabled: true + + ## the number of triggerer Pods to run + ## - if you set this >1 we recommend defining a `triggerer.podDisruptionBudget` + ## + replicas: 1 + + ## resource requests/limits for the triggerer Pods + ## - spec for ResourceRequirements: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core + ## + resources: {} + + ## the nodeSelector configs for the triggerer Pods + ## - docs for nodeSelector: + ## https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector + ## + nodeSelector: {} + + ## the affinity configs for the triggerer Pods + ## - spec for Affinity: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#affinity-v1-core + ## + affinity: {} + + ## the toleration configs for the triggerer Pods + ## - spec for Toleration: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#toleration-v1-core + ## + tolerations: [] + + ## the security context for the triggerer Pods + ## - spec for PodSecurityContext: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#podsecuritycontext-v1-core + ## + securityContext: {} + + ## labels for the triggerer Deployment + ## + labels: {} + + ## Pod labels for the triggerer Deployment + ## + podLabels: {} + + ## annotations for the triggerer Deployment + ## + annotations: {} + + ## Pod annotations for the triggerer Deployment + ## + podAnnotations: {} + + ## if we add the annotation: "cluster-autoscaler.kubernetes.io/safe-to-evict" = "true" + ## + safeToEvict: true + + ## configs for the PodDisruptionBudget of the triggerer Deployment + ## + podDisruptionBudget: + ## if a PodDisruptionBudget resource is created for the triggerer Deployment + ## + enabled: false + + ## the `apiVersion` to use for PodDisruptionBudget resources + ## - for Kubernetes 1.21 and later: "policy/v1" + ## - for Kubernetes 1.20 and before: "policy/v1beta1" + ## + apiVersion: policy/v1 + + ## the maximum unavailable pods/percentage for the triggerer Deployment + ## + maxUnavailable: "" + + ## the minimum available pods/percentage for the triggerer Deployment + ## + minAvailable: "" + + ## maximum number of triggers each triggerer will run at once (sets `AIRFLOW__TRIGGERER__DEFAULT_CAPACITY`) + ## + capacity: 1000 + + ## configs for the triggerer Pods' liveness probe + ## + livenessProbe: + enabled: true + initialDelaySeconds: 10 + periodSeconds: 30 + timeoutSeconds: 60 + failureThreshold: 5 + + ## extra pip packages to install in the triggerer Pod + ## + ## ____ EXAMPLE _______________ + ## extraPipPackages: + ## - "SomeProject==1.0.0" + ## + extraPipPackages: [] + + ## extra VolumeMounts for the triggerer Pods + ## - spec for VolumeMount: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#volumemount-v1-core + ## + extraVolumeMounts: [] + + ## extra Volumes for the triggerer Pods + ## - spec for Volume: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#volume-v1-core + ## + extraVolumes: [] + +################################### +## COMPONENT | Flower +################################### +flower: + ## if the airflow flower UI should be deployed + ## + enabled: true + + ## the number of flower Pods to run + ## - if you set this >1 we recommend defining a `flower.podDisruptionBudget` + ## + replicas: 1 + + ## resource requests/limits for the flower Pod + ## - spec for ResourceRequirements: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core + ## + resources: {} + + ## the nodeSelector configs for the flower Pods + ## - docs for nodeSelector: + ## https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector + ## + nodeSelector: {} + + ## the affinity configs for the flower Pods + ## - spec for Affinity: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#affinity-v1-core + ## + affinity: {} + + ## the toleration configs for the flower Pods + ## - spec for Toleration: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#toleration-v1-core + ## + tolerations: [] + + ## the security context for the flower Pods + ## - spec for PodSecurityContext: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#podsecuritycontext-v1-core + ## + securityContext: {} + + ## labels for the flower Deployment + ## + labels: {} + + ## Pod labels for the flower Deployment + ## + podLabels: {} + + ## annotations for the flower Deployment + ## + annotations: {} + + ## Pod annotations for the flower Deployment + ## + podAnnotations: {} + + ## if we add the annotation: "cluster-autoscaler.kubernetes.io/safe-to-evict" = "true" + ## + safeToEvict: true + + ## configs for the PodDisruptionBudget of the flower Deployment + ## + podDisruptionBudget: + ## if a PodDisruptionBudget resource is created for the flower Deployment + ## + enabled: false + + ## the `apiVersion` to use for PodDisruptionBudget resources + ## - for Kubernetes 1.21 and later: "policy/v1" + ## - for Kubernetes 1.20 and before: "policy/v1beta1" + ## + apiVersion: policy/v1 + + ## the maximum unavailable pods/percentage for the flower Deployment + ## + maxUnavailable: "" + + ## the minimum available pods/percentage for the flower Deployment + ## + minAvailable: "" + + ## the name of a pre-created secret containing the basic authentication value for flower + ## - this will override any value of `config.AIRFLOW__CELERY__FLOWER_BASIC_AUTH` + ## + basicAuthSecret: "" + + ## the key within `flower.basicAuthSecret` containing the basic authentication string + ## + basicAuthSecretKey: "" + + ## configs for the Service of the flower Pods + ## + service: + annotations: {} + type: ClusterIP + externalPort: 5555 + loadBalancerIP: "" + loadBalancerSourceRanges: [] + nodePort: + http: + + ## configs for the flower Pods' readinessProbe probe + ## + readinessProbe: + enabled: true + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + + ## configs for the flower Pods' liveness probe + ## + livenessProbe: + enabled: true + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 5 + failureThreshold: 6 + + ## extra pip packages to install in the flower Pod + ## + ## ____ EXAMPLE _______________ + ## extraPipPackages: + ## - "SomeProject==1.0.0" + ## + extraPipPackages: [] + + ## extra VolumeMounts for the flower Pods + ## - spec for VolumeMount: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#volumemount-v1-core + ## + extraVolumeMounts: [] + + ## extra Volumes for the flower Pods + ## - spec for Volume: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#volume-v1-core + ## + extraVolumes: [] + +################################### +## CONFIG | Airflow Logs +################################### +logs: + ## the airflow logs folder + ## + path: /opt/airflow/logs + + ## configs for the logs PVC + ## + persistence: + ## if a persistent volume is mounted at `logs.path` + ## + enabled: false + + ## the name of an existing PVC to use + ## + existingClaim: "" + + ## sub-path under `logs.persistence.existingClaim` to use + ## + subPath: "" + + ## the name of the StorageClass used by the PVC + ## - if set to "", then `PersistentVolumeClaim/spec.storageClassName` is omitted + ## - if set to "-", then `PersistentVolumeClaim/spec.storageClassName` is set to "" + ## + storageClass: "" + + ## the access mode of the PVC + ## - [WARNING] must be "ReadWriteMany" or airflow pods will fail to start + ## + accessMode: ReadWriteMany + + ## the size of PVC to request + ## + size: 1Gi + +################################### +## CONFIG | Airflow DAGs +################################### +dags: + ## the airflow dags folder + ## + path: /opt/airflow/dags + + ## configs for the dags PVC + ## + persistence: + ## if a persistent volume is mounted at `dags.path` + ## + enabled: false + + ## the name of an existing PVC to use + ## + existingClaim: "" + + ## sub-path under `dags.persistence.existingClaim` to use + ## + subPath: "" + + ## the name of the StorageClass used by the PVC + ## - if set to "", then `PersistentVolumeClaim/spec.storageClassName` is omitted + ## - if set to "-", then `PersistentVolumeClaim/spec.storageClassName` is set to "" + ## + storageClass: "" + + ## the access mode of the PVC + ## - [WARNING] must be "ReadOnlyMany" or "ReadWriteMany" otherwise airflow pods will fail to start + ## + accessMode: ReadOnlyMany + + ## the size of PVC to request + ## + size: 1Gi + + ## configs for the git-sync sidecar (https://github.com/kubernetes/git-sync) + ## + gitSync: + ## if the git-sync sidecar container is enabled + ## + enabled: false + + ## the git-sync container image + ## + image: + repository: registry.k8s.io/git-sync/git-sync + tag: v3.6.5 + pullPolicy: IfNotPresent + uid: 65533 + gid: 65533 + + ## resource requests/limits for the git-sync container + ## - spec for ResourceRequirements: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core + ## + resources: {} + + ## the url of the git repo + ## + ## ____ EXAMPLE _______________ + ## # https git repo + ## repo: "https://github.com/USERNAME/REPOSITORY.git" + ## + ## ____ EXAMPLE _______________ + ## # ssh git repo + ## repo: "git@github.com:USERNAME/REPOSITORY.git" + ## + repo: "" + + ## the sub-path within your repo where dags are located + ## - only dags under this path within your repo will be seen by airflow, + ## (note, the full repo will still be cloned) + ## + repoSubPath: "" + + ## the git branch to check out + ## + branch: master + + ## the git revision (tag or hash) to check out + ## + revision: HEAD + + ## shallow clone with a history truncated to the specified number of commits + ## + depth: 1 + + ## the number of seconds between syncs + ## + syncWait: 60 + + ## the max number of seconds allowed for a complete sync + ## + syncTimeout: 120 + + ## the git submodule behavior + ## - allowed values: "recursive", "shallow", "off" + ## + submodules: recursive + + ## the name of a pre-created Secret with git http credentials + ## + httpSecret: "" + + ## the key in `dags.gitSync.httpSecret` with your git username + ## + httpSecretUsernameKey: username + + ## the key in `dags.gitSync.httpSecret` with your git password/token + ## + httpSecretPasswordKey: password + + ## the name of a pre-created Secret with git ssh credentials + ## + sshSecret: "" + + ## the key in `dags.gitSync.sshSecret` with your ssh-key file + ## + sshSecretKey: id_rsa + + ## the string value of a "known_hosts" file (for SSH only) + ## - [WARNING] known_hosts verification will be disabled if left empty, making you more + ## vulnerable to repo spoofing attacks + ## + ## ____ EXAMPLE _______________ + ## sshKnownHosts: |- + ## ssh-rsa + ## + sshKnownHosts: "" + + ## the number of consecutive failures allowed before aborting + ## - the first sync must succeed + ## - a value of -1 will retry forever after the initial sync + ## + maxFailures: 0 + +################################### +## CONFIG | Kubernetes Ingress +################################### +ingress: + ## if we should deploy Ingress resources + ## + enabled: false + + ## the `apiVersion` to use for Ingress resources + ## - for Kubernetes 1.19 and later: "networking.k8s.io/v1" + ## - for Kubernetes 1.18 and before: "networking.k8s.io/v1beta1" + ## + apiVersion: networking.k8s.io/v1 + + ## configs for the Ingress of the web Service + ## + web: + ## annotations for the web Ingress + ## + annotations: {} + + ## additional labels for the web Ingress + ## + labels: {} + + ## the path for the web Ingress + ## - [WARNING] do NOT include the trailing slash (for root, set an empty string) + ## + ## ____ EXAMPLE _______________ + ## # webserver URL: http://example.com/airflow + ## path: "/airflow" + ## + path: "" + + ## the hostname for the web Ingress + ## + host: "" + + ## the Ingress Class for the web Ingress + ## - [WARNING] requires Kubernetes 1.18 or later, use "kubernetes.io/ingress.class" annotation for older versions + ## + ingressClassName: "" + + ## configs for web Ingress TLS + ## + tls: + ## enable TLS termination for the web Ingress + ## + enabled: false + + ## the name of a pre-created Secret containing a TLS private key and certificate + ## + secretName: "" + + ## http paths to add to the web Ingress before the default path + ## + ## ____ EXAMPLE _______________ + ## precedingPaths: + ## - path: "/*" + ## serviceName: "my-service" + ## servicePort: "port-name" + ## + precedingPaths: [] + + ## http paths to add to the web Ingress after the default path + ## + ## ____ EXAMPLE _______________ + ## succeedingPaths: + ## - path: "/extra-service" + ## serviceName: "my-service" + ## servicePort: "port-name" + ## + succeedingPaths: [] + + ## configs for the Ingress of the flower Service + ## + flower: + ## annotations for the flower Ingress + ## + annotations: {} + + ## additional labels for the flower Ingress + ## + labels: {} + + ## the path for the flower Ingress + ## - [WARNING] do NOT include the trailing slash (for root, set an empty string) + ## + ## ____ EXAMPLE _______________ + ## # flower URL: http://example.com/airflow/flower + ## path: "/airflow/flower" + ## + path: "" + + ## the hostname for the flower Ingress + ## + host: "" + + ## the Ingress Class for the flower Ingress + ## - [WARNING] requires Kubernetes 1.18 or later, use "kubernetes.io/ingress.class" annotation for older versions + ## + ingressClassName: "" + + ## configs for flower Ingress TLS + ## + tls: + ## enable TLS termination for the flower Ingress + ## + enabled: false + + ## the name of a pre-created Secret containing a TLS private key and certificate + ## + secretName: "" + + ## http paths to add to the flower Ingress before the default path + ## + ## ____ EXAMPLE _______________ + ## precedingPaths: + ## - path: "/*" + ## serviceName: "my-service" + ## servicePort: "port-name" + ## + precedingPaths: [] + + ## http paths to add to the flower Ingress after the default path + ## + ## ____ EXAMPLE _______________ + ## succeedingPaths: + ## - path: "/extra-service" + ## serviceName: "my-service" + ## servicePort: "port-name" + ## + succeedingPaths: [] + +################################### +## CONFIG | Kubernetes RBAC +################################### +rbac: + ## if Kubernetes RBAC resources are created + ## - these allow the service account to create/delete Pods in the airflow namespace, + ## which is required for the KubernetesPodOperator() to function + ## + create: true + + ## if the created RBAC Role has GET/LIST on Event resources + ## - this is needed for KubernetesPodOperator() to use `log_events_on_failure=True` + ## + events: true + +################################### +## CONFIG | Kubernetes ServiceAccount +################################### +serviceAccount: + ## if a Kubernetes ServiceAccount is created + ## - if `false`, you must create the service account outside this chart with name: `serviceAccount.name` + ## + create: true + + ## the name of the ServiceAccount + ## - by default the name is generated using the `airflow.serviceAccountName` template in `_helpers/common.tpl` + ## + name: "" + + ## annotations for the ServiceAccount + ## + ## ____ EXAMPLE _______________ + ## # EKS - IAM Roles for Service Accounts + ## annotations: + ## eks.amazonaws.com/role-arn: "arn:aws:iam::XXXXXXXXXX:role/<>" + ## + ## ____ EXAMPLE _______________ + ## # GKE - WorkloadIdentity + ## annotations: + ## iam.gke.io/gcp-service-account: "<>@<>.iam.gserviceaccount.com" + ## + annotations: {} + +################################### +## CONFIG | Kubernetes Extra Manifests +################################### +## a list of extra Kubernetes manifests that will be deployed alongside the chart +## - helm templates within these strings will be rendered +## +## ____ EXAMPLE _______________ +## extraManifests: +## - | +## apiVersion: v1 +## kind: Secret +## metadata: +## name: airflow-postgres +## data: +## postgresql-password: {{ `password1` | b64enc | quote }} +## - | +## apiVersion: apps/v1 +## kind: Deployment +## metadata: +## name: {{ include "airflow.fullname" . }}-busybox +## labels: +## app: {{ include "airflow.labels.app" . }} +## component: busybox +## chart: {{ include "airflow.labels.chart" . }} +## release: {{ .Release.Name }} +## heritage: {{ .Release.Service }} +## spec: +## replicas: 1 +## selector: +## matchLabels: +## app: {{ include "airflow.labels.app" . }} +## component: busybox +## release: {{ .Release.Name }} +## template: +## metadata: +## labels: +## app: {{ include "airflow.labels.app" . }} +## component: busybox +## release: {{ .Release.Name }} +## spec: +## containers: +## - name: busybox +## image: busybox:1.35 +## command: +## - "/bin/sh" +## - "-c" +## args: +## - | +## ## to break the infinite loop when we receive SIGTERM +## trap "exit 0" SIGTERM; +## ## keep the container running (so people can `kubectl exec -it` into it) +## while true; do +## echo "I am alive..."; +## sleep 30; +## done +## +extraManifests: [] + +################################### +## DATABASE | PgBouncer +################################### +pgbouncer: + ## if the pgbouncer Deployment is created + ## + enabled: true + + ## configs for the pgbouncer container image + ## + image: + repository: ghcr.io/airflow-helm/pgbouncer + tag: 1.18.0-patch.1 + pullPolicy: IfNotPresent + uid: 1001 + gid: 1001 + + ## resource requests/limits for the pgbouncer Pods + ## - spec for ResourceRequirements: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core + ## + resources: {} + + ## the nodeSelector configs for the pgbouncer Pods + ## - docs for nodeSelector: + ## https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector + ## + nodeSelector: {} + + ## the affinity configs for the pgbouncer Pods + ## - spec for Affinity: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#affinity-v1-core + ## + affinity: {} + + ## the toleration configs for the pgbouncer Pods + ## - spec for Toleration: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#toleration-v1-core + ## + tolerations: [] + + ## the security context for the pgbouncer Pods + ## - spec for PodSecurityContext: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#podsecuritycontext-v1-core + ## + securityContext: {} + + ## Labels for the pgbouncer Deployment + ## + labels: {} + + ## Pod labels for the pgbouncer Deployment + ## + podLabels: {} + + ## annotations for the pgbouncer Deployment + ## + annotations: {} + + ## Pod annotations for the pgbouncer Deployment + ## + podAnnotations: {} + + ## if we add the annotation: "cluster-autoscaler.kubernetes.io/safe-to-evict" = "true" + ## + safeToEvict: true + + ## configs for the PodDisruptionBudget of the pgbouncer Deployment + ## + podDisruptionBudget: + ## if a PodDisruptionBudget resource is created for the pgbouncer Deployment + ## + enabled: false + + ## the `apiVersion` to use for PodDisruptionBudget resources + ## - for Kubernetes 1.21 and later: "policy/v1" + ## - for Kubernetes 1.20 and before: "policy/v1beta1" + ## + apiVersion: policy/v1 + + ## the maximum unavailable pods/percentage for the pgbouncer Deployment + ## + maxUnavailable: + + ## the minimum available pods/percentage for the pgbouncer Deployment + ## + minAvailable: + + ## configs for the pgbouncer Pods' liveness probe + ## + livenessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 30 + timeoutSeconds: 60 + failureThreshold: 3 + + ## configs for the pgbouncer Pods' startup probe + ## + startupProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 15 + failureThreshold: 30 + + ## the maximum number of seconds to wait for queries upon pod termination, before force killing + ## + terminationGracePeriodSeconds: 120 + + ## sets pgbouncer config: `auth_type` + ## + authType: md5 + + ## sets pgbouncer config: `max_client_conn` + ## + maxClientConnections: 1000 + + ## sets pgbouncer config: `default_pool_size` + ## + poolSize: 20 + + ## sets pgbouncer config: `log_disconnections` + ## + logDisconnections: 0 + + ## sets pgbouncer config: `log_connections` + ## + logConnections: 0 + + ## ssl configs for: clients -> pgbouncer + ## + clientSSL: + ## sets pgbouncer config: `client_tls_sslmode` + ## + mode: prefer + + ## sets pgbouncer config: `client_tls_ciphers` + ## + ciphers: normal + + ## sets pgbouncer config: `client_tls_ca_file` + ## + caFile: + existingSecret: "" + existingSecretKey: root.crt + + ## sets pgbouncer config: `client_tls_key_file` + ## - [WARNING] a self-signed cert & key are generated if left empty + ## + keyFile: + existingSecret: "" + existingSecretKey: client.key + + ## sets pgbouncer config: `client_tls_cert_file` + ## - [WARNING] a self-signed cert & key are generated if left empty + ## + certFile: + existingSecret: "" + existingSecretKey: client.crt + + ## ssl configs for: pgbouncer -> postgres + ## + serverSSL: + ## sets pgbouncer config: `server_tls_sslmode` + ## + mode: prefer + + ## sets pgbouncer config: `server_tls_ciphers` + ## + ciphers: normal + + ## sets pgbouncer config: `server_tls_ca_file` + ## + caFile: + existingSecret: "" + existingSecretKey: root.crt + + ## sets pgbouncer config: `server_tls_key_file` + ## + keyFile: + existingSecret: "" + existingSecretKey: server.key + + ## sets pgbouncer config: `server_tls_cert_file` + ## + certFile: + existingSecret: "" + existingSecretKey: server.crt + +################################### +## DATABASE | Embedded Postgres +################################### +postgresql: + ## if the `stable/postgresql` chart is used + ## - [WARNING] the embedded Postgres is NOT SUITABLE for production deployments of Airflow + ## - [WARNING] consider using an external database with `externalDatabase.*` + ## - set to `false` if using `externalDatabase.*` + ## + enabled: true + + ## configs for the postgres container image + ## + image: + registry: ghcr.io + repository: airflow-helm/postgresql-bitnami + tag: 11.16-patch.0 + pullPolicy: IfNotPresent + + ## the postgres database to use + ## + postgresqlDatabase: airflow + + ## the postgres user to create + ## + postgresqlUsername: postgres + + ## the postgres user's password + ## + postgresqlPassword: airflow + + ## the name of a pre-created secret containing the postgres password + ## + existingSecret: "" + + ## the key within `postgresql.existingSecret` containing the password string + ## + existingSecretKey: "postgresql-password" + + ## configs for the PVC of postgresql + ## + persistence: + ## if postgres will use Persistent Volume Claims to store data + ## - [WARNING] if false, data will be LOST as postgres Pods restart + ## + enabled: true + + ## the name of the StorageClass used by the PVC + ## + storageClass: "" + + ## the access modes of the PVC + ## + accessModes: + - ReadWriteOnce + + ## the size of PVC to request + ## + size: 8Gi + + ## configs for the postgres StatefulSet + ## + master: + ## the nodeSelector configs for the postgres Pods + ## - docs for nodeSelector: + ## https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector + ## + nodeSelector: {} + + ## the affinity configs for the postgres Pods + ## - spec for Affinity: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#affinity-v1-core + ## + affinity: {} + + ## the toleration configs for the postgres Pods + ## - spec for Toleration: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#toleration-v1-core + ## + tolerations: [] + + ## annotations for the postgres Pods + ## + podAnnotations: + cluster-autoscaler.kubernetes.io/safe-to-evict: "true" + +################################### +## DATABASE | External Database +################################### +externalDatabase: + ## the type of external database + ## - allowed values: "mysql", "postgres" + ## + type: postgres + + ## the host of the external database + ## + host: localhost + + ## the port of the external database + ## + port: 5432 + + ## the database/scheme to use within the external database + ## + database: airflow + + ## the username for the external database + ## + user: airflow + + ## the name of a pre-created secret containing the external database user + ## - if set, this overrides `externalDatabase.user` + ## + userSecret: "" + + ## the key within `externalDatabase.userSecret` containing the user string + ## + userSecretKey: "postgresql-user" + + ## the password for the external database + ## - [WARNING] to avoid storing the password in plain-text within your values, + ## create a Kubernetes secret and use `externalDatabase.passwordSecret` + ## + password: "" + + ## the name of a pre-created secret containing the external database password + ## - if set, this overrides `externalDatabase.password` + ## + passwordSecret: "" + + ## the key within `externalDatabase.passwordSecret` containing the password string + ## + passwordSecretKey: "postgresql-password" + + ## extra connection-string properties for the external database + ## + ## ____ EXAMPLE _______________ + ## # require SSL (only for Postgres) + ## properties: "?sslmode=require" + ## + properties: "" + +################################### +## DATABASE | Embedded Redis +################################### +redis: + ## if the `stable/redis` chart is used + ## - set to `false` if `airflow.executor` is `KubernetesExecutor` + ## - set to `false` if using `externalRedis.*` + ## + enabled: true + + ## configs for the redis container image + ## + image: + registry: docker.io + repository: bitnami/redis + tag: 5.0.14-debian-10-r173 + pullPolicy: IfNotPresent + + ## the redis password + ## + password: airflow + + ## the name of a pre-created secret containing the redis password + ## + existingSecret: "" + + ## the key within `redis.existingSecret` containing the password string + ## + existingSecretPasswordKey: "redis-password" + + ## configs for redis cluster mode + ## + cluster: + ## if redis runs in cluster mode + ## + enabled: false + + ## the number of redis slaves + ## + slaveCount: 1 + + ## configs for the redis master StatefulSet + ## + master: + ## resource requests/limits for the redis master Pods + ## - spec for ResourceRequirements: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core + ## + resources: {} + + ## the nodeSelector configs for the redis master Pods + ## - docs for nodeSelector: + ## https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector + ## + nodeSelector: {} + + ## the affinity configs for the redis master Pods + ## - spec for Affinity: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#affinity-v1-core + ## + affinity: {} + + ## the toleration configs for the redis master Pods + ## - spec for Toleration: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#toleration-v1-core + ## + tolerations: [] + + ## annotations for the redis master Pods + ## + podAnnotations: + cluster-autoscaler.kubernetes.io/safe-to-evict: "true" + + ## configs for the PVC of the redis master Pods + ## + persistence: + ## use a PVC to persist data + ## + enabled: false + + ## the name of the StorageClass used by the PVC + ## + storageClass: "" + + ## the access mode of the PVC + ## + accessModes: + - ReadWriteOnce + + ## the size of PVC to request + ## + size: 8Gi + + ## configs for the redis slave StatefulSet + ## - only used if `redis.cluster.enabled` is `true` + ## + slave: + ## resource requests/limits for the slave Pods + ## - spec for ResourceRequirements: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#resourcerequirements-v1-core + ## + resources: {} + + ## the nodeSelector configs for the redis slave Pods + ## - docs for nodeSelector: + ## https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#nodeselector + ## + nodeSelector: {} + + ## the affinity configs for the redis slave Pods + ## - spec for Affinity: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#affinity-v1-core + ## + affinity: {} + + ## the toleration configs for the redis slave Pods + ## - spec for Toleration: + ## https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.20/#toleration-v1-core + ## + tolerations: [] + + ## annotations for the slave Pods + ## + podAnnotations: + cluster-autoscaler.kubernetes.io/safe-to-evict: "true" + + ## configs for the PVC of the redis slave Pods + ## + persistence: + ## use a PVC to persist data + ## + enabled: false + + ## the name of the StorageClass used by the PVC + ## + storageClass: "" + + ## the access mode of the PVC + ## + accessModes: + - ReadWriteOnce + + ## the size of PVC to request + ## + size: 8Gi + +################################### +## DATABASE | External Redis +################################### +externalRedis: + ## the host of the external redis + ## + host: localhost + + ## the port of the external redis + ## + port: 6379 + + ## the database number to use within the external redis + ## + databaseNumber: 1 + + ## the password for the external redis + ## - [WARNING] to avoid storing the password in plain-text within your values, + ## create a Kubernetes secret and use `externalRedis.passwordSecret` + ## + password: "" + + ## the name of a pre-created secret containing the external redis password + ## - if set, this overrides `externalRedis.password` + ## + passwordSecret: "" + + ## the key within `externalRedis.passwordSecret` containing the password string + ## + passwordSecretKey: "redis-password" + + ## extra connection-string properties for the external redis + ## + ## ____ EXAMPLE _______________ + ## properties: "?ssl_cert_reqs=CERT_OPTIONAL" + ## + properties: "" + +################################### +## CONFIG | ServiceMonitor (Prometheus Operator) +################################### +serviceMonitor: + ## if ServiceMonitor resources should be deployed for airflow webserver + ## - [WARNING] you will need a metrics exporter in your `airflow.image`, for example: + ## https://github.com/epoch8/airflow-exporter + ## - ServiceMonitor is a resource from prometheus-operator: + ## https://github.com/prometheus-operator/prometheus-operator + ## + enabled: false + + ## labels for ServiceMonitor, so that Prometheus can select it + ## + selector: + prometheus: kube-prometheus + + ## the ServiceMonitor web endpoint path + ## + path: /admin/metrics + + ## the ServiceMonitor web endpoint interval + ## + interval: "30s" + +################################### +## CONFIG | PrometheusRule (Prometheus Operator) +################################### +prometheusRule: + ## if PrometheusRule resources should be deployed for airflow webserver + ## - [WARNING] you will need a metrics exporter in your `airflow.image`, for example: + ## https://github.com/epoch8/airflow-exporter + ## - PrometheusRule is a resource from prometheus-operator: + ## https://github.com/prometheus-operator/prometheus-operator + ## + enabled: false + + ## labels for PrometheusRule, so that Prometheus can select it + ## + additionalLabels: {} + + ## alerting rules for Prometheus + ## - docs for alerting rules: https://prometheus.io/docs/prometheus/latest/configuration/alerting_rules/ + ## + groups: [] diff --git a/charts/deps/charts/mysql-9.7.2/.helmignore b/charts/deps/charts/mysql-9.7.2/.helmignore new file mode 100644 index 0000000..f0c1319 --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/.helmignore @@ -0,0 +1,21 @@ +# 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 +*~ +# Various IDEs +.project +.idea/ +*.tmproj diff --git a/charts/deps/charts/mysql-9.7.2/Chart.lock b/charts/deps/charts/mysql-9.7.2/Chart.lock new file mode 100644 index 0000000..9a7fd4d --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: common + repository: https://charts.bitnami.com/bitnami + version: 2.2.4 +digest: sha256:634d19e9b7f6e4c07d7c04a0161ab96b3f83335ebdd70b35b952319ef0a2586b +generated: "2023-04-01T13:13:50.11325071Z" diff --git a/charts/deps/charts/mysql-9.7.2/Chart.yaml b/charts/deps/charts/mysql-9.7.2/Chart.yaml new file mode 100644 index 0000000..dc01eb9 --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/Chart.yaml @@ -0,0 +1,29 @@ +annotations: + category: Database + licenses: Apache-2.0 +apiVersion: v2 +appVersion: 8.0.33 +dependencies: +- name: common + repository: https://charts.bitnami.com/bitnami + tags: + - bitnami-common + version: 2.x.x +description: MySQL is a fast, reliable, scalable, and easy to use open source relational + database system. Designed to handle mission-critical, heavy-load production applications. +home: https://github.com/bitnami/charts/tree/main/bitnami/mysql +icon: https://bitnami.com/assets/stacks/mysql/img/mysql-stack-220x234.png +keywords: +- mysql +- database +- sql +- cluster +- high availability +maintainers: +- name: Bitnami + url: https://github.com/bitnami/charts +name: mysql +sources: +- https://github.com/bitnami/containers/tree/main/bitnami/mysql +- https://mysql.com +version: 9.7.2 diff --git a/charts/deps/charts/mysql-9.7.2/README.md b/charts/deps/charts/mysql-9.7.2/README.md new file mode 100644 index 0000000..c3c86cb --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/README.md @@ -0,0 +1,551 @@ + + +# MySQL packaged by Bitnami + +MySQL is a fast, reliable, scalable, and easy to use open source relational database system. Designed to handle mission-critical, heavy-load production applications. + +[Overview of MySQL](http://www.mysql.com) + +Trademarks: This software listing is packaged by Bitnami. The respective trademarks mentioned in the offering are owned by the respective companies, and use of them does not imply any affiliation or endorsement. + +## TL;DR + +```console +helm repo add my-repo https://charts.bitnami.com/bitnami +helm install my-release my-repo/mysql +``` + +## Introduction + +This chart bootstraps a [MySQL](https://github.com/bitnami/containers/tree/main/bitnami/mysql) replication cluster deployment on a [Kubernetes](https://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager. + +Bitnami charts can be used with [Kubeapps](https://kubeapps.dev/) for deployment and management of Helm Charts in clusters. + +## Prerequisites + +- Kubernetes 1.19+ +- Helm 3.2.0+ +- PV provisioner support in the underlying infrastructure + +## Installing the Chart + +To install the chart with the release name `my-release`: + +```console +helm repo add my-repo https://charts.bitnami.com/bitnami +helm install my-release my-repo/mysql +``` + +These commands deploy MySQL on the Kubernetes cluster in the default configuration. The [Parameters](#parameters) section lists the parameters that can be configured during installation. + +> **Tip**: List all releases using `helm list` + +## Uninstalling the Chart + +To uninstall/delete the `my-release` deployment: + +```console +helm delete my-release +``` + +The command removes all the Kubernetes components associated with the chart and deletes the release. + +## Parameters + +### Global parameters + +| Name | Description | Value | +| ------------------------- | ----------------------------------------------- | ----- | +| `global.imageRegistry` | Global Docker image registry | `""` | +| `global.imagePullSecrets` | Global Docker registry secret names as an array | `[]` | +| `global.storageClass` | Global StorageClass for Persistent Volume(s) | `""` | + +### Common parameters + +| Name | Description | Value | +| ------------------------- | --------------------------------------------------------------------------------------------------------- | --------------- | +| `kubeVersion` | Force target Kubernetes version (using Helm capabilities if not set) | `""` | +| `nameOverride` | String to partially override common.names.fullname template (will maintain the release name) | `""` | +| `fullnameOverride` | String to fully override common.names.fullname template | `""` | +| `namespaceOverride` | String to fully override common.names.namespace | `""` | +| `clusterDomain` | Cluster domain | `cluster.local` | +| `commonAnnotations` | Common annotations to add to all MySQL resources (sub-charts are not considered). Evaluated as a template | `{}` | +| `commonLabels` | Common labels to add to all MySQL resources (sub-charts are not considered). Evaluated as a template | `{}` | +| `extraDeploy` | Array with extra yaml to deploy with the chart. Evaluated as a template | `[]` | +| `serviceBindings.enabled` | Create secret for service binding (Experimental) | `false` | +| `diagnosticMode.enabled` | Enable diagnostic mode (all probes will be disabled and the command will be overridden) | `false` | +| `diagnosticMode.command` | Command to override all containers in the deployment | `["sleep"]` | +| `diagnosticMode.args` | Args to override all containers in the deployment | `["infinity"]` | + +### MySQL common parameters + +| Name | Description | Value | +| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | +| `image.registry` | MySQL image registry | `docker.io` | +| `image.repository` | MySQL image repository | `bitnami/mysql` | +| `image.tag` | MySQL image tag (immutable tags are recommended) | `8.0.33-debian-11-r0` | +| `image.digest` | MySQL image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag | `""` | +| `image.pullPolicy` | MySQL image pull policy | `IfNotPresent` | +| `image.pullSecrets` | Specify docker-registry secret names as an array | `[]` | +| `image.debug` | Specify if debug logs should be enabled | `false` | +| `architecture` | MySQL architecture (`standalone` or `replication`) | `standalone` | +| `auth.rootPassword` | Password for the `root` user. Ignored if existing secret is provided | `""` | +| `auth.createDatabase` | Whether to create the .Values.auth.database or not | `true` | +| `auth.database` | Name for a custom database to create | `my_database` | +| `auth.username` | Name for a custom user to create | `""` | +| `auth.password` | Password for the new user. Ignored if existing secret is provided | `""` | +| `auth.replicationUser` | MySQL replication user | `replicator` | +| `auth.replicationPassword` | MySQL replication user password. Ignored if existing secret is provided | `""` | +| `auth.existingSecret` | Use existing secret for password details. The secret has to contain the keys `mysql-root-password`, `mysql-replication-password` and `mysql-password` | `""` | +| `auth.usePasswordFiles` | Mount credentials as files instead of using an environment variable | `false` | +| `auth.customPasswordFiles` | Use custom password files when `auth.usePasswordFiles` is set to `true`. Define path for keys `root` and `user`, also define `replicator` if `architecture` is set to `replication` | `{}` | +| `initdbScripts` | Dictionary of initdb scripts | `{}` | +| `initdbScriptsConfigMap` | ConfigMap with the initdb scripts (Note: Overrides `initdbScripts`) | `""` | + +### MySQL Primary parameters + +| Name | Description | Value | +| ----------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | ------------------- | +| `primary.name` | Name of the primary database (eg primary, master, leader, ...) | `primary` | +| `primary.command` | Override default container command on MySQL Primary container(s) (useful when using custom images) | `[]` | +| `primary.args` | Override default container args on MySQL Primary container(s) (useful when using custom images) | `[]` | +| `primary.lifecycleHooks` | for the MySQL Primary container(s) to automate configuration before or after startup | `{}` | +| `primary.hostAliases` | Deployment pod host aliases | `[]` | +| `primary.configuration` | Configure MySQL Primary with a custom my.cnf file | `""` | +| `primary.existingConfigmap` | Name of existing ConfigMap with MySQL Primary configuration. | `""` | +| `primary.updateStrategy.type` | Update strategy type for the MySQL primary statefulset | `RollingUpdate` | +| `primary.podAnnotations` | Additional pod annotations for MySQL primary pods | `{}` | +| `primary.podAffinityPreset` | MySQL primary pod affinity preset. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `primary.podAntiAffinityPreset` | MySQL primary pod anti-affinity preset. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` | `soft` | +| `primary.nodeAffinityPreset.type` | MySQL primary node affinity preset type. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `primary.nodeAffinityPreset.key` | MySQL primary node label key to match Ignored if `primary.affinity` is set. | `""` | +| `primary.nodeAffinityPreset.values` | MySQL primary node label values to match. Ignored if `primary.affinity` is set. | `[]` | +| `primary.affinity` | Affinity for MySQL primary pods assignment | `{}` | +| `primary.nodeSelector` | Node labels for MySQL primary pods assignment | `{}` | +| `primary.tolerations` | Tolerations for MySQL primary pods assignment | `[]` | +| `primary.priorityClassName` | MySQL primary pods' priorityClassName | `""` | +| `primary.runtimeClassName` | MySQL primary pods' runtimeClassName | `""` | +| `primary.schedulerName` | Name of the k8s scheduler (other than default) | `""` | +| `primary.terminationGracePeriodSeconds` | In seconds, time the given to the MySQL primary pod needs to terminate gracefully | `""` | +| `primary.topologySpreadConstraints` | Topology Spread Constraints for pod assignment | `[]` | +| `primary.podManagementPolicy` | podManagementPolicy to manage scaling operation of MySQL primary pods | `""` | +| `primary.podSecurityContext.enabled` | Enable security context for MySQL primary pods | `true` | +| `primary.podSecurityContext.fsGroup` | Group ID for the mounted volumes' filesystem | `1001` | +| `primary.containerSecurityContext.enabled` | MySQL primary container securityContext | `true` | +| `primary.containerSecurityContext.runAsUser` | User ID for the MySQL primary container | `1001` | +| `primary.containerSecurityContext.runAsNonRoot` | Set MySQL primary container's Security Context runAsNonRoot | `true` | +| `primary.resources.limits` | The resources limits for MySQL primary containers | `{}` | +| `primary.resources.requests` | The requested resources for MySQL primary containers | `{}` | +| `primary.livenessProbe.enabled` | Enable livenessProbe | `true` | +| `primary.livenessProbe.initialDelaySeconds` | Initial delay seconds for livenessProbe | `5` | +| `primary.livenessProbe.periodSeconds` | Period seconds for livenessProbe | `10` | +| `primary.livenessProbe.timeoutSeconds` | Timeout seconds for livenessProbe | `1` | +| `primary.livenessProbe.failureThreshold` | Failure threshold for livenessProbe | `3` | +| `primary.livenessProbe.successThreshold` | Success threshold for livenessProbe | `1` | +| `primary.readinessProbe.enabled` | Enable readinessProbe | `true` | +| `primary.readinessProbe.initialDelaySeconds` | Initial delay seconds for readinessProbe | `5` | +| `primary.readinessProbe.periodSeconds` | Period seconds for readinessProbe | `10` | +| `primary.readinessProbe.timeoutSeconds` | Timeout seconds for readinessProbe | `1` | +| `primary.readinessProbe.failureThreshold` | Failure threshold for readinessProbe | `3` | +| `primary.readinessProbe.successThreshold` | Success threshold for readinessProbe | `1` | +| `primary.startupProbe.enabled` | Enable startupProbe | `true` | +| `primary.startupProbe.initialDelaySeconds` | Initial delay seconds for startupProbe | `15` | +| `primary.startupProbe.periodSeconds` | Period seconds for startupProbe | `10` | +| `primary.startupProbe.timeoutSeconds` | Timeout seconds for startupProbe | `1` | +| `primary.startupProbe.failureThreshold` | Failure threshold for startupProbe | `10` | +| `primary.startupProbe.successThreshold` | Success threshold for startupProbe | `1` | +| `primary.customLivenessProbe` | Override default liveness probe for MySQL primary containers | `{}` | +| `primary.customReadinessProbe` | Override default readiness probe for MySQL primary containers | `{}` | +| `primary.customStartupProbe` | Override default startup probe for MySQL primary containers | `{}` | +| `primary.extraFlags` | MySQL primary additional command line flags | `""` | +| `primary.extraEnvVars` | Extra environment variables to be set on MySQL primary containers | `[]` | +| `primary.extraEnvVarsCM` | Name of existing ConfigMap containing extra env vars for MySQL primary containers | `""` | +| `primary.extraEnvVarsSecret` | Name of existing Secret containing extra env vars for MySQL primary containers | `""` | +| `primary.extraPorts` | Extra ports to expose | `[]` | +| `primary.persistence.enabled` | Enable persistence on MySQL primary replicas using a `PersistentVolumeClaim`. If false, use emptyDir | `true` | +| `primary.persistence.existingClaim` | Name of an existing `PersistentVolumeClaim` for MySQL primary replicas | `""` | +| `primary.persistence.subPath` | The name of a volume's sub path to mount for persistence | `""` | +| `primary.persistence.storageClass` | MySQL primary persistent volume storage Class | `""` | +| `primary.persistence.annotations` | MySQL primary persistent volume claim annotations | `{}` | +| `primary.persistence.accessModes` | MySQL primary persistent volume access Modes | `["ReadWriteOnce"]` | +| `primary.persistence.size` | MySQL primary persistent volume size | `8Gi` | +| `primary.persistence.selector` | Selector to match an existing Persistent Volume | `{}` | +| `primary.extraVolumes` | Optionally specify extra list of additional volumes to the MySQL Primary pod(s) | `[]` | +| `primary.extraVolumeMounts` | Optionally specify extra list of additional volumeMounts for the MySQL Primary container(s) | `[]` | +| `primary.initContainers` | Add additional init containers for the MySQL Primary pod(s) | `[]` | +| `primary.sidecars` | Add additional sidecar containers for the MySQL Primary pod(s) | `[]` | +| `primary.service.type` | MySQL Primary K8s service type | `ClusterIP` | +| `primary.service.ports.mysql` | MySQL Primary K8s service port | `3306` | +| `primary.service.nodePorts.mysql` | MySQL Primary K8s service node port | `""` | +| `primary.service.clusterIP` | MySQL Primary K8s service clusterIP IP | `""` | +| `primary.service.loadBalancerIP` | MySQL Primary loadBalancerIP if service type is `LoadBalancer` | `""` | +| `primary.service.externalTrafficPolicy` | Enable client source IP preservation | `Cluster` | +| `primary.service.loadBalancerSourceRanges` | Addresses that are allowed when MySQL Primary service is LoadBalancer | `[]` | +| `primary.service.extraPorts` | Extra ports to expose (normally used with the `sidecar` value) | `[]` | +| `primary.service.annotations` | Additional custom annotations for MySQL primary service | `{}` | +| `primary.service.sessionAffinity` | Session Affinity for Kubernetes service, can be "None" or "ClientIP" | `None` | +| `primary.service.sessionAffinityConfig` | Additional settings for the sessionAffinity | `{}` | +| `primary.service.headless.annotations` | Additional custom annotations for headless MySQL primary service. | `{}` | +| `primary.pdb.create` | Enable/disable a Pod Disruption Budget creation for MySQL primary pods | `false` | +| `primary.pdb.minAvailable` | Minimum number/percentage of MySQL primary pods that should remain scheduled | `1` | +| `primary.pdb.maxUnavailable` | Maximum number/percentage of MySQL primary pods that may be made unavailable | `""` | +| `primary.podLabels` | MySQL Primary pod label. If labels are same as commonLabels , this will take precedence | `{}` | + +### MySQL Secondary parameters + +| Name | Description | Value | +| ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | ------------------- | +| `secondary.name` | Name of the secondary database (eg secondary, slave, ...) | `secondary` | +| `secondary.replicaCount` | Number of MySQL secondary replicas | `1` | +| `secondary.hostAliases` | Deployment pod host aliases | `[]` | +| `secondary.command` | Override default container command on MySQL Secondary container(s) (useful when using custom images) | `[]` | +| `secondary.args` | Override default container args on MySQL Secondary container(s) (useful when using custom images) | `[]` | +| `secondary.lifecycleHooks` | for the MySQL Secondary container(s) to automate configuration before or after startup | `{}` | +| `secondary.configuration` | Configure MySQL Secondary with a custom my.cnf file | `""` | +| `secondary.existingConfigmap` | Name of existing ConfigMap with MySQL Secondary configuration. | `""` | +| `secondary.updateStrategy.type` | Update strategy type for the MySQL secondary statefulset | `RollingUpdate` | +| `secondary.podAnnotations` | Additional pod annotations for MySQL secondary pods | `{}` | +| `secondary.podAffinityPreset` | MySQL secondary pod affinity preset. Ignored if `secondary.affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `secondary.podAntiAffinityPreset` | MySQL secondary pod anti-affinity preset. Ignored if `secondary.affinity` is set. Allowed values: `soft` or `hard` | `soft` | +| `secondary.nodeAffinityPreset.type` | MySQL secondary node affinity preset type. Ignored if `secondary.affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `secondary.nodeAffinityPreset.key` | MySQL secondary node label key to match Ignored if `secondary.affinity` is set. | `""` | +| `secondary.nodeAffinityPreset.values` | MySQL secondary node label values to match. Ignored if `secondary.affinity` is set. | `[]` | +| `secondary.affinity` | Affinity for MySQL secondary pods assignment | `{}` | +| `secondary.nodeSelector` | Node labels for MySQL secondary pods assignment | `{}` | +| `secondary.tolerations` | Tolerations for MySQL secondary pods assignment | `[]` | +| `secondary.priorityClassName` | MySQL secondary pods' priorityClassName | `""` | +| `secondary.runtimeClassName` | MySQL secondary pods' runtimeClassName | `""` | +| `secondary.schedulerName` | Name of the k8s scheduler (other than default) | `""` | +| `secondary.terminationGracePeriodSeconds` | In seconds, time the given to the MySQL secondary pod needs to terminate gracefully | `""` | +| `secondary.topologySpreadConstraints` | Topology Spread Constraints for pod assignment | `[]` | +| `secondary.podManagementPolicy` | podManagementPolicy to manage scaling operation of MySQL secondary pods | `""` | +| `secondary.podSecurityContext.enabled` | Enable security context for MySQL secondary pods | `true` | +| `secondary.podSecurityContext.fsGroup` | Group ID for the mounted volumes' filesystem | `1001` | +| `secondary.containerSecurityContext.enabled` | MySQL secondary container securityContext | `true` | +| `secondary.containerSecurityContext.runAsUser` | User ID for the MySQL secondary container | `1001` | +| `secondary.containerSecurityContext.runAsNonRoot` | Set MySQL secondary container's Security Context runAsNonRoot | `true` | +| `secondary.resources.limits` | The resources limits for MySQL secondary containers | `{}` | +| `secondary.resources.requests` | The requested resources for MySQL secondary containers | `{}` | +| `secondary.livenessProbe.enabled` | Enable livenessProbe | `true` | +| `secondary.livenessProbe.initialDelaySeconds` | Initial delay seconds for livenessProbe | `5` | +| `secondary.livenessProbe.periodSeconds` | Period seconds for livenessProbe | `10` | +| `secondary.livenessProbe.timeoutSeconds` | Timeout seconds for livenessProbe | `1` | +| `secondary.livenessProbe.failureThreshold` | Failure threshold for livenessProbe | `3` | +| `secondary.livenessProbe.successThreshold` | Success threshold for livenessProbe | `1` | +| `secondary.readinessProbe.enabled` | Enable readinessProbe | `true` | +| `secondary.readinessProbe.initialDelaySeconds` | Initial delay seconds for readinessProbe | `5` | +| `secondary.readinessProbe.periodSeconds` | Period seconds for readinessProbe | `10` | +| `secondary.readinessProbe.timeoutSeconds` | Timeout seconds for readinessProbe | `1` | +| `secondary.readinessProbe.failureThreshold` | Failure threshold for readinessProbe | `3` | +| `secondary.readinessProbe.successThreshold` | Success threshold for readinessProbe | `1` | +| `secondary.startupProbe.enabled` | Enable startupProbe | `true` | +| `secondary.startupProbe.initialDelaySeconds` | Initial delay seconds for startupProbe | `15` | +| `secondary.startupProbe.periodSeconds` | Period seconds for startupProbe | `10` | +| `secondary.startupProbe.timeoutSeconds` | Timeout seconds for startupProbe | `1` | +| `secondary.startupProbe.failureThreshold` | Failure threshold for startupProbe | `15` | +| `secondary.startupProbe.successThreshold` | Success threshold for startupProbe | `1` | +| `secondary.customLivenessProbe` | Override default liveness probe for MySQL secondary containers | `{}` | +| `secondary.customReadinessProbe` | Override default readiness probe for MySQL secondary containers | `{}` | +| `secondary.customStartupProbe` | Override default startup probe for MySQL secondary containers | `{}` | +| `secondary.extraFlags` | MySQL secondary additional command line flags | `""` | +| `secondary.extraEnvVars` | An array to add extra environment variables on MySQL secondary containers | `[]` | +| `secondary.extraEnvVarsCM` | Name of existing ConfigMap containing extra env vars for MySQL secondary containers | `""` | +| `secondary.extraEnvVarsSecret` | Name of existing Secret containing extra env vars for MySQL secondary containers | `""` | +| `secondary.extraPorts` | Extra ports to expose | `[]` | +| `secondary.persistence.enabled` | Enable persistence on MySQL secondary replicas using a `PersistentVolumeClaim` | `true` | +| `secondary.persistence.existingClaim` | Name of an existing `PersistentVolumeClaim` for MySQL secondary replicas | `""` | +| `secondary.persistence.subPath` | The name of a volume's sub path to mount for persistence | `""` | +| `secondary.persistence.storageClass` | MySQL secondary persistent volume storage Class | `""` | +| `secondary.persistence.annotations` | MySQL secondary persistent volume claim annotations | `{}` | +| `secondary.persistence.accessModes` | MySQL secondary persistent volume access Modes | `["ReadWriteOnce"]` | +| `secondary.persistence.size` | MySQL secondary persistent volume size | `8Gi` | +| `secondary.persistence.selector` | Selector to match an existing Persistent Volume | `{}` | +| `secondary.extraVolumes` | Optionally specify extra list of additional volumes to the MySQL secondary pod(s) | `[]` | +| `secondary.extraVolumeMounts` | Optionally specify extra list of additional volumeMounts for the MySQL secondary container(s) | `[]` | +| `secondary.initContainers` | Add additional init containers for the MySQL secondary pod(s) | `[]` | +| `secondary.sidecars` | Add additional sidecar containers for the MySQL secondary pod(s) | `[]` | +| `secondary.service.type` | MySQL secondary Kubernetes service type | `ClusterIP` | +| `secondary.service.ports.mysql` | MySQL secondary Kubernetes service port | `3306` | +| `secondary.service.nodePorts.mysql` | MySQL secondary Kubernetes service node port | `""` | +| `secondary.service.clusterIP` | MySQL secondary Kubernetes service clusterIP IP | `""` | +| `secondary.service.loadBalancerIP` | MySQL secondary loadBalancerIP if service type is `LoadBalancer` | `""` | +| `secondary.service.externalTrafficPolicy` | Enable client source IP preservation | `Cluster` | +| `secondary.service.loadBalancerSourceRanges` | Addresses that are allowed when MySQL secondary service is LoadBalancer | `[]` | +| `secondary.service.extraPorts` | Extra ports to expose (normally used with the `sidecar` value) | `[]` | +| `secondary.service.annotations` | Additional custom annotations for MySQL secondary service | `{}` | +| `secondary.service.sessionAffinity` | Session Affinity for Kubernetes service, can be "None" or "ClientIP" | `None` | +| `secondary.service.sessionAffinityConfig` | Additional settings for the sessionAffinity | `{}` | +| `secondary.service.headless.annotations` | Additional custom annotations for headless MySQL secondary service. | `{}` | +| `secondary.pdb.create` | Enable/disable a Pod Disruption Budget creation for MySQL secondary pods | `false` | +| `secondary.pdb.minAvailable` | Minimum number/percentage of MySQL secondary pods that should remain scheduled | `1` | +| `secondary.pdb.maxUnavailable` | Maximum number/percentage of MySQL secondary pods that may be made unavailable | `""` | +| `secondary.podLabels` | Additional pod labels for MySQL secondary pods | `{}` | + +### RBAC parameters + +| Name | Description | Value | +| --------------------------------------------- | -------------------------------------------------------------- | ------- | +| `serviceAccount.create` | Enable the creation of a ServiceAccount for MySQL pods | `true` | +| `serviceAccount.name` | Name of the created ServiceAccount | `""` | +| `serviceAccount.annotations` | Annotations for MySQL Service Account | `{}` | +| `serviceAccount.automountServiceAccountToken` | Automount service account token for the server service account | `true` | +| `rbac.create` | Whether to create & use RBAC resources or not | `false` | +| `rbac.rules` | Custom RBAC rules to set | `[]` | + +### Network Policy + +| Name | Description | Value | +| ------------------------------------------ | --------------------------------------------------------------------------------------------------------------- | ------- | +| `networkPolicy.enabled` | Enable creation of NetworkPolicy resources | `false` | +| `networkPolicy.allowExternal` | The Policy model to apply. | `true` | +| `networkPolicy.explicitNamespacesSelector` | A Kubernetes LabelSelector to explicitly select namespaces from which ingress traffic could be allowed to MySQL | `{}` | + +### Volume Permissions parameters + +| Name | Description | Value | +| ------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | +| `volumePermissions.enabled` | Enable init container that changes the owner and group of the persistent volume(s) mountpoint to `runAsUser:fsGroup` | `false` | +| `volumePermissions.image.registry` | Init container volume-permissions image registry | `docker.io` | +| `volumePermissions.image.repository` | Init container volume-permissions image repository | `bitnami/bitnami-shell` | +| `volumePermissions.image.tag` | Init container volume-permissions image tag (immutable tags are recommended) | `11-debian-11-r108` | +| `volumePermissions.image.digest` | Init container volume-permissions image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag | `""` | +| `volumePermissions.image.pullPolicy` | Init container volume-permissions image pull policy | `IfNotPresent` | +| `volumePermissions.image.pullSecrets` | Specify docker-registry secret names as an array | `[]` | +| `volumePermissions.resources` | Init container volume-permissions resources | `{}` | + +### Metrics parameters + +| Name | Description | Value | +| ----------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | ------------------------- | +| `metrics.enabled` | Start a side-car prometheus exporter | `false` | +| `metrics.image.registry` | Exporter image registry | `docker.io` | +| `metrics.image.repository` | Exporter image repository | `bitnami/mysqld-exporter` | +| `metrics.image.tag` | Exporter image tag (immutable tags are recommended) | `0.14.0-debian-11-r109` | +| `metrics.image.digest` | Exporter image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag | `""` | +| `metrics.image.pullPolicy` | Exporter image pull policy | `IfNotPresent` | +| `metrics.image.pullSecrets` | Specify docker-registry secret names as an array | `[]` | +| `metrics.containerSecurityContext.enabled` | MySQL metrics container securityContext | `true` | +| `metrics.containerSecurityContext.runAsUser` | User ID for the MySQL metrics container | `1001` | +| `metrics.containerSecurityContext.runAsNonRoot` | Set MySQL metrics container's Security Context runAsNonRoot | `true` | +| `metrics.service.type` | Kubernetes service type for MySQL Prometheus Exporter | `ClusterIP` | +| `metrics.service.port` | MySQL Prometheus Exporter service port | `9104` | +| `metrics.service.annotations` | Prometheus exporter service annotations | `{}` | +| `metrics.extraArgs.primary` | Extra args to be passed to mysqld_exporter on Primary pods | `[]` | +| `metrics.extraArgs.secondary` | Extra args to be passed to mysqld_exporter on Secondary pods | `[]` | +| `metrics.resources.limits` | The resources limits for MySQL prometheus exporter containers | `{}` | +| `metrics.resources.requests` | The requested resources for MySQL prometheus exporter containers | `{}` | +| `metrics.livenessProbe.enabled` | Enable livenessProbe | `true` | +| `metrics.livenessProbe.initialDelaySeconds` | Initial delay seconds for livenessProbe | `120` | +| `metrics.livenessProbe.periodSeconds` | Period seconds for livenessProbe | `10` | +| `metrics.livenessProbe.timeoutSeconds` | Timeout seconds for livenessProbe | `1` | +| `metrics.livenessProbe.failureThreshold` | Failure threshold for livenessProbe | `3` | +| `metrics.livenessProbe.successThreshold` | Success threshold for livenessProbe | `1` | +| `metrics.readinessProbe.enabled` | Enable readinessProbe | `true` | +| `metrics.readinessProbe.initialDelaySeconds` | Initial delay seconds for readinessProbe | `30` | +| `metrics.readinessProbe.periodSeconds` | Period seconds for readinessProbe | `10` | +| `metrics.readinessProbe.timeoutSeconds` | Timeout seconds for readinessProbe | `1` | +| `metrics.readinessProbe.failureThreshold` | Failure threshold for readinessProbe | `3` | +| `metrics.readinessProbe.successThreshold` | Success threshold for readinessProbe | `1` | +| `metrics.serviceMonitor.enabled` | Create ServiceMonitor Resource for scraping metrics using PrometheusOperator | `false` | +| `metrics.serviceMonitor.namespace` | Specify the namespace in which the serviceMonitor resource will be created | `""` | +| `metrics.serviceMonitor.jobLabel` | The name of the label on the target service to use as the job name in prometheus. | `""` | +| `metrics.serviceMonitor.interval` | Specify the interval at which metrics should be scraped | `30s` | +| `metrics.serviceMonitor.scrapeTimeout` | Specify the timeout after which the scrape is ended | `""` | +| `metrics.serviceMonitor.relabelings` | RelabelConfigs to apply to samples before scraping | `[]` | +| `metrics.serviceMonitor.metricRelabelings` | MetricRelabelConfigs to apply to samples before ingestion | `[]` | +| `metrics.serviceMonitor.selector` | ServiceMonitor selector labels | `{}` | +| `metrics.serviceMonitor.honorLabels` | Specify honorLabels parameter to add the scrape endpoint | `false` | +| `metrics.serviceMonitor.labels` | Used to pass Labels that are used by the Prometheus installed in your cluster to select Service Monitors to work with | `{}` | +| `metrics.serviceMonitor.annotations` | ServiceMonitor annotations | `{}` | +| `metrics.prometheusRule.enabled` | Creates a Prometheus Operator prometheusRule (also requires `metrics.enabled` to be `true` and `metrics.prometheusRule.rules`) | `false` | +| `metrics.prometheusRule.namespace` | Namespace for the prometheusRule Resource (defaults to the Release Namespace) | `""` | +| `metrics.prometheusRule.additionalLabels` | Additional labels that can be used so prometheusRule will be discovered by Prometheus | `{}` | +| `metrics.prometheusRule.rules` | Prometheus Rule definitions | `[]` | + +The above parameters map to the env variables defined in [bitnami/mysql](https://github.com/bitnami/containers/tree/main/bitnami/mysql). For more information please refer to the [bitnami/mysql](https://github.com/bitnami/containers/tree/main/bitnami/mysql) image documentation. + +Specify each parameter using the `--set key=value[,key=value]` argument to `helm install`. For example, + +```console +helm install my-release \ + --set auth.rootPassword=secretpassword,auth.database=app_database \ + my-repo/mysql +``` + +The above command sets the MySQL `root` account password to `secretpassword`. Additionally it creates a database named `app_database`. + +> NOTE: Once this chart is deployed, it is not possible to change the application's access credentials, such as usernames or passwords, using Helm. To change these application credentials after deployment, delete any persistent volumes (PVs) used by the chart and re-deploy it, or use the application's built-in administrative tools if available. + +Alternatively, a YAML file that specifies the values for the parameters can be provided while installing the chart. For example, + +```console +helm install my-release -f values.yaml my-repo/mysql +``` + +> **Tip**: You can use the default [values.yaml](values.yaml) + +## Configuration and installation details + +### [Rolling VS Immutable tags](https://docs.bitnami.com/containers/how-to/understand-rolling-tags-containers/) + +It is strongly recommended to use immutable tags in a production environment. This ensures your deployment does not change automatically if the same tag is updated with a different image. + +Bitnami will release a new chart updating its containers if a new version of the main container, significant changes, or critical vulnerabilities exist. + +### Use a different MySQL version + +To modify the application version used in this chart, specify a different version of the image using the `image.tag` parameter and/or a different repository using the `image.repository` parameter. Refer to the [chart documentation for more information on these parameters and how to use them with images from a private registry](https://docs.bitnami.com/kubernetes/infrastructure/mysql/configuration/change-image-version/). + +### Customize a new MySQL instance + +The [Bitnami MySQL](https://github.com/bitnami/containers/tree/main/bitnami/mysql) image allows you to use your custom scripts to initialize a fresh instance. Custom scripts may be specified using the `initdbScripts` parameter. Alternatively, an external ConfigMap may be created with all the initialization scripts and the ConfigMap passed to the chart via the `initdbScriptsConfigMap` parameter. Note that this will override the `initdbScripts` parameter. + +The allowed extensions are `.sh`, `.sql` and `.sql.gz`. + +These scripts are treated differently depending on their extension. While `.sh` scripts are executed on all the nodes, `.sql` and `.sql.gz` scripts are only executed on the primary nodes. This is because `.sh` scripts support conditional tests to identify the type of node they are running on, while such tests are not supported in `.sql` or `sql.gz` files. + +Refer to the [chart documentation for more information and a usage example](http://docs.bitnami.com/kubernetes/infrastructure/mysql/configuration/customize-new-instance/). + +### Sidecars and Init Containers + +If you have a need for additional containers to run within the same pod as MySQL, you can do so via the `sidecars` config parameter. Simply define your container according to the Kubernetes container spec. + +```yaml +sidecars: + - name: your-image-name + image: your-image + imagePullPolicy: Always + ports: + - name: portname + containerPort: 1234 +``` + +Similarly, you can add extra init containers using the `initContainers` parameter. + +```yaml +initContainers: + - name: your-image-name + image: your-image + imagePullPolicy: Always + ports: + - name: portname + containerPort: 1234 +``` + +## Persistence + +The [Bitnami MySQL](https://github.com/bitnami/containers/tree/main/bitnami/mysql) image stores the MySQL data and configurations at the `/bitnami/mysql` path of the container. + +The chart mounts a [Persistent Volume](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) volume at this location. The volume is created using dynamic volume provisioning by default. An existing PersistentVolumeClaim can also be defined for this purpose. + +If you encounter errors when working with persistent volumes, refer to our [troubleshooting guide for persistent volumes](https://docs.bitnami.com/kubernetes/faq/troubleshooting/troubleshooting-persistence-volumes/). + +## Network Policy config + +To enable network policy for MySQL, install [a networking plugin that implements the Kubernetes NetworkPolicy spec](https://kubernetes.io/docs/tasks/administer-cluster/declare-network-policy#before-you-begin), and set `networkPolicy.enabled` to `true`. + +For Kubernetes v1.5 & v1.6, you must also turn on NetworkPolicy by setting the DefaultDeny namespace annotation. Note: this will enforce policy for _all_ pods in the namespace: + +```console +kubectl annotate namespace default "net.beta.kubernetes.io/network-policy={\"ingress\":{\"isolation\":\"DefaultDeny\"}}" +``` + +With NetworkPolicy enabled, traffic will be limited to just port 3306. + +For more precise policy, set `networkPolicy.allowExternal=false`. This will only allow pods with the generated client label to connect to MySQL. +This label will be displayed in the output of a successful install. + +## Pod affinity + +This chart allows you to set your custom affinity using the `XXX.affinity` parameter(s). Find more information about Pod affinity in the [Kubernetes documentation](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity). + +As an alternative, you can use the preset configurations for pod affinity, pod anti-affinity, and node affinity available at the [bitnami/common](https://github.com/bitnami/charts/tree/main/bitnami/common#affinities) chart. To do so, set the `XXX.podAffinityPreset`, `XXX.podAntiAffinityPreset`, or `XXX.nodeAffinityPreset` parameters. + +## Troubleshooting + +Find more information about how to deal with common errors related to Bitnami's Helm charts in [this troubleshooting guide](https://docs.bitnami.com/general/how-to/troubleshoot-helm-chart-issues). + +## Upgrading + +It's necessary to set the `auth.rootPassword` parameter when upgrading for readiness/liveness probes to work properly. When you install this chart for the first time, some notes will be displayed providing the credentials you must use under the 'Administrator credentials' section. Please note down the password and run the command below to upgrade your chart: + +```console +helm upgrade my-release my-repo/mysql --set auth.rootPassword=[ROOT_PASSWORD] +``` + +| Note: you need to substitute the placeholder _[ROOT_PASSWORD]_ with the value obtained in the installation notes. + +### To 9.0.0 + +This major release renames several values in this chart and adds missing features, in order to be aligned with the rest of the assets in the Bitnami charts repository. + +Affected values: + +- `schedulerName` was renamed as `primary.schedulerName` and `secondary.schedulerName`. +- The way how passwords are handled has been refactored and value `auth.forcePassword` has been removed. Now, the password configuration will have the following priority: + 1. Search for an already existing 'Secret' resource and reuse previous password. + 2. Password provided via the values.yaml + 3. If no secret existed, and no password was provided, the bitnami/mysql chart will set a randomly generated password. +- `primary.service.port` was renamed as `primary.service.ports.mysql`. +- `secondary.service.port` was renamed as `secondary.service.ports.mysql`. +- `primary.service.nodePort` was renamed as `primary.service.nodePorts.mysql`. +- `secondary.service.nodePort` was renamed as `secondary.service.nodePorts.mysql`. +- `primary.updateStrategy` and `secondary.updateStrategy` are now interpreted as an object and not a string. +- Values `primary.rollingUpdatePartition` and `secondary.rollingUpdatePartition` have been removed. In cases were they are needed, they can be set inside `.*updateStrategy`. +- `primary.pdb.enabled` was renamed as `primary.pdb.create`. +- `secondary.pdb.enabled` was renamed as `secondary.pdb.create`. +- `metrics.serviceMonitor.additionalLabels` was renamed as `metrics.serviceMonitor.labels` +- `metrics.serviceMonitor.relabellings` was removed, previously used to configured `metricRelabelings` field. We introduced two new values: `metrics.serviceMonitor.relabelings` and `metrics.serviceMonitor.metricRelabelings` that can be used to configured the serviceMonitor homonimous field. + +### To 8.0.0 + +- Several parameters were renamed or disappeared in favor of new ones on this major version: + - The terms _master_ and _slave_ have been replaced by the terms _primary_ and _secondary_. Therefore, parameters prefixed with `master` or `slave` are now prefixed with `primary` or `secondary`, respectively. + - Credentials parameters are reorganized under the `auth` parameter. + - `replication.enabled` parameter is deprecated in favor of `architecture` parameter that accepts two values: `standalone` and `replication`. +- Chart labels were adapted to follow the [Helm charts standard labels](https://helm.sh/docs/chart_best_practices/labels/#standard-labels). +- This version also introduces `bitnami/common`, a [library chart](https://helm.sh/docs/topics/library_charts/#helm) as a dependency. More documentation about this new utility could be found [here](https://github.com/bitnami/charts/tree/main/bitnami/common#bitnami-common-library-chart). Please, make sure that you have updated the chart dependencies before executing any upgrade. + +Consequences: + +- Backwards compatibility is not guaranteed. To upgrade to `8.0.0`, install a new release of the MySQL chart, and migrate the data from your previous release. You have 2 alternatives to do so: + - Create a backup of the database, and restore it on the new release using tools such as [mysqldump](https://dev.mysql.com/doc/refman/8.0/en/mysqldump.html). + - Reuse the PVC used to hold the master data on your previous release. To do so, use the `primary.persistence.existingClaim` parameter. The following example assumes that the release name is `mysql`: + +```console +helm install mysql my-repo/mysql --set auth.rootPassword=[ROOT_PASSWORD] --set primary.persistence.existingClaim=[EXISTING_PVC] +``` + +| Note: you need to substitute the placeholder _[EXISTING_PVC]_ with the name of the PVC used on your previous release, and _[ROOT_PASSWORD]_ with the root password used in your previous release. + +### To 7.0.0 + +[On November 13, 2020, Helm v2 support formally ended](https://github.com/helm/charts#status-of-the-project). This major version is the result of the required changes applied to the Helm Chart to be able to incorporate the different features added in Helm v3 and to be consistent with the Helm project itself regarding the Helm v2 EOL. + +[Learn more about this change and related upgrade considerations](https://docs.bitnami.com/kubernetes/infrastructure/mysql/administration/upgrade-helm3/). + +### To 3.0.0 + +Backwards compatibility is not guaranteed unless you modify the labels used on the chart's deployments. +Use the workaround below to upgrade from versions previous to 3.0.0. The following example assumes that the release name is mysql: + +```console +kubectl delete statefulset mysql-master --cascade=false +kubectl delete statefulset mysql-slave --cascade=false +``` + +## License + +Copyright © 2023 Bitnami + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/charts/deps/charts/mysql-9.7.2/charts/common/.helmignore b/charts/deps/charts/mysql-9.7.2/charts/common/.helmignore new file mode 100644 index 0000000..50af031 --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/charts/common/.helmignore @@ -0,0 +1,22 @@ +# 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 +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/charts/deps/charts/mysql-9.7.2/charts/common/Chart.yaml b/charts/deps/charts/mysql-9.7.2/charts/common/Chart.yaml new file mode 100644 index 0000000..8583e62 --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/charts/common/Chart.yaml @@ -0,0 +1,24 @@ +annotations: + category: Infrastructure + licenses: Apache-2.0 +apiVersion: v2 +appVersion: 2.2.4 +description: A Library Helm Chart for grouping common logic between bitnami charts. + This chart is not deployable by itself. +home: https://github.com/bitnami/charts/tree/main/bitnami/common +icon: https://bitnami.com/downloads/logos/bitnami-mark.png +keywords: +- common +- helper +- template +- function +- bitnami +maintainers: +- name: Bitnami + url: https://github.com/bitnami/charts +name: common +sources: +- https://github.com/bitnami/charts +- https://www.bitnami.com/ +type: library +version: 2.2.4 diff --git a/charts/deps/charts/mysql-9.7.2/charts/common/README.md b/charts/deps/charts/mysql-9.7.2/charts/common/README.md new file mode 100644 index 0000000..825639f --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/charts/common/README.md @@ -0,0 +1,233 @@ +# Bitnami Common Library Chart + +A [Helm Library Chart](https://helm.sh/docs/topics/library_charts/#helm) for grouping common logic between bitnami charts. + +## TL;DR + +```yaml +dependencies: + - name: common + version: 1.x.x + repository: https://charts.bitnami.com/bitnami +``` + +```console +helm dependency update +``` + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "common.names.fullname" . }} +data: + myvalue: "Hello World" +``` + +## Introduction + +This chart provides a common template helpers which can be used to develop new charts using [Helm](https://helm.sh) package manager. + +Bitnami charts can be used with [Kubeapps](https://kubeapps.dev/) for deployment and management of Helm Charts in clusters. + +## Prerequisites + +- Kubernetes 1.19+ +- Helm 3.2.0+ + +## Parameters + +## Special input schemas + +### ImageRoot + +```yaml +registry: + type: string + description: Docker registry where the image is located + example: docker.io + +repository: + type: string + description: Repository and image name + example: bitnami/nginx + +tag: + type: string + description: image tag + example: 1.16.1-debian-10-r63 + +pullPolicy: + type: string + description: Specify a imagePullPolicy. Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + +pullSecrets: + type: array + items: + type: string + description: Optionally specify an array of imagePullSecrets (evaluated as templates). + +debug: + type: boolean + description: Set to true if you would like to see extra information on logs + example: false + +## An instance would be: +# registry: docker.io +# repository: bitnami/nginx +# tag: 1.16.1-debian-10-r63 +# pullPolicy: IfNotPresent +# debug: false +``` + +### Persistence + +```yaml +enabled: + type: boolean + description: Whether enable persistence. + example: true + +storageClass: + type: string + description: Ghost data Persistent Volume Storage Class, If set to "-", storageClassName: "" which disables dynamic provisioning. + example: "-" + +accessMode: + type: string + description: Access mode for the Persistent Volume Storage. + example: ReadWriteOnce + +size: + type: string + description: Size the Persistent Volume Storage. + example: 8Gi + +path: + type: string + description: Path to be persisted. + example: /bitnami + +## An instance would be: +# enabled: true +# storageClass: "-" +# accessMode: ReadWriteOnce +# size: 8Gi +# path: /bitnami +``` + +### ExistingSecret + +```yaml +name: + type: string + description: Name of the existing secret. + example: mySecret +keyMapping: + description: Mapping between the expected key name and the name of the key in the existing secret. + type: object + +## An instance would be: +# name: mySecret +# keyMapping: +# password: myPasswordKey +``` + +#### Example of use + +When we store sensitive data for a deployment in a secret, some times we want to give to users the possibility of using theirs existing secrets. + +```yaml +# templates/secret.yaml +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "common.names.fullname" . }} + labels: + app: {{ include "common.names.fullname" . }} +type: Opaque +data: + password: {{ .Values.password | b64enc | quote }} + +# templates/dpl.yaml +--- +... + env: + - name: PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "common.secrets.name" (dict "existingSecret" .Values.existingSecret "context" $) }} + key: {{ include "common.secrets.key" (dict "existingSecret" .Values.existingSecret "key" "password") }} +... + +# values.yaml +--- +name: mySecret +keyMapping: + password: myPasswordKey +``` + +### ValidateValue + +#### NOTES.txt + +```console +{{- $validateValueConf00 := (dict "valueKey" "path.to.value00" "secret" "secretName" "field" "password-00") -}} +{{- $validateValueConf01 := (dict "valueKey" "path.to.value01" "secret" "secretName" "field" "password-01") -}} + +{{ include "common.validations.values.multiple.empty" (dict "required" (list $validateValueConf00 $validateValueConf01) "context" $) }} +``` + +If we force those values to be empty we will see some alerts + +```console +helm install test mychart --set path.to.value00="",path.to.value01="" + 'path.to.value00' must not be empty, please add '--set path.to.value00=$PASSWORD_00' to the command. To get the current value: + + export PASSWORD_00=$(kubectl get secret --namespace default secretName -o jsonpath="{.data.password-00}" | base64 -d) + + 'path.to.value01' must not be empty, please add '--set path.to.value01=$PASSWORD_01' to the command. To get the current value: + + export PASSWORD_01=$(kubectl get secret --namespace default secretName -o jsonpath="{.data.password-01}" | base64 -d) +``` + +## Upgrading + +### To 1.0.0 + +[On November 13, 2020, Helm v2 support was formally finished](https://github.com/helm/charts#status-of-the-project), this major version is the result of the required changes applied to the Helm Chart to be able to incorporate the different features added in Helm v3 and to be consistent with the Helm project itself regarding the Helm v2 EOL. + +#### What changes were introduced in this major version? + +- Previous versions of this Helm Chart use `apiVersion: v1` (installable by both Helm 2 and 3), this Helm Chart was updated to `apiVersion: v2` (installable by Helm 3 only). [Here](https://helm.sh/docs/topics/charts/#the-apiversion-field) you can find more information about the `apiVersion` field. +- Use `type: library`. [Here](https://v3.helm.sh/docs/faq/#library-chart-support) you can find more information. +- The different fields present in the *Chart.yaml* file has been ordered alphabetically in a homogeneous way for all the Bitnami Helm Charts + +#### Considerations when upgrading to this version + +- If you want to upgrade to this version from a previous one installed with Helm v3, you shouldn't face any issues +- If you want to upgrade to this version using Helm v2, this scenario is not supported as this version doesn't support Helm v2 anymore +- If you installed the previous version with Helm v2 and wants to upgrade to this version with Helm v3, please refer to the [official Helm documentation](https://helm.sh/docs/topics/v2_v3_migration/#migration-use-cases) about migrating from Helm v2 to v3 + +#### Useful links + +- +- +- + +## License + +Copyright © 2023 Bitnami + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/charts/deps/charts/mysql-9.7.2/charts/common/templates/_affinities.tpl b/charts/deps/charts/mysql-9.7.2/charts/common/templates/_affinities.tpl new file mode 100644 index 0000000..81902a6 --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/charts/common/templates/_affinities.tpl @@ -0,0 +1,106 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* +Return a soft nodeAffinity definition +{{ include "common.affinities.nodes.soft" (dict "key" "FOO" "values" (list "BAR" "BAZ")) -}} +*/}} +{{- define "common.affinities.nodes.soft" -}} +preferredDuringSchedulingIgnoredDuringExecution: + - preference: + matchExpressions: + - key: {{ .key }} + operator: In + values: + {{- range .values }} + - {{ . | quote }} + {{- end }} + weight: 1 +{{- end -}} + +{{/* +Return a hard nodeAffinity definition +{{ include "common.affinities.nodes.hard" (dict "key" "FOO" "values" (list "BAR" "BAZ")) -}} +*/}} +{{- define "common.affinities.nodes.hard" -}} +requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: {{ .key }} + operator: In + values: + {{- range .values }} + - {{ . | quote }} + {{- end }} +{{- end -}} + +{{/* +Return a nodeAffinity definition +{{ include "common.affinities.nodes" (dict "type" "soft" "key" "FOO" "values" (list "BAR" "BAZ")) -}} +*/}} +{{- define "common.affinities.nodes" -}} + {{- if eq .type "soft" }} + {{- include "common.affinities.nodes.soft" . -}} + {{- else if eq .type "hard" }} + {{- include "common.affinities.nodes.hard" . -}} + {{- end -}} +{{- end -}} + +{{/* +Return a topologyKey definition +{{ include "common.affinities.topologyKey" (dict "topologyKey" "BAR") -}} +*/}} +{{- define "common.affinities.topologyKey" -}} +{{ .topologyKey | default "kubernetes.io/hostname" -}} +{{- end -}} + +{{/* +Return a soft podAffinity/podAntiAffinity definition +{{ include "common.affinities.pods.soft" (dict "component" "FOO" "extraMatchLabels" .Values.extraMatchLabels "topologyKey" "BAR" "context" $) -}} +*/}} +{{- define "common.affinities.pods.soft" -}} +{{- $component := default "" .component -}} +{{- $extraMatchLabels := default (dict) .extraMatchLabels -}} +preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchLabels: {{- (include "common.labels.matchLabels" .context) | nindent 10 }} + {{- if not (empty $component) }} + {{ printf "app.kubernetes.io/component: %s" $component }} + {{- end }} + {{- range $key, $value := $extraMatchLabels }} + {{ $key }}: {{ $value | quote }} + {{- end }} + topologyKey: {{ include "common.affinities.topologyKey" (dict "topologyKey" .topologyKey) }} + weight: 1 +{{- end -}} + +{{/* +Return a hard podAffinity/podAntiAffinity definition +{{ include "common.affinities.pods.hard" (dict "component" "FOO" "extraMatchLabels" .Values.extraMatchLabels "topologyKey" "BAR" "context" $) -}} +*/}} +{{- define "common.affinities.pods.hard" -}} +{{- $component := default "" .component -}} +{{- $extraMatchLabels := default (dict) .extraMatchLabels -}} +requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchLabels: {{- (include "common.labels.matchLabels" .context) | nindent 8 }} + {{- if not (empty $component) }} + {{ printf "app.kubernetes.io/component: %s" $component }} + {{- end }} + {{- range $key, $value := $extraMatchLabels }} + {{ $key }}: {{ $value | quote }} + {{- end }} + topologyKey: {{ include "common.affinities.topologyKey" (dict "topologyKey" .topologyKey) }} +{{- end -}} + +{{/* +Return a podAffinity/podAntiAffinity definition +{{ include "common.affinities.pods" (dict "type" "soft" "key" "FOO" "values" (list "BAR" "BAZ")) -}} +*/}} +{{- define "common.affinities.pods" -}} + {{- if eq .type "soft" }} + {{- include "common.affinities.pods.soft" . -}} + {{- else if eq .type "hard" }} + {{- include "common.affinities.pods.hard" . -}} + {{- end -}} +{{- end -}} diff --git a/charts/deps/charts/mysql-9.7.2/charts/common/templates/_capabilities.tpl b/charts/deps/charts/mysql-9.7.2/charts/common/templates/_capabilities.tpl new file mode 100644 index 0000000..9d9b760 --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/charts/common/templates/_capabilities.tpl @@ -0,0 +1,154 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* +Return the target Kubernetes version +*/}} +{{- define "common.capabilities.kubeVersion" -}} +{{- if .Values.global }} + {{- if .Values.global.kubeVersion }} + {{- .Values.global.kubeVersion -}} + {{- else }} + {{- default .Capabilities.KubeVersion.Version .Values.kubeVersion -}} + {{- end -}} +{{- else }} +{{- default .Capabilities.KubeVersion.Version .Values.kubeVersion -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for poddisruptionbudget. +*/}} +{{- define "common.capabilities.policy.apiVersion" -}} +{{- if semverCompare "<1.21-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "policy/v1beta1" -}} +{{- else -}} +{{- print "policy/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for networkpolicy. +*/}} +{{- define "common.capabilities.networkPolicy.apiVersion" -}} +{{- if semverCompare "<1.7-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "extensions/v1beta1" -}} +{{- else -}} +{{- print "networking.k8s.io/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for cronjob. +*/}} +{{- define "common.capabilities.cronjob.apiVersion" -}} +{{- if semverCompare "<1.21-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "batch/v1beta1" -}} +{{- else -}} +{{- print "batch/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for deployment. +*/}} +{{- define "common.capabilities.deployment.apiVersion" -}} +{{- if semverCompare "<1.14-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "extensions/v1beta1" -}} +{{- else -}} +{{- print "apps/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for statefulset. +*/}} +{{- define "common.capabilities.statefulset.apiVersion" -}} +{{- if semverCompare "<1.14-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "apps/v1beta1" -}} +{{- else -}} +{{- print "apps/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for ingress. +*/}} +{{- define "common.capabilities.ingress.apiVersion" -}} +{{- if .Values.ingress -}} +{{- if .Values.ingress.apiVersion -}} +{{- .Values.ingress.apiVersion -}} +{{- else if semverCompare "<1.14-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "extensions/v1beta1" -}} +{{- else if semverCompare "<1.19-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "networking.k8s.io/v1beta1" -}} +{{- else -}} +{{- print "networking.k8s.io/v1" -}} +{{- end }} +{{- else if semverCompare "<1.14-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "extensions/v1beta1" -}} +{{- else if semverCompare "<1.19-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "networking.k8s.io/v1beta1" -}} +{{- else -}} +{{- print "networking.k8s.io/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for RBAC resources. +*/}} +{{- define "common.capabilities.rbac.apiVersion" -}} +{{- if semverCompare "<1.17-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "rbac.authorization.k8s.io/v1beta1" -}} +{{- else -}} +{{- print "rbac.authorization.k8s.io/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for CRDs. +*/}} +{{- define "common.capabilities.crd.apiVersion" -}} +{{- if semverCompare "<1.19-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "apiextensions.k8s.io/v1beta1" -}} +{{- else -}} +{{- print "apiextensions.k8s.io/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for APIService. +*/}} +{{- define "common.capabilities.apiService.apiVersion" -}} +{{- if semverCompare "<1.10-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "apiregistration.k8s.io/v1beta1" -}} +{{- else -}} +{{- print "apiregistration.k8s.io/v1" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for Horizontal Pod Autoscaler. +*/}} +{{- define "common.capabilities.hpa.apiVersion" -}} +{{- if semverCompare "<1.23-0" (include "common.capabilities.kubeVersion" .context) -}} +{{- if .beta2 -}} +{{- print "autoscaling/v2beta2" -}} +{{- else -}} +{{- print "autoscaling/v2beta1" -}} +{{- end -}} +{{- else -}} +{{- print "autoscaling/v2" -}} +{{- end -}} +{{- end -}} + +{{/* +Returns true if the used Helm version is 3.3+. +A way to check the used Helm version was not introduced until version 3.3.0 with .Capabilities.HelmVersion, which contains an additional "{}}" structure. +This check is introduced as a regexMatch instead of {{ if .Capabilities.HelmVersion }} because checking for the key HelmVersion in <3.3 results in a "interface not found" error. +**To be removed when the catalog's minimun Helm version is 3.3** +*/}} +{{- define "common.capabilities.supportsHelmVersion" -}} +{{- if regexMatch "{(v[0-9])*[^}]*}}$" (.Capabilities | toString ) }} + {{- true -}} +{{- end -}} +{{- end -}} diff --git a/charts/deps/charts/mysql-9.7.2/charts/common/templates/_errors.tpl b/charts/deps/charts/mysql-9.7.2/charts/common/templates/_errors.tpl new file mode 100644 index 0000000..a79cc2e --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/charts/common/templates/_errors.tpl @@ -0,0 +1,23 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Through error when upgrading using empty passwords values that must not be empty. + +Usage: +{{- $validationError00 := include "common.validations.values.single.empty" (dict "valueKey" "path.to.password00" "secret" "secretName" "field" "password-00") -}} +{{- $validationError01 := include "common.validations.values.single.empty" (dict "valueKey" "path.to.password01" "secret" "secretName" "field" "password-01") -}} +{{ include "common.errors.upgrade.passwords.empty" (dict "validationErrors" (list $validationError00 $validationError01) "context" $) }} + +Required password params: + - validationErrors - String - Required. List of validation strings to be return, if it is empty it won't throw error. + - context - Context - Required. Parent context. +*/}} +{{- define "common.errors.upgrade.passwords.empty" -}} + {{- $validationErrors := join "" .validationErrors -}} + {{- if and $validationErrors .context.Release.IsUpgrade -}} + {{- $errorString := "\nPASSWORDS ERROR: You must provide your current passwords when upgrading the release." -}} + {{- $errorString = print $errorString "\n Note that even after reinstallation, old credentials may be needed as they may be kept in persistent volume claims." -}} + {{- $errorString = print $errorString "\n Further information can be obtained at https://docs.bitnami.com/general/how-to/troubleshoot-helm-chart-issues/#credential-errors-while-upgrading-chart-releases" -}} + {{- $errorString = print $errorString "\n%s" -}} + {{- printf $errorString $validationErrors | fail -}} + {{- end -}} +{{- end -}} diff --git a/charts/deps/charts/mysql-9.7.2/charts/common/templates/_images.tpl b/charts/deps/charts/mysql-9.7.2/charts/common/templates/_images.tpl new file mode 100644 index 0000000..2e7b151 --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/charts/common/templates/_images.tpl @@ -0,0 +1,80 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Return the proper image name +{{ include "common.images.image" ( dict "imageRoot" .Values.path.to.the.image "global" .Values.global ) }} +*/}} +{{- define "common.images.image" -}} +{{- $registryName := .imageRoot.registry -}} +{{- $repositoryName := .imageRoot.repository -}} +{{- $separator := ":" -}} +{{- $termination := .imageRoot.tag | toString -}} +{{- if .global }} + {{- if .global.imageRegistry }} + {{- $registryName = .global.imageRegistry -}} + {{- end -}} +{{- end -}} +{{- if .imageRoot.digest }} + {{- $separator = "@" -}} + {{- $termination = .imageRoot.digest | toString -}} +{{- end -}} +{{- if $registryName }} + {{- printf "%s/%s%s%s" $registryName $repositoryName $separator $termination -}} +{{- else -}} + {{- printf "%s%s%s" $repositoryName $separator $termination -}} +{{- end -}} +{{- end -}} + +{{/* +Return the proper Docker Image Registry Secret Names (deprecated: use common.images.renderPullSecrets instead) +{{ include "common.images.pullSecrets" ( dict "images" (list .Values.path.to.the.image1, .Values.path.to.the.image2) "global" .Values.global) }} +*/}} +{{- define "common.images.pullSecrets" -}} + {{- $pullSecrets := list }} + + {{- if .global }} + {{- range .global.imagePullSecrets -}} + {{- $pullSecrets = append $pullSecrets . -}} + {{- end -}} + {{- end -}} + + {{- range .images -}} + {{- range .pullSecrets -}} + {{- $pullSecrets = append $pullSecrets . -}} + {{- end -}} + {{- end -}} + + {{- if (not (empty $pullSecrets)) }} +imagePullSecrets: + {{- range $pullSecrets }} + - name: {{ . }} + {{- end }} + {{- end }} +{{- end -}} + +{{/* +Return the proper Docker Image Registry Secret Names evaluating values as templates +{{ include "common.images.renderPullSecrets" ( dict "images" (list .Values.path.to.the.image1, .Values.path.to.the.image2) "context" $) }} +*/}} +{{- define "common.images.renderPullSecrets" -}} + {{- $pullSecrets := list }} + {{- $context := .context }} + + {{- if $context.Values.global }} + {{- range $context.Values.global.imagePullSecrets -}} + {{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" . "context" $context)) -}} + {{- end -}} + {{- end -}} + + {{- range .images -}} + {{- range .pullSecrets -}} + {{- $pullSecrets = append $pullSecrets (include "common.tplvalues.render" (dict "value" . "context" $context)) -}} + {{- end -}} + {{- end -}} + + {{- if (not (empty $pullSecrets)) }} +imagePullSecrets: + {{- range $pullSecrets }} + - name: {{ . }} + {{- end }} + {{- end }} +{{- end -}} diff --git a/charts/deps/charts/mysql-9.7.2/charts/common/templates/_ingress.tpl b/charts/deps/charts/mysql-9.7.2/charts/common/templates/_ingress.tpl new file mode 100644 index 0000000..831da9c --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/charts/common/templates/_ingress.tpl @@ -0,0 +1,68 @@ +{{/* vim: set filetype=mustache: */}} + +{{/* +Generate backend entry that is compatible with all Kubernetes API versions. + +Usage: +{{ include "common.ingress.backend" (dict "serviceName" "backendName" "servicePort" "backendPort" "context" $) }} + +Params: + - serviceName - String. Name of an existing service backend + - servicePort - String/Int. Port name (or number) of the service. It will be translated to different yaml depending if it is a string or an integer. + - context - Dict - Required. The context for the template evaluation. +*/}} +{{- define "common.ingress.backend" -}} +{{- $apiVersion := (include "common.capabilities.ingress.apiVersion" .context) -}} +{{- if or (eq $apiVersion "extensions/v1beta1") (eq $apiVersion "networking.k8s.io/v1beta1") -}} +serviceName: {{ .serviceName }} +servicePort: {{ .servicePort }} +{{- else -}} +service: + name: {{ .serviceName }} + port: + {{- if typeIs "string" .servicePort }} + name: {{ .servicePort }} + {{- else if or (typeIs "int" .servicePort) (typeIs "float64" .servicePort) }} + number: {{ .servicePort | int }} + {{- end }} +{{- end -}} +{{- end -}} + +{{/* +Print "true" if the API pathType field is supported +Usage: +{{ include "common.ingress.supportsPathType" . }} +*/}} +{{- define "common.ingress.supportsPathType" -}} +{{- if (semverCompare "<1.18-0" (include "common.capabilities.kubeVersion" .)) -}} +{{- print "false" -}} +{{- else -}} +{{- print "true" -}} +{{- end -}} +{{- end -}} + +{{/* +Returns true if the ingressClassname field is supported +Usage: +{{ include "common.ingress.supportsIngressClassname" . }} +*/}} +{{- define "common.ingress.supportsIngressClassname" -}} +{{- if semverCompare "<1.18-0" (include "common.capabilities.kubeVersion" .) -}} +{{- print "false" -}} +{{- else -}} +{{- print "true" -}} +{{- end -}} +{{- end -}} + +{{/* +Return true if cert-manager required annotations for TLS signed +certificates are set in the Ingress annotations +Ref: https://cert-manager.io/docs/usage/ingress/#supported-annotations +Usage: +{{ include "common.ingress.certManagerRequest" ( dict "annotations" .Values.path.to.the.ingress.annotations ) }} +*/}} +{{- define "common.ingress.certManagerRequest" -}} +{{ if or (hasKey .annotations "cert-manager.io/cluster-issuer") (hasKey .annotations "cert-manager.io/issuer") (hasKey .annotations "kubernetes.io/tls-acme") }} + {{- true -}} +{{- end -}} +{{- end -}} diff --git a/charts/deps/charts/mysql-9.7.2/charts/common/templates/_labels.tpl b/charts/deps/charts/mysql-9.7.2/charts/common/templates/_labels.tpl new file mode 100644 index 0000000..252066c --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/charts/common/templates/_labels.tpl @@ -0,0 +1,18 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Kubernetes standard labels +*/}} +{{- define "common.labels.standard" -}} +app.kubernetes.io/name: {{ include "common.names.name" . }} +helm.sh/chart: {{ include "common.names.chart" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end -}} + +{{/* +Labels to use on deploy.spec.selector.matchLabels and svc.spec.selector +*/}} +{{- define "common.labels.matchLabels" -}} +app.kubernetes.io/name: {{ include "common.names.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end -}} diff --git a/charts/deps/charts/mysql-9.7.2/charts/common/templates/_names.tpl b/charts/deps/charts/mysql-9.7.2/charts/common/templates/_names.tpl new file mode 100644 index 0000000..617a234 --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/charts/common/templates/_names.tpl @@ -0,0 +1,66 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "common.names.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "common.names.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | 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 "common.names.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 a default fully qualified dependency 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. +Usage: +{{ include "common.names.dependency.fullname" (dict "chartName" "dependency-chart-name" "chartValues" .Values.dependency-chart "context" $) }} +*/}} +{{- define "common.names.dependency.fullname" -}} +{{- if .chartValues.fullnameOverride -}} +{{- .chartValues.fullnameOverride | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- $name := default .chartName .chartValues.nameOverride -}} +{{- if contains $name .context.Release.Name -}} +{{- .context.Release.Name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s-%s" .context.Release.Name $name | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} +{{- end -}} + +{{/* +Allow the release namespace to be overridden for multi-namespace deployments in combined charts. +*/}} +{{- define "common.names.namespace" -}} +{{- default .Release.Namespace .Values.namespaceOverride | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Create a fully qualified app name adding the installation's namespace. +*/}} +{{- define "common.names.fullname.namespace" -}} +{{- printf "%s-%s" (include "common.names.fullname" .) (include "common.names.namespace" .) | trunc 63 | trimSuffix "-" -}} +{{- end -}} diff --git a/charts/deps/charts/mysql-9.7.2/charts/common/templates/_secrets.tpl b/charts/deps/charts/mysql-9.7.2/charts/common/templates/_secrets.tpl new file mode 100644 index 0000000..a1708b2 --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/charts/common/templates/_secrets.tpl @@ -0,0 +1,165 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Generate secret name. + +Usage: +{{ include "common.secrets.name" (dict "existingSecret" .Values.path.to.the.existingSecret "defaultNameSuffix" "mySuffix" "context" $) }} + +Params: + - existingSecret - ExistingSecret/String - Optional. The path to the existing secrets in the values.yaml given by the user + to be used instead of the default one. Allows for it to be of type String (just the secret name) for backwards compatibility. + +info: https://github.com/bitnami/charts/tree/main/bitnami/common#existingsecret + - defaultNameSuffix - String - Optional. It is used only if we have several secrets in the same deployment. + - context - Dict - Required. The context for the template evaluation. +*/}} +{{- define "common.secrets.name" -}} +{{- $name := (include "common.names.fullname" .context) -}} + +{{- if .defaultNameSuffix -}} +{{- $name = printf "%s-%s" $name .defaultNameSuffix | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{- with .existingSecret -}} +{{- if not (typeIs "string" .) -}} +{{- with .name -}} +{{- $name = . -}} +{{- end -}} +{{- else -}} +{{- $name = . -}} +{{- end -}} +{{- end -}} + +{{- printf "%s" $name -}} +{{- end -}} + +{{/* +Generate secret key. + +Usage: +{{ include "common.secrets.key" (dict "existingSecret" .Values.path.to.the.existingSecret "key" "keyName") }} + +Params: + - existingSecret - ExistingSecret/String - Optional. The path to the existing secrets in the values.yaml given by the user + to be used instead of the default one. Allows for it to be of type String (just the secret name) for backwards compatibility. + +info: https://github.com/bitnami/charts/tree/main/bitnami/common#existingsecret + - key - String - Required. Name of the key in the secret. +*/}} +{{- define "common.secrets.key" -}} +{{- $key := .key -}} + +{{- if .existingSecret -}} + {{- if not (typeIs "string" .existingSecret) -}} + {{- if .existingSecret.keyMapping -}} + {{- $key = index .existingSecret.keyMapping $.key -}} + {{- end -}} + {{- end }} +{{- end -}} + +{{- printf "%s" $key -}} +{{- end -}} + +{{/* +Generate secret password or retrieve one if already created. + +Usage: +{{ include "common.secrets.passwords.manage" (dict "secret" "secret-name" "key" "keyName" "providedValues" (list "path.to.password1" "path.to.password2") "length" 10 "strong" false "chartName" "chartName" "context" $) }} + +Params: + - secret - String - Required - Name of the 'Secret' resource where the password is stored. + - key - String - Required - Name of the key in the secret. + - providedValues - List - Required - The path to the validating value in the values.yaml, e.g: "mysql.password". Will pick first parameter with a defined value. + - length - int - Optional - Length of the generated random password. + - strong - Boolean - Optional - Whether to add symbols to the generated random password. + - chartName - String - Optional - Name of the chart used when said chart is deployed as a subchart. + - context - Context - Required - Parent context. + +The order in which this function returns a secret password: + 1. Already existing 'Secret' resource + (If a 'Secret' resource is found under the name provided to the 'secret' parameter to this function and that 'Secret' resource contains a key with the name passed as the 'key' parameter to this function then the value of this existing secret password will be returned) + 2. Password provided via the values.yaml + (If one of the keys passed to the 'providedValues' parameter to this function is a valid path to a key in the values.yaml and has a value, the value of the first key with a value will be returned) + 3. Randomly generated secret password + (A new random secret password with the length specified in the 'length' parameter will be generated and returned) + +*/}} +{{- define "common.secrets.passwords.manage" -}} + +{{- $password := "" }} +{{- $subchart := "" }} +{{- $chartName := default "" .chartName }} +{{- $passwordLength := default 10 .length }} +{{- $providedPasswordKey := include "common.utils.getKeyFromList" (dict "keys" .providedValues "context" $.context) }} +{{- $providedPasswordValue := include "common.utils.getValueFromKey" (dict "key" $providedPasswordKey "context" $.context) }} +{{- $secretData := (lookup "v1" "Secret" (include "common.names.namespace" .context) .secret).data }} +{{- if $secretData }} + {{- if hasKey $secretData .key }} + {{- $password = index $secretData .key | quote }} + {{- else }} + {{- printf "\nPASSWORDS ERROR: The secret \"%s\" does not contain the key \"%s\"\n" .secret .key | fail -}} + {{- end -}} +{{- else if $providedPasswordValue }} + {{- $password = $providedPasswordValue | toString | b64enc | quote }} +{{- else }} + + {{- if .context.Values.enabled }} + {{- $subchart = $chartName }} + {{- end -}} + + {{- $requiredPassword := dict "valueKey" $providedPasswordKey "secret" .secret "field" .key "subchart" $subchart "context" $.context -}} + {{- $requiredPasswordError := include "common.validations.values.single.empty" $requiredPassword -}} + {{- $passwordValidationErrors := list $requiredPasswordError -}} + {{- include "common.errors.upgrade.passwords.empty" (dict "validationErrors" $passwordValidationErrors "context" $.context) -}} + + {{- if .strong }} + {{- $subStr := list (lower (randAlpha 1)) (randNumeric 1) (upper (randAlpha 1)) | join "_" }} + {{- $password = randAscii $passwordLength }} + {{- $password = regexReplaceAllLiteral "\\W" $password "@" | substr 5 $passwordLength }} + {{- $password = printf "%s%s" $subStr $password | toString | shuffle | b64enc | quote }} + {{- else }} + {{- $password = randAlphaNum $passwordLength | b64enc | quote }} + {{- end }} +{{- end -}} +{{- printf "%s" $password -}} +{{- end -}} + +{{/* +Reuses the value from an existing secret, otherwise sets its value to a default value. + +Usage: +{{ include "common.secrets.lookup" (dict "secret" "secret-name" "key" "keyName" "defaultValue" .Values.myValue "context" $) }} + +Params: + - secret - String - Required - Name of the 'Secret' resource where the password is stored. + - key - String - Required - Name of the key in the secret. + - defaultValue - String - Required - The path to the validating value in the values.yaml, e.g: "mysql.password". Will pick first parameter with a defined value. + - context - Context - Required - Parent context. + +*/}} +{{- define "common.secrets.lookup" -}} +{{- $value := "" -}} +{{- $defaultValue := required "\n'common.secrets.lookup': Argument 'defaultValue' missing or empty" .defaultValue -}} +{{- $secretData := (lookup "v1" "Secret" (include "common.names.namespace" .context) .secret).data -}} +{{- if and $secretData (hasKey $secretData .key) -}} + {{- $value = index $secretData .key -}} +{{- else -}} + {{- $value = $defaultValue | toString | b64enc -}} +{{- end -}} +{{- printf "%s" $value -}} +{{- end -}} + +{{/* +Returns whether a previous generated secret already exists + +Usage: +{{ include "common.secrets.exists" (dict "secret" "secret-name" "context" $) }} + +Params: + - secret - String - Required - Name of the 'Secret' resource where the password is stored. + - context - Context - Required - Parent context. +*/}} +{{- define "common.secrets.exists" -}} +{{- $secret := (lookup "v1" "Secret" (include "common.names.namespace" .context) .secret) }} +{{- if $secret }} + {{- true -}} +{{- end -}} +{{- end -}} diff --git a/charts/deps/charts/mysql-9.7.2/charts/common/templates/_storage.tpl b/charts/deps/charts/mysql-9.7.2/charts/common/templates/_storage.tpl new file mode 100644 index 0000000..60e2a84 --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/charts/common/templates/_storage.tpl @@ -0,0 +1,23 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Return the proper Storage Class +{{ include "common.storage.class" ( dict "persistence" .Values.path.to.the.persistence "global" $) }} +*/}} +{{- define "common.storage.class" -}} + +{{- $storageClass := .persistence.storageClass -}} +{{- if .global -}} + {{- if .global.storageClass -}} + {{- $storageClass = .global.storageClass -}} + {{- end -}} +{{- end -}} + +{{- if $storageClass -}} + {{- if (eq "-" $storageClass) -}} + {{- printf "storageClassName: \"\"" -}} + {{- else }} + {{- printf "storageClassName: %s" $storageClass -}} + {{- end -}} +{{- end -}} + +{{- end -}} diff --git a/charts/deps/charts/mysql-9.7.2/charts/common/templates/_tplvalues.tpl b/charts/deps/charts/mysql-9.7.2/charts/common/templates/_tplvalues.tpl new file mode 100644 index 0000000..2db1668 --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/charts/common/templates/_tplvalues.tpl @@ -0,0 +1,13 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Renders a value that contains template. +Usage: +{{ include "common.tplvalues.render" ( dict "value" .Values.path.to.the.Value "context" $) }} +*/}} +{{- define "common.tplvalues.render" -}} + {{- if typeIs "string" .value }} + {{- tpl .value .context }} + {{- else }} + {{- tpl (.value | toYaml) .context }} + {{- end }} +{{- end -}} diff --git a/charts/deps/charts/mysql-9.7.2/charts/common/templates/_utils.tpl b/charts/deps/charts/mysql-9.7.2/charts/common/templates/_utils.tpl new file mode 100644 index 0000000..b1ead50 --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/charts/common/templates/_utils.tpl @@ -0,0 +1,62 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Print instructions to get a secret value. +Usage: +{{ include "common.utils.secret.getvalue" (dict "secret" "secret-name" "field" "secret-value-field" "context" $) }} +*/}} +{{- define "common.utils.secret.getvalue" -}} +{{- $varname := include "common.utils.fieldToEnvVar" . -}} +export {{ $varname }}=$(kubectl get secret --namespace {{ include "common.names.namespace" .context | quote }} {{ .secret }} -o jsonpath="{.data.{{ .field }}}" | base64 -d) +{{- end -}} + +{{/* +Build env var name given a field +Usage: +{{ include "common.utils.fieldToEnvVar" dict "field" "my-password" }} +*/}} +{{- define "common.utils.fieldToEnvVar" -}} + {{- $fieldNameSplit := splitList "-" .field -}} + {{- $upperCaseFieldNameSplit := list -}} + + {{- range $fieldNameSplit -}} + {{- $upperCaseFieldNameSplit = append $upperCaseFieldNameSplit ( upper . ) -}} + {{- end -}} + + {{ join "_" $upperCaseFieldNameSplit }} +{{- end -}} + +{{/* +Gets a value from .Values given +Usage: +{{ include "common.utils.getValueFromKey" (dict "key" "path.to.key" "context" $) }} +*/}} +{{- define "common.utils.getValueFromKey" -}} +{{- $splitKey := splitList "." .key -}} +{{- $value := "" -}} +{{- $latestObj := $.context.Values -}} +{{- range $splitKey -}} + {{- if not $latestObj -}} + {{- printf "please review the entire path of '%s' exists in values" $.key | fail -}} + {{- end -}} + {{- $value = ( index $latestObj . ) -}} + {{- $latestObj = $value -}} +{{- end -}} +{{- printf "%v" (default "" $value) -}} +{{- end -}} + +{{/* +Returns first .Values key with a defined value or first of the list if all non-defined +Usage: +{{ include "common.utils.getKeyFromList" (dict "keys" (list "path.to.key1" "path.to.key2") "context" $) }} +*/}} +{{- define "common.utils.getKeyFromList" -}} +{{- $key := first .keys -}} +{{- $reverseKeys := reverse .keys }} +{{- range $reverseKeys }} + {{- $value := include "common.utils.getValueFromKey" (dict "key" . "context" $.context ) }} + {{- if $value -}} + {{- $key = . }} + {{- end -}} +{{- end -}} +{{- printf "%s" $key -}} +{{- end -}} diff --git a/charts/deps/charts/mysql-9.7.2/charts/common/templates/_warnings.tpl b/charts/deps/charts/mysql-9.7.2/charts/common/templates/_warnings.tpl new file mode 100644 index 0000000..ae10fa4 --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/charts/common/templates/_warnings.tpl @@ -0,0 +1,14 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Warning about using rolling tag. +Usage: +{{ include "common.warnings.rollingTag" .Values.path.to.the.imageRoot }} +*/}} +{{- define "common.warnings.rollingTag" -}} + +{{- if and (contains "bitnami/" .repository) (not (.tag | toString | regexFind "-r\\d+$|sha256:")) }} +WARNING: Rolling tag detected ({{ .repository }}:{{ .tag }}), please note that it is strongly recommended to avoid using rolling tags in a production environment. ++info https://docs.bitnami.com/containers/how-to/understand-rolling-tags-containers/ +{{- end }} + +{{- end -}} diff --git a/charts/deps/charts/mysql-9.7.2/charts/common/templates/validations/_cassandra.tpl b/charts/deps/charts/mysql-9.7.2/charts/common/templates/validations/_cassandra.tpl new file mode 100644 index 0000000..ded1ae3 --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/charts/common/templates/validations/_cassandra.tpl @@ -0,0 +1,72 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Validate Cassandra required passwords are not empty. + +Usage: +{{ include "common.validations.values.cassandra.passwords" (dict "secret" "secretName" "subchart" false "context" $) }} +Params: + - secret - String - Required. Name of the secret where Cassandra values are stored, e.g: "cassandra-passwords-secret" + - subchart - Boolean - Optional. Whether Cassandra is used as subchart or not. Default: false +*/}} +{{- define "common.validations.values.cassandra.passwords" -}} + {{- $existingSecret := include "common.cassandra.values.existingSecret" . -}} + {{- $enabled := include "common.cassandra.values.enabled" . -}} + {{- $dbUserPrefix := include "common.cassandra.values.key.dbUser" . -}} + {{- $valueKeyPassword := printf "%s.password" $dbUserPrefix -}} + + {{- if and (or (not $existingSecret) (eq $existingSecret "\"\"")) (eq $enabled "true") -}} + {{- $requiredPasswords := list -}} + + {{- $requiredPassword := dict "valueKey" $valueKeyPassword "secret" .secret "field" "cassandra-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredPassword -}} + + {{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}} + + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for existingSecret. + +Usage: +{{ include "common.cassandra.values.existingSecret" (dict "context" $) }} +Params: + - subchart - Boolean - Optional. Whether Cassandra is used as subchart or not. Default: false +*/}} +{{- define "common.cassandra.values.existingSecret" -}} + {{- if .subchart -}} + {{- .context.Values.cassandra.dbUser.existingSecret | quote -}} + {{- else -}} + {{- .context.Values.dbUser.existingSecret | quote -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled cassandra. + +Usage: +{{ include "common.cassandra.values.enabled" (dict "context" $) }} +*/}} +{{- define "common.cassandra.values.enabled" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.cassandra.enabled -}} + {{- else -}} + {{- printf "%v" (not .context.Values.enabled) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for the key dbUser + +Usage: +{{ include "common.cassandra.values.key.dbUser" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether Cassandra is used as subchart or not. Default: false +*/}} +{{- define "common.cassandra.values.key.dbUser" -}} + {{- if .subchart -}} + cassandra.dbUser + {{- else -}} + dbUser + {{- end -}} +{{- end -}} diff --git a/charts/deps/charts/mysql-9.7.2/charts/common/templates/validations/_mariadb.tpl b/charts/deps/charts/mysql-9.7.2/charts/common/templates/validations/_mariadb.tpl new file mode 100644 index 0000000..b6906ff --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/charts/common/templates/validations/_mariadb.tpl @@ -0,0 +1,103 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Validate MariaDB required passwords are not empty. + +Usage: +{{ include "common.validations.values.mariadb.passwords" (dict "secret" "secretName" "subchart" false "context" $) }} +Params: + - secret - String - Required. Name of the secret where MariaDB values are stored, e.g: "mysql-passwords-secret" + - subchart - Boolean - Optional. Whether MariaDB is used as subchart or not. Default: false +*/}} +{{- define "common.validations.values.mariadb.passwords" -}} + {{- $existingSecret := include "common.mariadb.values.auth.existingSecret" . -}} + {{- $enabled := include "common.mariadb.values.enabled" . -}} + {{- $architecture := include "common.mariadb.values.architecture" . -}} + {{- $authPrefix := include "common.mariadb.values.key.auth" . -}} + {{- $valueKeyRootPassword := printf "%s.rootPassword" $authPrefix -}} + {{- $valueKeyUsername := printf "%s.username" $authPrefix -}} + {{- $valueKeyPassword := printf "%s.password" $authPrefix -}} + {{- $valueKeyReplicationPassword := printf "%s.replicationPassword" $authPrefix -}} + + {{- if and (or (not $existingSecret) (eq $existingSecret "\"\"")) (eq $enabled "true") -}} + {{- $requiredPasswords := list -}} + + {{- $requiredRootPassword := dict "valueKey" $valueKeyRootPassword "secret" .secret "field" "mariadb-root-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredRootPassword -}} + + {{- $valueUsername := include "common.utils.getValueFromKey" (dict "key" $valueKeyUsername "context" .context) }} + {{- if not (empty $valueUsername) -}} + {{- $requiredPassword := dict "valueKey" $valueKeyPassword "secret" .secret "field" "mariadb-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredPassword -}} + {{- end -}} + + {{- if (eq $architecture "replication") -}} + {{- $requiredReplicationPassword := dict "valueKey" $valueKeyReplicationPassword "secret" .secret "field" "mariadb-replication-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredReplicationPassword -}} + {{- end -}} + + {{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}} + + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for existingSecret. + +Usage: +{{ include "common.mariadb.values.auth.existingSecret" (dict "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MariaDB is used as subchart or not. Default: false +*/}} +{{- define "common.mariadb.values.auth.existingSecret" -}} + {{- if .subchart -}} + {{- .context.Values.mariadb.auth.existingSecret | quote -}} + {{- else -}} + {{- .context.Values.auth.existingSecret | quote -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled mariadb. + +Usage: +{{ include "common.mariadb.values.enabled" (dict "context" $) }} +*/}} +{{- define "common.mariadb.values.enabled" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.mariadb.enabled -}} + {{- else -}} + {{- printf "%v" (not .context.Values.enabled) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for architecture + +Usage: +{{ include "common.mariadb.values.architecture" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MariaDB is used as subchart or not. Default: false +*/}} +{{- define "common.mariadb.values.architecture" -}} + {{- if .subchart -}} + {{- .context.Values.mariadb.architecture -}} + {{- else -}} + {{- .context.Values.architecture -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for the key auth + +Usage: +{{ include "common.mariadb.values.key.auth" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MariaDB is used as subchart or not. Default: false +*/}} +{{- define "common.mariadb.values.key.auth" -}} + {{- if .subchart -}} + mariadb.auth + {{- else -}} + auth + {{- end -}} +{{- end -}} diff --git a/charts/deps/charts/mysql-9.7.2/charts/common/templates/validations/_mongodb.tpl b/charts/deps/charts/mysql-9.7.2/charts/common/templates/validations/_mongodb.tpl new file mode 100644 index 0000000..f820ec1 --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/charts/common/templates/validations/_mongodb.tpl @@ -0,0 +1,108 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Validate MongoDB® required passwords are not empty. + +Usage: +{{ include "common.validations.values.mongodb.passwords" (dict "secret" "secretName" "subchart" false "context" $) }} +Params: + - secret - String - Required. Name of the secret where MongoDB® values are stored, e.g: "mongodb-passwords-secret" + - subchart - Boolean - Optional. Whether MongoDB® is used as subchart or not. Default: false +*/}} +{{- define "common.validations.values.mongodb.passwords" -}} + {{- $existingSecret := include "common.mongodb.values.auth.existingSecret" . -}} + {{- $enabled := include "common.mongodb.values.enabled" . -}} + {{- $authPrefix := include "common.mongodb.values.key.auth" . -}} + {{- $architecture := include "common.mongodb.values.architecture" . -}} + {{- $valueKeyRootPassword := printf "%s.rootPassword" $authPrefix -}} + {{- $valueKeyUsername := printf "%s.username" $authPrefix -}} + {{- $valueKeyDatabase := printf "%s.database" $authPrefix -}} + {{- $valueKeyPassword := printf "%s.password" $authPrefix -}} + {{- $valueKeyReplicaSetKey := printf "%s.replicaSetKey" $authPrefix -}} + {{- $valueKeyAuthEnabled := printf "%s.enabled" $authPrefix -}} + + {{- $authEnabled := include "common.utils.getValueFromKey" (dict "key" $valueKeyAuthEnabled "context" .context) -}} + + {{- if and (or (not $existingSecret) (eq $existingSecret "\"\"")) (eq $enabled "true") (eq $authEnabled "true") -}} + {{- $requiredPasswords := list -}} + + {{- $requiredRootPassword := dict "valueKey" $valueKeyRootPassword "secret" .secret "field" "mongodb-root-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredRootPassword -}} + + {{- $valueUsername := include "common.utils.getValueFromKey" (dict "key" $valueKeyUsername "context" .context) }} + {{- $valueDatabase := include "common.utils.getValueFromKey" (dict "key" $valueKeyDatabase "context" .context) }} + {{- if and $valueUsername $valueDatabase -}} + {{- $requiredPassword := dict "valueKey" $valueKeyPassword "secret" .secret "field" "mongodb-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredPassword -}} + {{- end -}} + + {{- if (eq $architecture "replicaset") -}} + {{- $requiredReplicaSetKey := dict "valueKey" $valueKeyReplicaSetKey "secret" .secret "field" "mongodb-replica-set-key" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredReplicaSetKey -}} + {{- end -}} + + {{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}} + + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for existingSecret. + +Usage: +{{ include "common.mongodb.values.auth.existingSecret" (dict "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MongoDb is used as subchart or not. Default: false +*/}} +{{- define "common.mongodb.values.auth.existingSecret" -}} + {{- if .subchart -}} + {{- .context.Values.mongodb.auth.existingSecret | quote -}} + {{- else -}} + {{- .context.Values.auth.existingSecret | quote -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled mongodb. + +Usage: +{{ include "common.mongodb.values.enabled" (dict "context" $) }} +*/}} +{{- define "common.mongodb.values.enabled" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.mongodb.enabled -}} + {{- else -}} + {{- printf "%v" (not .context.Values.enabled) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for the key auth + +Usage: +{{ include "common.mongodb.values.key.auth" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MongoDB® is used as subchart or not. Default: false +*/}} +{{- define "common.mongodb.values.key.auth" -}} + {{- if .subchart -}} + mongodb.auth + {{- else -}} + auth + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for architecture + +Usage: +{{ include "common.mongodb.values.architecture" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MongoDB® is used as subchart or not. Default: false +*/}} +{{- define "common.mongodb.values.architecture" -}} + {{- if .subchart -}} + {{- .context.Values.mongodb.architecture -}} + {{- else -}} + {{- .context.Values.architecture -}} + {{- end -}} +{{- end -}} diff --git a/charts/deps/charts/mysql-9.7.2/charts/common/templates/validations/_mysql.tpl b/charts/deps/charts/mysql-9.7.2/charts/common/templates/validations/_mysql.tpl new file mode 100644 index 0000000..74472a0 --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/charts/common/templates/validations/_mysql.tpl @@ -0,0 +1,103 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Validate MySQL required passwords are not empty. + +Usage: +{{ include "common.validations.values.mysql.passwords" (dict "secret" "secretName" "subchart" false "context" $) }} +Params: + - secret - String - Required. Name of the secret where MySQL values are stored, e.g: "mysql-passwords-secret" + - subchart - Boolean - Optional. Whether MySQL is used as subchart or not. Default: false +*/}} +{{- define "common.validations.values.mysql.passwords" -}} + {{- $existingSecret := include "common.mysql.values.auth.existingSecret" . -}} + {{- $enabled := include "common.mysql.values.enabled" . -}} + {{- $architecture := include "common.mysql.values.architecture" . -}} + {{- $authPrefix := include "common.mysql.values.key.auth" . -}} + {{- $valueKeyRootPassword := printf "%s.rootPassword" $authPrefix -}} + {{- $valueKeyUsername := printf "%s.username" $authPrefix -}} + {{- $valueKeyPassword := printf "%s.password" $authPrefix -}} + {{- $valueKeyReplicationPassword := printf "%s.replicationPassword" $authPrefix -}} + + {{- if and (or (not $existingSecret) (eq $existingSecret "\"\"")) (eq $enabled "true") -}} + {{- $requiredPasswords := list -}} + + {{- $requiredRootPassword := dict "valueKey" $valueKeyRootPassword "secret" .secret "field" "mysql-root-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredRootPassword -}} + + {{- $valueUsername := include "common.utils.getValueFromKey" (dict "key" $valueKeyUsername "context" .context) }} + {{- if not (empty $valueUsername) -}} + {{- $requiredPassword := dict "valueKey" $valueKeyPassword "secret" .secret "field" "mysql-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredPassword -}} + {{- end -}} + + {{- if (eq $architecture "replication") -}} + {{- $requiredReplicationPassword := dict "valueKey" $valueKeyReplicationPassword "secret" .secret "field" "mysql-replication-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredReplicationPassword -}} + {{- end -}} + + {{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}} + + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for existingSecret. + +Usage: +{{ include "common.mysql.values.auth.existingSecret" (dict "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MySQL is used as subchart or not. Default: false +*/}} +{{- define "common.mysql.values.auth.existingSecret" -}} + {{- if .subchart -}} + {{- .context.Values.mysql.auth.existingSecret | quote -}} + {{- else -}} + {{- .context.Values.auth.existingSecret | quote -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled mysql. + +Usage: +{{ include "common.mysql.values.enabled" (dict "context" $) }} +*/}} +{{- define "common.mysql.values.enabled" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.mysql.enabled -}} + {{- else -}} + {{- printf "%v" (not .context.Values.enabled) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for architecture + +Usage: +{{ include "common.mysql.values.architecture" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MySQL is used as subchart or not. Default: false +*/}} +{{- define "common.mysql.values.architecture" -}} + {{- if .subchart -}} + {{- .context.Values.mysql.architecture -}} + {{- else -}} + {{- .context.Values.architecture -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for the key auth + +Usage: +{{ include "common.mysql.values.key.auth" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether MySQL is used as subchart or not. Default: false +*/}} +{{- define "common.mysql.values.key.auth" -}} + {{- if .subchart -}} + mysql.auth + {{- else -}} + auth + {{- end -}} +{{- end -}} diff --git a/charts/deps/charts/mysql-9.7.2/charts/common/templates/validations/_postgresql.tpl b/charts/deps/charts/mysql-9.7.2/charts/common/templates/validations/_postgresql.tpl new file mode 100644 index 0000000..164ec0d --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/charts/common/templates/validations/_postgresql.tpl @@ -0,0 +1,129 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Validate PostgreSQL required passwords are not empty. + +Usage: +{{ include "common.validations.values.postgresql.passwords" (dict "secret" "secretName" "subchart" false "context" $) }} +Params: + - secret - String - Required. Name of the secret where postgresql values are stored, e.g: "postgresql-passwords-secret" + - subchart - Boolean - Optional. Whether postgresql is used as subchart or not. Default: false +*/}} +{{- define "common.validations.values.postgresql.passwords" -}} + {{- $existingSecret := include "common.postgresql.values.existingSecret" . -}} + {{- $enabled := include "common.postgresql.values.enabled" . -}} + {{- $valueKeyPostgresqlPassword := include "common.postgresql.values.key.postgressPassword" . -}} + {{- $valueKeyPostgresqlReplicationEnabled := include "common.postgresql.values.key.replicationPassword" . -}} + {{- if and (or (not $existingSecret) (eq $existingSecret "\"\"")) (eq $enabled "true") -}} + {{- $requiredPasswords := list -}} + {{- $requiredPostgresqlPassword := dict "valueKey" $valueKeyPostgresqlPassword "secret" .secret "field" "postgresql-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredPostgresqlPassword -}} + + {{- $enabledReplication := include "common.postgresql.values.enabled.replication" . -}} + {{- if (eq $enabledReplication "true") -}} + {{- $requiredPostgresqlReplicationPassword := dict "valueKey" $valueKeyPostgresqlReplicationEnabled "secret" .secret "field" "postgresql-replication-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredPostgresqlReplicationPassword -}} + {{- end -}} + + {{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to decide whether evaluate global values. + +Usage: +{{ include "common.postgresql.values.use.global" (dict "key" "key-of-global" "context" $) }} +Params: + - key - String - Required. Field to be evaluated within global, e.g: "existingSecret" +*/}} +{{- define "common.postgresql.values.use.global" -}} + {{- if .context.Values.global -}} + {{- if .context.Values.global.postgresql -}} + {{- index .context.Values.global.postgresql .key | quote -}} + {{- end -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for existingSecret. + +Usage: +{{ include "common.postgresql.values.existingSecret" (dict "context" $) }} +*/}} +{{- define "common.postgresql.values.existingSecret" -}} + {{- $globalValue := include "common.postgresql.values.use.global" (dict "key" "existingSecret" "context" .context) -}} + + {{- if .subchart -}} + {{- default (.context.Values.postgresql.existingSecret | quote) $globalValue -}} + {{- else -}} + {{- default (.context.Values.existingSecret | quote) $globalValue -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled postgresql. + +Usage: +{{ include "common.postgresql.values.enabled" (dict "context" $) }} +*/}} +{{- define "common.postgresql.values.enabled" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.postgresql.enabled -}} + {{- else -}} + {{- printf "%v" (not .context.Values.enabled) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for the key postgressPassword. + +Usage: +{{ include "common.postgresql.values.key.postgressPassword" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether postgresql is used as subchart or not. Default: false +*/}} +{{- define "common.postgresql.values.key.postgressPassword" -}} + {{- $globalValue := include "common.postgresql.values.use.global" (dict "key" "postgresqlUsername" "context" .context) -}} + + {{- if not $globalValue -}} + {{- if .subchart -}} + postgresql.postgresqlPassword + {{- else -}} + postgresqlPassword + {{- end -}} + {{- else -}} + global.postgresql.postgresqlPassword + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled.replication. + +Usage: +{{ include "common.postgresql.values.enabled.replication" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether postgresql is used as subchart or not. Default: false +*/}} +{{- define "common.postgresql.values.enabled.replication" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.postgresql.replication.enabled -}} + {{- else -}} + {{- printf "%v" .context.Values.replication.enabled -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for the key replication.password. + +Usage: +{{ include "common.postgresql.values.key.replicationPassword" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether postgresql is used as subchart or not. Default: false +*/}} +{{- define "common.postgresql.values.key.replicationPassword" -}} + {{- if .subchart -}} + postgresql.replication.password + {{- else -}} + replication.password + {{- end -}} +{{- end -}} diff --git a/charts/deps/charts/mysql-9.7.2/charts/common/templates/validations/_redis.tpl b/charts/deps/charts/mysql-9.7.2/charts/common/templates/validations/_redis.tpl new file mode 100644 index 0000000..dcccfc1 --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/charts/common/templates/validations/_redis.tpl @@ -0,0 +1,76 @@ + +{{/* vim: set filetype=mustache: */}} +{{/* +Validate Redis® required passwords are not empty. + +Usage: +{{ include "common.validations.values.redis.passwords" (dict "secret" "secretName" "subchart" false "context" $) }} +Params: + - secret - String - Required. Name of the secret where redis values are stored, e.g: "redis-passwords-secret" + - subchart - Boolean - Optional. Whether redis is used as subchart or not. Default: false +*/}} +{{- define "common.validations.values.redis.passwords" -}} + {{- $enabled := include "common.redis.values.enabled" . -}} + {{- $valueKeyPrefix := include "common.redis.values.keys.prefix" . -}} + {{- $standarizedVersion := include "common.redis.values.standarized.version" . }} + + {{- $existingSecret := ternary (printf "%s%s" $valueKeyPrefix "auth.existingSecret") (printf "%s%s" $valueKeyPrefix "existingSecret") (eq $standarizedVersion "true") }} + {{- $existingSecretValue := include "common.utils.getValueFromKey" (dict "key" $existingSecret "context" .context) }} + + {{- $valueKeyRedisPassword := ternary (printf "%s%s" $valueKeyPrefix "auth.password") (printf "%s%s" $valueKeyPrefix "password") (eq $standarizedVersion "true") }} + {{- $valueKeyRedisUseAuth := ternary (printf "%s%s" $valueKeyPrefix "auth.enabled") (printf "%s%s" $valueKeyPrefix "usePassword") (eq $standarizedVersion "true") }} + + {{- if and (or (not $existingSecret) (eq $existingSecret "\"\"")) (eq $enabled "true") -}} + {{- $requiredPasswords := list -}} + + {{- $useAuth := include "common.utils.getValueFromKey" (dict "key" $valueKeyRedisUseAuth "context" .context) -}} + {{- if eq $useAuth "true" -}} + {{- $requiredRedisPassword := dict "valueKey" $valueKeyRedisPassword "secret" .secret "field" "redis-password" -}} + {{- $requiredPasswords = append $requiredPasswords $requiredRedisPassword -}} + {{- end -}} + + {{- include "common.validations.values.multiple.empty" (dict "required" $requiredPasswords "context" .context) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right value for enabled redis. + +Usage: +{{ include "common.redis.values.enabled" (dict "context" $) }} +*/}} +{{- define "common.redis.values.enabled" -}} + {{- if .subchart -}} + {{- printf "%v" .context.Values.redis.enabled -}} + {{- else -}} + {{- printf "%v" (not .context.Values.enabled) -}} + {{- end -}} +{{- end -}} + +{{/* +Auxiliary function to get the right prefix path for the values + +Usage: +{{ include "common.redis.values.key.prefix" (dict "subchart" "true" "context" $) }} +Params: + - subchart - Boolean - Optional. Whether redis is used as subchart or not. Default: false +*/}} +{{- define "common.redis.values.keys.prefix" -}} + {{- if .subchart -}}redis.{{- else -}}{{- end -}} +{{- end -}} + +{{/* +Checks whether the redis chart's includes the standarizations (version >= 14) + +Usage: +{{ include "common.redis.values.standarized.version" (dict "context" $) }} +*/}} +{{- define "common.redis.values.standarized.version" -}} + + {{- $standarizedAuth := printf "%s%s" (include "common.redis.values.keys.prefix" .) "auth" -}} + {{- $standarizedAuthValues := include "common.utils.getValueFromKey" (dict "key" $standarizedAuth "context" .context) }} + + {{- if $standarizedAuthValues -}} + {{- true -}} + {{- end -}} +{{- end -}} diff --git a/charts/deps/charts/mysql-9.7.2/charts/common/templates/validations/_validations.tpl b/charts/deps/charts/mysql-9.7.2/charts/common/templates/validations/_validations.tpl new file mode 100644 index 0000000..9a814cf --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/charts/common/templates/validations/_validations.tpl @@ -0,0 +1,46 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Validate values must not be empty. + +Usage: +{{- $validateValueConf00 := (dict "valueKey" "path.to.value" "secret" "secretName" "field" "password-00") -}} +{{- $validateValueConf01 := (dict "valueKey" "path.to.value" "secret" "secretName" "field" "password-01") -}} +{{ include "common.validations.values.empty" (dict "required" (list $validateValueConf00 $validateValueConf01) "context" $) }} + +Validate value params: + - valueKey - String - Required. The path to the validating value in the values.yaml, e.g: "mysql.password" + - secret - String - Optional. Name of the secret where the validating value is generated/stored, e.g: "mysql-passwords-secret" + - field - String - Optional. Name of the field in the secret data, e.g: "mysql-password" +*/}} +{{- define "common.validations.values.multiple.empty" -}} + {{- range .required -}} + {{- include "common.validations.values.single.empty" (dict "valueKey" .valueKey "secret" .secret "field" .field "context" $.context) -}} + {{- end -}} +{{- end -}} + +{{/* +Validate a value must not be empty. + +Usage: +{{ include "common.validations.value.empty" (dict "valueKey" "mariadb.password" "secret" "secretName" "field" "my-password" "subchart" "subchart" "context" $) }} + +Validate value params: + - valueKey - String - Required. The path to the validating value in the values.yaml, e.g: "mysql.password" + - secret - String - Optional. Name of the secret where the validating value is generated/stored, e.g: "mysql-passwords-secret" + - field - String - Optional. Name of the field in the secret data, e.g: "mysql-password" + - subchart - String - Optional - Name of the subchart that the validated password is part of. +*/}} +{{- define "common.validations.values.single.empty" -}} + {{- $value := include "common.utils.getValueFromKey" (dict "key" .valueKey "context" .context) }} + {{- $subchart := ternary "" (printf "%s." .subchart) (empty .subchart) }} + + {{- if not $value -}} + {{- $varname := "my-value" -}} + {{- $getCurrentValue := "" -}} + {{- if and .secret .field -}} + {{- $varname = include "common.utils.fieldToEnvVar" . -}} + {{- $getCurrentValue = printf " To get the current value:\n\n %s\n" (include "common.utils.secret.getvalue" .) -}} + {{- end -}} + {{- printf "\n '%s' must not be empty, please add '--set %s%s=$%s' to the command.%s" .valueKey $subchart .valueKey $varname $getCurrentValue -}} + {{- end -}} +{{- end -}} diff --git a/charts/deps/charts/mysql-9.7.2/charts/common/values.yaml b/charts/deps/charts/mysql-9.7.2/charts/common/values.yaml new file mode 100644 index 0000000..f2df68e --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/charts/common/values.yaml @@ -0,0 +1,5 @@ +## bitnami/common +## It is required by CI/CD tools and processes. +## @skip exampleValue +## +exampleValue: common-chart diff --git a/charts/deps/charts/mysql-9.7.2/mysql-9.7.2.tar b/charts/deps/charts/mysql-9.7.2/mysql-9.7.2.tar new file mode 100644 index 0000000000000000000000000000000000000000..14c483e3747f9e7bdae359904743917faba83a71 GIT binary patch literal 305664 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

!m7&|4aWr1OA_9+zaa0b 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 UIQ#VLCklL`z$Xg)n^WNb2MhR_ -- bash + +In order to replicate the container startup scripts execute this command: + + /opt/bitnami/scripts/mysql/entrypoint.sh /opt/bitnami/scripts/mysql/run.sh + +{{- else }} + +Tip: + + Watch the deployment status using the command: kubectl get pods -w --namespace {{ include "common.names.namespace" . }} + +Services: + + echo Primary: {{ include "mysql.primary.fullname" . }}.{{ include "common.names.namespace" . }}.svc.{{ .Values.clusterDomain }}:{{ .Values.primary.service.ports.mysql }} +{{- if eq .Values.architecture "replication" }} + echo Secondary: {{ include "mysql.secondary.fullname" . }}.{{ include "common.names.namespace" . }}.svc.{{ .Values.clusterDomain }}:{{ .Values.secondary.service.ports.mysql }} +{{- end }} + +Execute the following to get the administrator credentials: + + echo Username: root + MYSQL_ROOT_PASSWORD=$(kubectl get secret --namespace {{ include "common.names.namespace" . }} {{ template "mysql.secretName" . }} -o jsonpath="{.data.mysql-root-password}" | base64 -d) + +To connect to your database: + + 1. Run a pod that you can use as a client: + + kubectl run {{ include "common.names.fullname" . }}-client --rm --tty -i --restart='Never' --image {{ template "mysql.image" . }} --namespace {{ include "common.names.namespace" . }} --env MYSQL_ROOT_PASSWORD=$MYSQL_ROOT_PASSWORD --command -- bash + + 2. To connect to primary service (read/write): + + mysql -h {{ include "mysql.primary.fullname" . }}.{{ include "common.names.namespace" . }}.svc.{{ .Values.clusterDomain }} -uroot -p"$MYSQL_ROOT_PASSWORD" + +{{- if eq .Values.architecture "replication" }} + + 3. To connect to secondary service (read-only): + + mysql -h {{ include "mysql.secondary.fullname" . }}.{{ include "common.names.namespace" . }}.svc.{{ .Values.clusterDomain }} -uroot -p"$MYSQL_ROOT_PASSWORD" +{{- end }} + +{{ if and (.Values.networkPolicy.enabled) (not .Values.networkPolicy.allowExternal) }} +Note: Since NetworkPolicy is enabled, only pods with label {{ template "common.names.fullname" . }}-client=true" will be able to connect to MySQL. +{{- end }} + +{{- if .Values.metrics.enabled }} + +To access the MySQL Prometheus metrics from outside the cluster execute the following commands: + + kubectl port-forward --namespace {{ include "common.names.namespace" . }} svc/{{ printf "%s-metrics" (include "common.names.fullname" .) }} {{ .Values.metrics.service.port }}:{{ .Values.metrics.service.port }} & + curl http://127.0.0.1:{{ .Values.metrics.service.port }}/metrics + +{{- end }} + +{{ include "mysql.validateValues" . }} +{{ include "mysql.checkRollingTags" . }} +{{- end }} diff --git a/charts/deps/charts/mysql-9.7.2/templates/_helpers.tpl b/charts/deps/charts/mysql-9.7.2/templates/_helpers.tpl new file mode 100644 index 0000000..322826f --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/templates/_helpers.tpl @@ -0,0 +1,161 @@ +{{/* vim: set filetype=mustache: */}} + +{{- define "mysql.primary.fullname" -}} +{{- if eq .Values.architecture "replication" }} +{{- printf "%s-%s" (include "common.names.fullname" .) .Values.primary.name | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- include "common.names.fullname" . -}} +{{- end -}} +{{- end -}} + +{{- define "mysql.secondary.fullname" -}} +{{- printf "%s-%s" (include "common.names.fullname" .) .Values.secondary.name | trunc 63 | trimSuffix "-" -}} +{{- end -}} + +{{/* +Return the proper MySQL image name +*/}} +{{- define "mysql.image" -}} +{{- include "common.images.image" (dict "imageRoot" .Values.image "global" .Values.global) }} +{{- end -}} + +{{/* +Return the proper metrics image name +*/}} +{{- define "mysql.metrics.image" -}} +{{- include "common.images.image" (dict "imageRoot" .Values.metrics.image "global" .Values.global) }} +{{- end -}} + +{{/* +Return the proper image name (for the init container volume-permissions image) +*/}} +{{- define "mysql.volumePermissions.image" -}} +{{- include "common.images.image" (dict "imageRoot" .Values.volumePermissions.image "global" .Values.global) }} +{{- end -}} + +{{/* +Return the proper Docker Image Registry Secret Names +*/}} +{{- define "mysql.imagePullSecrets" -}} +{{- include "common.images.pullSecrets" (dict "images" (list .Values.image .Values.metrics.image .Values.volumePermissions.image) "global" .Values.global) }} +{{- end -}} + +{{/* +Get the initialization scripts ConfigMap name. +*/}} +{{- define "mysql.initdbScriptsCM" -}} +{{- if .Values.initdbScriptsConfigMap -}} + {{- printf "%s" (tpl .Values.initdbScriptsConfigMap $) -}} +{{- else -}} + {{- printf "%s-init-scripts" (include "mysql.primary.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* + Returns the proper service account name depending if an explicit service account name is set + in the values file. If the name is not set it will default to either mysql.fullname if serviceAccount.create + is true or default otherwise. +*/}} +{{- define "mysql.serviceAccountName" -}} + {{- if .Values.serviceAccount.create -}} + {{ default (include "common.names.fullname" .) .Values.serviceAccount.name }} + {{- else -}} + {{ default "default" .Values.serviceAccount.name }} + {{- end -}} +{{- end -}} + +{{/* +Return the configmap with the MySQL Primary configuration +*/}} +{{- define "mysql.primary.configmapName" -}} +{{- if .Values.primary.existingConfigmap -}} + {{- printf "%s" (tpl .Values.primary.existingConfigmap $) -}} +{{- else -}} + {{- printf "%s" (include "mysql.primary.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Return true if a configmap object should be created for MySQL Secondary +*/}} +{{- define "mysql.primary.createConfigmap" -}} +{{- if and .Values.primary.configuration (not .Values.primary.existingConfigmap) }} + {{- true -}} +{{- else -}} +{{- end -}} +{{- end -}} + +{{/* +Return the configmap with the MySQL Primary configuration +*/}} +{{- define "mysql.secondary.configmapName" -}} +{{- if .Values.secondary.existingConfigmap -}} + {{- printf "%s" (tpl .Values.secondary.existingConfigmap $) -}} +{{- else -}} + {{- printf "%s" (include "mysql.secondary.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Return true if a configmap object should be created for MySQL Secondary +*/}} +{{- define "mysql.secondary.createConfigmap" -}} +{{- if and (eq .Values.architecture "replication") .Values.secondary.configuration (not .Values.secondary.existingConfigmap) }} + {{- true -}} +{{- else -}} +{{- end -}} +{{- end -}} + +{{/* +Return the secret with MySQL credentials +*/}} +{{- define "mysql.secretName" -}} + {{- if .Values.auth.existingSecret -}} + {{- printf "%s" (tpl .Values.auth.existingSecret $) -}} + {{- else -}} + {{- printf "%s" (include "common.names.fullname" .) -}} + {{- end -}} +{{- end -}} + +{{/* +Return true if a secret object should be created for MySQL +*/}} +{{- define "mysql.createSecret" -}} +{{- if and (not .Values.auth.existingSecret) (not .Values.auth.customPasswordFiles) }} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Returns the available value for certain key in an existing secret (if it exists), +otherwise it generates a random value. +*/}} +{{- define "getValueFromSecret" }} + {{- $len := (default 16 .Length) | int -}} + {{- $obj := (lookup "v1" "Secret" .Namespace .Name).data -}} + {{- if $obj }} + {{- index $obj .Key | b64dec -}} + {{- else -}} + {{- randAlphaNum $len -}} + {{- end -}} +{{- end }} + +{{/* Check if there are rolling tags in the images */}} +{{- define "mysql.checkRollingTags" -}} +{{- include "common.warnings.rollingTag" .Values.image }} +{{- include "common.warnings.rollingTag" .Values.metrics.image }} +{{- include "common.warnings.rollingTag" .Values.volumePermissions.image }} +{{- end -}} + +{{/* +Compile all warnings into a single message, and call fail. +*/}} +{{- define "mysql.validateValues" -}} +{{- $messages := list -}} +{{- $messages := without $messages "" -}} +{{- $message := join "\n" $messages -}} + +{{- if $message -}} +{{- printf "\nVALUES VALIDATION:\n%s" $message | fail -}} +{{- end -}} +{{- end -}} diff --git a/charts/deps/charts/mysql-9.7.2/templates/extra-list.yaml b/charts/deps/charts/mysql-9.7.2/templates/extra-list.yaml new file mode 100644 index 0000000..9ac65f9 --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/templates/extra-list.yaml @@ -0,0 +1,4 @@ +{{- range .Values.extraDeploy }} +--- +{{ include "common.tplvalues.render" (dict "value" . "context" $) }} +{{- end }} diff --git a/charts/deps/charts/mysql-9.7.2/templates/metrics-svc.yaml b/charts/deps/charts/mysql-9.7.2/templates/metrics-svc.yaml new file mode 100644 index 0000000..4d3339b --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/templates/metrics-svc.yaml @@ -0,0 +1,29 @@ +{{- if .Values.metrics.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ printf "%s-metrics" (include "common.names.fullname" .) }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + app.kubernetes.io/component: metrics + {{- if or .Values.metrics.service.annotations .Values.commonAnnotations }} + annotations: + {{- if .Values.metrics.service.annotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.metrics.service.annotations "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- end }} +spec: + type: {{ .Values.metrics.service.type }} + ports: + - port: {{ .Values.metrics.service.port }} + targetPort: metrics + protocol: TCP + name: metrics + selector: {{- include "common.labels.matchLabels" $ | nindent 4 }} +{{- end }} diff --git a/charts/deps/charts/mysql-9.7.2/templates/networkpolicy.yaml b/charts/deps/charts/mysql-9.7.2/templates/networkpolicy.yaml new file mode 100644 index 0000000..6b62bb5 --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/templates/networkpolicy.yaml @@ -0,0 +1,40 @@ +{{- if .Values.networkPolicy.enabled }} +kind: NetworkPolicy +apiVersion: {{ template "common.capabilities.networkPolicy.apiVersion" . }} +metadata: + name: {{ template "common.names.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + podSelector: + matchLabels: + {{- include "common.labels.matchLabels" . | nindent 6 }} + ingress: + # Allow inbound connections + - ports: + - port: {{ .Values.primary.service.ports.mysql }} + {{- if not .Values.networkPolicy.allowExternal }} + from: + - podSelector: + matchLabels: + {{ template "common.names.fullname" . }}-client: "true" + {{- if .Values.networkPolicy.explicitNamespacesSelector }} + namespaceSelector: +{{ toYaml .Values.networkPolicy.explicitNamespacesSelector | indent 12 }} + {{- end }} + - podSelector: + matchLabels: + {{- include "common.labels.matchLabels" . | nindent 14 }} + {{- end }} + {{- if .Values.metrics.enabled }} + # Allow prometheus scrapes + - ports: + - port: 9104 + {{- end }} +{{- end }} diff --git a/charts/deps/charts/mysql-9.7.2/templates/primary/configmap.yaml b/charts/deps/charts/mysql-9.7.2/templates/primary/configmap.yaml new file mode 100644 index 0000000..82d0774 --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/templates/primary/configmap.yaml @@ -0,0 +1,18 @@ +{{- if (include "mysql.primary.createConfigmap" .) }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "mysql.primary.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: primary + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +data: + my.cnf: |- + {{- include "common.tplvalues.render" ( dict "value" .Values.primary.configuration "context" $ ) | nindent 4 }} +{{- end -}} diff --git a/charts/deps/charts/mysql-9.7.2/templates/primary/initialization-configmap.yaml b/charts/deps/charts/mysql-9.7.2/templates/primary/initialization-configmap.yaml new file mode 100644 index 0000000..a34f80d --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/templates/primary/initialization-configmap.yaml @@ -0,0 +1,17 @@ +{{- if and .Values.initdbScripts (not .Values.initdbScriptsConfigMap) }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ printf "%s-init-scripts" (include "mysql.primary.fullname" .) }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: primary + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +data: +{{- include "common.tplvalues.render" (dict "value" .Values.initdbScripts "context" .) | nindent 2 }} +{{- end }} diff --git a/charts/deps/charts/mysql-9.7.2/templates/primary/pdb.yaml b/charts/deps/charts/mysql-9.7.2/templates/primary/pdb.yaml new file mode 100644 index 0000000..ca22a0e --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/templates/primary/pdb.yaml @@ -0,0 +1,25 @@ +{{- if .Values.primary.pdb.create }} +apiVersion: {{ include "common.capabilities.policy.apiVersion" . }} +kind: PodDisruptionBudget +metadata: + name: {{ include "mysql.primary.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: primary + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + {{- if .Values.primary.pdb.minAvailable }} + minAvailable: {{ .Values.primary.pdb.minAvailable }} + {{- end }} + {{- if .Values.primary.pdb.maxUnavailable }} + maxUnavailable: {{ .Values.primary.pdb.maxUnavailable }} + {{- end }} + selector: + matchLabels: {{ include "common.labels.matchLabels" . | nindent 6 }} + app.kubernetes.io/component: primary +{{- end }} diff --git a/charts/deps/charts/mysql-9.7.2/templates/primary/statefulset.yaml b/charts/deps/charts/mysql-9.7.2/templates/primary/statefulset.yaml new file mode 100644 index 0000000..ac20482 --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/templates/primary/statefulset.yaml @@ -0,0 +1,388 @@ +apiVersion: {{ include "common.capabilities.statefulset.apiVersion" . }} +kind: StatefulSet +metadata: + name: {{ include "mysql.primary.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: primary + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + replicas: 1 + podManagementPolicy: {{ .Values.primary.podManagementPolicy | quote }} + selector: + matchLabels: {{ include "common.labels.matchLabels" . | nindent 6 }} + app.kubernetes.io/component: primary + serviceName: {{ include "mysql.primary.fullname" . }} + {{- if .Values.primary.updateStrategy }} + updateStrategy: {{- toYaml .Values.primary.updateStrategy | nindent 4 }} + {{- end }} + template: + metadata: + annotations: + {{- if (include "mysql.primary.createConfigmap" .) }} + checksum/configuration: {{ include (print $.Template.BasePath "/primary/configmap.yaml") . | sha256sum }} + {{- end }} + {{- if .Values.primary.podAnnotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.primary.podAnnotations "context" $) | nindent 8 }} + {{- end }} + labels: {{- include "common.labels.standard" . | nindent 8 }} + app.kubernetes.io/component: primary + {{- if .Values.primary.podLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.primary.podLabels "context" $ ) | nindent 8 }} + {{- end }} + spec: + serviceAccountName: {{ template "mysql.serviceAccountName" . }} + {{- include "mysql.imagePullSecrets" . | nindent 6 }} + {{- if .Values.primary.hostAliases }} + hostAliases: {{- include "common.tplvalues.render" (dict "value" .Values.primary.hostAliases "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.primary.affinity }} + affinity: {{- include "common.tplvalues.render" (dict "value" .Values.primary.affinity "context" $) | nindent 8 }} + {{- else }} + affinity: + podAffinity: {{- include "common.affinities.pods" (dict "type" .Values.primary.podAffinityPreset "context" $) | nindent 10 }} + podAntiAffinity: {{- include "common.affinities.pods" (dict "type" .Values.primary.podAntiAffinityPreset "context" $) | nindent 10 }} + nodeAffinity: {{- include "common.affinities.nodes" (dict "type" .Values.primary.nodeAffinityPreset.type "key" .Values.primary.nodeAffinityPreset.key "values" .Values.primary.nodeAffinityPreset.values) | nindent 10 }} + {{- end }} + {{- if .Values.primary.nodeSelector }} + nodeSelector: {{- include "common.tplvalues.render" (dict "value" .Values.primary.nodeSelector "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.primary.tolerations }} + tolerations: {{- include "common.tplvalues.render" (dict "value" .Values.primary.tolerations "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.primary.priorityClassName }} + priorityClassName: {{ .Values.primary.priorityClassName | quote }} + {{- end }} + {{- if .Values.primary.runtimeClassName }} + runtimeClassName: {{ .Values.primary.runtimeClassName | quote }} + {{- end }} + {{- if .Values.primary.schedulerName }} + schedulerName: {{ .Values.primary.schedulerName | quote }} + {{- end }} + {{- if .Values.primary.topologySpreadConstraints }} + topologySpreadConstraints: {{- include "common.tplvalues.render" (dict "value" .Values.primary.topologySpreadConstraints "context" .) | nindent 8 }} + {{- end }} + {{- if .Values.primary.podSecurityContext.enabled }} + securityContext: {{- omit .Values.primary.podSecurityContext "enabled" | toYaml | nindent 8 }} + {{- end }} + {{- if .Values.primary.terminationGracePeriodSeconds }} + terminationGracePeriodSeconds: {{ .Values.primary.terminationGracePeriodSeconds }} + {{- end }} + initContainers: + {{- if and .Values.primary.podSecurityContext.enabled .Values.volumePermissions.enabled .Values.primary.persistence.enabled }} + - name: volume-permissions + image: {{ include "mysql.volumePermissions.image" . }} + imagePullPolicy: {{ .Values.volumePermissions.image.pullPolicy | quote }} + command: + - /bin/bash + - -ec + - | + mkdir -p "/bitnami/mysql" + chown "{{ .Values.primary.containerSecurityContext.runAsUser }}:{{ .Values.primary.podSecurityContext.fsGroup }}" "/bitnami/mysql" + find "/bitnami/mysql" -mindepth 1 -maxdepth 1 -not -name ".snapshot" -not -name "lost+found" | xargs -r chown -R "{{ .Values.primary.containerSecurityContext.runAsUser }}:{{ .Values.primary.podSecurityContext.fsGroup }}" + securityContext: + runAsUser: 0 + {{- if .Values.volumePermissions.resources }} + resources: {{- toYaml .Values.volumePermissions.resources | nindent 12 }} + {{- end }} + volumeMounts: + - name: data + mountPath: /bitnami/mysql + {{- if .Values.primary.persistence.subPath }} + subPath: {{ .Values.primary.persistence.subPath }} + {{- end }} + {{- end }} + {{- if .Values.primary.initContainers }} + {{- include "common.tplvalues.render" (dict "value" .Values.primary.initContainers "context" $) | nindent 8 }} + {{- end }} + containers: + - name: mysql + image: {{ include "mysql.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy | quote }} + {{- if .Values.primary.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.primary.containerSecurityContext "enabled" | toYaml | nindent 12 }} + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 12 }} + {{- else if .Values.primary.command }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.primary.command "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 12 }} + {{- else if .Values.primary.args }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.primary.args "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.primary.lifecycleHooks }} + lifecycle: {{- include "common.tplvalues.render" (dict "value" .Values.primary.lifecycleHooks "context" $) | nindent 12 }} + {{- end }} + env: + - name: BITNAMI_DEBUG + value: {{ ternary "true" "false" (or .Values.image.debug .Values.diagnosticMode.enabled) | quote }} + {{- if .Values.auth.usePasswordFiles }} + - name: MYSQL_ROOT_PASSWORD_FILE + value: {{ default "/opt/bitnami/mysql/secrets/mysql-root-password" .Values.auth.customPasswordFiles.root }} + {{- else }} + - name: MYSQL_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "mysql.secretName" . }} + key: mysql-root-password + {{- end }} + {{- if not (empty .Values.auth.username) }} + - name: MYSQL_USER + value: {{ .Values.auth.username | quote }} + {{- if .Values.auth.usePasswordFiles }} + - name: MYSQL_PASSWORD_FILE + value: {{ default "/opt/bitnami/mysql/secrets/mysql-password" .Values.auth.customPasswordFiles.user }} + {{- else }} + - name: MYSQL_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "mysql.secretName" . }} + key: mysql-password + {{- end }} + {{- end }} + {{- if and .Values.auth.createDatabase .Values.auth.database }} + - name: MYSQL_DATABASE + value: {{ .Values.auth.database | quote }} + {{- end }} + {{- if eq .Values.architecture "replication" }} + - name: MYSQL_REPLICATION_MODE + value: "master" + - name: MYSQL_REPLICATION_USER + value: {{ .Values.auth.replicationUser | quote }} + {{- if .Values.auth.usePasswordFiles }} + - name: MYSQL_REPLICATION_PASSWORD_FILE + value: {{ default "/opt/bitnami/mysql/secrets/mysql-replication-password" .Values.auth.customPasswordFiles.replicator }} + {{- else }} + - name: MYSQL_REPLICATION_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "mysql.secretName" . }} + key: mysql-replication-password + {{- end }} + {{- end }} + {{- if .Values.primary.extraFlags }} + - name: MYSQL_EXTRA_FLAGS + value: "{{ .Values.primary.extraFlags }}" + {{- end }} + {{- if .Values.primary.extraEnvVars }} + {{- include "common.tplvalues.render" (dict "value" .Values.primary.extraEnvVars "context" $) | nindent 12 }} + {{- end }} + envFrom: + {{- if .Values.primary.extraEnvVarsCM }} + - configMapRef: + name: {{ include "common.tplvalues.render" (dict "value" .Values.primary.extraEnvVarsCM "context" $) }} + {{- end }} + {{- if .Values.primary.extraEnvVarsSecret }} + - secretRef: + name: {{ include "common.tplvalues.render" (dict "value" .Values.primary.extraEnvVarsSecret "context" $) }} + {{- end }} + ports: + - name: mysql + containerPort: 3306 + {{- if .Values.primary.extraPorts }} + {{- include "common.tplvalues.render" (dict "value" .Values.primary.extraPorts "context" $) | nindent 12 }} + {{- end }} + {{- if not .Values.diagnosticMode.enabled }} + {{- if .Values.primary.customLivenessProbe }} + livenessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.primary.customLivenessProbe "context" $) | nindent 12 }} + {{- else if .Values.primary.livenessProbe.enabled }} + livenessProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.primary.livenessProbe "enabled") "context" $) | nindent 12 }} + exec: + command: + - /bin/bash + - -ec + - | + password_aux="${MYSQL_ROOT_PASSWORD:-}" + if [[ -f "${MYSQL_ROOT_PASSWORD_FILE:-}" ]]; then + password_aux=$(cat "$MYSQL_ROOT_PASSWORD_FILE") + fi + mysqladmin status -uroot -p"${password_aux}" + {{- end }} + {{- if .Values.primary.customReadinessProbe }} + readinessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.primary.customReadinessProbe "context" $) | nindent 12 }} + {{- else if .Values.primary.readinessProbe.enabled }} + readinessProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.primary.readinessProbe "enabled") "context" $) | nindent 12 }} + exec: + command: + - /bin/bash + - -ec + - | + password_aux="${MYSQL_ROOT_PASSWORD:-}" + if [[ -f "${MYSQL_ROOT_PASSWORD_FILE:-}" ]]; then + password_aux=$(cat "$MYSQL_ROOT_PASSWORD_FILE") + fi + mysqladmin status -uroot -p"${password_aux}" + {{- end }} + {{- if .Values.primary.customStartupProbe }} + startupProbe: {{- include "common.tplvalues.render" (dict "value" .Values.primary.customStartupProbe "context" $) | nindent 12 }} + {{- else if .Values.primary.startupProbe.enabled }} + startupProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.primary.startupProbe "enabled") "context" $) | nindent 12 }} + exec: + command: + - /bin/bash + - -ec + - | + password_aux="${MYSQL_ROOT_PASSWORD:-}" + if [[ -f "${MYSQL_ROOT_PASSWORD_FILE:-}" ]]; then + password_aux=$(cat "$MYSQL_ROOT_PASSWORD_FILE") + fi + mysqladmin status -uroot -p"${password_aux}" + {{- end }} + {{- end }} + {{- if .Values.primary.resources }} + resources: {{ toYaml .Values.primary.resources | nindent 12 }} + {{- end }} + volumeMounts: + - name: data + mountPath: /bitnami/mysql + {{- if .Values.primary.persistence.subPath }} + subPath: {{ .Values.primary.persistence.subPath }} + {{- end }} + {{- if or .Values.initdbScriptsConfigMap .Values.initdbScripts }} + - name: custom-init-scripts + mountPath: /docker-entrypoint-initdb.d + {{- end }} + {{- if or .Values.primary.configuration .Values.primary.existingConfigmap }} + - name: config + mountPath: /opt/bitnami/mysql/conf/my.cnf + subPath: my.cnf + {{- end }} + {{- if and .Values.auth.usePasswordFiles (not .Values.auth.customPasswordFiles) }} + - name: mysql-credentials + mountPath: /opt/bitnami/mysql/secrets/ + {{- end }} + {{- if .Values.primary.extraVolumeMounts }} + {{- include "common.tplvalues.render" (dict "value" .Values.primary.extraVolumeMounts "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.metrics.enabled }} + - name: metrics + image: {{ include "mysql.metrics.image" . }} + imagePullPolicy: {{ .Values.metrics.image.pullPolicy | quote }} + {{- if .Values.metrics.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.metrics.containerSecurityContext "enabled" | toYaml | nindent 12 }} + {{- end }} + env: + {{- if .Values.auth.usePasswordFiles }} + - name: MYSQL_ROOT_PASSWORD_FILE + value: {{ default "/opt/bitnami/mysqld-exporter/secrets/mysql-root-password" .Values.auth.customPasswordFiles.root }} + {{- else }} + - name: MYSQL_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: {{ include "mysql.secretName" . }} + key: mysql-root-password + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 12 }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 12 }} + {{- else }} + command: + - /bin/bash + - -ec + - | + password_aux="${MYSQL_ROOT_PASSWORD:-}" + if [[ -f "${MYSQL_ROOT_PASSWORD_FILE:-}" ]]; then + password_aux=$(cat "$MYSQL_ROOT_PASSWORD_FILE") + fi + DATA_SOURCE_NAME="root:${password_aux}@(localhost:3306)/" /bin/mysqld_exporter {{- range .Values.metrics.extraArgs.primary }} {{ . }} {{- end }} + {{- end }} + ports: + - name: metrics + containerPort: 9104 + {{- if not .Values.diagnosticMode.enabled }} + {{- if .Values.metrics.livenessProbe.enabled }} + livenessProbe: {{- omit .Values.metrics.livenessProbe "enabled" | toYaml | nindent 12 }} + httpGet: + path: /metrics + port: metrics + {{- end }} + {{- if .Values.metrics.readinessProbe.enabled }} + readinessProbe: {{- omit .Values.metrics.readinessProbe "enabled" | toYaml | nindent 12 }} + httpGet: + path: /metrics + port: metrics + {{- end }} + {{- end }} + {{- if .Values.metrics.resources }} + resources: {{- toYaml .Values.metrics.resources | nindent 12 }} + {{- end }} + {{- if and .Values.auth.usePasswordFiles (not .Values.auth.customPasswordFiles) }} + volumeMounts: + - name: mysql-credentials + mountPath: /opt/bitnami/mysqld-exporter/secrets/ + {{- end }} + {{- end }} + {{- if .Values.primary.sidecars }} + {{- include "common.tplvalues.render" (dict "value" .Values.primary.sidecars "context" $) | nindent 8 }} + {{- end }} + volumes: + {{- if or .Values.primary.configuration .Values.primary.existingConfigmap }} + - name: config + configMap: + name: {{ include "mysql.primary.configmapName" . }} + {{- end }} + {{- if or .Values.initdbScriptsConfigMap .Values.initdbScripts }} + - name: custom-init-scripts + configMap: + name: {{ include "mysql.initdbScriptsCM" . }} + {{- end }} + {{- if and .Values.auth.usePasswordFiles (not .Values.auth.customPasswordFiles) }} + - name: mysql-credentials + secret: + secretName: {{ include "mysql.secretName" . }} + items: + - key: mysql-root-password + path: mysql-root-password + - key: mysql-password + path: mysql-password + {{- if eq .Values.architecture "replication" }} + - key: mysql-replication-password + path: mysql-replication-password + {{- end }} + {{- end }} + {{- if .Values.primary.extraVolumes }} + {{- include "common.tplvalues.render" (dict "value" .Values.primary.extraVolumes "context" $) | nindent 8 }} + {{- end }} + {{- if and .Values.primary.persistence.enabled .Values.primary.persistence.existingClaim }} + - name: data + persistentVolumeClaim: + claimName: {{ tpl .Values.primary.persistence.existingClaim . }} + {{- else if not .Values.primary.persistence.enabled }} + - name: data + emptyDir: {} + {{- else if and .Values.primary.persistence.enabled (not .Values.primary.persistence.existingClaim) }} + volumeClaimTemplates: + - metadata: + name: data + labels: {{ include "common.labels.matchLabels" . | nindent 10 }} + app.kubernetes.io/component: primary + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" (dict "value" .Values.commonLabels "context" $) | nindent 10 }} + {{- end }} + annotations: + {{- if .Values.primary.persistence.annotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.primary.persistence.annotations "context" $) | nindent 10 }} + {{- end }} + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.commonAnnotations "context" $) | nindent 10 }} + {{- end }} + spec: + accessModes: + {{- range .Values.primary.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.primary.persistence.size | quote }} + {{- include "common.storage.class" (dict "persistence" .Values.primary.persistence "global" .Values.global) | nindent 8 }} + {{- if .Values.primary.persistence.selector }} + selector: {{- include "common.tplvalues.render" (dict "value" .Values.primary.persistence.selector "context" $) | nindent 10 }} + {{- end -}} + {{- end }} diff --git a/charts/deps/charts/mysql-9.7.2/templates/primary/svc-headless.yaml b/charts/deps/charts/mysql-9.7.2/templates/primary/svc-headless.yaml new file mode 100644 index 0000000..c430d94 --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/templates/primary/svc-headless.yaml @@ -0,0 +1,29 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "mysql.primary.fullname" . }}-headless + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: primary + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if or .Values.primary.service.headless.annotations .Values.commonAnnotations }} + annotations: + {{- if .Values.primary.service.headless.annotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.primary.service.headless.annotations "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- end }} +spec: + type: ClusterIP + clusterIP: None + publishNotReadyAddresses: true + ports: + - name: mysql + port: {{ .Values.primary.service.ports.mysql }} + targetPort: mysql + selector: {{ include "common.labels.matchLabels" . | nindent 4 }} + app.kubernetes.io/component: primary diff --git a/charts/deps/charts/mysql-9.7.2/templates/primary/svc.yaml b/charts/deps/charts/mysql-9.7.2/templates/primary/svc.yaml new file mode 100644 index 0000000..b61d453 --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/templates/primary/svc.yaml @@ -0,0 +1,52 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "mysql.primary.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: primary + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + annotations: + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.primary.service.annotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.primary.service.annotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.primary.service.type }} + {{- if and .Values.primary.service.clusterIP (eq .Values.primary.service.type "ClusterIP") }} + clusterIP: {{ .Values.primary.service.clusterIP }} + {{- end }} + {{- if .Values.primary.service.sessionAffinity }} + sessionAffinity: {{ .Values.primary.service.sessionAffinity }} + {{- end }} + {{- if .Values.primary.service.sessionAffinityConfig }} + sessionAffinityConfig: {{- include "common.tplvalues.render" (dict "value" .Values.primary.service.sessionAffinityConfig "context" $) | nindent 4 }} + {{- end }} + {{- if or (eq .Values.primary.service.type "LoadBalancer") (eq .Values.primary.service.type "NodePort") }} + externalTrafficPolicy: {{ .Values.primary.service.externalTrafficPolicy | quote }} + {{- end }} + {{- if and (eq .Values.primary.service.type "LoadBalancer") (not (empty .Values.primary.service.loadBalancerSourceRanges)) }} + loadBalancerSourceRanges: {{ .Values.primary.service.loadBalancerSourceRanges }} + {{- end }} + {{- if and (eq .Values.primary.service.type "LoadBalancer") (not (empty .Values.primary.service.loadBalancerIP)) }} + loadBalancerIP: {{ .Values.primary.service.loadBalancerIP }} + {{- end }} + ports: + - name: mysql + port: {{ .Values.primary.service.ports.mysql }} + protocol: TCP + targetPort: mysql + {{- if (and (or (eq .Values.primary.service.type "NodePort") (eq .Values.primary.service.type "LoadBalancer")) .Values.primary.service.nodePorts.mysql) }} + nodePort: {{ .Values.primary.service.nodePorts.mysql }} + {{- else if eq .Values.primary.service.type "ClusterIP" }} + nodePort: null + {{- end }} + {{- if .Values.primary.service.extraPorts }} + {{- include "common.tplvalues.render" (dict "value" .Values.primary.service.extraPorts "context" $) | nindent 4 }} + {{- end }} + selector: {{ include "common.labels.matchLabels" . | nindent 4 }} + app.kubernetes.io/component: primary diff --git a/charts/deps/charts/mysql-9.7.2/templates/prometheusrule.yaml b/charts/deps/charts/mysql-9.7.2/templates/prometheusrule.yaml new file mode 100644 index 0000000..64fa44f --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/templates/prometheusrule.yaml @@ -0,0 +1,22 @@ +{{- if and .Values.metrics.enabled .Values.metrics.prometheusRule.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: PrometheusRule +metadata: + name: {{ include "common.names.fullname" . }} + namespace: {{ default .Release.Namespace .Values.metrics.prometheusRule.namespace }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: metrics + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.metrics.prometheusRule.additionalLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.prometheusRule.additionalLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + groups: + - name: {{ include "common.names.fullname" . }} + rules: {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.prometheusRule.rules "context" $ ) | nindent 6 }} +{{- end }} diff --git a/charts/deps/charts/mysql-9.7.2/templates/role.yaml b/charts/deps/charts/mysql-9.7.2/templates/role.yaml new file mode 100644 index 0000000..1ccc00a --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/templates/role.yaml @@ -0,0 +1,24 @@ +{{- if and .Values.serviceAccount.create .Values.rbac.create }} +apiVersion: {{ include "common.capabilities.rbac.apiVersion" . }} +kind: Role +metadata: + name: {{ include "common.names.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +rules: + - apiGroups: + - "" + resources: + - endpoints + verbs: + - get + {{- if .Values.rbac.rules }} + {{- include "common.tplvalues.render" ( dict "value" .Values.rbac.rules "context" $ ) | nindent 2 }} + {{- end }} +{{- end }} diff --git a/charts/deps/charts/mysql-9.7.2/templates/rolebinding.yaml b/charts/deps/charts/mysql-9.7.2/templates/rolebinding.yaml new file mode 100644 index 0000000..9b05208 --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/templates/rolebinding.yaml @@ -0,0 +1,21 @@ +{{- if and .Values.serviceAccount.create .Values.rbac.create }} +kind: RoleBinding +apiVersion: {{ include "common.capabilities.rbac.apiVersion" . }} +metadata: + name: {{ include "common.names.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +subjects: + - kind: ServiceAccount + name: {{ include "mysql.serviceAccountName" . }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: {{ include "common.names.fullname" . -}} +{{- end }} diff --git a/charts/deps/charts/mysql-9.7.2/templates/secondary/configmap.yaml b/charts/deps/charts/mysql-9.7.2/templates/secondary/configmap.yaml new file mode 100644 index 0000000..c94724f --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/templates/secondary/configmap.yaml @@ -0,0 +1,18 @@ +{{- if (include "mysql.secondary.createConfigmap" .) }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "mysql.secondary.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: secondary + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +data: + my.cnf: |- + {{- include "common.tplvalues.render" ( dict "value" .Values.secondary.configuration "context" $ ) | nindent 4 }} +{{- end -}} diff --git a/charts/deps/charts/mysql-9.7.2/templates/secondary/pdb.yaml b/charts/deps/charts/mysql-9.7.2/templates/secondary/pdb.yaml new file mode 100644 index 0000000..b4a5aee --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/templates/secondary/pdb.yaml @@ -0,0 +1,25 @@ +{{- if and (eq .Values.architecture "replication") .Values.secondary.pdb.create }} +apiVersion: {{ include "common.capabilities.policy.apiVersion" . }} +kind: PodDisruptionBudget +metadata: + name: {{ include "mysql.secondary.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: secondary + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + {{- if .Values.secondary.pdb.minAvailable }} + minAvailable: {{ .Values.secondary.pdb.minAvailable }} + {{- end }} + {{- if .Values.secondary.pdb.maxUnavailable }} + maxUnavailable: {{ .Values.secondary.pdb.maxUnavailable }} + {{- end }} + selector: + matchLabels: {{ include "common.labels.matchLabels" . | nindent 6 }} + app.kubernetes.io/component: secondary +{{- end }} diff --git a/charts/deps/charts/mysql-9.7.2/templates/secondary/statefulset.yaml b/charts/deps/charts/mysql-9.7.2/templates/secondary/statefulset.yaml new file mode 100644 index 0000000..237786d --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/templates/secondary/statefulset.yaml @@ -0,0 +1,369 @@ +{{- if eq .Values.architecture "replication" }} +apiVersion: {{ include "common.capabilities.statefulset.apiVersion" . }} +kind: StatefulSet +metadata: + name: {{ include "mysql.secondary.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: secondary + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + replicas: {{ .Values.secondary.replicaCount }} + podManagementPolicy: {{ .Values.secondary.podManagementPolicy | quote }} + selector: + matchLabels: {{ include "common.labels.matchLabels" . | nindent 6 }} + app.kubernetes.io/component: secondary + serviceName: {{ include "mysql.secondary.fullname" . }} + {{- if .Values.secondary.updateStrategy }} + updateStrategy: {{- toYaml .Values.secondary.updateStrategy | nindent 4 }} + {{- end }} + template: + metadata: + annotations: + {{- if (include "mysql.secondary.createConfigmap" .) }} + checksum/configuration: {{ include (print $.Template.BasePath "/secondary/configmap.yaml") . | sha256sum }} + {{- end }} + {{- if .Values.secondary.podAnnotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.secondary.podAnnotations "context" $) | nindent 8 }} + {{- end }} + labels: {{- include "common.labels.standard" . | nindent 8 }} + app.kubernetes.io/component: secondary + {{- if .Values.secondary.podLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.secondary.podLabels "context" $ ) | nindent 8 }} + {{- end }} + spec: + serviceAccountName: {{ include "mysql.serviceAccountName" . }} + {{- include "mysql.imagePullSecrets" . | nindent 6 }} + {{- if .Values.secondary.hostAliases }} + hostAliases: {{- include "common.tplvalues.render" (dict "value" .Values.secondary.hostAliases "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.secondary.affinity }} + affinity: {{- include "common.tplvalues.render" (dict "value" .Values.secondary.affinity "context" $) | nindent 8 }} + {{- else }} + affinity: + podAffinity: {{- include "common.affinities.pods" (dict "type" .Values.secondary.podAffinityPreset "context" $) | nindent 10 }} + podAntiAffinity: {{- include "common.affinities.pods" (dict "type" .Values.secondary.podAntiAffinityPreset "context" $) | nindent 10 }} + nodeAffinity: {{- include "common.affinities.nodes" (dict "type" .Values.secondary.nodeAffinityPreset.type "key" .Values.secondary.nodeAffinityPreset.key "values" .Values.secondary.nodeAffinityPreset.values) | nindent 10 }} + {{- end }} + {{- if .Values.secondary.nodeSelector }} + nodeSelector: {{- include "common.tplvalues.render" (dict "value" .Values.secondary.nodeSelector "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.secondary.tolerations }} + tolerations: {{- include "common.tplvalues.render" (dict "value" .Values.secondary.tolerations "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.secondary.priorityClassName }} + priorityClassName: {{ .Values.secondary.priorityClassName | quote }} + {{- end }} + {{- if .Values.secondary.runtimeClassName }} + runtimeClassName: {{ .Values.secondary.runtimeClassName | quote }} + {{- end }} + {{- if .Values.secondary.schedulerName }} + schedulerName: {{ .Values.secondary.schedulerName | quote }} + {{- end }} + {{- if .Values.secondary.topologySpreadConstraints }} + topologySpreadConstraints: {{- include "common.tplvalues.render" (dict "value" .Values.secondary.topologySpreadConstraints "context" .) | nindent 8 }} + {{- end }} + {{- if .Values.secondary.podSecurityContext.enabled }} + securityContext: {{- omit .Values.secondary.podSecurityContext "enabled" | toYaml | nindent 8 }} + {{- end }} + {{- if .Values.secondary.terminationGracePeriodSeconds }} + terminationGracePeriodSeconds: {{ .Values.secondary.terminationGracePeriodSeconds }} + {{- end }} + initContainers: + {{- if and .Values.secondary.podSecurityContext.enabled .Values.volumePermissions.enabled .Values.secondary.persistence.enabled }} + - name: volume-permissions + image: {{ include "mysql.volumePermissions.image" . }} + imagePullPolicy: {{ .Values.volumePermissions.image.pullPolicy | quote }} + command: + - /bin/bash + - -ec + - | + mkdir -p "/bitnami/mysql" + chown "{{ .Values.secondary.containerSecurityContext.runAsUser }}:{{ .Values.secondary.podSecurityContext.fsGroup }}" "/bitnami/mysql" + find "/bitnami/mysql" -mindepth 1 -maxdepth 1 -not -name ".snapshot" -not -name "lost+found" | xargs -r chown -R "{{ .Values.secondary.containerSecurityContext.runAsUser }}:{{ .Values.secondary.podSecurityContext.fsGroup }}" + securityContext: + runAsUser: 0 + {{- if .Values.volumePermissions.resources }} + resources: {{- toYaml .Values.volumePermissions.resources | nindent 12 }} + {{- end }} + volumeMounts: + - name: data + mountPath: /bitnami/mysql + {{- if .Values.secondary.persistence.subPath }} + subPath: {{ .Values.secondary.persistence.subPath }} + {{- end }} + {{- end }} + {{- if .Values.secondary.initContainers }} + {{- include "common.tplvalues.render" (dict "value" .Values.secondary.initContainers "context" $) | nindent 8 }} + {{- end }} + containers: + - name: mysql + image: {{ include "mysql.image" . }} + imagePullPolicy: {{ .Values.image.pullPolicy | quote }} + {{- if .Values.secondary.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.secondary.containerSecurityContext "enabled" | toYaml | nindent 12 }} + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 12 }} + {{- else if .Values.secondary.command }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.secondary.command "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 12 }} + {{- else if .Values.secondary.args }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.secondary.args "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.secondary.lifecycleHooks }} + lifecycle: {{- include "common.tplvalues.render" (dict "value" .Values.secondary.lifecycleHooks "context" $) | nindent 12 }} + {{- end }} + env: + - name: BITNAMI_DEBUG + value: {{ ternary "true" "false" (or .Values.image.debug .Values.diagnosticMode.enabled) | quote }} + - name: MYSQL_REPLICATION_MODE + value: "slave" + - name: MYSQL_MASTER_HOST + value: {{ include "mysql.primary.fullname" . }} + - name: MYSQL_MASTER_PORT_NUMBER + value: {{ .Values.primary.service.ports.mysql | quote }} + - name: MYSQL_MASTER_ROOT_USER + value: "root" + - name: MYSQL_REPLICATION_USER + value: {{ .Values.auth.replicationUser | quote }} + {{- if .Values.auth.usePasswordFiles }} + - name: MYSQL_MASTER_ROOT_PASSWORD_FILE + value: {{ default "/opt/bitnami/mysql/secrets/mysql-root-password" .Values.auth.customPasswordFiles.root }} + - name: MYSQL_REPLICATION_PASSWORD_FILE + value: {{ default "/opt/bitnami/mysql/secrets/mysql-replication-password" .Values.auth.customPasswordFiles.replicator }} + {{- else }} + - name: MYSQL_MASTER_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "mysql.secretName" . }} + key: mysql-root-password + - name: MYSQL_REPLICATION_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "mysql.secretName" . }} + key: mysql-replication-password + {{- end }} + {{- if .Values.secondary.extraFlags }} + - name: MYSQL_EXTRA_FLAGS + value: "{{ .Values.secondary.extraFlags }}" + {{- end }} + {{- if .Values.secondary.extraEnvVars }} + {{- include "common.tplvalues.render" (dict "value" .Values.secondary.extraEnvVars "context" $) | nindent 12 }} + {{- end }} + envFrom: + {{- if .Values.secondary.extraEnvVarsCM }} + - configMapRef: + name: {{ include "common.tplvalues.render" (dict "value" .Values.secondary.extraEnvVarsCM "context" $) }} + {{- end }} + {{- if .Values.secondary.extraEnvVarsSecret }} + - secretRef: + name: {{ include "common.tplvalues.render" (dict "value" .Values.secondary.extraEnvVarsSecret "context" $) }} + {{- end }} + ports: + - name: mysql + containerPort: 3306 + {{- if .Values.secondary.extraPorts }} + {{- include "common.tplvalues.render" (dict "value" .Values.secondary.extraPorts "context" $) | nindent 12 }} + {{- end }} + {{- if not .Values.diagnosticMode.enabled }} + {{- if .Values.secondary.customLivenessProbe }} + livenessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.secondary.customLivenessProbe "context" $) | nindent 12 }} + {{- else if .Values.secondary.livenessProbe.enabled }} + livenessProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.secondary.livenessProbe "enabled") "context" $) | nindent 12 }} + exec: + command: + - /bin/bash + - -ec + - | + password_aux="${MYSQL_MASTER_ROOT_PASSWORD:-}" + if [[ -f "${MYSQL_MASTER_ROOT_PASSWORD_FILE:-}" ]]; then + password_aux=$(cat "$MYSQL_MASTER_ROOT_PASSWORD_FILE") + fi + mysqladmin status -uroot -p"${password_aux}" + {{- end }} + {{- if .Values.secondary.customReadinessProbe }} + readinessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.secondary.customReadinessProbe "context" $) | nindent 12 }} + {{- else if .Values.secondary.readinessProbe.enabled }} + readinessProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.secondary.readinessProbe "enabled") "context" $) | nindent 12 }} + exec: + command: + - /bin/bash + - -ec + - | + password_aux="${MYSQL_MASTER_ROOT_PASSWORD:-}" + if [[ -f "${MYSQL_MASTER_ROOT_PASSWORD_FILE:-}" ]]; then + password_aux=$(cat "$MYSQL_MASTER_ROOT_PASSWORD_FILE") + fi + mysqladmin status -uroot -p"${password_aux}" + {{- end }} + {{- if .Values.secondary.customStartupProbe }} + startupProbe: {{- include "common.tplvalues.render" (dict "value" .Values.secondary.customStartupProbe "context" $) | nindent 12 }} + {{- else if .Values.secondary.startupProbe.enabled }} + startupProbe: {{- include "common.tplvalues.render" (dict "value" (omit .Values.secondary.startupProbe "enabled") "context" $) | nindent 12 }} + exec: + command: + - /bin/bash + - -ec + - | + password_aux="${MYSQL_MASTER_ROOT_PASSWORD:-}" + if [[ -f "${MYSQL_MASTER_ROOT_PASSWORD_FILE:-}" ]]; then + password_aux=$(cat "$MYSQL_MASTER_ROOT_PASSWORD_FILE") + fi + mysqladmin status -uroot -p"${password_aux}" + {{- end }} + {{- end }} + {{- if .Values.secondary.resources }} + resources: {{ toYaml .Values.secondary.resources | nindent 12 }} + {{- end }} + volumeMounts: + - name: data + mountPath: /bitnami/mysql + {{- if .Values.secondary.persistence.subPath }} + subPath: {{ .Values.secondary.persistence.subPath }} + {{- end }} + {{- if or .Values.initdbScriptsConfigMap .Values.initdbScripts }} + - name: custom-init-scripts + mountPath: /docker-entrypoint-initdb.d + {{- end }} + {{- if or .Values.secondary.configuration .Values.secondary.existingConfigmap }} + - name: config + mountPath: /opt/bitnami/mysql/conf/my.cnf + subPath: my.cnf + {{- end }} + {{- if and .Values.auth.usePasswordFiles (not .Values.auth.customPasswordFiles) }} + - name: mysql-credentials + mountPath: /opt/bitnami/mysql/secrets/ + {{- end }} + {{- if .Values.secondary.extraVolumeMounts }} + {{- include "common.tplvalues.render" (dict "value" .Values.secondary.extraVolumeMounts "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.metrics.enabled }} + - name: metrics + image: {{ include "mysql.metrics.image" . }} + imagePullPolicy: {{ .Values.metrics.image.pullPolicy | quote }} + {{- if .Values.metrics.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.metrics.containerSecurityContext "enabled" | toYaml | nindent 12 }} + {{- end }} + env: + {{- if .Values.auth.usePasswordFiles }} + - name: MYSQL_ROOT_PASSWORD_FILE + value: {{ default "/opt/bitnami/mysqld-exporter/secrets/mysql-root-password" .Values.auth.customPasswordFiles.root }} + {{- else }} + - name: MYSQL_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: {{ template "mysql.secretName" . }} + key: mysql-root-password + {{- end }} + {{- if .Values.diagnosticMode.enabled }} + command: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.command "context" $) | nindent 12 }} + args: {{- include "common.tplvalues.render" (dict "value" .Values.diagnosticMode.args "context" $) | nindent 12 }} + {{- else }} + command: + - /bin/bash + - -ec + - | + password_aux="${MYSQL_ROOT_PASSWORD:-}" + if [[ -f "${MYSQL_ROOT_PASSWORD_FILE:-}" ]]; then + password_aux=$(cat "$MYSQL_ROOT_PASSWORD_FILE") + fi + DATA_SOURCE_NAME="root:${password_aux}@(localhost:3306)/" /bin/mysqld_exporter {{- range .Values.metrics.extraArgs.secondary }} {{ . }} {{- end }} + {{- end }} + ports: + - name: metrics + containerPort: 9104 + {{- if not .Values.diagnosticMode.enabled }} + {{- if .Values.metrics.livenessProbe.enabled }} + livenessProbe: {{- omit .Values.metrics.livenessProbe "enabled" | toYaml | nindent 12 }} + httpGet: + path: /metrics + port: metrics + {{- end }} + {{- if .Values.metrics.readinessProbe.enabled }} + readinessProbe: {{- omit .Values.metrics.readinessProbe "enabled" | toYaml | nindent 12 }} + httpGet: + path: /metrics + port: metrics + {{- end }} + {{- end }} + {{- if .Values.metrics.resources }} + resources: {{- toYaml .Values.metrics.resources | nindent 12 }} + {{- end }} + {{- if and .Values.auth.usePasswordFiles (not .Values.auth.customPasswordFiles) }} + volumeMounts: + - name: mysql-credentials + mountPath: /opt/bitnami/mysqld-exporter/secrets/ + {{- end }} + {{- end }} + {{- if .Values.secondary.sidecars }} + {{- include "common.tplvalues.render" (dict "value" .Values.secondary.sidecars "context" $) | nindent 8 }} + {{- end }} + volumes: + {{- if or .Values.initdbScriptsConfigMap .Values.initdbScripts }} + - name: custom-init-scripts + configMap: + name: {{ include "mysql.initdbScriptsCM" . }} + {{- end }} + {{- if or .Values.secondary.configuration .Values.secondary.existingConfigmap }} + - name: config + configMap: + name: {{ include "mysql.secondary.configmapName" . }} + {{- end }} + {{- if and .Values.auth.usePasswordFiles (not .Values.auth.customPasswordFiles) }} + - name: mysql-credentials + secret: + secretName: {{ template "mysql.secretName" . }} + items: + - key: mysql-root-password + path: mysql-root-password + - key: mysql-replication-password + path: mysql-replication-password + {{- end }} + {{- if .Values.secondary.extraVolumes }} + {{- include "common.tplvalues.render" (dict "value" .Values.secondary.extraVolumes "context" $) | nindent 8 }} + {{- end }} + {{- if and .Values.secondary.persistence.enabled .Values.secondary.persistence.existingClaim }} + - name: data + persistentVolumeClaim: + claimName: {{ tpl .Values.secondary.persistence.existingClaim . }} + {{- else if not .Values.secondary.persistence.enabled }} + - name: data + emptyDir: {} + {{- else }} + volumeClaimTemplates: + - metadata: + name: data + labels: {{ include "common.labels.matchLabels" . | nindent 10 }} + app.kubernetes.io/component: secondary + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" (dict "value" .Values.commonLabels "context" $) | nindent 10 }} + {{- end }} + annotations: + {{- if .Values.secondary.persistence.annotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.secondary.persistence.annotations "context" $) | nindent 10 }} + {{- end }} + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.commonAnnotations "context" $) | nindent 10 }} + {{- end }} + spec: + accessModes: + {{- range .Values.secondary.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.secondary.persistence.size | quote }} + {{- include "common.storage.class" (dict "persistence" .Values.secondary.persistence "global" .Values.global) | nindent 8 }} + {{- if .Values.secondary.persistence.selector }} + selector: {{- include "common.tplvalues.render" (dict "value" .Values.secondary.persistence.selector "context" $) | nindent 10 }} + {{- end -}} + {{- end }} +{{- end }} diff --git a/charts/deps/charts/mysql-9.7.2/templates/secondary/svc-headless.yaml b/charts/deps/charts/mysql-9.7.2/templates/secondary/svc-headless.yaml new file mode 100644 index 0000000..44cfa4a --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/templates/secondary/svc-headless.yaml @@ -0,0 +1,31 @@ +{{- if eq .Values.architecture "replication" }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "mysql.secondary.fullname" . }}-headless + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: secondary + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if or .Values.secondary.service.headless.annotations .Values.commonAnnotations }} + annotations: + {{- if .Values.secondary.service.headless.annotations }} + {{- include "common.tplvalues.render" (dict "value" .Values.secondary.service.headless.annotations "context" $) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- end }} +spec: + type: ClusterIP + clusterIP: None + publishNotReadyAddresses: true + ports: + - name: mysql + port: {{ .Values.secondary.service.ports.mysql }} + targetPort: mysql + selector: {{ include "common.labels.matchLabels" . | nindent 4 }} + app.kubernetes.io/component: secondary +{{- end }} diff --git a/charts/deps/charts/mysql-9.7.2/templates/secondary/svc.yaml b/charts/deps/charts/mysql-9.7.2/templates/secondary/svc.yaml new file mode 100644 index 0000000..e6e662c --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/templates/secondary/svc.yaml @@ -0,0 +1,54 @@ +{{- if eq .Values.architecture "replication" }} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "mysql.secondary.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: secondary + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + annotations: + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.secondary.service.annotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.secondary.service.annotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.secondary.service.type }} + {{- if and .Values.secondary.service.clusterIP (eq .Values.secondary.service.type "ClusterIP") }} + clusterIP: {{ .Values.secondary.service.clusterIP }} + {{- end }} + {{- if .Values.secondary.service.sessionAffinity }} + sessionAffinity: {{ .Values.secondary.service.sessionAffinity }} + {{- end }} + {{- if .Values.secondary.service.sessionAffinityConfig }} + sessionAffinityConfig: {{- include "common.tplvalues.render" (dict "value" .Values.secondary.service.sessionAffinityConfig "context" $) | nindent 4 }} + {{- end }} + {{- if or (eq .Values.secondary.service.type "LoadBalancer") (eq .Values.secondary.service.type "NodePort") }} + externalTrafficPolicy: {{ .Values.secondary.service.externalTrafficPolicy | quote }} + {{- end }} + {{- if and (eq .Values.secondary.service.type "LoadBalancer") (not (empty .Values.secondary.service.loadBalancerSourceRanges)) }} + loadBalancerSourceRanges: {{ .Values.secondary.service.loadBalancerSourceRanges }} + {{- end }} + {{- if and (eq .Values.secondary.service.type "LoadBalancer") (not (empty .Values.secondary.service.loadBalancerIP)) }} + loadBalancerIP: {{ .Values.secondary.service.loadBalancerIP }} + {{- end }} + ports: + - name: mysql + port: {{ .Values.secondary.service.ports.mysql }} + protocol: TCP + targetPort: mysql + {{- if (and (or (eq .Values.secondary.service.type "NodePort") (eq .Values.secondary.service.type "LoadBalancer")) .Values.secondary.service.nodePorts.mysql) }} + nodePort: {{ .Values.secondary.service.nodePorts.mysql }} + {{- else if eq .Values.secondary.service.type "ClusterIP" }} + nodePort: null + {{- end }} + {{- if .Values.secondary.service.extraPorts }} + {{- include "common.tplvalues.render" (dict "value" .Values.secondary.service.extraPorts "context" $) | nindent 4 }} + {{- end }} + selector: {{ include "common.labels.matchLabels" . | nindent 4 }} + app.kubernetes.io/component: secondary +{{- end }} diff --git a/charts/deps/charts/mysql-9.7.2/templates/secrets.yaml b/charts/deps/charts/mysql-9.7.2/templates/secrets.yaml new file mode 100644 index 0000000..e2aa443 --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/templates/secrets.yaml @@ -0,0 +1,78 @@ +{{- $host := include "mysql.primary.fullname" . }} +{{- $port := print .Values.primary.service.ports.mysql }} +{{- $rootPassword := include "common.secrets.passwords.manage" (dict "secret" (include "mysql.secretName" .) "key" "mysql-root-password" "length" 10 "providedValues" (list "auth.rootPassword") "context" $) | trimAll "\"" | b64dec }} +{{- $password := include "common.secrets.passwords.manage" (dict "secret" (include "mysql.secretName" .) "key" "mysql-password" "length" 10 "providedValues" (list "auth.password") "context" $) | trimAll "\"" | b64dec }} +{{- if eq (include "mysql.createSecret" .) "true" }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "common.names.fullname" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +type: Opaque +data: + mysql-root-password: {{ print $rootPassword | b64enc | quote }} + mysql-password: {{ print $password | b64enc | quote }} + {{- if eq .Values.architecture "replication" }} + mysql-replication-password: {{ include "common.secrets.passwords.manage" (dict "secret" (include "common.names.fullname" .) "key" "mysql-replication-password" "length" 10 "providedValues" (list "auth.replicationPassword") "context" $) }} + {{- end }} +{{- end }} +{{- if .Values.serviceBindings.enabled }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "common.names.fullname" . }}-svcbind-root + namespace: {{ .Release.Namespace | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +type: servicebinding.io/mysql +data: + provider: {{ print "bitnami" | b64enc | quote }} + type: {{ print "mysql" | b64enc | quote }} + host: {{ print $host | b64enc | quote }} + port: {{ print $port | b64enc | quote }} + user: {{ print "root" | b64enc | quote }} + password: {{ print $rootPassword | b64enc | quote }} + uri: {{ printf "mysql://root:%s@%s:%s" $rootPassword $host $port | b64enc | quote }} + +{{- if .Values.auth.username }} +{{- $database := .Values.auth.database }} +--- +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "common.names.fullname" . }}-svcbind-custom-user + namespace: {{ .Release.Namespace | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +type: servicebinding.io/mysql +data: + provider: {{ print "bitnami" | b64enc | quote }} + type: {{ print "mysql" | b64enc | quote }} + host: {{ print $host | b64enc | quote }} + port: {{ print $port | b64enc | quote }} + user: {{ print .Values.auth.username | b64enc | quote }} + {{- if $database }} + database: {{ print $database | b64enc | quote }} + {{- end }} + password: {{ print $password | b64enc | quote }} + uri: {{ printf "mysql://%s:%s@%s:%s/%s" .Values.auth.username $password $host $port $database | b64enc | quote }} +{{- end }} +{{- end }} diff --git a/charts/deps/charts/mysql-9.7.2/templates/serviceaccount.yaml b/charts/deps/charts/mysql-9.7.2/templates/serviceaccount.yaml new file mode 100644 index 0000000..5044961 --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/templates/serviceaccount.yaml @@ -0,0 +1,23 @@ +{{- if .Values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "mysql.serviceAccountName" . }} + namespace: {{ include "common.names.namespace" . | quote }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + annotations: + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.serviceAccount.annotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.serviceAccount.annotations "context" $ ) | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automountServiceAccountToken }} +{{- if (not .Values.auth.customPasswordFiles) }} +secrets: + - name: {{ template "mysql.secretName" . }} +{{- end }} +{{- end }} diff --git a/charts/deps/charts/mysql-9.7.2/templates/servicemonitor.yaml b/charts/deps/charts/mysql-9.7.2/templates/servicemonitor.yaml new file mode 100644 index 0000000..47a9dad --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/templates/servicemonitor.yaml @@ -0,0 +1,49 @@ +{{- if and .Values.metrics.enabled .Values.metrics.serviceMonitor.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + name: {{ include "common.names.fullname" . }} + namespace: {{ default (include "common.names.namespace" .) .Values.metrics.serviceMonitor.namespace }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.metrics.serviceMonitor.labels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.serviceMonitor.labels "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.commonLabels }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonLabels "context" $ ) | nindent 4 }} + {{- end }} + annotations: + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- if .Values.metrics.serviceMonitor.annotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.serviceMonitor.annotations "context" $ ) | nindent 4 }} + {{- end }} +spec: + jobLabel: {{ .Values.metrics.serviceMonitor.jobLabel | quote }} + endpoints: + - port: metrics + {{- if .Values.metrics.serviceMonitor.interval }} + interval: {{ .Values.metrics.serviceMonitor.interval }} + {{- end }} + {{- if .Values.metrics.serviceMonitor.scrapeTimeout }} + scrapeTimeout: {{ .Values.metrics.serviceMonitor.scrapeTimeout }} + {{- end }} + {{- if .Values.metrics.serviceMonitor.honorLabels }} + honorLabels: {{ .Values.metrics.serviceMonitor.honorLabels }} + {{- end }} + {{- if .Values.metrics.serviceMonitor.metricRelabelings }} + metricRelabelings: {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.serviceMonitor.metricRelabelings "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.metrics.serviceMonitor.relabelings }} + relabelings: {{- include "common.tplvalues.render" ( dict "value" .Values.metrics.serviceMonitor.relabelings "context" $) | nindent 8 }} + {{- end }} + namespaceSelector: + matchNames: + - {{ include "common.names.namespace" . | quote }} + selector: + matchLabels: {{- include "common.labels.matchLabels" . | nindent 6 }} + app.kubernetes.io/component: metrics + {{- if .Values.metrics.serviceMonitor.selector }} + {{- include "common.tplvalues.render" (dict "value" .Values.metrics.serviceMonitor.selector "context" $) | nindent 6 }} + {{- end }} +{{- end }} diff --git a/charts/deps/charts/mysql-9.7.2/values.schema.json b/charts/deps/charts/mysql-9.7.2/values.schema.json new file mode 100644 index 0000000..df59156 --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/values.schema.json @@ -0,0 +1,195 @@ +{ + "$schema": "http://json-schema.org/schema#", + "type": "object", + "properties": { + "architecture": { + "type": "string", + "title": "MySQL architecture", + "form": true, + "description": "Allowed values: `standalone` or `replication`", + "enum": ["standalone", "replication"] + }, + "auth": { + "type": "object", + "title": "Authentication configuration", + "form": true, + "required": ["username", "password"], + "if": { + "properties": { + "createDatabase": { "enum": [ true ] } + } + }, + "then": { + "properties": { + "database": { + "pattern": "[a-zA-Z0-9]{1,64}" + } + } + }, + "properties": { + "rootPassword": { + "type": "string", + "title": "MySQL root password", + "description": "Defaults to a random 10-character alphanumeric string if not set" + }, + "database": { + "type": "string", + "title": "MySQL custom database name", + "maxLength": 64 + }, + "username": { + "type": "string", + "title": "MySQL custom username" + }, + "password": { + "type": "string", + "title": "MySQL custom password" + }, + "replicationUser": { + "type": "string", + "title": "MySQL replication username" + }, + "replicationPassword": { + "type": "string", + "title": "MySQL replication password" + }, + "createDatabase": { + "type": "boolean", + "title": "MySQL create custom database" + } + } + }, + "primary": { + "type": "object", + "title": "Primary database configuration", + "form": true, + "properties": { + "podSecurityContext": { + "type": "object", + "title": "MySQL primary Pod security context", + "properties": { + "enabled": { + "type": "boolean", + "default": false + }, + "fsGroup": { + "type": "integer", + "default": 1001, + "hidden": { + "value": false, + "path": "primary/podSecurityContext/enabled" + } + } + } + }, + "containerSecurityContext": { + "type": "object", + "title": "MySQL primary container security context", + "properties": { + "enabled": { + "type": "boolean", + "default": false + }, + "runAsUser": { + "type": "integer", + "default": 1001, + "hidden": { + "value": false, + "path": "primary/containerSecurityContext/enabled" + } + } + } + }, + "persistence": { + "type": "object", + "title": "Enable persistence using Persistent Volume Claims", + "properties": { + "enabled": { + "type": "boolean", + "default": true, + "title": "If true, use a Persistent Volume Claim, If false, use emptyDir" + }, + "size": { + "type": "string", + "title": "Persistent Volume Size", + "form": true, + "render": "slider", + "sliderMin": 1, + "sliderUnit": "Gi", + "hidden": { + "value": false, + "path": "primary/persistence/enabled" + } + } + } + } + } + }, + "secondary": { + "type": "object", + "title": "Secondary database configuration", + "form": true, + "properties": { + "podSecurityContext": { + "type": "object", + "title": "MySQL secondary Pod security context", + "properties": { + "enabled": { + "type": "boolean", + "default": false + }, + "fsGroup": { + "type": "integer", + "default": 1001, + "hidden": { + "value": false, + "path": "secondary/podSecurityContext/enabled" + } + } + } + }, + "containerSecurityContext": { + "type": "object", + "title": "MySQL secondary container security context", + "properties": { + "enabled": { + "type": "boolean", + "default": false + }, + "runAsUser": { + "type": "integer", + "default": 1001, + "hidden": { + "value": false, + "path": "secondary/containerSecurityContext/enabled" + } + } + } + }, + "persistence": { + "type": "object", + "title": "Enable persistence using Persistent Volume Claims", + "properties": { + "enabled": { + "type": "boolean", + "default": true, + "title": "If true, use a Persistent Volume Claim, If false, use emptyDir" + }, + "size": { + "type": "string", + "title": "Persistent Volume Size", + "form": true, + "render": "slider", + "sliderMin": 1, + "sliderUnit": "Gi", + "hidden": { + "value": false, + "path": "secondary/persistence/enabled" + } + } + } + } + } + } + } +} diff --git a/charts/deps/charts/mysql-9.7.2/values.yaml b/charts/deps/charts/mysql-9.7.2/values.yaml new file mode 100644 index 0000000..bc585f1 --- /dev/null +++ b/charts/deps/charts/mysql-9.7.2/values.yaml @@ -0,0 +1,1245 @@ +## @section Global parameters +## Global Docker image parameters +## Please, note that this will override the image parameters, including dependencies, configured to use the global value +## Current available global Docker image parameters: imageRegistry, imagePullSecrets and storageClass +## + +## @param global.imageRegistry Global Docker image registry +## @param global.imagePullSecrets Global Docker registry secret names as an array +## @param global.storageClass Global StorageClass for Persistent Volume(s) +## +global: + imageRegistry: "" + ## E.g. + ## imagePullSecrets: + ## - myRegistryKeySecretName + ## + imagePullSecrets: [] + storageClass: "" + +## @section Common parameters +## + +## @param kubeVersion Force target Kubernetes version (using Helm capabilities if not set) +## +kubeVersion: "" +## @param nameOverride String to partially override common.names.fullname template (will maintain the release name) +## +nameOverride: "" +## @param fullnameOverride String to fully override common.names.fullname template +## +fullnameOverride: "" +## @param namespaceOverride String to fully override common.names.namespace +## +namespaceOverride: "" +## @param clusterDomain Cluster domain +## +clusterDomain: cluster.local +## @param commonAnnotations Common annotations to add to all MySQL resources (sub-charts are not considered). Evaluated as a template +## +commonAnnotations: {} +## @param commonLabels Common labels to add to all MySQL resources (sub-charts are not considered). Evaluated as a template +## +commonLabels: {} +## @param extraDeploy Array with extra yaml to deploy with the chart. Evaluated as a template +## +extraDeploy: [] + +## @param serviceBindings.enabled Create secret for service binding (Experimental) +## Ref: https://servicebinding.io/service-provider/ +## +serviceBindings: + enabled: false + +## Enable diagnostic mode in the deployment +## +diagnosticMode: + ## @param diagnosticMode.enabled Enable diagnostic mode (all probes will be disabled and the command will be overridden) + ## + enabled: false + ## @param diagnosticMode.command Command to override all containers in the deployment + ## + command: + - sleep + ## @param diagnosticMode.args Args to override all containers in the deployment + ## + args: + - infinity + +## @section MySQL common parameters +## + +## Bitnami MySQL image +## ref: https://hub.docker.com/r/bitnami/mysql/tags/ +## @param image.registry MySQL image registry +## @param image.repository MySQL image repository +## @param image.tag MySQL image tag (immutable tags are recommended) +## @param image.digest MySQL image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag +## @param image.pullPolicy MySQL image pull policy +## @param image.pullSecrets Specify docker-registry secret names as an array +## @param image.debug Specify if debug logs should be enabled +## +image: + registry: docker.io + repository: bitnami/mysql + tag: 8.0.33-debian-11-r0 + digest: "" + ## Specify a imagePullPolicy + ## Defaults to 'Always' if image tag is 'latest', else set to 'IfNotPresent' + ## ref: https://kubernetes.io/docs/user-guide/images/#pre-pulling-images + ## + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets (secrets must be manually created in the namespace) + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## Example: + ## pullSecrets: + ## - myRegistryKeySecretName + ## + pullSecrets: [] + ## Set to true if you would like to see extra information on logs + ## It turns BASH and/or NAMI debugging in the image + ## + debug: false +## @param architecture MySQL architecture (`standalone` or `replication`) +## +architecture: standalone +## MySQL Authentication parameters +## +auth: + ## @param auth.rootPassword Password for the `root` user. Ignored if existing secret is provided + ## ref: https://github.com/bitnami/containers/tree/main/bitnami/mysql#setting-the-root-password-on-first-run + ## + rootPassword: "" + ## @param auth.createDatabase Whether to create the .Values.auth.database or not + ## ref: https://github.com/bitnami/containers/tree/main/bitnami/mysql#creating-a-database-on-first-run + ## + createDatabase: true + ## @param auth.database Name for a custom database to create + ## ref: https://github.com/bitnami/containers/tree/main/bitnami/mysql#creating-a-database-on-first-run + ## + database: "my_database" + ## @param auth.username Name for a custom user to create + ## ref: https://github.com/bitnami/containers/tree/main/bitnami/mysql#creating-a-database-user-on-first-run + ## + username: "" + ## @param auth.password Password for the new user. Ignored if existing secret is provided + ## + password: "" + ## @param auth.replicationUser MySQL replication user + ## ref: https://github.com/bitnami/containers/tree/main/bitnami/mysql#setting-up-a-replication-cluster + ## + replicationUser: replicator + ## @param auth.replicationPassword MySQL replication user password. Ignored if existing secret is provided + ## + replicationPassword: "" + ## @param auth.existingSecret Use existing secret for password details. The secret has to contain the keys `mysql-root-password`, `mysql-replication-password` and `mysql-password` + ## NOTE: When it's set the auth.rootPassword, auth.password, auth.replicationPassword are ignored. + ## + existingSecret: "" + ## @param auth.usePasswordFiles Mount credentials as files instead of using an environment variable + ## + usePasswordFiles: false + ## @param auth.customPasswordFiles Use custom password files when `auth.usePasswordFiles` is set to `true`. Define path for keys `root` and `user`, also define `replicator` if `architecture` is set to `replication` + ## Example: + ## customPasswordFiles: + ## root: /vault/secrets/mysql-root + ## user: /vault/secrets/mysql-user + ## replicator: /vault/secrets/mysql-replicator + ## + customPasswordFiles: {} +## @param initdbScripts Dictionary of initdb scripts +## Specify dictionary of scripts to be run at first boot +## Example: +## initdbScripts: +## my_init_script.sh: | +## #!/bin/bash +## echo "Do something." +## +initdbScripts: {} +## @param initdbScriptsConfigMap ConfigMap with the initdb scripts (Note: Overrides `initdbScripts`) +## +initdbScriptsConfigMap: "" + +## @section MySQL Primary parameters +## + +primary: + ## @param primary.name Name of the primary database (eg primary, master, leader, ...) + ## + name: primary + ## @param primary.command Override default container command on MySQL Primary container(s) (useful when using custom images) + ## + command: [] + ## @param primary.args Override default container args on MySQL Primary container(s) (useful when using custom images) + ## + args: [] + ## @param primary.lifecycleHooks for the MySQL Primary container(s) to automate configuration before or after startup + ## + lifecycleHooks: {} + ## @param primary.hostAliases Deployment pod host aliases + ## https://kubernetes.io/docs/concepts/services-networking/add-entries-to-pod-etc-hosts-with-host-aliases/ + ## + hostAliases: [] + ## @param primary.configuration [string] Configure MySQL Primary with a custom my.cnf file + ## ref: https://mysql.com/kb/en/mysql/configuring-mysql-with-mycnf/#example-of-configuration-file + ## + configuration: |- + [mysqld] + default_authentication_plugin=mysql_native_password + skip-name-resolve + explicit_defaults_for_timestamp + basedir=/opt/bitnami/mysql + plugin_dir=/opt/bitnami/mysql/lib/plugin + port=3306 + socket=/opt/bitnami/mysql/tmp/mysql.sock + datadir=/bitnami/mysql/data + tmpdir=/opt/bitnami/mysql/tmp + max_allowed_packet=16M + bind-address=* + pid-file=/opt/bitnami/mysql/tmp/mysqld.pid + log-error=/opt/bitnami/mysql/logs/mysqld.log + character-set-server=UTF8 + collation-server=utf8_general_ci + slow_query_log=0 + slow_query_log_file=/opt/bitnami/mysql/logs/mysqld.log + long_query_time=10.0 + + [client] + port=3306 + socket=/opt/bitnami/mysql/tmp/mysql.sock + default-character-set=UTF8 + plugin_dir=/opt/bitnami/mysql/lib/plugin + + [manager] + port=3306 + socket=/opt/bitnami/mysql/tmp/mysql.sock + pid-file=/opt/bitnami/mysql/tmp/mysqld.pid + ## @param primary.existingConfigmap Name of existing ConfigMap with MySQL Primary configuration. + ## NOTE: When it's set the 'configuration' parameter is ignored + ## + existingConfigmap: "" + ## @param primary.updateStrategy.type Update strategy type for the MySQL primary statefulset + ## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies + ## + updateStrategy: + type: RollingUpdate + ## @param primary.podAnnotations Additional pod annotations for MySQL primary pods + ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + ## + podAnnotations: {} + ## @param primary.podAffinityPreset MySQL primary pod affinity preset. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity + ## + podAffinityPreset: "" + ## @param primary.podAntiAffinityPreset MySQL primary pod anti-affinity preset. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity + ## + podAntiAffinityPreset: soft + ## MySQL Primary node affinity preset + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity + ## + nodeAffinityPreset: + ## @param primary.nodeAffinityPreset.type MySQL primary node affinity preset type. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` + ## + type: "" + ## @param primary.nodeAffinityPreset.key MySQL primary node label key to match Ignored if `primary.affinity` is set. + ## E.g. + ## key: "kubernetes.io/e2e-az-name" + ## + key: "" + ## @param primary.nodeAffinityPreset.values MySQL primary node label values to match. Ignored if `primary.affinity` is set. + ## E.g. + ## values: + ## - e2e-az1 + ## - e2e-az2 + ## + values: [] + ## @param primary.affinity Affinity for MySQL primary pods assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity + ## Note: podAffinityPreset, podAntiAffinityPreset, and nodeAffinityPreset will be ignored when it's set + ## + affinity: {} + ## @param primary.nodeSelector Node labels for MySQL primary pods assignment + ## ref: https://kubernetes.io/docs/user-guide/node-selection/ + ## + nodeSelector: {} + ## @param primary.tolerations Tolerations for MySQL primary pods assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ + ## + tolerations: [] + ## @param primary.priorityClassName MySQL primary pods' priorityClassName + ## + priorityClassName: "" + ## @param primary.runtimeClassName MySQL primary pods' runtimeClassName + ## + runtimeClassName: "" + ## @param primary.schedulerName Name of the k8s scheduler (other than default) + ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ + ## + schedulerName: "" + ## @param primary.terminationGracePeriodSeconds In seconds, time the given to the MySQL primary pod needs to terminate gracefully + ## ref: https://kubernetes.io/docs/concepts/workloads/pods/pod/#termination-of-pods + ## + terminationGracePeriodSeconds: "" + ## @param primary.topologySpreadConstraints Topology Spread Constraints for pod assignment + ## https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ + ## The value is evaluated as a template + ## + topologySpreadConstraints: [] + ## @param primary.podManagementPolicy podManagementPolicy to manage scaling operation of MySQL primary pods + ## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#pod-management-policies + ## + podManagementPolicy: "" + ## MySQL primary Pod security context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod + ## @param primary.podSecurityContext.enabled Enable security context for MySQL primary pods + ## @param primary.podSecurityContext.fsGroup Group ID for the mounted volumes' filesystem + ## + podSecurityContext: + enabled: true + fsGroup: 1001 + ## MySQL primary container security context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container + ## @param primary.containerSecurityContext.enabled MySQL primary container securityContext + ## @param primary.containerSecurityContext.runAsUser User ID for the MySQL primary container + ## @param primary.containerSecurityContext.runAsNonRoot Set MySQL primary container's Security Context runAsNonRoot + ## + containerSecurityContext: + enabled: true + runAsUser: 1001 + runAsNonRoot: true + ## MySQL primary container's resource requests and limits + ## ref: https://kubernetes.io/docs/user-guide/compute-resources/ + ## We usually recommend not to specify default resources and to leave this as a conscious + ## choice for the user. This also increases chances charts run on environments with little + ## resources, such as Minikube. If you do want to specify resources, uncomment the following + ## lines, adjust them as necessary, and remove the curly braces after 'resources:'. + ## @param primary.resources.limits The resources limits for MySQL primary containers + ## @param primary.resources.requests The requested resources for MySQL primary containers + ## + resources: + ## Example: + ## limits: + ## cpu: 250m + ## memory: 256Mi + ## + limits: {} + ## Examples: + ## requests: + ## cpu: 250m + ## memory: 256Mi + ## + requests: {} + ## Configure extra options for liveness probe + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes + ## @param primary.livenessProbe.enabled Enable livenessProbe + ## @param primary.livenessProbe.initialDelaySeconds Initial delay seconds for livenessProbe + ## @param primary.livenessProbe.periodSeconds Period seconds for livenessProbe + ## @param primary.livenessProbe.timeoutSeconds Timeout seconds for livenessProbe + ## @param primary.livenessProbe.failureThreshold Failure threshold for livenessProbe + ## @param primary.livenessProbe.successThreshold Success threshold for livenessProbe + ## + livenessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 1 + failureThreshold: 3 + successThreshold: 1 + ## Configure extra options for readiness probe + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes + ## @param primary.readinessProbe.enabled Enable readinessProbe + ## @param primary.readinessProbe.initialDelaySeconds Initial delay seconds for readinessProbe + ## @param primary.readinessProbe.periodSeconds Period seconds for readinessProbe + ## @param primary.readinessProbe.timeoutSeconds Timeout seconds for readinessProbe + ## @param primary.readinessProbe.failureThreshold Failure threshold for readinessProbe + ## @param primary.readinessProbe.successThreshold Success threshold for readinessProbe + ## + readinessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 1 + failureThreshold: 3 + successThreshold: 1 + ## Configure extra options for startupProbe probe + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes + ## @param primary.startupProbe.enabled Enable startupProbe + ## @param primary.startupProbe.initialDelaySeconds Initial delay seconds for startupProbe + ## @param primary.startupProbe.periodSeconds Period seconds for startupProbe + ## @param primary.startupProbe.timeoutSeconds Timeout seconds for startupProbe + ## @param primary.startupProbe.failureThreshold Failure threshold for startupProbe + ## @param primary.startupProbe.successThreshold Success threshold for startupProbe + ## + startupProbe: + enabled: true + initialDelaySeconds: 15 + periodSeconds: 10 + timeoutSeconds: 1 + failureThreshold: 10 + successThreshold: 1 + ## @param primary.customLivenessProbe Override default liveness probe for MySQL primary containers + ## + customLivenessProbe: {} + ## @param primary.customReadinessProbe Override default readiness probe for MySQL primary containers + ## + customReadinessProbe: {} + ## @param primary.customStartupProbe Override default startup probe for MySQL primary containers + ## + customStartupProbe: {} + ## @param primary.extraFlags MySQL primary additional command line flags + ## Can be used to specify command line flags, for example: + ## E.g. + ## extraFlags: "--max-connect-errors=1000 --max_connections=155" + ## + extraFlags: "" + ## @param primary.extraEnvVars Extra environment variables to be set on MySQL primary containers + ## E.g. + ## extraEnvVars: + ## - name: TZ + ## value: "Europe/Paris" + ## + extraEnvVars: [] + ## @param primary.extraEnvVarsCM Name of existing ConfigMap containing extra env vars for MySQL primary containers + ## + extraEnvVarsCM: "" + ## @param primary.extraEnvVarsSecret Name of existing Secret containing extra env vars for MySQL primary containers + ## + extraEnvVarsSecret: "" + ## @param primary.extraPorts Extra ports to expose + ## + extraPorts: [] + ## Enable persistence using Persistent Volume Claims + ## ref: https://kubernetes.io/docs/user-guide/persistent-volumes/ + ## + persistence: + ## @param primary.persistence.enabled Enable persistence on MySQL primary replicas using a `PersistentVolumeClaim`. If false, use emptyDir + ## + enabled: true + ## @param primary.persistence.existingClaim Name of an existing `PersistentVolumeClaim` for MySQL primary replicas + ## NOTE: When it's set the rest of persistence parameters are ignored + ## + existingClaim: "" + ## @param primary.persistence.subPath The name of a volume's sub path to mount for persistence + ## + subPath: "" + ## @param primary.persistence.storageClass MySQL primary persistent volume storage Class + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + storageClass: "" + ## @param primary.persistence.annotations MySQL primary persistent volume claim annotations + ## + annotations: {} + ## @param primary.persistence.accessModes MySQL primary persistent volume access Modes + ## + accessModes: + - ReadWriteOnce + ## @param primary.persistence.size MySQL primary persistent volume size + ## + size: 8Gi + ## @param primary.persistence.selector Selector to match an existing Persistent Volume + ## selector: + ## matchLabels: + ## app: my-app + ## + selector: {} + ## @param primary.extraVolumes Optionally specify extra list of additional volumes to the MySQL Primary pod(s) + ## + extraVolumes: [] + ## @param primary.extraVolumeMounts Optionally specify extra list of additional volumeMounts for the MySQL Primary container(s) + ## + extraVolumeMounts: [] + ## @param primary.initContainers Add additional init containers for the MySQL Primary pod(s) + ## + initContainers: [] + ## @param primary.sidecars Add additional sidecar containers for the MySQL Primary pod(s) + ## + sidecars: [] + ## MySQL Primary Service parameters + ## + service: + ## @param primary.service.type MySQL Primary K8s service type + ## + type: ClusterIP + ## @param primary.service.ports.mysql MySQL Primary K8s service port + ## + ports: + mysql: 3306 + ## @param primary.service.nodePorts.mysql MySQL Primary K8s service node port + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport + ## + nodePorts: + mysql: "" + ## @param primary.service.clusterIP MySQL Primary K8s service clusterIP IP + ## e.g: + ## clusterIP: None + ## + clusterIP: "" + ## @param primary.service.loadBalancerIP MySQL Primary loadBalancerIP if service type is `LoadBalancer` + ## Set the LoadBalancer service type to internal only + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer + ## + loadBalancerIP: "" + ## @param primary.service.externalTrafficPolicy Enable client source IP preservation + ## ref https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip + ## + externalTrafficPolicy: Cluster + ## @param primary.service.loadBalancerSourceRanges Addresses that are allowed when MySQL Primary service is LoadBalancer + ## https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/#restrict-access-for-loadbalancer-service + ## E.g. + ## loadBalancerSourceRanges: + ## - 10.10.10.0/24 + ## + loadBalancerSourceRanges: [] + ## @param primary.service.extraPorts Extra ports to expose (normally used with the `sidecar` value) + ## + extraPorts: [] + ## @param primary.service.annotations Additional custom annotations for MySQL primary service + ## + annotations: {} + ## @param primary.service.sessionAffinity Session Affinity for Kubernetes service, can be "None" or "ClientIP" + ## If "ClientIP", consecutive client requests will be directed to the same Pod + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies + ## + sessionAffinity: None + ## @param primary.service.sessionAffinityConfig Additional settings for the sessionAffinity + ## sessionAffinityConfig: + ## clientIP: + ## timeoutSeconds: 300 + ## + sessionAffinityConfig: {} + ## Headless service properties + ## + headless: + ## @param primary.service.headless.annotations Additional custom annotations for headless MySQL primary service. + ## + annotations: {} + + ## MySQL primary Pod Disruption Budget configuration + ## ref: https://kubernetes.io/docs/tasks/run-application/configure-pdb/ + ## + pdb: + ## @param primary.pdb.create Enable/disable a Pod Disruption Budget creation for MySQL primary pods + ## + create: false + ## @param primary.pdb.minAvailable Minimum number/percentage of MySQL primary pods that should remain scheduled + ## + minAvailable: 1 + ## @param primary.pdb.maxUnavailable Maximum number/percentage of MySQL primary pods that may be made unavailable + ## + maxUnavailable: "" + ## @param primary.podLabels MySQL Primary pod label. If labels are same as commonLabels , this will take precedence + ## + podLabels: {} + +## @section MySQL Secondary parameters +## + +secondary: + ## @param secondary.name Name of the secondary database (eg secondary, slave, ...) + ## + name: secondary + ## @param secondary.replicaCount Number of MySQL secondary replicas + ## + replicaCount: 1 + ## @param secondary.hostAliases Deployment pod host aliases + ## https://kubernetes.io/docs/concepts/services-networking/add-entries-to-pod-etc-hosts-with-host-aliases/ + ## + hostAliases: [] + ## @param secondary.command Override default container command on MySQL Secondary container(s) (useful when using custom images) + ## + command: [] + ## @param secondary.args Override default container args on MySQL Secondary container(s) (useful when using custom images) + ## + args: [] + ## @param secondary.lifecycleHooks for the MySQL Secondary container(s) to automate configuration before or after startup + ## + lifecycleHooks: {} + ## @param secondary.configuration [string] Configure MySQL Secondary with a custom my.cnf file + ## ref: https://mysql.com/kb/en/mysql/configuring-mysql-with-mycnf/#example-of-configuration-file + ## + configuration: |- + [mysqld] + default_authentication_plugin=mysql_native_password + skip-name-resolve + explicit_defaults_for_timestamp + basedir=/opt/bitnami/mysql + plugin_dir=/opt/bitnami/mysql/lib/plugin + port=3306 + socket=/opt/bitnami/mysql/tmp/mysql.sock + datadir=/bitnami/mysql/data + tmpdir=/opt/bitnami/mysql/tmp + max_allowed_packet=16M + bind-address=* + pid-file=/opt/bitnami/mysql/tmp/mysqld.pid + log-error=/opt/bitnami/mysql/logs/mysqld.log + character-set-server=UTF8 + collation-server=utf8_general_ci + slow_query_log=0 + slow_query_log_file=/opt/bitnami/mysql/logs/mysqld.log + long_query_time=10.0 + + [client] + port=3306 + socket=/opt/bitnami/mysql/tmp/mysql.sock + default-character-set=UTF8 + plugin_dir=/opt/bitnami/mysql/lib/plugin + + [manager] + port=3306 + socket=/opt/bitnami/mysql/tmp/mysql.sock + pid-file=/opt/bitnami/mysql/tmp/mysqld.pid + ## @param secondary.existingConfigmap Name of existing ConfigMap with MySQL Secondary configuration. + ## NOTE: When it's set the 'configuration' parameter is ignored + ## + existingConfigmap: "" + ## @param secondary.updateStrategy.type Update strategy type for the MySQL secondary statefulset + ## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies + ## + updateStrategy: + type: RollingUpdate + ## @param secondary.podAnnotations Additional pod annotations for MySQL secondary pods + ## ref: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + ## + podAnnotations: {} + ## @param secondary.podAffinityPreset MySQL secondary pod affinity preset. Ignored if `secondary.affinity` is set. Allowed values: `soft` or `hard` + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity + ## + podAffinityPreset: "" + ## @param secondary.podAntiAffinityPreset MySQL secondary pod anti-affinity preset. Ignored if `secondary.affinity` is set. Allowed values: `soft` or `hard` + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity + ## Allowed values: soft, hard + ## + podAntiAffinityPreset: soft + ## MySQL Secondary node affinity preset + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity + ## + nodeAffinityPreset: + ## @param secondary.nodeAffinityPreset.type MySQL secondary node affinity preset type. Ignored if `secondary.affinity` is set. Allowed values: `soft` or `hard` + ## + type: "" + ## @param secondary.nodeAffinityPreset.key MySQL secondary node label key to match Ignored if `secondary.affinity` is set. + ## E.g. + ## key: "kubernetes.io/e2e-az-name" + ## + key: "" + ## @param secondary.nodeAffinityPreset.values MySQL secondary node label values to match. Ignored if `secondary.affinity` is set. + ## E.g. + ## values: + ## - e2e-az1 + ## - e2e-az2 + ## + values: [] + ## @param secondary.affinity Affinity for MySQL secondary pods assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity + ## Note: podAffinityPreset, podAntiAffinityPreset, and nodeAffinityPreset will be ignored when it's set + ## + affinity: {} + ## @param secondary.nodeSelector Node labels for MySQL secondary pods assignment + ## ref: https://kubernetes.io/docs/user-guide/node-selection/ + ## + nodeSelector: {} + ## @param secondary.tolerations Tolerations for MySQL secondary pods assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ + ## + tolerations: [] + ## @param secondary.priorityClassName MySQL secondary pods' priorityClassName + ## + priorityClassName: "" + ## @param secondary.runtimeClassName MySQL secondary pods' runtimeClassName + ## + runtimeClassName: "" + ## @param secondary.schedulerName Name of the k8s scheduler (other than default) + ## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ + ## + schedulerName: "" + ## @param secondary.terminationGracePeriodSeconds In seconds, time the given to the MySQL secondary pod needs to terminate gracefully + ## ref: https://kubernetes.io/docs/concepts/workloads/pods/pod/#termination-of-pods + ## + terminationGracePeriodSeconds: "" + ## @param secondary.topologySpreadConstraints Topology Spread Constraints for pod assignment + ## https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ + ## The value is evaluated as a template + ## + topologySpreadConstraints: [] + ## @param secondary.podManagementPolicy podManagementPolicy to manage scaling operation of MySQL secondary pods + ## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#pod-management-policies + ## + podManagementPolicy: "" + ## MySQL secondary Pod security context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod + ## @param secondary.podSecurityContext.enabled Enable security context for MySQL secondary pods + ## @param secondary.podSecurityContext.fsGroup Group ID for the mounted volumes' filesystem + ## + podSecurityContext: + enabled: true + fsGroup: 1001 + ## MySQL secondary container security context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container + ## @param secondary.containerSecurityContext.enabled MySQL secondary container securityContext + ## @param secondary.containerSecurityContext.runAsUser User ID for the MySQL secondary container + ## @param secondary.containerSecurityContext.runAsNonRoot Set MySQL secondary container's Security Context runAsNonRoot + ## + containerSecurityContext: + enabled: true + runAsUser: 1001 + runAsNonRoot: true + ## MySQL secondary container's resource requests and limits + ## ref: https://kubernetes.io/docs/user-guide/compute-resources/ + ## We usually recommend not to specify default resources and to leave this as a conscious + ## choice for the user. This also increases chances charts run on environments with little + ## resources, such as Minikube. If you do want to specify resources, uncomment the following + ## lines, adjust them as necessary, and remove the curly braces after 'resources:'. + ## @param secondary.resources.limits The resources limits for MySQL secondary containers + ## @param secondary.resources.requests The requested resources for MySQL secondary containers + ## + resources: + ## Example: + ## limits: + ## cpu: 250m + ## memory: 256Mi + ## + limits: {} + ## Examples: + ## requests: + ## cpu: 250m + ## memory: 256Mi + ## + requests: {} + ## Configure extra options for liveness probe + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes + ## @param secondary.livenessProbe.enabled Enable livenessProbe + ## @param secondary.livenessProbe.initialDelaySeconds Initial delay seconds for livenessProbe + ## @param secondary.livenessProbe.periodSeconds Period seconds for livenessProbe + ## @param secondary.livenessProbe.timeoutSeconds Timeout seconds for livenessProbe + ## @param secondary.livenessProbe.failureThreshold Failure threshold for livenessProbe + ## @param secondary.livenessProbe.successThreshold Success threshold for livenessProbe + ## + livenessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 1 + failureThreshold: 3 + successThreshold: 1 + ## Configure extra options for readiness probe + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes + ## @param secondary.readinessProbe.enabled Enable readinessProbe + ## @param secondary.readinessProbe.initialDelaySeconds Initial delay seconds for readinessProbe + ## @param secondary.readinessProbe.periodSeconds Period seconds for readinessProbe + ## @param secondary.readinessProbe.timeoutSeconds Timeout seconds for readinessProbe + ## @param secondary.readinessProbe.failureThreshold Failure threshold for readinessProbe + ## @param secondary.readinessProbe.successThreshold Success threshold for readinessProbe + ## + readinessProbe: + enabled: true + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 1 + failureThreshold: 3 + successThreshold: 1 + ## Configure extra options for startupProbe probe + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/#configure-probes + ## @param secondary.startupProbe.enabled Enable startupProbe + ## @param secondary.startupProbe.initialDelaySeconds Initial delay seconds for startupProbe + ## @param secondary.startupProbe.periodSeconds Period seconds for startupProbe + ## @param secondary.startupProbe.timeoutSeconds Timeout seconds for startupProbe + ## @param secondary.startupProbe.failureThreshold Failure threshold for startupProbe + ## @param secondary.startupProbe.successThreshold Success threshold for startupProbe + ## + startupProbe: + enabled: true + initialDelaySeconds: 15 + periodSeconds: 10 + timeoutSeconds: 1 + failureThreshold: 15 + successThreshold: 1 + ## @param secondary.customLivenessProbe Override default liveness probe for MySQL secondary containers + ## + customLivenessProbe: {} + ## @param secondary.customReadinessProbe Override default readiness probe for MySQL secondary containers + ## + customReadinessProbe: {} + ## @param secondary.customStartupProbe Override default startup probe for MySQL secondary containers + ## + customStartupProbe: {} + ## @param secondary.extraFlags MySQL secondary additional command line flags + ## Can be used to specify command line flags, for example: + ## E.g. + ## extraFlags: "--max-connect-errors=1000 --max_connections=155" + ## + extraFlags: "" + ## @param secondary.extraEnvVars An array to add extra environment variables on MySQL secondary containers + ## E.g. + ## extraEnvVars: + ## - name: TZ + ## value: "Europe/Paris" + ## + extraEnvVars: [] + ## @param secondary.extraEnvVarsCM Name of existing ConfigMap containing extra env vars for MySQL secondary containers + ## + extraEnvVarsCM: "" + ## @param secondary.extraEnvVarsSecret Name of existing Secret containing extra env vars for MySQL secondary containers + ## + extraEnvVarsSecret: "" + ## @param secondary.extraPorts Extra ports to expose + ## + extraPorts: [] + ## Enable persistence using Persistent Volume Claims + ## ref: https://kubernetes.io/docs/user-guide/persistent-volumes/ + ## + persistence: + ## @param secondary.persistence.enabled Enable persistence on MySQL secondary replicas using a `PersistentVolumeClaim` + ## + enabled: true + ## @param secondary.persistence.existingClaim Name of an existing `PersistentVolumeClaim` for MySQL secondary replicas + ## NOTE: When it's set the rest of persistence parameters are ignored + ## + existingClaim: "" + ## @param secondary.persistence.subPath The name of a volume's sub path to mount for persistence + ## + subPath: "" + ## @param secondary.persistence.storageClass MySQL secondary persistent volume storage Class + ## If defined, storageClassName: + ## If set to "-", storageClassName: "", which disables dynamic provisioning + ## If undefined (the default) or set to null, no storageClassName spec is + ## set, choosing the default provisioner. (gp2 on AWS, standard on + ## GKE, AWS & OpenStack) + ## + storageClass: "" + ## @param secondary.persistence.annotations MySQL secondary persistent volume claim annotations + ## + annotations: {} + ## @param secondary.persistence.accessModes MySQL secondary persistent volume access Modes + ## + accessModes: + - ReadWriteOnce + ## @param secondary.persistence.size MySQL secondary persistent volume size + ## + size: 8Gi + ## @param secondary.persistence.selector Selector to match an existing Persistent Volume + ## selector: + ## matchLabels: + ## app: my-app + ## + selector: {} + ## @param secondary.extraVolumes Optionally specify extra list of additional volumes to the MySQL secondary pod(s) + ## + extraVolumes: [] + ## @param secondary.extraVolumeMounts Optionally specify extra list of additional volumeMounts for the MySQL secondary container(s) + ## + extraVolumeMounts: [] + ## @param secondary.initContainers Add additional init containers for the MySQL secondary pod(s) + ## + initContainers: [] + ## @param secondary.sidecars Add additional sidecar containers for the MySQL secondary pod(s) + ## + sidecars: [] + ## MySQL Secondary Service parameters + ## + service: + ## @param secondary.service.type MySQL secondary Kubernetes service type + ## + type: ClusterIP + ## @param secondary.service.ports.mysql MySQL secondary Kubernetes service port + ## + ports: + mysql: 3306 + ## @param secondary.service.nodePorts.mysql MySQL secondary Kubernetes service node port + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#type-nodeport + ## + nodePorts: + mysql: "" + ## @param secondary.service.clusterIP MySQL secondary Kubernetes service clusterIP IP + ## e.g: + ## clusterIP: None + ## + clusterIP: "" + ## @param secondary.service.loadBalancerIP MySQL secondary loadBalancerIP if service type is `LoadBalancer` + ## Set the LoadBalancer service type to internal only + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#internal-load-balancer + ## + loadBalancerIP: "" + ## @param secondary.service.externalTrafficPolicy Enable client source IP preservation + ## ref https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip + ## + externalTrafficPolicy: Cluster + ## @param secondary.service.loadBalancerSourceRanges Addresses that are allowed when MySQL secondary service is LoadBalancer + ## https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/#restrict-access-for-loadbalancer-service + ## E.g. + ## loadBalancerSourceRanges: + ## - 10.10.10.0/24 + ## + loadBalancerSourceRanges: [] + ## @param secondary.service.extraPorts Extra ports to expose (normally used with the `sidecar` value) + ## + extraPorts: [] + ## @param secondary.service.annotations Additional custom annotations for MySQL secondary service + ## + annotations: {} + ## @param secondary.service.sessionAffinity Session Affinity for Kubernetes service, can be "None" or "ClientIP" + ## If "ClientIP", consecutive client requests will be directed to the same Pod + ## ref: https://kubernetes.io/docs/concepts/services-networking/service/#virtual-ips-and-service-proxies + ## + sessionAffinity: None + ## @param secondary.service.sessionAffinityConfig Additional settings for the sessionAffinity + ## sessionAffinityConfig: + ## clientIP: + ## timeoutSeconds: 300 + ## + sessionAffinityConfig: {} + ## Headless service properties + ## + headless: + ## @param secondary.service.headless.annotations Additional custom annotations for headless MySQL secondary service. + ## + annotations: {} + + ## MySQL secondary Pod Disruption Budget configuration + ## ref: https://kubernetes.io/docs/tasks/run-application/configure-pdb/ + ## + pdb: + ## @param secondary.pdb.create Enable/disable a Pod Disruption Budget creation for MySQL secondary pods + ## + create: false + ## @param secondary.pdb.minAvailable Minimum number/percentage of MySQL secondary pods that should remain scheduled + ## + minAvailable: 1 + ## @param secondary.pdb.maxUnavailable Maximum number/percentage of MySQL secondary pods that may be made unavailable + ## + maxUnavailable: "" + ## @param secondary.podLabels Additional pod labels for MySQL secondary pods + ## + podLabels: {} + +## @section RBAC parameters +## + +## MySQL pods ServiceAccount +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/ +## +serviceAccount: + ## @param serviceAccount.create Enable the creation of a ServiceAccount for MySQL pods + ## + create: true + ## @param serviceAccount.name Name of the created ServiceAccount + ## If not set and create is true, a name is generated using the mysql.fullname template + ## + name: "" + ## @param serviceAccount.annotations Annotations for MySQL Service Account + ## + annotations: {} + ## @param serviceAccount.automountServiceAccountToken Automount service account token for the server service account + ## + automountServiceAccountToken: true + +## Role Based Access +## ref: https://kubernetes.io/docs/admin/authorization/rbac/ +## +rbac: + ## @param rbac.create Whether to create & use RBAC resources or not + ## + create: false + ## @param rbac.rules Custom RBAC rules to set + ## e.g: + ## rules: + ## - apiGroups: + ## - "" + ## resources: + ## - pods + ## verbs: + ## - get + ## - list + ## + rules: [] + +## @section Network Policy +## + +## MySQL Nework Policy configuration +## +networkPolicy: + ## @param networkPolicy.enabled Enable creation of NetworkPolicy resources + ## + enabled: false + ## @param networkPolicy.allowExternal The Policy model to apply. + ## When set to false, only pods with the correct + ## client label will have network access to the port MySQL is listening + ## on. When true, MySQL will accept connections from any source + ## (with the correct destination port). + ## + allowExternal: true + ## @param networkPolicy.explicitNamespacesSelector A Kubernetes LabelSelector to explicitly select namespaces from which ingress traffic could be allowed to MySQL + ## If explicitNamespacesSelector is missing or set to {}, only client Pods that are in the networkPolicy's namespace + ## and that match other criteria, the ones that have the good label, can reach the DB. + ## But sometimes, we want the DB to be accessible to clients from other namespaces, in this case, we can use this + ## LabelSelector to select these namespaces, note that the networkPolicy's namespace should also be explicitly added. + ## + ## Example: + ## explicitNamespacesSelector: + ## matchLabels: + ## role: frontend + ## matchExpressions: + ## - {key: role, operator: In, values: [frontend]} + ## + explicitNamespacesSelector: {} + +## @section Volume Permissions parameters +## + +## Init containers parameters: +## volumePermissions: Change the owner and group of the persistent volume mountpoint to runAsUser:fsGroup values from the securityContext section. +## +volumePermissions: + ## @param volumePermissions.enabled Enable init container that changes the owner and group of the persistent volume(s) mountpoint to `runAsUser:fsGroup` + ## + enabled: false + ## @param volumePermissions.image.registry Init container volume-permissions image registry + ## @param volumePermissions.image.repository Init container volume-permissions image repository + ## @param volumePermissions.image.tag Init container volume-permissions image tag (immutable tags are recommended) + ## @param volumePermissions.image.digest Init container volume-permissions image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag + ## @param volumePermissions.image.pullPolicy Init container volume-permissions image pull policy + ## @param volumePermissions.image.pullSecrets Specify docker-registry secret names as an array + ## + image: + registry: docker.io + repository: bitnami/bitnami-shell + tag: 11-debian-11-r108 + digest: "" + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## e.g: + ## pullSecrets: + ## - myRegistryKeySecretName + ## + pullSecrets: [] + ## @param volumePermissions.resources Init container volume-permissions resources + ## + resources: {} + +## @section Metrics parameters +## + +## Mysqld Prometheus exporter parameters +## +metrics: + ## @param metrics.enabled Start a side-car prometheus exporter + ## + enabled: false + ## @param metrics.image.registry Exporter image registry + ## @param metrics.image.repository Exporter image repository + ## @param metrics.image.tag Exporter image tag (immutable tags are recommended) + ## @param metrics.image.digest Exporter image digest in the way sha256:aa.... Please note this parameter, if set, will override the tag + ## @param metrics.image.pullPolicy Exporter image pull policy + ## @param metrics.image.pullSecrets Specify docker-registry secret names as an array + ## + image: + registry: docker.io + repository: bitnami/mysqld-exporter + tag: 0.14.0-debian-11-r109 + digest: "" + pullPolicy: IfNotPresent + ## Optionally specify an array of imagePullSecrets. + ## Secrets must be manually created in the namespace. + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ + ## e.g: + ## pullSecrets: + ## - myRegistryKeySecretName + ## + pullSecrets: [] + ## MySQL metrics container security context + ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container + ## @param metrics.containerSecurityContext.enabled MySQL metrics container securityContext + ## @param metrics.containerSecurityContext.runAsUser User ID for the MySQL metrics container + ## @param metrics.containerSecurityContext.runAsNonRoot Set MySQL metrics container's Security Context runAsNonRoot + ## + containerSecurityContext: + enabled: true + runAsUser: 1001 + runAsNonRoot: true + ## MySQL Prometheus exporter service parameters + ## Mysqld Prometheus exporter liveness and readiness probes + ## ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes + ## @param metrics.service.type Kubernetes service type for MySQL Prometheus Exporter + ## @param metrics.service.port MySQL Prometheus Exporter service port + ## @param metrics.service.annotations [object] Prometheus exporter service annotations + ## + service: + type: ClusterIP + port: 9104 + annotations: + prometheus.io/scrape: "true" + prometheus.io/port: "{{ .Values.metrics.service.port }}" + ## @param metrics.extraArgs.primary Extra args to be passed to mysqld_exporter on Primary pods + ## @param metrics.extraArgs.secondary Extra args to be passed to mysqld_exporter on Secondary pods + ## ref: https://github.com/prometheus/mysqld_exporter/ + ## E.g. + ## - --collect.auto_increment.columns + ## - --collect.binlog_size + ## - --collect.engine_innodb_status + ## - --collect.engine_tokudb_status + ## - --collect.global_status + ## - --collect.global_variables + ## - --collect.info_schema.clientstats + ## - --collect.info_schema.innodb_metrics + ## - --collect.info_schema.innodb_tablespaces + ## - --collect.info_schema.innodb_cmp + ## - --collect.info_schema.innodb_cmpmem + ## - --collect.info_schema.processlist + ## - --collect.info_schema.processlist.min_time + ## - --collect.info_schema.query_response_time + ## - --collect.info_schema.tables + ## - --collect.info_schema.tables.databases + ## - --collect.info_schema.tablestats + ## - --collect.info_schema.userstats + ## - --collect.perf_schema.eventsstatements + ## - --collect.perf_schema.eventsstatements.digest_text_limit + ## - --collect.perf_schema.eventsstatements.limit + ## - --collect.perf_schema.eventsstatements.timelimit + ## - --collect.perf_schema.eventswaits + ## - --collect.perf_schema.file_events + ## - --collect.perf_schema.file_instances + ## - --collect.perf_schema.indexiowaits + ## - --collect.perf_schema.tableiowaits + ## - --collect.perf_schema.tablelocks + ## - --collect.perf_schema.replication_group_member_stats + ## - --collect.slave_status + ## - --collect.slave_hosts + ## - --collect.heartbeat + ## - --collect.heartbeat.database + ## - --collect.heartbeat.table + ## + extraArgs: + primary: [] + secondary: [] + ## Mysqld Prometheus exporter resource requests and limits + ## ref: https://kubernetes.io/docs/user-guide/compute-resources/ + ## We usually recommend not to specify default resources and to leave this as a conscious + ## choice for the user. This also increases chances charts run on environments with little + ## resources, such as Minikube. If you do want to specify resources, uncomment the following + ## lines, adjust them as necessary, and remove the curly braces after 'resources:'. + ## @param metrics.resources.limits The resources limits for MySQL prometheus exporter containers + ## @param metrics.resources.requests The requested resources for MySQL prometheus exporter containers + ## + resources: + ## Example: + ## limits: + ## cpu: 100m + ## memory: 256Mi + ## + limits: {} + ## Examples: + ## requests: + ## cpu: 100m + ## memory: 256Mi + ## + requests: {} + ## Mysqld Prometheus exporter liveness probe + ## ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes + ## @param metrics.livenessProbe.enabled Enable livenessProbe + ## @param metrics.livenessProbe.initialDelaySeconds Initial delay seconds for livenessProbe + ## @param metrics.livenessProbe.periodSeconds Period seconds for livenessProbe + ## @param metrics.livenessProbe.timeoutSeconds Timeout seconds for livenessProbe + ## @param metrics.livenessProbe.failureThreshold Failure threshold for livenessProbe + ## @param metrics.livenessProbe.successThreshold Success threshold for livenessProbe + ## + livenessProbe: + enabled: true + initialDelaySeconds: 120 + periodSeconds: 10 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 3 + ## Mysqld Prometheus exporter readiness probe + ## ref: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#container-probes + ## @param metrics.readinessProbe.enabled Enable readinessProbe + ## @param metrics.readinessProbe.initialDelaySeconds Initial delay seconds for readinessProbe + ## @param metrics.readinessProbe.periodSeconds Period seconds for readinessProbe + ## @param metrics.readinessProbe.timeoutSeconds Timeout seconds for readinessProbe + ## @param metrics.readinessProbe.failureThreshold Failure threshold for readinessProbe + ## @param metrics.readinessProbe.successThreshold Success threshold for readinessProbe + ## + readinessProbe: + enabled: true + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 3 + ## Prometheus Service Monitor + ## ref: https://github.com/coreos/prometheus-operator + ## + serviceMonitor: + ## @param metrics.serviceMonitor.enabled Create ServiceMonitor Resource for scraping metrics using PrometheusOperator + ## + enabled: false + ## @param metrics.serviceMonitor.namespace Specify the namespace in which the serviceMonitor resource will be created + ## + namespace: "" + ## @param metrics.serviceMonitor.jobLabel The name of the label on the target service to use as the job name in prometheus. + ## + jobLabel: "" + ## @param metrics.serviceMonitor.interval Specify the interval at which metrics should be scraped + ## + interval: 30s + ## @param metrics.serviceMonitor.scrapeTimeout Specify the timeout after which the scrape is ended + ## e.g: + ## scrapeTimeout: 30s + ## + scrapeTimeout: "" + ## @param metrics.serviceMonitor.relabelings RelabelConfigs to apply to samples before scraping + ## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#relabelconfig + ## + relabelings: [] + ## @param metrics.serviceMonitor.metricRelabelings MetricRelabelConfigs to apply to samples before ingestion + ## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#relabelconfig + ## + metricRelabelings: [] + ## @param metrics.serviceMonitor.selector ServiceMonitor selector labels + ## ref: https://github.com/bitnami/charts/tree/main/bitnami/prometheus-operator#prometheus-configuration + ## + ## selector: + ## prometheus: my-prometheus + ## + selector: {} + ## @param metrics.serviceMonitor.honorLabels Specify honorLabels parameter to add the scrape endpoint + ## + honorLabels: false + ## @param metrics.serviceMonitor.labels Used to pass Labels that are used by the Prometheus installed in your cluster to select Service Monitors to work with + ## ref: https://github.com/coreos/prometheus-operator/blob/master/Documentation/api.md#prometheusspec + ## + labels: {} + ## @param metrics.serviceMonitor.annotations ServiceMonitor annotations + ## + annotations: {} + + ## Prometheus Operator prometheusRule configuration + ## + prometheusRule: + ## @param metrics.prometheusRule.enabled Creates a Prometheus Operator prometheusRule (also requires `metrics.enabled` to be `true` and `metrics.prometheusRule.rules`) + ## + enabled: false + ## @param metrics.prometheusRule.namespace Namespace for the prometheusRule Resource (defaults to the Release Namespace) + ## + namespace: "" + ## @param metrics.prometheusRule.additionalLabels Additional labels that can be used so prometheusRule will be discovered by Prometheus + ## + additionalLabels: {} + ## @param metrics.prometheusRule.rules Prometheus Rule definitions + ## - alert: Mysql-Down + ## expr: absent(up{job="mysql"} == 1) + ## for: 5m + ## labels: + ## severity: warning + ## service: mariadb + ## annotations: + ## message: 'MariaDB instance {{`{{`}} $labels.instance {{`}}`}} is down' + ## summary: MariaDB instance is down + ## + rules: [] diff --git a/charts/deps/charts/opensearch-2.12.1/.helmignore b/charts/deps/charts/opensearch-2.12.1/.helmignore new file mode 100644 index 0000000..0e8a0eb --- /dev/null +++ b/charts/deps/charts/opensearch-2.12.1/.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/deps/charts/opensearch-2.12.1/CHANGELOG.md b/charts/deps/charts/opensearch-2.12.1/CHANGELOG.md new file mode 100644 index 0000000..41868f0 --- /dev/null +++ b/charts/deps/charts/opensearch-2.12.1/CHANGELOG.md @@ -0,0 +1,282 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +--- +## [Unreleased] +### Added +### Changed +### Deprecated +### Removed +### Fixed +### Security +--- +## [2.12.1] +### Added +### Changed +### Deprecated +### Removed +### Fixed +- Add imagePullPolicy to fsgroup-volume init container +### Security +--- +## [2.12.0] +### Added +- Updated OpenSearch appVersion to 2.7.0 +### Changed +### Deprecated +### Removed +### Fixed +### Security +--- +## [2.11.5] +### Added +- Update the Readme and comments section. +### Changed +### Deprecated +### Removed +### Fixed +### Security +--- +## [2.11.4] +### Added +- Added custom opensearch deployment annotation through values.yaml +### Changed +### Deprecated +### Removed +### Fixed +### Security +--- +## [2.11.3] +### Added +- Support setting ipFamilyPolicy on Service +- Support setting ipFamilies on Service +--- +## [2.11.2] +### Added +- Service ports for performance analyzer +### Changed +### Deprecated +### Removed +### Fixed +### Security +--- +## [2.11.1] +### Added +- Support for lifecycle in the opensearch container in the StatefulSet +### Changed +### Deprecated +### Removed +### Fixed +### Security +--- +## [2.11.0] +### Added +### Changed +- Updated OpenSearch appVersion to 2.6.0 +### Deprecated +### Removed +### Fixed +### Security +--- +## [2.10.0] +### Added +### Changed +- Updated OpenSearch appVersion to 2.5.0 +### Deprecated +### Removed +### Fixed +### Security +--- +## [2.9.1] +### Added +- Support for http- and transport-hostPort +### Changed +### Deprecated +### Removed +### Fixed +### Security +--- +## [2.9.0] +### Added +### Changed +- Updated OpenSearch appVersion to 2.4.1 +### Deprecated +### Removed +### Fixed +### Security +--- +## [2.8.2] +### Added +### Changed +### Deprecated +### Removed +### Fixed +- Fix path in securityConfig section, it was changed in ver.2. See: [Issue #127](https://github.com/opensearch-project/opensearch-plugins/issues/127) +### Security +--- +## [2.8.1] +### Added +- added "plugins.enabled" and "plugins.installList" to the readme +### Changed +- Bumped version to 2.8.1 +### Deprecated +### Removed +### Fixed +### Security +--- +## [2.8.0] +### Added +### Changed +- Updated OpenSearch appVersion to 2.4.0 +### Deprecated +### Removed +### Fixed +### Security +--- +## [2.7.0] +### Added +- Add option to enable the use of `sysctlInit` to set sysctl vm.max_map_count through privileged `initContainer`. See: [Issue #87](https://github.com/opensearch-project/helm-charts/issues/87) +### Changed +### Deprecated +### Removed +### Fixed +### Security +--- +## [2.6.2] +### Added +- Liveness probe for statefulset +### Changed +### Deprecated +### Removed +### Fixed +### Security +--- +## [2.6.1] +### Added +- Template configmap content by tpl function +### Changed +### Deprecated +### Removed +### Fixed +### Security +--- +## [2.6.0] +### Added +### Changed +- Updated version to 2.6.0 and appVersion to "2.3.0". +### Deprecated +### Removed +### Fixed +### Security +--- +## [2.5.1] +### Added +- Helm chart-releaser parallel release issue, updated version to 2.5.1. +### Changed +### Deprecated +### Removed +### Fixed +### Security +--- +## [2.5.0] +### Added +- Updated version to 2.5.0 and appVersion to "2.2.1". +### Changed +### Deprecated +### Removed +### Fixed +### Security +--- +## [2.4.1] +### Added +- Add "singleNode" feature to disable the "cluster.initial_master_nodes" env var +### Changed +### Deprecated +### Removed +### Fixed +### Security +--- +## [2.4.0] +### Added +- Updated version to 2.4.0 and appVersion to "2.2.0". +### Changed +### Deprecated +### Removed +### Fixed +### Security +--- +## [2.3.0] +### Added +- Updated version to 2.3.0 and appVersion to "2.1.0". +### Changed +### Deprecated +### Removed +### Fixed +### Security +--- +## [2.2.0] +### Added +- Add feature for readinessProbe and startupProbe +### Changed +### Deprecated +### Removed +### Fixed +### Security +--- +## [2.1.0] +### Added +### Changed +- Updated version to 2.1.0 and appVersion to "2.0.1". +### Deprecated +### Removed +### Fixed +### Security +--- +## [2.0.1] +### Added +### Changed +- Updated version to 2.0.1 and appVersion to "2.0.0". +### Deprecated +### Removed +### Fixed +### Security +--- +## [2.0.0] +### Added +### Changed +- Updated version to 2.0.0 and appVersion to "2.0.0-rc1". +### Deprecated +### Removed +### Fixed +### Security + + +[Unreleased]: https://github.com/opensearch-project/helm-charts/compare/opensearch-2.12.0...HEAD +[2.12.0]: https://github.com/opensearch-project/helm-charts/compare/opensearch-2.11.5...opensearch-2.12.0 +[2.11.5]: https://github.com/opensearch-project/helm-charts/compare/opensearch-2.11.4...opensearch-2.11.5 +[2.11.4]: https://github.com/opensearch-project/helm-charts/compare/opensearch-2.11.3...opensearch-2.11.4 +[2.11.3]: https://github.com/opensearch-project/helm-charts/compare/opensearch-2.11.2...opensearch-2.11.3 +[2.11.2]: https://github.com/opensearch-project/helm-charts/compare/opensearch-2.11.1...opensearch-2.11.2 +[2.11.1]: https://github.com/opensearch-project/helm-charts/compare/opensearch-2.11.0...opensearch-2.11.1 +[2.11.0]: https://github.com/opensearch-project/helm-charts/compare/opensearch-2.10.0...opensearch-2.11.0 +[2.10.0]: https://github.com/opensearch-project/helm-charts/compare/opensearch-2.9.1...opensearch-2.10.0 +[2.9.1]: https://github.com/opensearch-project/helm-charts/compare/opensearch-2.9.0...opensearch-2.9.1 +[2.9.0]: https://github.com/opensearch-project/helm-charts/compare/opensearch-2.8.2...opensearch-2.9.0 +[2.8.2]: https://github.com/opensearch-project/helm-charts/compare/opensearch-2.8.1...opensearch-2.8.2 +[2.8.1]: https://github.com/opensearch-project/helm-charts/compare/opensearch-2.8.0...opensearch-2.8.1 +[2.8.0]: https://github.com/opensearch-project/helm-charts/compare/opensearch-2.7.0...opensearch-2.8.0 +[2.7.0]: https://github.com/opensearch-project/helm-charts/compare/opensearch-2.6.1...opensearch-2.7.0 +[2.6.2]: https://github.com/opensearch-project/helm-charts/compare/opensearch-2.6.1...opensearch-2.6.2 +[2.6.1]: https://github.com/opensearch-project/helm-charts/compare/opensearch-2.6.0...opensearch-2.6.1 +[2.6.0]: https://github.com/opensearch-project/helm-charts/compare/opensearch-2.5.0...opensearch-2.6.0 +[2.5.1]: https://github.com/opensearch-project/helm-charts/compare/opensearch-2.5.0...opensearch-2.5.1 +[2.5.0]: https://github.com/opensearch-project/helm-charts/compare/opensearch-2.4.1...opensearch-2.5.0 +[2.4.1]: https://github.com/opensearch-project/helm-charts/compare/opensearch-2.4.0...opensearch-2.4.1 +[2.4.0]: https://github.com/opensearch-project/helm-charts/compare/opensearch-2.3.0...opensearch-2.4.0 +[2.3.0]: https://github.com/opensearch-project/helm-charts/compare/opensearch-2.2.0...opensearch-2.3.0 +[2.2.0]: https://github.com/opensearch-project/helm-charts/compare/opensearch-2.1.0...opensearch-2.2.0 +[2.1.0]: https://github.com/opensearch-project/helm-charts/compare/opensearch-2.0.1...opensearch-2.1.0 +[2.0.1]: https://github.com/opensearch-project/helm-charts/compare/opensearch-2.0.0...opensearch-2.0.1 diff --git a/charts/deps/charts/opensearch-2.12.1/Chart.yaml b/charts/deps/charts/opensearch-2.12.1/Chart.yaml new file mode 100644 index 0000000..ae5b7e0 --- /dev/null +++ b/charts/deps/charts/opensearch-2.12.1/Chart.yaml @@ -0,0 +1,13 @@ +apiVersion: v2 +appVersion: 2.7.0 +description: A Helm chart for OpenSearch +maintainers: +- name: DandyDeveloper +- name: bbarani +- name: gaiksaya +- name: peterzhuamazon +- name: prudhvigodithi +- name: TheAlgo +name: opensearch +type: application +version: 2.12.1 diff --git a/charts/deps/charts/opensearch-2.12.1/README.md b/charts/deps/charts/opensearch-2.12.1/README.md new file mode 100644 index 0000000..2a3aee3 --- /dev/null +++ b/charts/deps/charts/opensearch-2.12.1/README.md @@ -0,0 +1,184 @@ +# OpenSearch Helm Chart + +This Helm chart installs [OpenSearch](https://github.com/opensearch-project/OpenSearch) with configurable TLS, RBAC and much more configurations. This chart caters a number of different use cases and setups. + +- [OpenSearch Helm Chart](#opensearch-helm-chart) + - [Requirements](#requirements) + - [Installing](#installing) + - [Uninstalling](#uninstalling) + - [Configuration](#configuration) + +## Requirements + +* Kubernetes >= 1.14 +* Helm >= 2.17.0 +* We recommend you to have 8 GiB of memory available for this deployment, or at least 4 GiB for the minimum requirement. Else, the deployment is expected to fail. + +## Installing + +Once you've added this Helm repository as per the repository-level [README](../../README.md#installing) then you can install the chart as follows: + + ```shell + helm install my-release opensearch/opensearch + ``` + +The command deploys OpenSearch with its associated components (data statefulsets, masters, clients) on the Kubernetes cluster in the default configuration. + +**NOTE:** If using Helm 2 then you'll need to add the [`--name`](https://v2.helm.sh/docs/helm/#options-21) command line argument. If unspecified, Helm 2 will autogenerate a name for you. + +## Uninstalling +To delete/uninstall the chart with the release name `my-release`: + +```shell +helm uninstall my-release +``` + +## Configuration + +| Parameter | Description | Default | +|------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------| +| `antiAffinityTopologyKey` | The [anti-affinity][] topology key. By default this will prevent multiple Opensearch nodes from running on the same Kubernetes node | `kubernetes.io/hostname` | +| `antiAffinity` | Setting this to hard enforces the [anti-affinity][] rules. If it is set to soft it will be done "best effort". Other values will be ignored | `hard` | +| `clusterName` | This will be used as the OpenSearch cluster name and should be unique per cluster in the namespace | `opensearch-cluster` | +| `enableServiceLinks` | Set to false to disabling service links, which can cause slow pod startup times when there are many services in the current namespace. | `true` | +| `envFrom` | Templatable string to be passed to the [environment from variables][] which will be appended to the `envFrom:` definition for the container | `[]` | +| `config` | Allows you to add any config files in `/usr/share/opensearch/config/` such as `opensearch.yml` and `log4j2.properties`. String or map format may be used for specifying content of each configuration file. In case of string format, the whole content of the config file will be replaced by new config file value when in case of using map format content of configuration file will be a result of merge. In both cases content passed through tpl. See [values.yaml][] for an example of the formatting (passed through tpl) | `{}` | +| `opensearchJavaOpts` | Java options for OpenSearch. This is where you should configure the jvm heap size | `-Xmx1g -Xms1g` | +| `majorVersion` | Used to set major version specific configuration. If you are using a custom image and not running the default OpenSearch version you will need to set this to the version you are running (e.g. `majorVersion: 1`) | `""` | +| `global.dockerRegistry` | Set if you want to change the default docker registry, e.g. a private one. | `""` | +| `extraContainers` | Array of extra containers | `""` | +| `extraEnvs` | Extra environments variables to be passed to OpenSearch services | `[]` | +| `extraInitContainers` | Array of extra init containers | `[]` | +| `extraVolumeMounts` | Array of extra volume mounts | `[] ` | +| `extraVolumes` | Array of extra volumes to be added | `[]` | +| `fullnameOverride` | Overrides the `clusterName` and `nodeGroup` when used in the naming of resources. This should only be used when using a single `nodeGroup`, otherwise you will have name conflicts | `""` | +| `hostAliases` | Configurable [hostAliases][] | `[]` | +| `httpHostPort` | Expose another http-port as hostPort. Refer to documentation for more information and requirements about using hostPorts. | `""` | +| `httpPort` | The http port that Kubernetes will use for the healthchecks and the service. If you change this you will also need to set `http.port` in `extraEnvs` | `9200` | +| `image.pullPolicy` | The Kubernetes [imagePullPolicy][] value | `IfNotPresent` | +| `imagePullSecrets` | Configuration for [imagePullSecrets][] so that you can use a private registry for your image | `[]` | +| `image.tag` | The OpenSearch Docker image tag | `1.0.0` | +| `image.repository` | The OpenSearch Docker image | `opensearchproject/opensearch` | +| `ingress` | Configurable [ingress][] to expose the OpenSearch service. See [values.yaml][] for an example | see [values.yaml][] | +| `initResources` | Allows you to set the [resources][] for the `initContainer` in the StatefulSet | `{}` | +| `keystore` | Allows you map Kubernetes secrets into the keystore. | `[]` | +| `labels` | Configurable [labels][] applied to all OpenSearch pods | `{}` | +| `masterService` | The service name used to connect to the masters. You only need to set this if your master `nodeGroup` is set to something other than `master` | `""` | +| `maxUnavailable` | The [maxUnavailable][] value for the pod disruption budget. By default this will prevent Kubernetes from having more than 1 unhealthy pod in the node group | `1` | +| `metricsPort` | The metrics port (for Performance Analyzer) that Kubernetes will use for the service. | `9600` | +| `nameOverride` | Overrides the `clusterName` when used in the naming of resources | `""` | +| `networkHost` | Value for the `network.host OpenSearch setting` | `0.0.0.0` | +| `networkPolicy.create` | Enable network policy creation for OpenSearch | `false` +| `nodeAffinity` | Value for the [node affinity settings][] | `{}` | +| `nodeGroup` | This is the name that will be used for each group of nodes in the cluster. The name will be `clusterName-nodeGroup-X` , `nameOverride-nodeGroup-X` if a `nameOverride` is specified, and `fullnameOverride-X` if a `fullnameOverride` is specified | `master` | +| `nodeSelector` | Configurable [nodeSelector][] so that you can target specific nodes for your OpenSearch cluster | `{}` | +| `persistence` | Enables a persistent volume for OpenSearch data. | see [values.yaml][] | +| `persistence.enableInitChown` | Disable the `fsgroup-volume` initContainer that will update permissions on the persistent disk. | `true` | +| `podAnnotations` | Configurable [annotations][] applied to all OpenSearch pods | `{}` | +| `podManagementPolicy` | By default Kubernetes [deploys StatefulSets serially][]. This deploys them in parallel so that they can discover each other | `Parallel` | +| `podSecurityContext` | Allows you to set the [securityContext][] for the pod | see [values.yaml][] | +| `podSecurityPolicy` | Configuration for create a pod security policy with minimal permissions to run this Helm chart with `create: true`. Also can be used to reference an external pod security policy with `name: "externalPodSecurityPolicy"` | see [values.yaml][] | +| `priorityClassName` | The name of the [PriorityClass][]. No default is supplied as the PriorityClass must be created first | `""` | | +| `rbac` | Configuration for creating a role, role binding and ServiceAccount as part of this Helm chart with `create: true`. Also can be used to reference an external ServiceAccount with `serviceAccountName: "externalServiceAccountName"` | see [values.yaml][] | +| `replicas` | Kubernetes replica count for the StatefulSet (i.e. how many pods) | `3` | +| `resources` | Allows you to set the [resources][] for the StatefulSet | see [values.yaml][] | +| `roles` | A list of the specific node [roles][] for the `nodeGroup` | see [values.yaml][] | +| `singleNode` | If `discovery.type` in the opensearch configuration is set to `"single-node"`, this should be set to `true`. If `true`, replicas will be forced to `1`. | `false` | +| `schedulerName` | Name of the [alternate scheduler][] | `""` | +| `secretMounts` | Allows you easily mount a secret as a file inside the StatefulSet. Useful for mounting certificates and other secrets. See [values.yaml][] for an example | `[]` | +| `securityConfig` | Configure the opensearch security plugin. There are multiple ways to inject configuration into the chart, see [values.yaml][] details. | By default an insecure demonstration configuration is set. This **must** be changed before going to production. | +| `securityContext` | Allows you to set the [securityContext][] for the container | see [values.yaml][] | +| `service.annotations` | [LoadBalancer annotations][] that Kubernetes will use for the service. This will configure load balancer if `service.type` is `LoadBalancer` | `{}` | +| `service.headless.annotations` | Allow you to set annotations on the headless service | `{}` | +| `service.externalTrafficPolicy` | Some cloud providers allow you to specify the [LoadBalancer externalTrafficPolicy][]. Kubernetes will use this to preserve the client source IP. This will configure load balancer if `service.type` is `LoadBalancer` | `""` | +| `service.httpPortName` | The name of the http port within the service | `http` | +| `service.labelsHeadless` | Labels to be added to headless service | `{}` | +| `service.labels` | Labels to be added to non-headless service | `{}` | +| `service.loadBalancerIP` | Some cloud providers allow you to specify the [loadBalancer][] IP. If the `loadBalancerIP` field is not specified, the IP is dynamically assigned. If you specify a `loadBalancerIP` but your cloud provider does not support the feature, it is ignored. | `""` | +| `service.loadBalancerSourceRanges` | The IP ranges that are allowed to access | `[]` | +| `service.metricsPortName` | The name of the metrics port (for Performance Analyzer) within the service | `metrics` | +| `service.nodePort` | Custom [nodePort][] port that can be set if you are using `service.type: nodePort` | `""` | +| `service.transportPortName` | The name of the transport port within the service | `transport` | +| `service.type` | OpenSearch [Service Types][] | `ClusterIP` | +| `service.ipFamilyPolicy` | This sets the preferred ip addresses in case of a dual-stack server, there are three options [PreferDualStack, SingleStack, RequireDualStack], [more information on dual stack](https://kubernetes.io/docs/concepts/services-networking/dual-stack/) | `""` | +| `service.ipFamilies` | Sets the preferred IP variants and in which order they are preferred, the first family you list is used for the legacy .spec.ClusterIP field, [more information on dual stack](https://kubernetes.io/docs/concepts/services-networking/dual-stack/) | `""` | +| `sidecarResources` | Allows you to set the [resources][] for the sidecar containers in the StatefulSet | {} | +| `sysctlInit` | Allows you to enable the `sysctlInit` to set sysctl vm.max_map_count through privileged `initContainer`. | `enabled: false` | +| `sysctlVmMaxMapCount` | Sets the [vm.max_map_count][] needed for OpenSearch | `262144` | +| `terminationGracePeriod` | The [terminationGracePeriod][] in seconds used when trying to stop the pod | `120` | +| `tolerations` | Configurable [tolerations][] | `[]` | +| `topologySpreadConstraints` | Configuration for pod [topologySpreadConstraints][] | `[]` | +| `transportHostPort` | Expose another transport port as hostPort. Refer to documentation for more information and requirements about using hostPorts. | `""` | +| `transportPort` | The transport port that Kubernetes will use for the service. If you change this you will also need to set transport port configuration in `extraEnvs` | `9300` | +| `updateStrategy` | The [updateStrategy][] for the StatefulSet. By default Kubernetes will wait for the cluster to be green after upgrading each pod. Setting this to `OnDelete` will allow you to manually delete each pod during upgrades | `RollingUpdate` | +| `volumeClaimTemplate` | Configuration for the [volumeClaimTemplate for StatefulSets][]. You will want to adjust the storage (default `30Gi` ) and the `storageClassName` if you are using a different storage class | see [values.yaml][] | +| `extraObjects` | Array of extra K8s manifests to deploy | list `[]` | | +| `livenessProbe` | Configuration fields for the liveness [probe][] | see [exampleLiveness][] in `values.yaml` +| `readinessProbe` | Configuration fields for the readiness [probe][] | see [exampleReadiness][] in `values.yaml` +| `startupProbe` | Configuration fields for the startup [probe][] | see [exampleStartup][] in `values.yaml` | +| `plugins.enabled` | Allow/disallow to add 3rd Party / Custom plugins not offered in the default OpenSearchDashboards image | false | +| `plugins.installList` | Array containing the Opensearch Dashboards plugins to be installed in container | [] | +| `opensearchLifecycle` | Allows you to configure lifecycle hooks for the OpenSearch container in the StatefulSet | {} | +| `openSearchAnnotations` | Allows you to configure custom annotation in the StatefullSet of the OpenSearch container | {} | + + +[anti-affinity]: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity + +[environment from variables]: https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/#configure-all-key-value-pairs-in-a-configmap-as-container-environment-variables + +[values.yaml]:https://github.com/opensearch-project/helm-charts/blob/main/charts/opensearch/values.yaml + +[hostAliases]: https://kubernetes.io/docs/concepts/services-networking/add-entries-to-pod-etc-hosts-with-host-aliases/ + +[image.pullPolicy]: https://kubernetes.io/docs/concepts/containers/images/#updating-images +[imagePullSecrets]: https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/ +[ingress]: https://kubernetes.io/docs/concepts/services-networking/ingress/ +[resources]: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/ + +[labels]: https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/ + +[maxUnavailable]: https://kubernetes.io/docs/tasks/run-application/configure-pdb/#specifying-a-poddisruptionbudget + +[node affinity settings]: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#node-affinity-beta-feature + +[nodeSelector]: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector + +[annotations]: https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/ + +[deploys statefulsets serially]: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#pod-management-policies + +[securityContext]: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ + +[priorityClass]: https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#priorityclass + +[roles]: https://opensearch.org/docs/opensearch/cluster/ + +[alternate scheduler]: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/#specify-schedulers-for-pods + +[loadBalancer annotations]: https://kubernetes.io/docs/concepts/services-networking/service/#ssl-support-on-aws +[loadBalancer externalTrafficPolicy]: https://kubernetes.io/docs/tasks/access-application-cluster/create-external-load-balancer/#preserving-the-client-source-ip +[loadBalancer]: https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer +[maxUnavailable]: https://kubernetes.io/docs/tasks/run-application/configure-pdb/#specifying-a-poddisruptionbudget + +[nodePort]: https://kubernetes.io/docs/concepts/services-networking/service/#nodeport + +[nodeSelector]: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#nodeselector + +[vm.max_map_count]: https://opensearch.org/docs/opensearch/install/important-settings/ + +[terminationGracePeriod]: https://kubernetes.io/docs/concepts/workloads/pods/pod/#termination-of-pods +[tolerations]: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ + +[updateStrategy]: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/ +[volumeClaimTemplate for statefulsets]: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#stable-storage + +[service types]: https://kubernetes.io/docs/concepts/services-networking/service/#publishing-services-service-types + +[topologySpreadConstraints]: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints + +[probe]: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#define-readiness-probes + +[exampleStartup]: https://github.com/opensearch-project/helm-charts/blob/main/charts/opensearch/values.yaml#332 +[exampleLiveness]: https://github.com/opensearch-project/helm-charts/blob/main/charts/opensearch/values.yaml#340 +[exampleReadiness]: https://github.com/opensearch-project/helm-charts/blob/main/charts/opensearch/values.yaml#349 + diff --git a/charts/deps/charts/opensearch-2.12.1/ci/ci-ingress-class-name-values.yaml b/charts/deps/charts/opensearch-2.12.1/ci/ci-ingress-class-name-values.yaml new file mode 100644 index 0000000..13ee25a --- /dev/null +++ b/charts/deps/charts/opensearch-2.12.1/ci/ci-ingress-class-name-values.yaml @@ -0,0 +1,429 @@ +--- +clusterName: "opensearch-cluster" +nodeGroup: "master" + +# The service that non master groups will try to connect to when joining the cluster +# This should be set to clusterName + "-" + nodeGroup for your master group +masterService: "opensearch-cluster-master" + +# OpenSearch roles that will be applied to this nodeGroup +# These will be set as environment variable "node.roles". E.g. node.roles=master,ingest,data,remote_cluster_client +roles: + - master + - ingest + - data + - remote_cluster_client + +replicas: 1 + +# if not set, falls back to parsing .Values.imageTag, then .Chart.appVersion. +majorVersion: "" + +global: + # Set if you want to change the default docker registry, e.g. a private one. + dockerRegistry: "" + +# Allows you to add any config files in {{ .Values.opensearchHome }}/config +opensearchHome: /usr/share/opensearch +# such as opensearch.yml and log4j2.properties +config: + # Values must be YAML literal style scalar / YAML multiline string. + # : | + # + # log4j2.properties: | + # status = error + # + # appender.console.type = Console + # appender.console.name = console + # appender.console.layout.type = PatternLayout + # appender.console.layout.pattern = [%d{ISO8601}][%-5p][%-25c{1.}] [%node_name]%marker %m%n + # + # rootLogger.level = info + # rootLogger.appenderRef.console.ref = console + opensearch.yml: | + cluster.name: opensearch-cluster + + # Bind to all interfaces because we don't know what IP address Docker will assign to us. + network.host: 0.0.0.0 + transport.host: localhost + transport.tcp.port: 9300 + + # Setting network.host to a non-loopback address enables the annoying bootstrap checks. "Single-node" mode disables them again. + # discovery.type: single-node + + # Start OpenSearch Security Demo Configuration + # WARNING: revise all the lines below before you go into production + plugins: + security: + ssl: + transport: + pemcert_filepath: esnode.pem + pemkey_filepath: esnode-key.pem + pemtrustedcas_filepath: root-ca.pem + enforce_hostname_verification: false + http: + enabled: true + pemcert_filepath: esnode.pem + pemkey_filepath: esnode-key.pem + pemtrustedcas_filepath: root-ca.pem + allow_unsafe_democertificates: true + allow_default_init_securityindex: true + authcz: + admin_dn: + - CN=kirk,OU=client,O=client,L=test,C=de + audit.type: internal_opensearch + enable_snapshot_restore_privilege: true + check_snapshot_restore_write_privileges: true + restapi: + roles_enabled: ["all_access", "security_rest_api_access"] + system_indices: + enabled: true + indices: + [ + ".opendistro-alerting-config", + ".opendistro-alerting-alert*", + ".opendistro-anomaly-results*", + ".opendistro-anomaly-detector*", + ".opendistro-anomaly-checkpoints", + ".opendistro-anomaly-detection-state", + ".opendistro-reports-*", + ".opendistro-notifications-*", + ".opendistro-notebooks", + ".opendistro-asynchronous-search-response*", + ] + ######## End OpenSearch Security Demo Configuration ######## + # log4j2.properties: + +# Extra environment variables to append to this nodeGroup +# This will be appended to the current 'env:' key. You can use any of the kubernetes env +# syntax here +extraEnvs: [] +# - name: MY_ENVIRONMENT_VAR +# value: the_value_goes_here + +# Allows you to load environment variables from kubernextes secret or config map +envFrom: [] +# - secretRef: +# name: env-secret +# - configMapRef: +# name: config-map + +# A list of secrets and their paths to mount inside the pod +# This is useful for mounting certificates for security and for mounting +# the X-Pack license +secretMounts: [] + +hostAliases: [] +# - ip: "127.0.0.1" +# hostnames: +# - "foo.local" +# - "bar.local" + +image: + repository: "opensearchproject/opensearch" + # override image tag, which is .Chart.AppVersion by default + tag: "" + pullPolicy: "IfNotPresent" + +podAnnotations: {} + # iam.amazonaws.com/role: es-cluster + +# additionals labels +labels: {} + +opensearchJavaOpts: "-Xmx512M -Xms512M" + +resources: + requests: + cpu: "1000m" + memory: "100Mi" + +initResources: {} + # limits: + # cpu: "25m" + # # memory: "128Mi" + # requests: + # cpu: "25m" + # memory: "128Mi" + +sidecarResources: {} + # limits: + # cpu: "25m" + # # memory: "128Mi" + # requests: + # cpu: "25m" + # memory: "128Mi" + +networkHost: "0.0.0.0" + +rbac: + create: false + serviceAccountAnnotations: {} + serviceAccountName: "" + +podSecurityPolicy: + create: false + name: "" + spec: + privileged: true + fsGroup: + rule: RunAsAny + runAsUser: + rule: RunAsAny + seLinux: + rule: RunAsAny + supplementalGroups: + rule: RunAsAny + volumes: + - secret + - configMap + - persistentVolumeClaim + - emptyDir + +persistence: + enabled: true + # Set to false to disable the `fsgroup-volume` initContainer that will update permissions on the persistent disk. + enableInitChown: true + # override image, which is busybox by default + # image: busybox + # override image tag, which is latest by default + # imageTag: + labels: + # Add default labels for the volumeClaimTemplate of the StatefulSet + enabled: false + # OpenSearch Persistent Volume Storage Class + # If defined, storageClassName: + # If set to "-", storageClassName: "", which disables dynamic provisioning + # If undefined (the default) or set to null, no storageClassName spec is + # set, choosing the default provisioner. (gp2 on AWS, standard on + # GKE, AWS & OpenStack) + # + # storageClass: "-" + accessModes: + - ReadWriteOnce + size: 8Gi + annotations: {} + +extraVolumes: [] + # - name: extras + # emptyDir: {} + +extraVolumeMounts: [] + # - name: extras + # mountPath: /usr/share/extras + # readOnly: true + +extraContainers: [] + # - name: do-something + # image: busybox + # command: ['do', 'something'] + +extraInitContainers: [] + # - name: do-somethings + # image: busybox + # command: ['do', 'something'] + +# This is the PriorityClass settings as defined in +# https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#priorityclass +priorityClassName: "" + +# By default this will make sure two pods don't end up on the same node +# Changing this to a region would allow you to spread pods across regions +antiAffinityTopologyKey: "kubernetes.io/hostname" + +# Hard means that by default pods will only be scheduled if there are enough nodes for them +# and that they will never end up on the same node. Setting this to soft will do this "best effort" +antiAffinity: "soft" + +# This is the node affinity settings as defined in +# https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#node-affinity-beta-feature +nodeAffinity: {} + +# The default is to deploy all pods serially. By setting this to parallel all pods are started at +# the same time when bootstrapping the cluster +podManagementPolicy: "Parallel" + +# The environment variables injected by service links are not used, but can lead to slow OpenSearch boot times when +# there are many services in the current namespace. +# If you experience slow pod startups you probably want to set this to `false`. +enableServiceLinks: true + +protocol: https +httpPort: 9200 +transportPort: 9300 +metricsPort: 9600 + +service: + labels: {} + labelsHeadless: {} + headless: + annotations: {} + type: ClusterIP + nodePort: "" + annotations: {} + httpPortName: http + transportPortName: transport + loadBalancerIP: "" + loadBalancerSourceRanges: [] + externalTrafficPolicy: "" + +updateStrategy: RollingUpdate + +# This is the max unavailable setting for the pod disruption budget +# The default value of 1 will make sure that kubernetes won't allow more than 1 +# of your pods to be unavailable during maintenance +maxUnavailable: 1 + +podSecurityContext: + fsGroup: 1000 + runAsUser: 1000 + +securityContext: + capabilities: + drop: + - ALL + # readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + +securityConfig: + enabled: true + path: "/usr/share/opensearch/plugins/opensearch-security/securityconfig" + actionGroupsSecret: + configSecret: + internalUsersSecret: + rolesSecret: + rolesMappingSecret: + tenantsSecret: + # The following option simplifies securityConfig by using a single secret and + # specifying the config files as keys in the secret instead of creating + # different secrets for for each config file. + # Note that this is an alternative to the individual secret configuration + # above and shouldn't be used if the above secrets are used. + config: + # There are multiple ways to define the configuration here: + # * If you define anything under data, the chart will automatically create + # a secret and mount it. + # * If you define securityConfigSecret, the chart will assume this secret is + # created externally and mount it. + # * It is an error to define both data and securityConfigSecret. + securityConfigSecret: "" + data: {} + # config.yml: |- + # internal_users.yml: |- + # roles.yml: |- + # roles_mapping.yml: |- + # action_groups.yml: |- + # tenants.yml: |- + +# How long to wait for opensearch to stop gracefully +terminationGracePeriod: 120 + +sysctlVmMaxMapCount: 262144 + +startupProbe: + tcpSocket: + port: 9200 + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 3 + failureThreshold: 30 + +readinessProbe: + failureThreshold: 3 + initialDelaySeconds: 900 + periodSeconds: 10 + successThreshold: 3 + timeoutSeconds: 2 + +## Use an alternate scheduler. +## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ +## +schedulerName: "" + +imagePullSecrets: [] +nodeSelector: {} +tolerations: [] + +# Enabling this will publically expose your OpenSearch instance. +# Only enable this if you have security enabled on your cluster +ingress: + enabled: true + + # For Kubernetes >= 1.18 you should specify the ingress-controller via the field ingressClassName + # See https://kubernetes.io/blog/2020/04/02/improvements-to-the-ingress-api-in-kubernetes-1.18/#specifying-the-class-of-an-ingress + ingressClassName: nginx + + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + path: / + hosts: + - chart-example.local + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +nameOverride: "" +fullnameOverride: "" + +masterTerminationFix: false + +lifecycle: {} + # preStop: + # exec: + # command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"] + # postStart: + # exec: + # command: + # - bash + # - -c + # - | + # #!/bin/bash + # # Add a template to adjust number of shards/replicas1 + # TEMPLATE_NAME=my_template + # INDEX_PATTERN="logstash-*" + # SHARD_COUNT=8 + # REPLICA_COUNT=1 + # ES_URL=http://localhost:9200 + # while [[ "$(curl -s -o /dev/null -w '%{http_code}\n' $ES_URL)" != "200" ]]; do sleep 1; done + # curl -XPUT "$ES_URL/_template/$TEMPLATE_NAME" -H 'Content-Type: application/json' -d'{"index_patterns":['\""$INDEX_PATTERN"\"'],"settings":{"number_of_shards":'$SHARD_COUNT',"number_of_replicas":'$REPLICA_COUNT'}}' + +keystore: [] + +networkPolicy: + ## Enable creation of NetworkPolicy resources. Only Ingress traffic is filtered for now. + ## In order for a Pod to access OpenSearch, it needs to have the following label: + ## {{ template "uname" . }}-client: "true" + ## Example for default configuration to access HTTP port: + ## opensearch-master-http-client: "true" + ## Example for default configuration to access transport port: + ## opensearch-master-transport-client: "true" + + http: + enabled: false + +# Deprecated +# please use the above podSecurityContext.fsGroup instead +fsGroup: "" + +## Set optimal sysctl's through securityContext. This requires privilege. Can be disabled if +## the system has already been preconfigured. (Ex: https://www.elastic.co/guide/en/elasticsearch/reference/current/vm-max-map-count.html) +## Also see: https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/ +sysctl: + enabled: false + +## Set optimal sysctl's through privileged initContainer. +sysctlInit: + enabled: true + # override image, which is busybox by default + # image: busybox + # override image tag, which is latest by default + # imageTag: + +## Enable to add 3rd Party / Custom plugins not offered in the default OpenSearch image. +plugins: + enabled: false + installList: [] + # - example-fake-plugin diff --git a/charts/deps/charts/opensearch-2.12.1/ci/ci-rbac-enabled-values.yaml b/charts/deps/charts/opensearch-2.12.1/ci/ci-rbac-enabled-values.yaml new file mode 100644 index 0000000..01892e3 --- /dev/null +++ b/charts/deps/charts/opensearch-2.12.1/ci/ci-rbac-enabled-values.yaml @@ -0,0 +1,429 @@ +--- +clusterName: "opensearch-cluster" +nodeGroup: "master" + +# The service that non master groups will try to connect to when joining the cluster +# This should be set to clusterName + "-" + nodeGroup for your master group +masterService: "opensearch-cluster-master" + +# OpenSearch roles that will be applied to this nodeGroup +# These will be set as environment variable "node.roles". E.g. node.roles=master,ingest,data,remote_cluster_client +roles: + - master + - ingest + - data + - remote_cluster_client + +replicas: 1 + +# if not set, falls back to parsing .Values.imageTag, then .Chart.appVersion. +majorVersion: "" + +global: + # Set if you want to change the default docker registry, e.g. a private one. + dockerRegistry: "" + +# Allows you to add any config files in {{ .Values.opensearchHome }}/config +opensearchHome: /usr/share/opensearch +# such as opensearch.yml and log4j2.properties +config: + # Values must be YAML literal style scalar / YAML multiline string. + # : | + # + # log4j2.properties: | + # status = error + # + # appender.console.type = Console + # appender.console.name = console + # appender.console.layout.type = PatternLayout + # appender.console.layout.pattern = [%d{ISO8601}][%-5p][%-25c{1.}] [%node_name]%marker %m%n + # + # rootLogger.level = info + # rootLogger.appenderRef.console.ref = console + opensearch.yml: | + cluster.name: opensearch-cluster + + # Bind to all interfaces because we don't know what IP address Docker will assign to us. + network.host: 0.0.0.0 + transport.host: localhost + transport.tcp.port: 9300 + + # Setting network.host to a non-loopback address enables the annoying bootstrap checks. "Single-node" mode disables them again. + # discovery.type: single-node + + # Start OpenSearch Security Demo Configuration + # WARNING: revise all the lines below before you go into production + plugins: + security: + ssl: + transport: + pemcert_filepath: esnode.pem + pemkey_filepath: esnode-key.pem + pemtrustedcas_filepath: root-ca.pem + enforce_hostname_verification: false + http: + enabled: true + pemcert_filepath: esnode.pem + pemkey_filepath: esnode-key.pem + pemtrustedcas_filepath: root-ca.pem + allow_unsafe_democertificates: true + allow_default_init_securityindex: true + authcz: + admin_dn: + - CN=kirk,OU=client,O=client,L=test,C=de + audit.type: internal_opensearch + enable_snapshot_restore_privilege: true + check_snapshot_restore_write_privileges: true + restapi: + roles_enabled: ["all_access", "security_rest_api_access"] + system_indices: + enabled: true + indices: + [ + ".opendistro-alerting-config", + ".opendistro-alerting-alert*", + ".opendistro-anomaly-results*", + ".opendistro-anomaly-detector*", + ".opendistro-anomaly-checkpoints", + ".opendistro-anomaly-detection-state", + ".opendistro-reports-*", + ".opendistro-notifications-*", + ".opendistro-notebooks", + ".opendistro-asynchronous-search-response*", + ] + ######## End OpenSearch Security Demo Configuration ######## + # log4j2.properties: + +# Extra environment variables to append to this nodeGroup +# This will be appended to the current 'env:' key. You can use any of the kubernetes env +# syntax here +extraEnvs: [] +# - name: MY_ENVIRONMENT_VAR +# value: the_value_goes_here + +# Allows you to load environment variables from kubernextes secret or config map +envFrom: [] +# - secretRef: +# name: env-secret +# - configMapRef: +# name: config-map + +# A list of secrets and their paths to mount inside the pod +# This is useful for mounting certificates for security and for mounting +# the X-Pack license +secretMounts: [] + +hostAliases: [] +# - ip: "127.0.0.1" +# hostnames: +# - "foo.local" +# - "bar.local" + +image: + repository: "opensearchproject/opensearch" + # override image tag, which is .Chart.AppVersion by default + tag: "" + pullPolicy: "IfNotPresent" + +podAnnotations: {} + # iam.amazonaws.com/role: es-cluster + +# additionals labels +labels: {} + +opensearchJavaOpts: "-Xmx512M -Xms512M" + +resources: + requests: + cpu: "1000m" + memory: "100Mi" + +initResources: {} + # limits: + # cpu: "25m" + # # memory: "128Mi" + # requests: + # cpu: "25m" + # memory: "128Mi" + +sidecarResources: {} + # limits: + # cpu: "25m" + # # memory: "128Mi" + # requests: + # cpu: "25m" + # memory: "128Mi" + +networkHost: "0.0.0.0" + +rbac: + create: true + serviceAccountAnnotations: {} + serviceAccountName: "" + +podSecurityPolicy: + create: false + name: "" + spec: + privileged: true + fsGroup: + rule: RunAsAny + runAsUser: + rule: RunAsAny + seLinux: + rule: RunAsAny + supplementalGroups: + rule: RunAsAny + volumes: + - secret + - configMap + - persistentVolumeClaim + - emptyDir + +persistence: + enabled: true + # Set to false to disable the `fsgroup-volume` initContainer that will update permissions on the persistent disk. + enableInitChown: true + # override image, which is busybox by default + # image: busybox + # override image tag, which is latest by default + # imageTag: + labels: + # Add default labels for the volumeClaimTemplate of the StatefulSet + enabled: false + # OpenSearch Persistent Volume Storage Class + # If defined, storageClassName: + # If set to "-", storageClassName: "", which disables dynamic provisioning + # If undefined (the default) or set to null, no storageClassName spec is + # set, choosing the default provisioner. (gp2 on AWS, standard on + # GKE, AWS & OpenStack) + # + # storageClass: "-" + accessModes: + - ReadWriteOnce + size: 8Gi + annotations: {} + +extraVolumes: [] + # - name: extras + # emptyDir: {} + +extraVolumeMounts: [] + # - name: extras + # mountPath: /usr/share/extras + # readOnly: true + +extraContainers: [] + # - name: do-something + # image: busybox + # command: ['do', 'something'] + +extraInitContainers: [] + # - name: do-somethings + # image: busybox + # command: ['do', 'something'] + +# This is the PriorityClass settings as defined in +# https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#priorityclass +priorityClassName: "" + +# By default this will make sure two pods don't end up on the same node +# Changing this to a region would allow you to spread pods across regions +antiAffinityTopologyKey: "kubernetes.io/hostname" + +# Hard means that by default pods will only be scheduled if there are enough nodes for them +# and that they will never end up on the same node. Setting this to soft will do this "best effort" +antiAffinity: "soft" + +# This is the node affinity settings as defined in +# https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#node-affinity-beta-feature +nodeAffinity: {} + +# The default is to deploy all pods serially. By setting this to parallel all pods are started at +# the same time when bootstrapping the cluster +podManagementPolicy: "Parallel" + +# The environment variables injected by service links are not used, but can lead to slow OpenSearch boot times when +# there are many services in the current namespace. +# If you experience slow pod startups you probably want to set this to `false`. +enableServiceLinks: true + +protocol: https +httpPort: 9200 +transportPort: 9300 +metricsPort: 9600 + +service: + labels: {} + labelsHeadless: {} + headless: + annotations: {} + type: ClusterIP + nodePort: "" + annotations: {} + httpPortName: http + transportPortName: transport + loadBalancerIP: "" + loadBalancerSourceRanges: [] + externalTrafficPolicy: "" + +updateStrategy: RollingUpdate + +# This is the max unavailable setting for the pod disruption budget +# The default value of 1 will make sure that kubernetes won't allow more than 1 +# of your pods to be unavailable during maintenance +maxUnavailable: 1 + +podSecurityContext: + fsGroup: 1000 + runAsUser: 1000 + +securityContext: + capabilities: + drop: + - ALL + # readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + +securityConfig: + enabled: true + path: "/usr/share/opensearch/plugins/opensearch-security/securityconfig" + actionGroupsSecret: + configSecret: + internalUsersSecret: + rolesSecret: + rolesMappingSecret: + tenantsSecret: + # The following option simplifies securityConfig by using a single secret and + # specifying the config files as keys in the secret instead of creating + # different secrets for for each config file. + # Note that this is an alternative to the individual secret configuration + # above and shouldn't be used if the above secrets are used. + config: + # There are multiple ways to define the configuration here: + # * If you define anything under data, the chart will automatically create + # a secret and mount it. + # * If you define securityConfigSecret, the chart will assume this secret is + # created externally and mount it. + # * It is an error to define both data and securityConfigSecret. + securityConfigSecret: "" + data: {} + # config.yml: |- + # internal_users.yml: |- + # roles.yml: |- + # roles_mapping.yml: |- + # action_groups.yml: |- + # tenants.yml: |- + +# How long to wait for opensearch to stop gracefully +terminationGracePeriod: 120 + +sysctlVmMaxMapCount: 262144 + +startupProbe: + tcpSocket: + port: 9200 + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 3 + failureThreshold: 30 + +readinessProbe: + failureThreshold: 3 + initialDelaySeconds: 900 + periodSeconds: 10 + successThreshold: 3 + timeoutSeconds: 2 + +## Use an alternate scheduler. +## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ +## +schedulerName: "" + +imagePullSecrets: [] +nodeSelector: {} +tolerations: [] + +# Enabling this will publically expose your OpenSearch instance. +# Only enable this if you have security enabled on your cluster +ingress: + enabled: false + + # For Kubernetes >= 1.18 you should specify the ingress-controller via the field ingressClassName + # See https://kubernetes.io/blog/2020/04/02/improvements-to-the-ingress-api-in-kubernetes-1.18/#specifying-the-class-of-an-ingress + ingressClassName: nginx + + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + path: / + hosts: + - chart-example.local + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +nameOverride: "" +fullnameOverride: "" + +masterTerminationFix: false + +lifecycle: {} + # preStop: + # exec: + # command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"] + # postStart: + # exec: + # command: + # - bash + # - -c + # - | + # #!/bin/bash + # # Add a template to adjust number of shards/replicas1 + # TEMPLATE_NAME=my_template + # INDEX_PATTERN="logstash-*" + # SHARD_COUNT=8 + # REPLICA_COUNT=1 + # ES_URL=http://localhost:9200 + # while [[ "$(curl -s -o /dev/null -w '%{http_code}\n' $ES_URL)" != "200" ]]; do sleep 1; done + # curl -XPUT "$ES_URL/_template/$TEMPLATE_NAME" -H 'Content-Type: application/json' -d'{"index_patterns":['\""$INDEX_PATTERN"\"'],"settings":{"number_of_shards":'$SHARD_COUNT',"number_of_replicas":'$REPLICA_COUNT'}}' + +keystore: [] + +networkPolicy: + ## Enable creation of NetworkPolicy resources. Only Ingress traffic is filtered for now. + ## In order for a Pod to access OpenSearch, it needs to have the following label: + ## {{ template "uname" . }}-client: "true" + ## Example for default configuration to access HTTP port: + ## opensearch-master-http-client: "true" + ## Example for default configuration to access transport port: + ## opensearch-master-transport-client: "true" + + http: + enabled: false + +# Deprecated +# please use the above podSecurityContext.fsGroup instead +fsGroup: "" + +## Set optimal sysctl's through securityContext. This requires privilege. Can be disabled if +## the system has already been preconfigured. (Ex: https://www.elastic.co/guide/en/elasticsearch/reference/current/vm-max-map-count.html) +## Also see: https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/ +sysctl: + enabled: false + +## Set optimal sysctl's through privileged initContainer. +sysctlInit: + enabled: false + # override image, which is busybox by default + # image: busybox + # override image tag, which is latest by default + # imageTag: + +## Enable to add 3rd Party / Custom plugins not offered in the default OpenSearch image. +plugins: + enabled: false + installList: [] + # - example-fake-plugin diff --git a/charts/deps/charts/opensearch-2.12.1/ci/ci-values.yaml b/charts/deps/charts/opensearch-2.12.1/ci/ci-values.yaml new file mode 100644 index 0000000..9b0308b --- /dev/null +++ b/charts/deps/charts/opensearch-2.12.1/ci/ci-values.yaml @@ -0,0 +1,416 @@ +--- +clusterName: "opensearch-cluster" +nodeGroup: "master" + +# The service that non master groups will try to connect to when joining the cluster +# This should be set to clusterName + "-" + nodeGroup for your master group +masterService: "opensearch-cluster-master" + +# OpenSearch roles that will be applied to this nodeGroup +# These will be set as environment variable "node.roles". E.g. node.roles=master,ingest,data,remote_cluster_client +roles: + - master + - ingest + - data + - remote_cluster_client + +replicas: 1 + +# if not set, falls back to parsing .Values.imageTag, then .Chart.appVersion. +majorVersion: "" + +global: + # Set if you want to change the default docker registry, e.g. a private one. + dockerRegistry: "" + +# Allows you to add any config files in {{ .Values.opensearchHome }}/config +opensearchHome: /usr/share/opensearch +# such as opensearch.yml and log4j2.properties +config: + # Values must be YAML literal style scalar / YAML multiline string. + # : | + # + # log4j2.properties: | + # status = error + # + # appender.console.type = Console + # appender.console.name = console + # appender.console.layout.type = PatternLayout + # appender.console.layout.pattern = [%d{ISO8601}][%-5p][%-25c{1.}] [%node_name]%marker %m%n + # + # rootLogger.level = info + # rootLogger.appenderRef.console.ref = console + opensearch.yml: | + cluster.name: opensearch-cluster + + # Bind to all interfaces because we don't know what IP address Docker will assign to us. + network.host: 0.0.0.0 + transport.host: localhost + transport.tcp.port: 9300 + + # Setting network.host to a non-loopback address enables the annoying bootstrap checks. "Single-node" mode disables them again. + # discovery.type: single-node + + # Start OpenSearch Security Demo Configuration + # WARNING: revise all the lines below before you go into production + plugins: + security: + ssl: + transport: + pemcert_filepath: esnode.pem + pemkey_filepath: esnode-key.pem + pemtrustedcas_filepath: root-ca.pem + enforce_hostname_verification: false + http: + enabled: true + pemcert_filepath: esnode.pem + pemkey_filepath: esnode-key.pem + pemtrustedcas_filepath: root-ca.pem + allow_unsafe_democertificates: true + allow_default_init_securityindex: true + authcz: + admin_dn: + - CN=kirk,OU=client,O=client,L=test,C=de + audit.type: internal_opensearch + enable_snapshot_restore_privilege: true + check_snapshot_restore_write_privileges: true + restapi: + roles_enabled: ["all_access", "security_rest_api_access"] + system_indices: + enabled: true + indices: + [ + ".opendistro-alerting-config", + ".opendistro-alerting-alert*", + ".opendistro-anomaly-results*", + ".opendistro-anomaly-detector*", + ".opendistro-anomaly-checkpoints", + ".opendistro-anomaly-detection-state", + ".opendistro-reports-*", + ".opendistro-notifications-*", + ".opendistro-notebooks", + ".opendistro-asynchronous-search-response*", + ] + ######## End OpenSearch Security Demo Configuration ######## + # log4j2.properties: + +# Extra environment variables to append to this nodeGroup +# This will be appended to the current 'env:' key. You can use any of the kubernetes env +# syntax here +extraEnvs: [] +# - name: MY_ENVIRONMENT_VAR +# value: the_value_goes_here + +# Allows you to load environment variables from kubernextes secret or config map +envFrom: [] +# - secretRef: +# name: env-secret +# - configMapRef: +# name: config-map + +# A list of secrets and their paths to mount inside the pod +# This is useful for mounting certificates for security and for mounting +# the X-Pack license +secretMounts: [] + +hostAliases: [] +# - ip: "127.0.0.1" +# hostnames: +# - "foo.local" +# - "bar.local" + + +image: + repository: "opensearchproject/opensearch" + # override image tag, which is .Chart.AppVersion by default + tag: "" + pullPolicy: "IfNotPresent" + + +podAnnotations: {} + # iam.amazonaws.com/role: es-cluster + +# additionals labels +labels: {} + +opensearchJavaOpts: "-Xmx512M -Xms512M" + +resources: + requests: + cpu: "1000m" + memory: "100Mi" + +initResources: {} + # limits: + # cpu: "25m" + # # memory: "128Mi" + # requests: + # cpu: "25m" + # memory: "128Mi" + +sidecarResources: {} + # limits: + # cpu: "25m" + # # memory: "128Mi" + # requests: + # cpu: "25m" + # memory: "128Mi" + +networkHost: "0.0.0.0" + +rbac: + create: false + serviceAccountAnnotations: {} + serviceAccountName: "" + +podSecurityPolicy: + create: false + name: "" + spec: + privileged: true + fsGroup: + rule: RunAsAny + runAsUser: + rule: RunAsAny + seLinux: + rule: RunAsAny + supplementalGroups: + rule: RunAsAny + volumes: + - secret + - configMap + - persistentVolumeClaim + - emptyDir + +persistence: + enabled: true + # Set to false to disable the `fsgroup-volume` initContainer that will update permissions on the persistent disk. + enableInitChown: true + # override image, which is busybox by default + # image: busybox + # override image tag, which is latest by default + # imageTag: + labels: + # Add default labels for the volumeClaimTemplate of the StatefulSet + enabled: false + # OpenSearch Persistent Volume Storage Class + # If defined, storageClassName: + # If set to "-", storageClassName: "", which disables dynamic provisioning + # If undefined (the default) or set to null, no storageClassName spec is + # set, choosing the default provisioner. (gp2 on AWS, standard on + # GKE, AWS & OpenStack) + # + # storageClass: "-" + accessModes: + - ReadWriteOnce + size: 8Gi + annotations: {} + +extraVolumes: [] + # - name: extras + # emptyDir: {} + +extraVolumeMounts: [] + # - name: extras + # mountPath: /usr/share/extras + # readOnly: true + +extraContainers: [] + # - name: do-something + # image: busybox + # command: ['do', 'something'] + +extraInitContainers: [] + # - name: do-somethings + # image: busybox + # command: ['do', 'something'] + +# This is the PriorityClass settings as defined in +# https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#priorityclass +priorityClassName: "" + +# By default this will make sure two pods don't end up on the same node +# Changing this to a region would allow you to spread pods across regions +antiAffinityTopologyKey: "kubernetes.io/hostname" + +# Hard means that by default pods will only be scheduled if there are enough nodes for them +# and that they will never end up on the same node. Setting this to soft will do this "best effort" +antiAffinity: "soft" + +# This is the node affinity settings as defined in +# https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#node-affinity-beta-feature +nodeAffinity: {} + +# The default is to deploy all pods serially. By setting this to parallel all pods are started at +# the same time when bootstrapping the cluster +podManagementPolicy: "Parallel" + +# The environment variables injected by service links are not used, but can lead to slow OpenSearch boot times when +# there are many services in the current namespace. +# If you experience slow pod startups you probably want to set this to `false`. +enableServiceLinks: true + +protocol: https +httpPort: 9200 +transportPort: 9300 +metricsPort: 9600 + +service: + labels: {} + labelsHeadless: {} + headless: + annotations: {} + type: ClusterIP + nodePort: "" + annotations: {} + httpPortName: http + transportPortName: transport + loadBalancerIP: "" + loadBalancerSourceRanges: [] + externalTrafficPolicy: "" + +updateStrategy: RollingUpdate + +# This is the max unavailable setting for the pod disruption budget +# The default value of 1 will make sure that kubernetes won't allow more than 1 +# of your pods to be unavailable during maintenance +maxUnavailable: 1 + +podSecurityContext: + fsGroup: 1000 + runAsUser: 1000 + +securityContext: + capabilities: + drop: + - ALL + # readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + +securityConfig: + enabled: true + path: "/usr/share/opensearch/plugins/opensearch-security/securityconfig" + actionGroupsSecret: + configSecret: + internalUsersSecret: + rolesSecret: + rolesMappingSecret: + tenantsSecret: + # The following option simplifies securityConfig by using a single secret and + # specifying the config files as keys in the secret instead of creating + # different secrets for for each config file. + # Note that this is an alternative to the individual secret configuration + # above and shouldn't be used if the above secrets are used. + config: + # There are multiple ways to define the configuration here: + # * If you define anything under data, the chart will automatically create + # a secret and mount it. + # * If you define securityConfigSecret, the chart will assume this secret is + # created externally and mount it. + # * It is an error to define both data and securityConfigSecret. + securityConfigSecret: "" + data: {} + # config.yml: |- + # internal_users.yml: |- + # roles.yml: |- + # roles_mapping.yml: |- + # action_groups.yml: |- + # tenants.yml: |- + +# How long to wait for opensearch to stop gracefully +terminationGracePeriod: 120 + +sysctlVmMaxMapCount: 262144 + +## Use an alternate scheduler. +## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ +## +schedulerName: "" + +imagePullSecrets: [] +nodeSelector: {} +tolerations: [] + +# Enabling this will publically expose your OpenSearch instance. +# Only enable this if you have security enabled on your cluster +ingress: + enabled: false + + # For Kubernetes >= 1.18 you should specify the ingress-controller via the field ingressClassName + # See https://kubernetes.io/blog/2020/04/02/improvements-to-the-ingress-api-in-kubernetes-1.18/#specifying-the-class-of-an-ingress + ingressClassName: nginx + + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + path: / + hosts: + - chart-example.local + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +nameOverride: "" +fullnameOverride: "" + +masterTerminationFix: false + +lifecycle: {} + # preStop: + # exec: + # command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"] + # postStart: + # exec: + # command: + # - bash + # - -c + # - | + # #!/bin/bash + # # Add a template to adjust number of shards/replicas1 + # TEMPLATE_NAME=my_template + # INDEX_PATTERN="logstash-*" + # SHARD_COUNT=8 + # REPLICA_COUNT=1 + # ES_URL=http://localhost:9200 + # while [[ "$(curl -s -o /dev/null -w '%{http_code}\n' $ES_URL)" != "200" ]]; do sleep 1; done + # curl -XPUT "$ES_URL/_template/$TEMPLATE_NAME" -H 'Content-Type: application/json' -d'{"index_patterns":['\""$INDEX_PATTERN"\"'],"settings":{"number_of_shards":'$SHARD_COUNT',"number_of_replicas":'$REPLICA_COUNT'}}' + +keystore: [] + +networkPolicy: + ## Enable creation of NetworkPolicy resources. Only Ingress traffic is filtered for now. + ## In order for a Pod to access OpenSearch, it needs to have the following label: + ## {{ template "uname" . }}-client: "true" + ## Example for default configuration to access HTTP port: + ## opensearch-master-http-client: "true" + ## Example for default configuration to access transport port: + ## opensearch-master-transport-client: "true" + + http: + enabled: false + +# Deprecated +# please use the above podSecurityContext.fsGroup instead +fsGroup: "" + +## Set optimal sysctl's through securityContext. This requires privilege. Can be disabled if +## the system has already been preconfigured. (Ex: https://www.elastic.co/guide/en/elasticsearch/reference/current/vm-max-map-count.html) +## Also see: https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/ +sysctl: + enabled: false + +## Set optimal sysctl's through privileged initContainer. +sysctlInit: + enabled: false + # override image, which is busybox by default + # image: busybox + # override image tag, which is latest by default + # imageTag: + +## Enable to add 3rd Party / Custom plugins not offered in the default OpenSearch image. +plugins: + enabled: false + installList: [] + # - example-fake-plugin diff --git a/charts/deps/charts/opensearch-2.12.1/opensearch-2.12.1.tar b/charts/deps/charts/opensearch-2.12.1/opensearch-2.12.1.tar new file mode 100644 index 0000000000000000000000000000000000000000..a5f1c756220e622722397790bb3c6bbc084df300 GIT binary patch literal 143872 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 literal 0 HcmV?d00001 diff --git a/charts/deps/charts/opensearch-2.12.1/templates/NOTES.txt b/charts/deps/charts/opensearch-2.12.1/templates/NOTES.txt new file mode 100644 index 0000000..110e677 --- /dev/null +++ b/charts/deps/charts/opensearch-2.12.1/templates/NOTES.txt @@ -0,0 +1,2 @@ +Watch all cluster members come up. + $ kubectl get pods --namespace={{ .Release.Namespace }} -l app.kubernetes.io/component={{ template "opensearch.uname" . }} -w diff --git a/charts/deps/charts/opensearch-2.12.1/templates/_helpers.tpl b/charts/deps/charts/opensearch-2.12.1/templates/_helpers.tpl new file mode 100644 index 0000000..f7dc47d --- /dev/null +++ b/charts/deps/charts/opensearch-2.12.1/templates/_helpers.tpl @@ -0,0 +1,144 @@ +{{/* vim: set filetype=mustache: */}} +{{/* +Expand the name of the chart. +*/}} +{{- define "opensearch.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "opensearch.fullname" -}} +{{- if contains .Chart.Name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name .Chart.Name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "opensearch.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "opensearch.labels" -}} +helm.sh/chart: {{ include "opensearch.chart" . }} +{{ include "opensearch.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +app.kubernetes.io/component: {{ include "opensearch.uname" . }} +{{- with .Values.labels }} +{{ toYaml . }} +{{- end }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "opensearch.selectorLabels" -}} +app.kubernetes.io/name: {{ include "opensearch.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{- define "opensearch.uname" -}} +{{- if empty .Values.fullnameOverride -}} +{{- if empty .Values.nameOverride -}} +{{ .Values.clusterName }}-{{ .Values.nodeGroup }} +{{- else -}} +{{ .Values.nameOverride }}-{{ .Values.nodeGroup }} +{{- end -}} +{{- else -}} +{{ .Values.fullnameOverride }} +{{- end -}} +{{- end -}} + +{{- define "opensearch.masterService" -}} +{{- if empty .Values.masterService -}} +{{- if empty .Values.fullnameOverride -}} +{{- if empty .Values.nameOverride -}} +{{ .Values.clusterName }}-master +{{- else -}} +{{ .Values.nameOverride }}-master +{{- end -}} +{{- else -}} +{{ .Values.fullnameOverride }} +{{- end -}} +{{- else -}} +{{ .Values.masterService }} +{{- end -}} +{{- end -}} + +{{- define "opensearch.serviceName" -}} +{{- if eq .Values.nodeGroup "master" }} +{{- include "opensearch.masterService" . }} +{{- else }} +{{- include "opensearch.uname" . }} +{{- end }} +{{- end -}} + +{{- define "opensearch.endpoints" -}} +{{- $replicas := int (toString (.Values.replicas)) }} +{{- $uname := (include "opensearch.uname" .) }} + {{- range $i, $e := untilStep 0 $replicas 1 -}} +{{ $uname }}-{{ $i }}, + {{- end -}} +{{- end -}} + +{{- define "opensearch.majorVersion" -}} +{{- if .Values.majorVersion }} + {{- .Values.majorVersion }} +{{- else }} + {{- $version := semver (coalesce .Values.image.tag .Chart.AppVersion "1") }} + {{- $version.Major }} +{{- end }} +{{- end }} + +{{- define "opensearch.dockerRegistry" -}} +{{- if eq .Values.global.dockerRegistry "" -}} + {{- .Values.global.dockerRegistry -}} +{{- else -}} + {{- .Values.global.dockerRegistry | trimSuffix "/" | printf "%s/" -}} +{{- end -}} +{{- end -}} + +{{- define "opensearch.roles" -}} +{{- range $.Values.roles -}} +{{ . }}, +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for ingress. +*/}} +{{- define "opensearch.ingress.apiVersion" -}} + {{- if and (.Capabilities.APIVersions.Has "networking.k8s.io/v1") (semverCompare ">= 1.19-0" .Capabilities.KubeVersion.Version) -}} + {{- print "networking.k8s.io/v1" -}} + {{- else if .Capabilities.APIVersions.Has "networking.k8s.io/v1beta1" -}} + {{- print "networking.k8s.io/v1beta1" -}} + {{- else -}} + {{- print "extensions/v1beta1" -}} + {{- end -}} +{{- end -}} + +{{/* +Return if ingress is stable. +*/}} +{{- define "opensearch.ingress.isStable" -}} + {{- eq (include "opensearch.ingress.apiVersion" .) "networking.k8s.io/v1" -}} +{{- end -}} +{{/* +Return if ingress supports ingressClassName. +*/}} +{{- define "opensearch.ingress.supportsIngressClassName" -}} + {{- or (eq (include "opensearch.ingress.isStable" .) "true") (and (eq (include "opensearch.ingress.apiVersion" .) "networking.k8s.io/v1beta1") (semverCompare ">= 1.18-0" .Capabilities.KubeVersion.Version)) -}} +{{- end -}} diff --git a/charts/deps/charts/opensearch-2.12.1/templates/configmap.yaml b/charts/deps/charts/opensearch-2.12.1/templates/configmap.yaml new file mode 100644 index 0000000..4f2961b --- /dev/null +++ b/charts/deps/charts/opensearch-2.12.1/templates/configmap.yaml @@ -0,0 +1,18 @@ +{{- $root := . }} +{{- if .Values.config }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "opensearch.uname" . }}-config + labels: + {{- include "opensearch.labels" . | nindent 4 }} +data: +{{- range $configName, $configYaml := .Values.config }} + {{ $configName }}: | + {{- if (eq (kindOf $configYaml) "map")}} + {{- tpl (toYaml $configYaml) $root | nindent 4 }} + {{- else -}} + {{- tpl $configYaml $root | nindent 4 }} + {{- end -}} +{{- end -}} +{{- end -}} diff --git a/charts/deps/charts/opensearch-2.12.1/templates/extraManifests.yaml b/charts/deps/charts/opensearch-2.12.1/templates/extraManifests.yaml new file mode 100644 index 0000000..2855904 --- /dev/null +++ b/charts/deps/charts/opensearch-2.12.1/templates/extraManifests.yaml @@ -0,0 +1,4 @@ +{{ range .Values.extraObjects }} +--- +{{ tpl (toYaml .) $ }} +{{ end }} \ No newline at end of file diff --git a/charts/deps/charts/opensearch-2.12.1/templates/ingress.yaml b/charts/deps/charts/opensearch-2.12.1/templates/ingress.yaml new file mode 100644 index 0000000..7a4d0da --- /dev/null +++ b/charts/deps/charts/opensearch-2.12.1/templates/ingress.yaml @@ -0,0 +1,62 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "opensearch.serviceName" . -}} +{{- $servicePort := .Values.httpPort -}} +{{- $ingressPath := .Values.ingress.path -}} +{{- $ingressApiIsStable := eq (include "opensearch.ingress.isStable" .) "true" -}} +{{- $ingressSupportsIngressClassName := eq (include "opensearch.ingress.supportsIngressClassName" .) "true" -}} +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1beta1 +{{- else -}} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + {{- include "opensearch.labels" . | nindent 4 }} +{{- with .Values.ingress.annotations }} + annotations: +{{ toYaml . | indent 4 }} +{{- end }} +spec: + {{- if and $ingressSupportsIngressClassName .Values.ingress.ingressClassName }} + ingressClassName: {{ .Values.ingress.ingressClassName }} + {{- end -}} +{{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} +{{- end }} + rules: + {{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} + {{- range .Values.ingress.hosts }} + - host: {{ . | quote }} + http: + paths: + - path: {{ $ingressPath }} + pathType: Prefix + backend: + service: + name: {{ $fullName }} + port: + number: {{ $servicePort }} + {{- end }} + {{- else -}} + {{- range .Values.ingress.hosts }} + - host: {{ . | quote }} + http: + paths: + - path: {{ $ingressPath }} + backend: + serviceName: {{ $fullName }} + servicePort: {{ $servicePort }} + {{- end }} + {{- end }} +{{- end }} diff --git a/charts/deps/charts/opensearch-2.12.1/templates/networkpolicy.yaml b/charts/deps/charts/opensearch-2.12.1/templates/networkpolicy.yaml new file mode 100644 index 0000000..51cd263 --- /dev/null +++ b/charts/deps/charts/opensearch-2.12.1/templates/networkpolicy.yaml @@ -0,0 +1,17 @@ +{{- if .Values.networkPolicy.create -}} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ template "opensearch.uname" . }}-opensearch-net + labels: + {{- include "opensearch.labels" . | nindent 4 }} +spec: + ingress: + - from: + - podSelector: + matchLabels: + {{ template "opensearch.uname" . }}-transport-client: "true" + podSelector: + matchLabels: + {{ template "opensearch.uname" . }}-transport-client: "true" +{{- end }} diff --git a/charts/deps/charts/opensearch-2.12.1/templates/poddisruptionbudget.yaml b/charts/deps/charts/opensearch-2.12.1/templates/poddisruptionbudget.yaml new file mode 100644 index 0000000..68ab5b6 --- /dev/null +++ b/charts/deps/charts/opensearch-2.12.1/templates/poddisruptionbudget.yaml @@ -0,0 +1,17 @@ +{{- if .Values.maxUnavailable }} +{{- if semverCompare ">=1.21-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: policy/v1 +{{- else -}} +apiVersion: policy/v1beta1 +{{- end }} +kind: PodDisruptionBudget +metadata: + name: "{{ template "opensearch.uname" . }}-pdb" + labels: + {{- include "opensearch.labels" . | nindent 4 }} +spec: + maxUnavailable: {{ .Values.maxUnavailable }} + selector: + matchLabels: + {{- include "opensearch.selectorLabels" . | nindent 6 }} +{{- end }} diff --git a/charts/deps/charts/opensearch-2.12.1/templates/podsecuritypolicy.yaml b/charts/deps/charts/opensearch-2.12.1/templates/podsecuritypolicy.yaml new file mode 100644 index 0000000..76f36fa --- /dev/null +++ b/charts/deps/charts/opensearch-2.12.1/templates/podsecuritypolicy.yaml @@ -0,0 +1,17 @@ +{{- if semverCompare "<1.25-0" .Capabilities.KubeVersion.GitVersion -}} +{{- if .Values.podSecurityPolicy.create -}} +{{- $fullName := include "opensearch.uname" . -}} +apiVersion: policy/v1beta1 +kind: PodSecurityPolicy +metadata: + name: {{ default $fullName .Values.podSecurityPolicy.name | quote }} + labels: + {{- include "opensearch.labels" . | nindent 4 }} +spec: +{{ toYaml .Values.podSecurityPolicy.spec | indent 2 }} +{{- if .Values.sysctl.enabled }} + allowedUnsafeSysctls: + - vm.max_map_count +{{- end }} +{{- end }} +{{- end }} diff --git a/charts/deps/charts/opensearch-2.12.1/templates/role.yaml b/charts/deps/charts/opensearch-2.12.1/templates/role.yaml new file mode 100644 index 0000000..cba3cf9 --- /dev/null +++ b/charts/deps/charts/opensearch-2.12.1/templates/role.yaml @@ -0,0 +1,22 @@ +{{- if .Values.rbac.create -}} +{{- $fullName := include "opensearch.uname" . -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ $fullName | quote }} + labels: + {{- include "opensearch.labels" . | nindent 4 }} +rules: + - apiGroups: + - extensions + resources: + - podsecuritypolicies + resourceNames: + {{- if eq .Values.podSecurityPolicy.name "" }} + - {{ $fullName | quote }} + {{- else }} + - {{ .Values.podSecurityPolicy.name | quote }} + {{- end }} + verbs: + - use +{{- end -}} diff --git a/charts/deps/charts/opensearch-2.12.1/templates/rolebinding.yaml b/charts/deps/charts/opensearch-2.12.1/templates/rolebinding.yaml new file mode 100644 index 0000000..0445517 --- /dev/null +++ b/charts/deps/charts/opensearch-2.12.1/templates/rolebinding.yaml @@ -0,0 +1,21 @@ +{{- if .Values.rbac.create -}} +{{- $fullName := include "opensearch.uname" . -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ $fullName | quote }} + labels: + {{- include "opensearch.labels" . | nindent 4 }} +subjects: + - kind: ServiceAccount + {{- if eq .Values.rbac.serviceAccountName "" }} + name: {{ $fullName | quote }} + {{- else }} + name: {{ .Values.rbac.serviceAccountName | quote }} + {{- end }} + namespace: {{ .Release.Namespace | quote }} +roleRef: + kind: Role + name: {{ $fullName | quote }} + apiGroup: rbac.authorization.k8s.io +{{- end -}} diff --git a/charts/deps/charts/opensearch-2.12.1/templates/securityconfig.yaml b/charts/deps/charts/opensearch-2.12.1/templates/securityconfig.yaml new file mode 100644 index 0000000..13d6364 --- /dev/null +++ b/charts/deps/charts/opensearch-2.12.1/templates/securityconfig.yaml @@ -0,0 +1,19 @@ +{{- if .Values.securityConfig.config.data -}} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "opensearch.uname" . }}-securityconfig + namespace: {{ .Release.Namespace }} + labels: + {{- include "opensearch.labels" . | nindent 4 }} +type: Opaque +stringData: +{{- range $key, $val := .Values.securityConfig.config.data }} + {{ $key }}: | + {{- if (eq (kindOf $val) "map")}} + {{- tpl (toYaml $val) $ | nindent 4 }} + {{- else }} + {{- tpl $val $ | nindent 4 }} + {{- end }} +{{- end }} +{{- end }} diff --git a/charts/deps/charts/opensearch-2.12.1/templates/service.yaml b/charts/deps/charts/opensearch-2.12.1/templates/service.yaml new file mode 100644 index 0000000..4c28b2f --- /dev/null +++ b/charts/deps/charts/opensearch-2.12.1/templates/service.yaml @@ -0,0 +1,72 @@ +--- +kind: Service +apiVersion: v1 +metadata: + name: {{ template "opensearch.serviceName" . }} + labels: + {{- include "opensearch.labels" . | nindent 4 }} +{{- if .Values.service.labels }} +{{ toYaml .Values.service.labels | indent 4 }} +{{- end }} + annotations: +{{ toYaml .Values.service.annotations | indent 4 }} +spec: + type: {{ .Values.service.type }} + {{- if (semverCompare ">= 1.23-0" .Capabilities.KubeVersion.Version) }} + {{- if .Values.service.ipFamilyPolicy }} + ipFamilyPolicy: {{ .Values.service.ipFamilyPolicy }} + {{- end }} + {{- if .Values.service.ipFamilies }} + ipFamilies: {{ .Values.service.ipFamilies }} + {{- end }} + {{- end }} + selector: + {{- include "opensearch.selectorLabels" . | nindent 4 }} + ports: + - name: {{ .Values.service.httpPortName | default "http" }} + protocol: TCP + port: {{ .Values.httpPort }} +{{- if .Values.service.nodePort }} + nodePort: {{ .Values.service.nodePort }} +{{- end }} + - name: {{ .Values.service.transportPortName | default "transport" }} + protocol: TCP + port: {{ .Values.transportPort }} +{{- if .Values.service.loadBalancerIP }} + loadBalancerIP: {{ .Values.service.loadBalancerIP }} +{{- end }} +{{- with .Values.service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: +{{ toYaml . | indent 4 }} +{{- end }} +{{- if .Values.service.externalTrafficPolicy }} + externalTrafficPolicy: {{ .Values.service.externalTrafficPolicy }} +{{- end }} +--- +kind: Service +apiVersion: v1 +metadata: + name: {{ template "opensearch.serviceName" . }}-headless + labels: + {{- include "opensearch.labels" . | nindent 4 }} +{{- if .Values.service.labelsHeadless }} +{{ toYaml .Values.service.labelsHeadless | indent 4 }} +{{- end }} + annotations: + service.alpha.kubernetes.io/tolerate-unready-endpoints: "true" +{{- if .Values.service.headless.annotations }} +{{ toYaml .Values.service.headless.annotations | indent 4 }} +{{- end }} +spec: + clusterIP: None # This is needed for statefulset hostnames like opensearch-0 to resolve + # Create endpoints also if the related pod isn't ready + publishNotReadyAddresses: true + selector: + {{- include "opensearch.selectorLabels" . | nindent 4 }} + ports: + - name: {{ .Values.service.httpPortName | default "http" }} + port: {{ .Values.httpPort }} + - name: {{ .Values.service.transportPortName | default "transport" }} + port: {{ .Values.transportPort }} + - name: {{ .Values.service.metricsPortName | default "metrics" }} + port: {{ .Values.metricsPort }} diff --git a/charts/deps/charts/opensearch-2.12.1/templates/serviceaccount.yaml b/charts/deps/charts/opensearch-2.12.1/templates/serviceaccount.yaml new file mode 100644 index 0000000..81e2fcf --- /dev/null +++ b/charts/deps/charts/opensearch-2.12.1/templates/serviceaccount.yaml @@ -0,0 +1,17 @@ +{{- if .Values.rbac.create -}} +{{- $fullName := include "opensearch.uname" . -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + {{- if eq .Values.rbac.serviceAccountName "" }} + name: {{ $fullName | quote }} + {{- else }} + name: {{ .Values.rbac.serviceAccountName | quote }} + {{- end }} + labels: + {{- include "opensearch.labels" . | nindent 4 }} + annotations: + {{- with .Values.rbac.serviceAccountAnnotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end -}} diff --git a/charts/deps/charts/opensearch-2.12.1/templates/statefulset.yaml b/charts/deps/charts/opensearch-2.12.1/templates/statefulset.yaml new file mode 100644 index 0000000..e605ce0 --- /dev/null +++ b/charts/deps/charts/opensearch-2.12.1/templates/statefulset.yaml @@ -0,0 +1,541 @@ +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ template "opensearch.uname" . }} + labels: + {{- include "opensearch.labels" . | nindent 4 }} + annotations: + majorVersion: "{{ include "opensearch.majorVersion" . }}" + {{- with .Values.openSearchAnnotations }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + serviceName: {{ template "opensearch.serviceName" . }}-headless + selector: + matchLabels: + {{- include "opensearch.selectorLabels" . | nindent 6 }} + {{- if .Values.singleNode }} + replicas: 1 + {{- else }} + replicas: {{ .Values.replicas }} + {{- end }} + podManagementPolicy: {{ .Values.podManagementPolicy }} + updateStrategy: + type: {{ .Values.updateStrategy }} + {{- if .Values.persistence.enabled }} + volumeClaimTemplates: + - metadata: + name: {{ template "opensearch.uname" . }} + {{- if .Values.persistence.labels.enabled }} + labels: + {{- include "opensearch.labels" . | nindent 8 }} + {{- end }} + {{- with .Values.persistence.annotations }} + annotations: +{{ toYaml . | indent 8 }} + {{- end }} + spec: + accessModes: + {{- range .Values.persistence.accessModes }} + - {{ . | quote }} + {{- end }} + resources: + requests: + storage: {{ .Values.persistence.size | quote }} + {{- if .Values.persistence.storageClass }} + {{- if (eq "-" .Values.persistence.storageClass) }} + storageClassName: "" + {{- else }} + storageClassName: "{{ .Values.persistence.storageClass }}" + {{- end }} + {{- end }} + {{- end }} + template: + metadata: + name: "{{ template "opensearch.uname" . }}" + labels: + {{- include "opensearch.labels" . | nindent 8 }} + annotations: + {{- range $key, $value := .Values.podAnnotations }} + {{ $key }}: {{ $value | quote }} + {{- end }} + {{- /* This forces a restart if the configmap has changed */}} + {{- if .Values.config }} + configchecksum: {{ include (print .Template.BasePath "/configmap.yaml") . | sha256sum | trunc 63 }} + {{- end }} + {{- if .Values.securityConfig.config.data }} + securityconfigchecksum: {{ include (print .Template.BasePath "/securityconfig.yaml") . | sha256sum | trunc 63 }} + {{- end }} + spec: + {{- if .Values.schedulerName }} + schedulerName: "{{ .Values.schedulerName }}" + {{- end }} + securityContext: +{{ toYaml .Values.podSecurityContext | indent 8 }} + {{- if .Values.sysctl.enabled }} + sysctls: + - name: vm.max_map_count + value: {{ .Values.sysctlVmMaxMapCount | quote }} + {{- end }} + {{- if .Values.fsGroup }} + fsGroup: {{ .Values.fsGroup }} # Deprecated value, please use .Values.podSecurityContext.fsGroup + {{- end }} + {{- if and .Values.rbac.create (eq .Values.rbac.serviceAccountName "") }} + serviceAccountName: "{{ template "opensearch.uname" . }}" + {{- else }} + serviceAccountName: {{ .Values.rbac.serviceAccountName | quote }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: +{{ toYaml . | indent 6 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: +{{ toYaml . | indent 8 }} + {{- end }} + {{- if or (eq .Values.antiAffinity "hard") (eq .Values.antiAffinity "soft") .Values.nodeAffinity }} + {{- if .Values.priorityClassName }} + priorityClassName: {{ .Values.priorityClassName }} + {{- end }} + affinity: + {{- end }} + {{- if eq .Values.antiAffinity "hard" }} + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchExpressions: + - key: app.kubernetes.io/instance + operator: In + values: + - {{ .Release.Name }} + - key: app.kubernetes.io/name + operator: In + values: + - {{ include "opensearch.name" . }} + topologyKey: {{ .Values.antiAffinityTopologyKey }} + {{- else if eq .Values.antiAffinity "soft" }} + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 1 + podAffinityTerm: + topologyKey: {{ .Values.antiAffinityTopologyKey }} + labelSelector: + matchExpressions: + - key: app.kubernetes.io/instance + operator: In + values: + - {{ .Release.Name }} + - key: app.kubernetes.io/name + operator: In + values: + - {{ include "opensearch.name" . }} + {{- end }} + {{- with .Values.nodeAffinity }} + nodeAffinity: +{{ toYaml . | indent 10 }} + {{- end }} + {{- if .Values.topologySpreadConstraints }} + topologySpreadConstraints: + {{- toYaml .Values.topologySpreadConstraints | nindent 8 }} + {{- end }} + terminationGracePeriodSeconds: {{ .Values.terminationGracePeriod }} + volumes: + {{- range .Values.secretMounts }} + - name: {{ .name | required "secretMount .name is required" }} + secret: + secretName: {{ .secretName | required "secretMount .secretName is required" }} + {{- if .defaultMode }} + defaultMode: {{ .defaultMode }} + {{- end }} + {{- end }} + {{- if .Values.config }} + - name: config + configMap: + name: {{ template "opensearch.uname" . }}-config + {{- end }} + {{- if and .Values.securityConfig.config.data .Values.securityConfig.config.securityConfigSecret }} + {{ fail "Only one of .Values.securityConfig.config.data and .Values.securityConfig.config.securityConfigSecret may be defined. Please see the comment in values.yaml describing usage." }} + {{- end }} + {{- if .Values.securityConfig.config.data }} + - name: security-config-data + secret: + secretName: {{ include "opensearch.uname" . }}-securityconfig + {{- end }} + {{- with .Values.securityConfig.config.securityConfigSecret }} + - name: security-config-complete + secret: + secretName: {{ . | quote }} + {{- end }} + {{- if .Values.securityConfig.actionGroupsSecret }} + - name: action-groups + secret: + secretName: {{ .Values.securityConfig.actionGroupsSecret }} + {{- end }} + {{- if .Values.securityConfig.configSecret }} + - name: security-config + secret: + secretName: {{ .Values.securityConfig.configSecret }} + {{- end }} + {{- if .Values.securityConfig.internalUsersSecret }} + - name: internal-users-config + secret: + secretName: {{ .Values.securityConfig.internalUsersSecret }} + {{- end }} + {{- if .Values.securityConfig.rolesSecret }} + - name: roles + secret: + secretName: {{ .Values.securityConfig.rolesSecret }} + {{- end }} + {{- if .Values.securityConfig.rolesMappingSecret }} + - name: role-mapping + secret: + secretName: {{ .Values.securityConfig.rolesMappingSecret }} + {{- end -}} + {{- if .Values.securityConfig.tenantsSecret }} + - name: tenants + secret: + secretName: {{ .Values.securityConfig.tenantsSecret }} + {{- end }} +{{- if .Values.keystore }} + - name: keystore + emptyDir: {} + {{- range .Values.keystore }} + - name: keystore-{{ .secretName }} + secret: {{ toYaml . | nindent 12 }} + {{- end }} +{{ end }} + {{- if .Values.extraVolumes }} + # Currently some extra blocks accept strings + # to continue with backwards compatibility this is being kept + # whilst also allowing for yaml to be specified too. + {{- if eq "string" (printf "%T" .Values.extraVolumes) }} +{{ tpl .Values.extraVolumes . | indent 6 }} + {{- else }} +{{ toYaml .Values.extraVolumes | indent 6 }} + {{- end }} + {{- end }} + {{- if .Values.imagePullSecrets }} + imagePullSecrets: +{{ toYaml .Values.imagePullSecrets | indent 8 }} + {{- end }} + enableServiceLinks: {{ .Values.enableServiceLinks }} + {{- if .Values.hostAliases }} + hostAliases: {{ toYaml .Values.hostAliases | nindent 8 }} + {{- end }} + {{- if or (.Values.extraInitContainers) (.Values.keystore) (.Values.persistence.enabled) (.Values.sysctlInit.enabled) }} + initContainers: +{{- if and .Values.persistence.enabled .Values.persistence.enableInitChown }} + - name: fsgroup-volume + image: "{{ template "opensearch.dockerRegistry" . }}{{ .Values.persistence.image | default "busybox" }}:{{ .Values.persistence.imageTag | default "latest" }}" + imagePullPolicy: "{{ .Values.image.pullPolicy }}" + command: ['sh', '-c'] + args: + - 'chown -R 1000:1000 /usr/share/opensearch/data' + securityContext: + runAsUser: 0 + resources: + {{ toYaml .Values.initResources | nindent 10 }} + volumeMounts: + - name: "{{ template "opensearch.uname" . }}" + mountPath: {{ .Values.opensearchHome }}/data +{{- end }} +{{- if .Values.sysctlInit.enabled }} + - name: sysctl + image: "{{ template "opensearch.dockerRegistry" . }}{{ .Values.sysctlInit.image | default "busybox" }}:{{ .Values.sysctlInit.imageTag | default "latest" }}" + imagePullPolicy: "{{ .Values.image.pullPolicy }}" + command: + - sh + - -c + - | + set -xe + DESIRED="{{ .Values.sysctlVmMaxMapCount }}" + CURRENT=$(sysctl -n vm.max_map_count) + if [ "$DESIRED" -gt "$CURRENT" ]; then + sysctl -w vm.max_map_count=$DESIRED + fi + securityContext: + runAsUser: 0 + privileged: true + resources: + {{ toYaml .Values.initResources | nindent 10 }} +{{- end }} +{{ if .Values.keystore }} + - name: keystore + image: "{{ template "opensearch.dockerRegistry" . }}{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: "{{ .Values.image.pullPolicy }}" + command: + - sh + - -c + - | + #!/usr/bin/env bash + set -euo pipefail + + {{ .Values.opensearchHome }}/bin/opensearch-keystore create + + for i in /tmp/keystoreSecrets/*/*; do + key=$(basename $i) + echo "Adding file $i to keystore key $key" + {{ .Values.opensearchHome }}/bin/opensearch-keystore add-file "$key" "$i" + done + + # Add the bootstrap password since otherwise the opensearch entrypoint tries to do this on startup + if [ ! -z ${PASSWORD+x} ]; then + echo 'Adding env $PASSWORD to keystore as key bootstrap.password' + echo "$PASSWORD" | {{ .Values.opensearchHome }}/bin/opensearch-keystore add -x bootstrap.password + fi + + cp -a {{ .Values.opensearchHome }}/config/opensearch.keystore /tmp/keystore/ + env: {{ toYaml .Values.extraEnvs | nindent 10 }} + envFrom: {{ toYaml .Values.envFrom | nindent 10 }} + resources: + {{ toYaml .Values.initResources | nindent 10 }} + volumeMounts: + - name: keystore + mountPath: /tmp/keystore + {{- range .Values.keystore }} + - name: keystore-{{ .secretName }} + mountPath: /tmp/keystoreSecrets/{{ .secretName }} + {{- end }} +{{ end }} + {{- if .Values.extraInitContainers }} + # Currently some extra blocks accept strings + # to continue with backwards compatibility this is being kept + # whilst also allowing for yaml to be specified too. + {{- if eq "string" (printf "%T" .Values.extraInitContainers) }} +{{ tpl .Values.extraInitContainers . | indent 6 }} + {{- else }} +{{ toYaml .Values.extraInitContainers | indent 6 }} + {{- end }} + {{- end }} + {{- end }} + containers: + - name: "{{ template "opensearch.name" . }}" + securityContext: +{{ toYaml .Values.securityContext | indent 10 }} + {{- if .Values.plugins.enabled }} + command: + - sh + - -c + - | + #!/usr/bin/env bash + set -euo pipefail + + {{- range $plugin := .Values.plugins.installList }} + ./bin/opensearch-plugin install -b {{ $plugin }} + {{- end }} + + bash opensearch-docker-entrypoint.sh + {{- end }} + + image: "{{ template "opensearch.dockerRegistry" . }}{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: "{{ .Values.image.pullPolicy }}" + readinessProbe: +{{ toYaml .Values.readinessProbe | indent 10 }} + {{- if .Values.livenessProbe }} + livenessProbe: +{{ toYaml .Values.livenessProbe | indent 10 }} + {{- end }} + {{- if semverCompare ">=1.16-0" .Capabilities.KubeVersion.Version }} + startupProbe: +{{ toYaml .Values.startupProbe | indent 10 }} + {{- end }} + ports: + - name: http + containerPort: {{ .Values.httpPort }} + {{- if .Values.httpHostPort }} + hostPort: {{ .Values.httpHostPort }} + {{- end }} + - name: transport + containerPort: {{ .Values.transportPort }} + {{- if .Values.transportHostPort }} + hostPort: {{ .Values.transportHostPort }} + {{- end }} + - name: metrics + containerPort: {{ .Values.metricsPort }} + resources: +{{ toYaml .Values.resources | indent 10 }} + env: + - name: node.name + valueFrom: + fieldRef: + fieldPath: metadata.name + {{- if (and (has "master" .Values.roles) (not .Values.singleNode)) }} + - name: cluster.initial_master_nodes + value: "{{ template "opensearch.endpoints" . }}" + {{- end }} + - name: discovery.seed_hosts + value: "{{ template "opensearch.masterService" . }}-headless" + - name: cluster.name + value: "{{ .Values.clusterName }}" + - name: network.host + value: "{{ .Values.networkHost }}" + - name: OPENSEARCH_JAVA_OPTS + value: "{{ .Values.opensearchJavaOpts }}" + - name: node.roles + value: "{{ template "opensearch.roles" . }}" + {{- if .Values.singleNode }} + - name: discovery.type + value: "single-node" + {{- end }} +{{- if .Values.extraEnvs }} +{{ toYaml .Values.extraEnvs | indent 8 }} +{{- end }} +{{- if .Values.envFrom }} + envFrom: +{{ toYaml .Values.envFrom | indent 8 }} +{{- end }} +{{- if .Values.opensearchLifecycle }} + lifecycle: +{{ toYaml .Values.opensearchLifecycle | indent 10 }} +{{- end }} + volumeMounts: + {{- if .Values.persistence.enabled }} + - name: "{{ template "opensearch.uname" . }}" + mountPath: {{ .Values.opensearchHome }}/data + {{- end }} + {{- if .Values.keystore }} + - name: keystore + mountPath: {{ .Values.opensearchHome }}/config/opensearch.keystore + subPath: opensearch.keystore + {{- end }} + {{- if .Values.securityConfig.enabled }} + {{- if .Values.securityConfig.actionGroupsSecret }} + - mountPath: {{ .Values.securityConfig.path }}/action_groups.yml + name: action-groups + subPath: action_groups.yml + {{- end }} + {{- if .Values.securityConfig.configSecret }} + - mountPath: {{ .Values.securityConfig.path }}/config.yml + name: security-config + subPath: config.yml + {{- end }} + {{- if .Values.securityConfig.internalUsersSecret }} + - mountPath: {{ .Values.securityConfig.path }}/internal_users.yml + name: internal-users-config + subPath: internal_users.yml + {{- end }} + {{- if .Values.securityConfig.rolesSecret }} + - mountPath: {{ .Values.securityConfig.path }}/roles.yml + name: roles + subPath: roles.yml + {{- end }} + {{- if .Values.securityConfig.rolesMappingSecret }} + - mountPath: {{ .Values.securityConfig.path }}/roles_mapping.yml + name: role-mapping + subPath: roles_mapping.yml + {{- end }} + {{- if .Values.securityConfig.tenantsSecret }} + - mountPath: {{ .Values.securityConfig.path }}/tenants.yml + name: tenants + subPath: tenants.yml + {{- end }} + {{- if .Values.securityConfig.config.data }} + {{- if .Values.securityConfig.config.dataComplete }} + - mountPath: {{ .Values.securityConfig.path }} + name: security-config-data + {{- else }} + {{- range $key, $_ := .Values.securityConfig.config.data }} + - mountPath: {{ $.Values.securityConfig.path }}/{{ $key }} + name: security-config-data + subPath: {{ $key }} + {{- end }} + {{- end }} + {{- else if .Values.securityConfig.config.securityConfigSecret }} + - mountPath: {{ .Values.securityConfig.path }} + name: security-config-complete + {{- end }} + {{- end }} + {{- range .Values.secretMounts }} + - name: {{ .name | required "secretMount .name is required" }} + mountPath: {{ .path | required "secretMount .path is required" }} + {{- if .subPath }} + subPath: {{ .subPath }} + {{- end }} + {{- end }} + {{- range $path, $config := .Values.config }} + - name: config + mountPath: {{ $.Values.opensearchHome }}/config/{{ $path }} + subPath: {{ $path }} + {{- end -}} + {{- if .Values.extraVolumeMounts }} + # Currently some extra blocks accept strings + # to continue with backwards compatibility this is being kept + # whilst also allowing for yaml to be specified too. + {{- if eq "string" (printf "%T" .Values.extraVolumeMounts) }} +{{ tpl .Values.extraVolumeMounts . | indent 8 }} + {{- else }} +{{ toYaml .Values.extraVolumeMounts | indent 8 }} + {{- end }} + {{- end }} + {{- if .Values.masterTerminationFix }} + {{- if has "master" .Values.roles }} + # This sidecar will prevent slow master re-election + - name: opensearch-master-graceful-termination-handler + image: "{{ template "opensearch.dockerRegistry" . }}{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: "{{ .Values.image.pullPolicy }}" + command: + - "sh" + - -c + - | + #!/usr/bin/env bash + set -eo pipefail + + http () { + local path="${1}" + if [ -n "${USERNAME}" ] && [ -n "${PASSWORD}" ]; then + BASIC_AUTH="-u ${USERNAME}:${PASSWORD}" + else + BASIC_AUTH='' + fi + curl -XGET -s -k --fail ${BASIC_AUTH} {{ .Values.protocol }}://{{ template "opensearch.masterService" . }}:{{ .Values.httpPort }}${path} + } + + cleanup () { + while true ; do + local master="$(http "/_cat/master?h=node" || echo "")" + if [[ $master == "{{ template "opensearch.masterService" . }}"* && $master != "${NODE_NAME}" ]]; then + echo "This node is not master." + break + fi + echo "This node is still master, waiting gracefully for it to step down" + sleep 1 + done + + exit 0 + } + + trap cleanup SIGTERM + + sleep infinity & + wait $! + resources: +{{ toYaml .Values.sidecarResources | indent 10 }} + env: + - name: NODE_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + {{- if .Values.extraEnvs }} +{{ toYaml .Values.extraEnvs | indent 8 }} + {{- end }} + {{- if .Values.envFrom }} + envFrom: +{{ toYaml .Values.envFrom | indent 8 }} + {{- end }} + {{- end }} + {{- end }} +{{- if .Values.lifecycle }} + lifecycle: +{{ toYaml .Values.lifecycle | indent 10 }} +{{- end }} + {{- if .Values.extraContainers }} + # Currently some extra blocks accept strings + # to continue with backwards compatibility this is being kept + # whilst also allowing for yaml to be specified too. + {{- if eq "string" (printf "%T" .Values.extraContainers) }} +{{ tpl .Values.extraContainers . | indent 6 }} + {{- else }} +{{ toYaml .Values.extraContainers | indent 6 }} + {{- end }} + {{- end }} diff --git a/charts/deps/charts/opensearch-2.12.1/values.yaml b/charts/deps/charts/opensearch-2.12.1/values.yaml new file mode 100644 index 0000000..12465f3 --- /dev/null +++ b/charts/deps/charts/opensearch-2.12.1/values.yaml @@ -0,0 +1,501 @@ +--- +clusterName: "opensearch-cluster" +nodeGroup: "master" + +# If discovery.type in the opensearch configuration is set to "single-node", +# this should be set to "true" +# If "true", replicas will be forced to 1 +singleNode: false + +# The service that non master groups will try to connect to when joining the cluster +# This should be set to clusterName + "-" + nodeGroup for your master group +masterService: "opensearch-cluster-master" + +# OpenSearch roles that will be applied to this nodeGroup +# These will be set as environment variable "node.roles". E.g. node.roles=master,ingest,data,remote_cluster_client +roles: + - master + - ingest + - data + - remote_cluster_client + +replicas: 3 + +# if not set, falls back to parsing .Values.imageTag, then .Chart.appVersion. +majorVersion: "" + +global: + # Set if you want to change the default docker registry, e.g. a private one. + dockerRegistry: "" + +# Allows you to add any config files in {{ .Values.opensearchHome }}/config +opensearchHome: /usr/share/opensearch +# such as opensearch.yml and log4j2.properties +config: + # Values must be YAML literal style scalar / YAML multiline string. + # : | + # + # log4j2.properties: | + # status = error + # + # appender.console.type = Console + # appender.console.name = console + # appender.console.layout.type = PatternLayout + # appender.console.layout.pattern = [%d{ISO8601}][%-5p][%-25c{1.}] [%node_name]%marker %m%n + # + # rootLogger.level = info + # rootLogger.appenderRef.console.ref = console + opensearch.yml: | + cluster.name: opensearch-cluster + + # Bind to all interfaces because we don't know what IP address Docker will assign to us. + network.host: 0.0.0.0 + + # Setting network.host to a non-loopback address enables the annoying bootstrap checks. "Single-node" mode disables them again. + # Implicitly done if ".singleNode" is set to "true". + # discovery.type: single-node + + # Start OpenSearch Security Demo Configuration + # WARNING: revise all the lines below before you go into production + plugins: + security: + ssl: + transport: + pemcert_filepath: esnode.pem + pemkey_filepath: esnode-key.pem + pemtrustedcas_filepath: root-ca.pem + enforce_hostname_verification: false + http: + enabled: true + pemcert_filepath: esnode.pem + pemkey_filepath: esnode-key.pem + pemtrustedcas_filepath: root-ca.pem + allow_unsafe_democertificates: true + allow_default_init_securityindex: true + authcz: + admin_dn: + - CN=kirk,OU=client,O=client,L=test,C=de + audit.type: internal_opensearch + enable_snapshot_restore_privilege: true + check_snapshot_restore_write_privileges: true + restapi: + roles_enabled: ["all_access", "security_rest_api_access"] + system_indices: + enabled: true + indices: + [ + ".opendistro-alerting-config", + ".opendistro-alerting-alert*", + ".opendistro-anomaly-results*", + ".opendistro-anomaly-detector*", + ".opendistro-anomaly-checkpoints", + ".opendistro-anomaly-detection-state", + ".opendistro-reports-*", + ".opendistro-notifications-*", + ".opendistro-notebooks", + ".opendistro-asynchronous-search-response*", + ] + ######## End OpenSearch Security Demo Configuration ######## + # log4j2.properties: + +# Extra environment variables to append to this nodeGroup +# This will be appended to the current 'env:' key. You can use any of the kubernetes env +# syntax here +extraEnvs: [] +# - name: MY_ENVIRONMENT_VAR +# value: the_value_goes_here + +# Allows you to load environment variables from kubernetes secret or config map +envFrom: [] +# - secretRef: +# name: env-secret +# - configMapRef: +# name: config-map + +# A list of secrets and their paths to mount inside the pod +# This is useful for mounting certificates for security and for mounting +# the X-Pack license +secretMounts: [] + +hostAliases: [] +# - ip: "127.0.0.1" +# hostnames: +# - "foo.local" +# - "bar.local" + +image: + repository: "opensearchproject/opensearch" + # override image tag, which is .Chart.AppVersion by default + tag: "" + pullPolicy: "IfNotPresent" + +podAnnotations: {} + # iam.amazonaws.com/role: es-cluster + +# OpenSearch Statefulset annotations +openSearchAnnotations: {} + +# additionals labels +labels: {} + +opensearchJavaOpts: "-Xmx512M -Xms512M" + +resources: + requests: + cpu: "1000m" + memory: "100Mi" + +initResources: {} +# limits: +# cpu: "25m" +# memory: "128Mi" +# requests: +# cpu: "25m" +# memory: "128Mi" + +sidecarResources: {} +# limits: +# cpu: "25m" +# memory: "128Mi" +# requests: +# cpu: "25m" +# memory: "128Mi" + +networkHost: "0.0.0.0" + +rbac: + create: false + serviceAccountAnnotations: {} + serviceAccountName: "" + +podSecurityPolicy: + create: false + name: "" + spec: + privileged: true + fsGroup: + rule: RunAsAny + runAsUser: + rule: RunAsAny + seLinux: + rule: RunAsAny + supplementalGroups: + rule: RunAsAny + volumes: + - secret + - configMap + - persistentVolumeClaim + - emptyDir + +persistence: + enabled: true + # Set to false to disable the `fsgroup-volume` initContainer that will update permissions on the persistent disk. + enableInitChown: true + # override image, which is busybox by default + # image: busybox + # override image tag, which is latest by default + # imageTag: + labels: + # Add default labels for the volumeClaimTemplate of the StatefulSet + enabled: false + # OpenSearch Persistent Volume Storage Class + # If defined, storageClassName: + # If set to "-", storageClassName: "", which disables dynamic provisioning + # If undefined (the default) or set to null, no storageClassName spec is + # set, choosing the default provisioner. (gp2 on AWS, standard on + # GKE, AWS & OpenStack) + # + # storageClass: "-" + accessModes: + - ReadWriteOnce + size: 8Gi + annotations: {} + +extraVolumes: [] + # - name: extras + # emptyDir: {} + +extraVolumeMounts: [] + # - name: extras + # mountPath: /usr/share/extras + # readOnly: true + +extraContainers: [] + # - name: do-something + # image: busybox + # command: ['do', 'something'] + +extraInitContainers: [] + # - name: do-somethings + # image: busybox + # command: ['do', 'something'] + +# This is the PriorityClass settings as defined in +# https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#priorityclass +priorityClassName: "" + +# By default this will make sure two pods don't end up on the same node +# Changing this to a region would allow you to spread pods across regions +antiAffinityTopologyKey: "kubernetes.io/hostname" + +# Hard means that by default pods will only be scheduled if there are enough nodes for them +# and that they will never end up on the same node. Setting this to soft will do this "best effort" +antiAffinity: "soft" + +# This is the node affinity settings as defined in +# https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#node-affinity-beta-feature +nodeAffinity: {} + +# This is the pod topology spread constraints +# https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/ +topologySpreadConstraints: [] + +# The default is to deploy all pods serially. By setting this to parallel all pods are started at +# the same time when bootstrapping the cluster +podManagementPolicy: "Parallel" + +# The environment variables injected by service links are not used, but can lead to slow OpenSearch boot times when +# there are many services in the current namespace. +# If you experience slow pod startups you probably want to set this to `false`. +enableServiceLinks: true + +protocol: https +httpPort: 9200 +transportPort: 9300 +metricsPort: 9600 +httpHostPort: "" +transportHostPort: "" + + +service: + labels: {} + labelsHeadless: {} + headless: + annotations: {} + type: ClusterIP + # The IP family and IP families options are to set the behaviour in a dual-stack environment + # Omitting these values will let the service fall back to whatever the CNI dictates the defaults + # should be + # + # ipFamilyPolicy: SingleStack + # ipFamilies: + # - IPv4 + nodePort: "" + annotations: {} + httpPortName: http + transportPortName: transport + metricsPortName: metrics + loadBalancerIP: "" + loadBalancerSourceRanges: [] + externalTrafficPolicy: "" + +updateStrategy: RollingUpdate + +# This is the max unavailable setting for the pod disruption budget +# The default value of 1 will make sure that kubernetes won't allow more than 1 +# of your pods to be unavailable during maintenance +maxUnavailable: 1 + +podSecurityContext: + fsGroup: 1000 + runAsUser: 1000 + +securityContext: + capabilities: + drop: + - ALL + # readOnlyRootFilesystem: true + runAsNonRoot: true + runAsUser: 1000 + +securityConfig: + enabled: true + path: "/usr/share/opensearch/config/opensearch-security" + actionGroupsSecret: + configSecret: + internalUsersSecret: + rolesSecret: + rolesMappingSecret: + tenantsSecret: + # The following option simplifies securityConfig by using a single secret and + # specifying the config files as keys in the secret instead of creating + # different secrets for for each config file. + # Note that this is an alternative to the individual secret configuration + # above and shouldn't be used if the above secrets are used. + config: + # There are multiple ways to define the configuration here: + # * If you define anything under data, the chart will automatically create + # a secret and mount it. This is best option to choose if you want to override all the + # existing yml files at once. + # * If you define securityConfigSecret, the chart will assume this secret is + # created externally and mount it. This is best option to choose if your intention is to + # only update a single yml file. + # * It is an error to define both data and securityConfigSecret. + securityConfigSecret: "" + dataComplete: true + data: {} + # config.yml: |- + # internal_users.yml: |- + # roles.yml: |- + # roles_mapping.yml: |- + # action_groups.yml: |- + # tenants.yml: |- + +# How long to wait for opensearch to stop gracefully +terminationGracePeriod: 120 + +sysctlVmMaxMapCount: 262144 + +startupProbe: + tcpSocket: + port: 9200 + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 3 + failureThreshold: 30 + +livenessProbe: {} + # periodSeconds: 20 + # timeoutSeconds: 5 + # failureThreshold: 10 + # successThreshold: 1 + # initialDelaySeconds: 10 + # tcpSocket: + # port: 9200 + +readinessProbe: + tcpSocket: + port: 9200 + periodSeconds: 5 + timeoutSeconds: 3 + failureThreshold: 3 + +## Use an alternate scheduler. +## ref: https://kubernetes.io/docs/tasks/administer-cluster/configure-multiple-schedulers/ +## +schedulerName: "" + +imagePullSecrets: [] +nodeSelector: {} +tolerations: [] + +# Enabling this will publically expose your OpenSearch instance. +# Only enable this if you have security enabled on your cluster +ingress: + enabled: false + # For Kubernetes >= 1.18 you should specify the ingress-controller via the field ingressClassName + # See https://kubernetes.io/blog/2020/04/02/improvements-to-the-ingress-api-in-kubernetes-1.18/#specifying-the-class-of-an-ingress + # ingressClassName: nginx + + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + path: / + hosts: + - chart-example.local + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +nameOverride: "" +fullnameOverride: "" + +masterTerminationFix: false + +opensearchLifecycle: {} + # preStop: + # exec: + # command: ["/bin/sh", "-c", "echo Hello from the preStart handler > /usr/share/message"] + # postStart: + # exec: + # command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"] + +lifecycle: {} + # preStop: + # exec: + # command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"] + # postStart: + # exec: + # command: + # - bash + # - -c + # - | + # #!/bin/bash + # # Add a template to adjust number of shards/replicas1 + # TEMPLATE_NAME=my_template + # INDEX_PATTERN="logstash-*" + # SHARD_COUNT=8 + # REPLICA_COUNT=1 + # ES_URL=http://localhost:9200 + # while [[ "$(curl -s -o /dev/null -w '%{http_code}\n' $ES_URL)" != "200" ]]; do sleep 1; done + # curl -XPUT "$ES_URL/_template/$TEMPLATE_NAME" -H 'Content-Type: application/json' -d'{"index_patterns":['\""$INDEX_PATTERN"\"'],"settings":{"number_of_shards":'$SHARD_COUNT',"number_of_replicas":'$REPLICA_COUNT'}}' + +keystore: [] +# To add secrets to the keystore: +# - secretName: opensearch-encryption-key + +networkPolicy: + create: false + ## Enable creation of NetworkPolicy resources. Only Ingress traffic is filtered for now. + ## In order for a Pod to access OpenSearch, it needs to have the following label: + ## {{ template "uname" . }}-client: "true" + ## Example for default configuration to access HTTP port: + ## opensearch-master-http-client: "true" + ## Example for default configuration to access transport port: + ## opensearch-master-transport-client: "true" + + http: + enabled: false + +# Deprecated +# please use the above podSecurityContext.fsGroup instead +fsGroup: "" + +## Set optimal sysctl's through securityContext. This requires privilege. Can be disabled if +## the system has already been preconfigured. (Ex: https://www.elastic.co/guide/en/elasticsearch/reference/current/vm-max-map-count.html) +## Also see: https://kubernetes.io/docs/tasks/administer-cluster/sysctl-cluster/ +sysctl: + enabled: false + +## Set optimal sysctl's through privileged initContainer. +sysctlInit: + enabled: false + # override image, which is busybox by default + # image: busybox + # override image tag, which is latest by default + # imageTag: + +## Enable to add 3rd Party / Custom plugins not offered in the default OpenSearch image. +plugins: + enabled: false + installList: [] + # - example-fake-plugin + +# -- Array of extra K8s manifests to deploy +extraObjects: [] + # - apiVersion: secrets-store.csi.x-k8s.io/v1 + # kind: SecretProviderClass + # metadata: + # name: argocd-secrets-store + # spec: + # provider: aws + # parameters: + # objects: | + # - objectName: "argocd" + # objectType: "secretsmanager" + # jmesPath: + # - path: "client_id" + # objectAlias: "client_id" + # - path: "client_secret" + # objectAlias: "client_secret" + # secretObjects: + # - data: + # - key: client_id + # objectName: client_id + # - key: client_secret + # objectName: client_secret + # secretName: argocd-secrets-store + # type: Opaque + # labels: + # app.kubernetes.io/part-of: argocd From 40c95e73e55b0a47cb3174db59a47b860a5e08fa Mon Sep 17 00:00:00 2001 From: abimichel Date: Tue, 30 Jan 2024 16:03:11 -0800 Subject: [PATCH 04/29] added mysql auto-label --- auto-label.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/auto-label.yaml b/auto-label.yaml index 8ef583e..d4f0253 100644 --- a/auto-label.yaml +++ b/auto-label.yaml @@ -17,7 +17,7 @@ spec: - Pod selector: matchLabels: - ## WIP ## + app.kubernetes.io/instance: mysql mutate: patchStrategicMerge: metadata: From 14f0ac015b7315b5d210ba8acae4c3310a9f4d1a Mon Sep 17 00:00:00 2001 From: abimichel Date: Tue, 30 Jan 2024 16:03:40 -0800 Subject: [PATCH 05/29] reduced size of database PVC --- charts/deps/charts/mysql-9.7.2/values.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/deps/charts/mysql-9.7.2/values.yaml b/charts/deps/charts/mysql-9.7.2/values.yaml index bc585f1..b2957be 100644 --- a/charts/deps/charts/mysql-9.7.2/values.yaml +++ b/charts/deps/charts/mysql-9.7.2/values.yaml @@ -440,7 +440,7 @@ primary: - ReadWriteOnce ## @param primary.persistence.size MySQL primary persistent volume size ## - size: 8Gi + size: 500Mi # Abi: previously 8Gi ## @param primary.persistence.selector Selector to match an existing Persistent Volume ## selector: ## matchLabels: @@ -820,7 +820,7 @@ secondary: - ReadWriteOnce ## @param secondary.persistence.size MySQL secondary persistent volume size ## - size: 8Gi + size: 500Mi # Abi: previously 8Gi ## @param secondary.persistence.selector Selector to match an existing Persistent Volume ## selector: ## matchLabels: From ed11d3fdb0eeb492a87ff63de1257125a124ae82 Mon Sep 17 00:00:00 2001 From: abimichel Date: Tue, 30 Jan 2024 16:44:51 -0800 Subject: [PATCH 06/29] fixed mysql scc --- charts/deps/charts/mysql-9.7.2/values.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/charts/deps/charts/mysql-9.7.2/values.yaml b/charts/deps/charts/mysql-9.7.2/values.yaml index b2957be..c426ad0 100644 --- a/charts/deps/charts/mysql-9.7.2/values.yaml +++ b/charts/deps/charts/mysql-9.7.2/values.yaml @@ -296,7 +296,7 @@ primary: ## @param primary.podSecurityContext.fsGroup Group ID for the mounted volumes' filesystem ## podSecurityContext: - enabled: true + enabled: false # Abi: OCP security context is set to 'restricted' by default fsGroup: 1001 ## MySQL primary container security context ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container @@ -305,7 +305,7 @@ primary: ## @param primary.containerSecurityContext.runAsNonRoot Set MySQL primary container's Security Context runAsNonRoot ## containerSecurityContext: - enabled: true + enabled: false # Abi: OCP security context is set to 'restricted' by default runAsUser: 1001 runAsNonRoot: true ## MySQL primary container's resource requests and limits @@ -676,7 +676,7 @@ secondary: ## @param secondary.podSecurityContext.fsGroup Group ID for the mounted volumes' filesystem ## podSecurityContext: - enabled: true + enabled: false # Abi: OCP security context is set to 'restricted' by default fsGroup: 1001 ## MySQL secondary container security context ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container @@ -685,7 +685,7 @@ secondary: ## @param secondary.containerSecurityContext.runAsNonRoot Set MySQL secondary container's Security Context runAsNonRoot ## containerSecurityContext: - enabled: true + enabled: false # Abi: OCP security context is set to 'restricted' by default runAsUser: 1001 runAsNonRoot: true ## MySQL secondary container's resource requests and limits @@ -1060,7 +1060,7 @@ metrics: ## @param metrics.containerSecurityContext.runAsNonRoot Set MySQL metrics container's Security Context runAsNonRoot ## containerSecurityContext: - enabled: true + enabled: false # Abi: OCP security context is set to 'restricted' by default runAsUser: 1001 runAsNonRoot: true ## MySQL Prometheus exporter service parameters From 163980a0a73deab6b48ac9c0c668868d1fb1e21d Mon Sep 17 00:00:00 2001 From: abimichel Date: Tue, 30 Jan 2024 16:58:56 -0800 Subject: [PATCH 07/29] deleted opensearch poddisruptionbudget.yaml --- .../templates/poddisruptionbudget.yaml | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 charts/deps/charts/opensearch-2.12.1/templates/poddisruptionbudget.yaml diff --git a/charts/deps/charts/opensearch-2.12.1/templates/poddisruptionbudget.yaml b/charts/deps/charts/opensearch-2.12.1/templates/poddisruptionbudget.yaml deleted file mode 100644 index 68ab5b6..0000000 --- a/charts/deps/charts/opensearch-2.12.1/templates/poddisruptionbudget.yaml +++ /dev/null @@ -1,17 +0,0 @@ -{{- if .Values.maxUnavailable }} -{{- if semverCompare ">=1.21-0" .Capabilities.KubeVersion.GitVersion -}} -apiVersion: policy/v1 -{{- else -}} -apiVersion: policy/v1beta1 -{{- end }} -kind: PodDisruptionBudget -metadata: - name: "{{ template "opensearch.uname" . }}-pdb" - labels: - {{- include "opensearch.labels" . | nindent 4 }} -spec: - maxUnavailable: {{ .Values.maxUnavailable }} - selector: - matchLabels: - {{- include "opensearch.selectorLabels" . | nindent 6 }} -{{- end }} From fed520516ff17b882379b21f61cafc18ec3c44d4 Mon Sep 17 00:00:00 2001 From: abimichel Date: Tue, 30 Jan 2024 17:00:06 -0800 Subject: [PATCH 08/29] initial openmetadata-admin service account --- service-account.yaml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 service-account.yaml diff --git a/service-account.yaml b/service-account.yaml new file mode 100644 index 0000000..30a4feb --- /dev/null +++ b/service-account.yaml @@ -0,0 +1,6 @@ +kind: ServiceAccount +apiVersion: v1 +metadata: + name: openmetadata-admin +imagePullSecrets: + - name: artifactory-pull \ No newline at end of file From f6d21f6b4d3c4b6ca780ede382b4afb9715a6c52 Mon Sep 17 00:00:00 2001 From: abimichel Date: Tue, 30 Jan 2024 17:38:40 -0800 Subject: [PATCH 09/29] fixed opensearch scc --- .../opensearch-2.12.1/templates/statefulset.yaml | 3 +-- charts/deps/charts/opensearch-2.12.1/values.yaml | 16 ++++++++-------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/charts/deps/charts/opensearch-2.12.1/templates/statefulset.yaml b/charts/deps/charts/opensearch-2.12.1/templates/statefulset.yaml index e605ce0..471875a 100644 --- a/charts/deps/charts/opensearch-2.12.1/templates/statefulset.yaml +++ b/charts/deps/charts/opensearch-2.12.1/templates/statefulset.yaml @@ -232,8 +232,7 @@ spec: command: ['sh', '-c'] args: - 'chown -R 1000:1000 /usr/share/opensearch/data' - securityContext: - runAsUser: 0 + securityContext: {} resources: {{ toYaml .Values.initResources | nindent 10 }} volumeMounts: diff --git a/charts/deps/charts/opensearch-2.12.1/values.yaml b/charts/deps/charts/opensearch-2.12.1/values.yaml index 12465f3..3cc7491 100644 --- a/charts/deps/charts/opensearch-2.12.1/values.yaml +++ b/charts/deps/charts/opensearch-2.12.1/values.yaml @@ -19,7 +19,7 @@ roles: - data - remote_cluster_client -replicas: 3 +replicas: 1 # Abi: previously 3 # if not set, falls back to parsing .Values.imageTag, then .Chart.appVersion. majorVersion: "" @@ -142,7 +142,7 @@ opensearchJavaOpts: "-Xmx512M -Xms512M" resources: requests: - cpu: "1000m" + cpu: "100m" # Abi: lowered memory: "100Mi" initResources: {} @@ -208,7 +208,7 @@ persistence: # storageClass: "-" accessModes: - ReadWriteOnce - size: 8Gi + size: 500Mi # Abi: previously 8Gi annotations: {} extraVolumes: [] @@ -294,11 +294,11 @@ updateStrategy: RollingUpdate # This is the max unavailable setting for the pod disruption budget # The default value of 1 will make sure that kubernetes won't allow more than 1 # of your pods to be unavailable during maintenance -maxUnavailable: 1 +# maxUnavailable: 1 # Abi: removed PDB for now -podSecurityContext: - fsGroup: 1000 - runAsUser: 1000 +podSecurityContext: # Abi: removed runAsUser: 0 from statefulset.yaml + # fsGroup: 1000 # Abi: allow for random UID + # runAsUser: 1000 # Abi: allow for random UID securityContext: capabilities: @@ -306,7 +306,7 @@ securityContext: - ALL # readOnlyRootFilesystem: true runAsNonRoot: true - runAsUser: 1000 + # runAsUser: 1000 # Abi: allow for random UID securityConfig: enabled: true From 9394aba58208dc8564c914dc7caca809abd1fbe3 Mon Sep 17 00:00:00 2001 From: abimichel Date: Wed, 31 Jan 2024 10:55:56 -0800 Subject: [PATCH 10/29] custom busybox image --- .github/workflows/docker-publish.yml | 71 +++++++++++++++++++ .../containers/opensearch/busybox/Dockerfile | 5 ++ 2 files changed, 76 insertions(+) create mode 100644 .github/workflows/docker-publish.yml create mode 100644 charts/deps/containers/opensearch/busybox/Dockerfile diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml new file mode 100644 index 0000000..3c25176 --- /dev/null +++ b/.github/workflows/docker-publish.yml @@ -0,0 +1,71 @@ +name: Push to GHCR + +on: + push: + branches: [ "security-context-changes" ] + +env: + # Abi: pull ghcr.io/bcgov/nr-openmetadata=:main + REGISTRY: ghcr.io + PATH: charts/deps/containers/opensearch/busybox + IMAGE_NAME: ${{ github.repository }}-opensearch-busybox + + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + # Abi: this sets the working directory to the specific GitHub folder + with: + path: ${{ env.PATH }} + + - name: Install cosign + if: github.event_name != 'pull_request' + uses: sigstore/cosign-installer@6e04d228eb30da1757ee4e1dd75a0ec73a653e06 #v3.1.1 + with: + cosign-release: 'v2.1.1' + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 + + - name: Log into registry ${{ env.REGISTRY }} + if: github.event_name != 'pull_request' + uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + - name: Build and push Docker image + id: build-and-push + uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0 + with: + # Abi: to help the action find the Dockerfile to build from + context: ${{ env.PATH }}/ + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Sign the published Docker image + if: ${{ github.event_name != 'pull_request' }} + env: + # https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable + TAGS: ${{ steps.meta.outputs.tags }} + DIGEST: ${{ steps.build-and-push.outputs.digest }} + + run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST} \ No newline at end of file diff --git a/charts/deps/containers/opensearch/busybox/Dockerfile b/charts/deps/containers/opensearch/busybox/Dockerfile new file mode 100644 index 0000000..5219039 --- /dev/null +++ b/charts/deps/containers/opensearch/busybox/Dockerfile @@ -0,0 +1,5 @@ +# based on line 230 of statefulset.yaml +FROM busybox:latest + +# the statefulset init container is supposed to change ownership of the /usr/share/opensearch/data directory: chown -R 1000:1000 /usr/share/opensearch/data +RUN chmod -R g+rwX /usr \ No newline at end of file From c62bb515cc86a6793dd3f5f7100371c8b92ef35d Mon Sep 17 00:00:00 2001 From: abimichel Date: Wed, 31 Jan 2024 11:05:53 -0800 Subject: [PATCH 11/29] modified: .github/workflows/docker-publish.yml --- .github/workflows/docker-publish.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 3c25176..978ad5d 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -22,9 +22,6 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v3 - # Abi: this sets the working directory to the specific GitHub folder - with: - path: ${{ env.PATH }} - name: Install cosign if: github.event_name != 'pull_request' From 2b7c5b9e295542bc64c33daa1ee83d62e03ae7b7 Mon Sep 17 00:00:00 2001 From: abimichel Date: Wed, 31 Jan 2024 11:15:18 -0800 Subject: [PATCH 12/29] added branch ref to actions checkout --- .github/workflows/docker-publish.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index 978ad5d..b718463 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -22,6 +22,9 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v3 + # Abi: delete before merging PR + with: + ref: security-context-changes - name: Install cosign if: github.event_name != 'pull_request' From 0a2187773034fd6b302bf7add71b199f4840d082 Mon Sep 17 00:00:00 2001 From: abimichel Date: Wed, 31 Jan 2024 11:18:00 -0800 Subject: [PATCH 13/29] modified: .github/workflows/docker-publish.yml --- .github/workflows/docker-publish.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish.yml index b718463..ec93979 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish.yml @@ -7,9 +7,8 @@ on: env: # Abi: pull ghcr.io/bcgov/nr-openmetadata=:main REGISTRY: ghcr.io - PATH: charts/deps/containers/opensearch/busybox + DOCKERFILE_PATH: charts/deps/containers/opensearch/busybox IMAGE_NAME: ${{ github.repository }}-opensearch-busybox - jobs: build: @@ -54,7 +53,7 @@ jobs: uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0 with: # Abi: to help the action find the Dockerfile to build from - context: ${{ env.PATH }}/ + context: ${{ env.DOCKERFILE_PATH }}/ push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} From dbc997ae6dfcc71161732021f383c09c35f4a220 Mon Sep 17 00:00:00 2001 From: abimichel Date: Wed, 31 Jan 2024 11:38:54 -0800 Subject: [PATCH 14/29] move custom opensearch image to GHCR --- ...publish.yml => docker-publish-busybox.yml} | 4 +- .../workflows/docker-publish-opensearch.yml | 70 +++++++++++++++++++ .../deps/containers}/opensearch/Dockerfile | 0 3 files changed, 72 insertions(+), 2 deletions(-) rename .github/workflows/{docker-publish.yml => docker-publish-busybox.yml} (94%) create mode 100644 .github/workflows/docker-publish-opensearch.yml rename {openmetadata-dependencies-1.2.5/charts => charts/deps/containers}/opensearch/Dockerfile (100%) diff --git a/.github/workflows/docker-publish.yml b/.github/workflows/docker-publish-busybox.yml similarity index 94% rename from .github/workflows/docker-publish.yml rename to .github/workflows/docker-publish-busybox.yml index ec93979..bf268fb 100644 --- a/.github/workflows/docker-publish.yml +++ b/.github/workflows/docker-publish-busybox.yml @@ -5,10 +5,10 @@ on: branches: [ "security-context-changes" ] env: - # Abi: pull ghcr.io/bcgov/nr-openmetadata=:main + # Abi: pull ghcr.io/bcgov/nr-openmetadata-busybox:security-context-changes REGISTRY: ghcr.io DOCKERFILE_PATH: charts/deps/containers/opensearch/busybox - IMAGE_NAME: ${{ github.repository }}-opensearch-busybox + IMAGE_NAME: ${{ github.repository }}-busybox jobs: build: diff --git a/.github/workflows/docker-publish-opensearch.yml b/.github/workflows/docker-publish-opensearch.yml new file mode 100644 index 0000000..00695ed --- /dev/null +++ b/.github/workflows/docker-publish-opensearch.yml @@ -0,0 +1,70 @@ +name: Push to GHCR + +on: + push: + branches: [ "security-context-changes" ] + +env: + # Abi: pull ghcr.io/bcgov/nr-openmetadata-opensearch:security-context-changes + REGISTRY: ghcr.io + DOCKERFILE_PATH: charts/deps/containers/opensearch + IMAGE_NAME: ${{ github.repository }}-opensearch + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + id-token: write + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + # Abi: delete before merging PR + with: + ref: security-context-changes + + - name: Install cosign + if: github.event_name != 'pull_request' + uses: sigstore/cosign-installer@6e04d228eb30da1757ee4e1dd75a0ec73a653e06 #v3.1.1 + with: + cosign-release: 'v2.1.1' + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 + + - name: Log into registry ${{ env.REGISTRY }} + if: github.event_name != 'pull_request' + uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract Docker metadata + id: meta + uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + + - name: Build and push Docker image + id: build-and-push + uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0 + with: + # Abi: to help the action find the Dockerfile to build from + context: ${{ env.DOCKERFILE_PATH }}/ + push: ${{ github.event_name != 'pull_request' }} + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Sign the published Docker image + if: ${{ github.event_name != 'pull_request' }} + env: + # https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable + TAGS: ${{ steps.meta.outputs.tags }} + DIGEST: ${{ steps.build-and-push.outputs.digest }} + + run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST} \ No newline at end of file diff --git a/openmetadata-dependencies-1.2.5/charts/opensearch/Dockerfile b/charts/deps/containers/opensearch/Dockerfile similarity index 100% rename from openmetadata-dependencies-1.2.5/charts/opensearch/Dockerfile rename to charts/deps/containers/opensearch/Dockerfile From 0bfd965e5504cfb94e969c61a4484b24342de9bf Mon Sep 17 00:00:00 2001 From: abimichel Date: Wed, 31 Jan 2024 11:51:23 -0800 Subject: [PATCH 15/29] test simplified dockerfile --- charts/deps/containers/opensearch/Dockerfile | 111 +------------------ 1 file changed, 2 insertions(+), 109 deletions(-) diff --git a/charts/deps/containers/opensearch/Dockerfile b/charts/deps/containers/opensearch/Dockerfile index 3786655..9db39dc 100644 --- a/charts/deps/containers/opensearch/Dockerfile +++ b/charts/deps/containers/opensearch/Dockerfile @@ -1,111 +1,4 @@ -# Copyright OpenSearch Contributors -# SPDX-License-Identifier: Apache-2.0 +FROM opensearchproject/opensearch +RUN chmod -R g+rwX /usr/share/opensearch -# This dockerfile generates an AmazonLinux-based image containing an OpenSearch installation. -# It assumes that the working directory contains these files: an OpenSearch tarball (opensearch.tgz), log4j2.properties, opensearch.yml, opensearch-docker-entrypoint.sh, opensearch-onetime-setup.sh. -# Build arguments: -# VERSION: Required. Used to label the image. -# UID: Optional. Specify the opensearch userid. Defaults to 1000. -# GID: Optional. Specify the opensearch groupid. Defaults to 1000. -# OPENSEARCH_HOME: Optional. Specify the opensearch root directory. Defaults to /usr/share/opensearch. - - -########################### Stage 0 ######################## -FROM amazonlinux:2 AS linux_stage_0 - -ARG UID=1000660000 -ARG GID=1000660000 -ARG TEMP_DIR=/tmp/opensearch -ARG OPENSEARCH_HOME=/usr/share/opensearch -ARG OPENSEARCH_PATH_CONF=$OPENSEARCH_HOME/config -ARG SECURITY_PLUGIN_DIR=$OPENSEARCH_HOME/plugins/opensearch-security -ARG PERFORMANCE_ANALYZER_PLUGIN_CONFIG_DIR=$OPENSEARCH_PATH_CONF/opensearch-performance-analyzer -ARG OS_VERSION=2.5.0 -# Update packages -# Install the tools we need: tar and gzip to unpack the OpenSearch tarball, and shadow-utils to give us `groupadd` and `useradd`. -# Install which to allow running of securityadmin.sh -RUN yum update -y && yum install -y tar gzip shadow-utils which && yum clean all - -# Create an opensearch user, group, and directory -RUN groupadd -g $GID opensearch && \ - adduser -u $UID -g $GID -d $OPENSEARCH_HOME opensearch && \ - mkdir $TEMP_DIR - -RUN mkdir /usr/share/elasticsearch -WORKDIR /usr/share/elasticsearch - -RUN set -eux ; \ - cur_arch="" ; \ - case "$(arch)" in \ - aarch64) cur_arch='arm64' ;; \ - x86_64) cur_arch='x64' ;; \ - *) echo >&2 ; echo >&2 "Unsupported architecture $(arch)" ; echo >&2 ; exit 1 ;; \ - esac ; \ - curl --retry 10 -S -L --output $TEMP_DIR/opensearch.tar.gz https://artifacts.opensearch.org/releases/bundle/opensearch/$OS_VERSION/opensearch-$OS_VERSION-linux-$cur_arch.tar.gz; \ - curl --output $TEMP_DIR/opensearch.pgp https://artifacts.opensearch.org/publickeys/opensearch.pgp; \ - gpg --import $TEMP_DIR/opensearch.pgp; \ - curl --output $TEMP_DIR/opensearch.tar.gz.sig https://artifacts.opensearch.org/releases/bundle/opensearch/$OS_VERSION/opensearch-$OS_VERSION-linux-$cur_arch.tar.gz.sig; \ - gpg --verify $TEMP_DIR/opensearch.tar.gz.sig $TEMP_DIR/opensearch.tar.gz; - -RUN tar --warning=no-timestamp -zxf $TEMP_DIR/opensearch.tar.gz -C $OPENSEARCH_HOME --strip-components=1 && \ - mkdir -p $OPENSEARCH_HOME/data && chown -Rv $UID:$GID $OPENSEARCH_HOME/data && \ - if [[ -d $SECURITY_PLUGIN_DIR ]] ; then chmod -v 750 $SECURITY_PLUGIN_DIR/tools/* ; fi && \ - rm -rf $TEMP_DIR - -COPY config/* $OPENSEARCH_PATH_CONF/ -COPY bin/* $OPENSEARCH_HOME/ -RUN if [[ -d $PERFORMANCE_ANALYZER_PLUGIN_CONFIG_DIR ]] ; then mv $OPENSEARCH_PATH_CONF/performance-analyzer.properties $PERFORMANCE_ANALYZER_PLUGIN_CONFIG_DIR/ ; fi -########################### Stage 1 ######################## -# Copy working directory to the actual release docker images -FROM amazonlinux:2 - -ARG UID=1000660000 -ARG GID=1000660000 -ARG OPENSEARCH_HOME=/usr/share/opensearch -ARG OS_VERSION=2.5.0 - -RUN yum update -y && yum install -y tar gzip shadow-utils which && yum clean all - -# Create an opensearch user, group -RUN groupadd -g $GID opensearch && \ - adduser -u $UID -g $GID -d $OPENSEARCH_HOME opensearch - -# Copy from Stage0 -COPY --from=linux_stage_0 --chown=$UID:$GID $OPENSEARCH_HOME $OPENSEARCH_HOME -WORKDIR $OPENSEARCH_HOME - -# Set $JAVA_HOME -RUN echo "export JAVA_HOME=$OPENSEARCH_HOME/jdk" >> /etc/profile.d/java_home.sh && \ - echo "export PATH=\$PATH:\$JAVA_HOME/bin" >> /etc/profile.d/java_home.sh -ENV JAVA_HOME=$OPENSEARCH_HOME/jdk -ENV PATH=$PATH:$JAVA_HOME/bin:$OPENSEARCH_HOME/bin - -# Add k-NN lib directory to library loading path variable -ENV LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$OPENSEARCH_HOME/plugins/opensearch-knn/lib" - -# Change user -USER $UID - - -# Setup OpenSearch -# Disable security demo installation during image build, and allow user to disable during startup of the container -# Enable security plugin during image build, and allow user to disable during startup of the container -ARG DISABLE_INSTALL_DEMO_CONFIG=true -ARG DISABLE_SECURITY_PLUGIN=false -RUN ./opensearch-onetime-setup.sh - -EXPOSE 9200 9300 9600 9650 - -# Label -LABEL org.label-schema.schema-version="1.0" \ - org.label-schema.name="opensearch" \ - org.label-schema.version="$OS_VERSION" \ - org.label-schema.url="https://opensearch.org" \ - org.label-schema.vcs-url="https://github.com/OpenSearch" \ - org.label-schema.license="Apache-2.0" \ - org.label-schema.vendor="OpenSearch" - -# CMD to run - ENTRYPOINT ["./opensearch-docker-entrypoint.sh"] - CMD ["opensearch"] From 12b4f0b26ae722c3b3e8ee0c86bd82382699d3d0 Mon Sep 17 00:00:00 2001 From: abimichel Date: Wed, 31 Jan 2024 11:51:59 -0800 Subject: [PATCH 16/29] added opensearch auto-label --- auto-label.yaml | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/auto-label.yaml b/auto-label.yaml index d4f0253..da0ca48 100644 --- a/auto-label.yaml +++ b/auto-label.yaml @@ -4,23 +4,22 @@ metadata: annotations: policies.kyverno.io/subject: Label policies.kyverno.io/title: Add Labels to all OpenMetadata pods - name: add-labels + name: add-openmetadata-labels spec: background: false failurePolicy: Ignore - validationFailureAction: audit rules: - - match: - any: - - resources: - kinds: - - Pod - selector: - matchLabels: - app.kubernetes.io/instance: mysql - mutate: - patchStrategicMerge: - metadata: - labels: - DataClass: Medium - name: add-data-class-label \ No newline at end of file + - match: + any: + - resources: + kinds: + - Pod + - resources: + kinds: + - Pod + mutate: + patchStrategicMerge: + metadata: + labels: + DataClass: Medium + name: add-data-class-label \ No newline at end of file From 2fe64b072f29cf1b0281a0363f0bc86a3d4c87d0 Mon Sep 17 00:00:00 2001 From: abimichel Date: Wed, 31 Jan 2024 12:26:48 -0800 Subject: [PATCH 17/29] increased working dir permissions and added tag --- charts/deps/containers/opensearch/Dockerfile | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/charts/deps/containers/opensearch/Dockerfile b/charts/deps/containers/opensearch/Dockerfile index 9db39dc..5e66822 100644 --- a/charts/deps/containers/opensearch/Dockerfile +++ b/charts/deps/containers/opensearch/Dockerfile @@ -1,4 +1,6 @@ -FROM opensearchproject/opensearch +# tag should match up with Chart.appVersion +FROM opensearchproject/opensearch:2.7.0 -RUN chmod -R g+rwX /usr/share/opensearch +# grant permission to working dir +RUN chmod -R 777 /usr/share/opensearch/ From 907dbdb3e539c4a84530a98471758879d44136d7 Mon Sep 17 00:00:00 2001 From: abimichel Date: Wed, 31 Jan 2024 13:24:56 -0800 Subject: [PATCH 18/29] changed scc and set to use GHCR --- .../deps/charts/opensearch-2.12.1/values.yaml | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/charts/deps/charts/opensearch-2.12.1/values.yaml b/charts/deps/charts/opensearch-2.12.1/values.yaml index 3cc7491..507e3ad 100644 --- a/charts/deps/charts/opensearch-2.12.1/values.yaml +++ b/charts/deps/charts/opensearch-2.12.1/values.yaml @@ -22,7 +22,7 @@ roles: replicas: 1 # Abi: previously 3 # if not set, falls back to parsing .Values.imageTag, then .Chart.appVersion. -majorVersion: "" +majorVersion: 2.7.0 # Abi: Chart.appversion = 2.7.0 global: # Set if you want to change the default docker registry, e.g. a private one. @@ -124,10 +124,10 @@ hostAliases: [] # - "bar.local" image: - repository: "opensearchproject/opensearch" + repository: ghcr.io/bcgov/nr-openmetadata-opensearch # Abi: previously "opensearchproject/opensearch" # override image tag, which is .Chart.AppVersion by default - tag: "" - pullPolicy: "IfNotPresent" + tag: security-context-changes # Abi: was previously defaulting to version # 2.7.0 + pullPolicy: "Always" podAnnotations: {} # iam.amazonaws.com/role: es-cluster @@ -190,11 +190,11 @@ podSecurityPolicy: persistence: enabled: true # Set to false to disable the `fsgroup-volume` initContainer that will update permissions on the persistent disk. - enableInitChown: true + enableInitChown: false # Abi: disables line 234 on the statefulset.yaml, as permissions are now handled with custom dockerfile for random UID # override image, which is busybox by default - # image: busybox + image: ghcr.io/bcgov/nr-openmetadata-opensearch-busybox # Abi: set to main branch after PR merge # override image tag, which is latest by default - # imageTag: + imageTag: security-context-changes # will become main labels: # Add default labels for the volumeClaimTemplate of the StatefulSet enabled: false @@ -296,9 +296,9 @@ updateStrategy: RollingUpdate # of your pods to be unavailable during maintenance # maxUnavailable: 1 # Abi: removed PDB for now -podSecurityContext: # Abi: removed runAsUser: 0 from statefulset.yaml - # fsGroup: 1000 # Abi: allow for random UID - # runAsUser: 1000 # Abi: allow for random UID +podSecurityContext: # Abi: removed runAsUser: 0 from initContainer in statefulset.yaml + # fsGroup: 1000 # Abi: allow for GID=0 + # runAsUser: 1000 # Abi: allow for random UID securityContext: capabilities: @@ -306,7 +306,7 @@ securityContext: - ALL # readOnlyRootFilesystem: true runAsNonRoot: true - # runAsUser: 1000 # Abi: allow for random UID + # runAsUser: 1000 # Abi: allow for random UID securityConfig: enabled: true @@ -375,7 +375,8 @@ readinessProbe: ## schedulerName: "" -imagePullSecrets: [] +imagePullSecrets: + - name: "artifactory-pull" nodeSelector: {} tolerations: [] From d0bb7c55244131c8a4109249ab8acbea03aa7ba3 Mon Sep 17 00:00:00 2001 From: abimichel Date: Wed, 31 Jan 2024 13:26:10 -0800 Subject: [PATCH 19/29] no longer need the image pull secret --- .../deps/charts/opensearch-2.12.1/values.yaml | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/charts/deps/charts/opensearch-2.12.1/values.yaml b/charts/deps/charts/opensearch-2.12.1/values.yaml index 507e3ad..37be863 100644 --- a/charts/deps/charts/opensearch-2.12.1/values.yaml +++ b/charts/deps/charts/opensearch-2.12.1/values.yaml @@ -22,7 +22,7 @@ roles: replicas: 1 # Abi: previously 3 # if not set, falls back to parsing .Values.imageTag, then .Chart.appVersion. -majorVersion: 2.7.0 # Abi: Chart.appversion = 2.7.0 +majorVersion: 2.7.0 # Abi: Chart.appVersion = 2.7.0 global: # Set if you want to change the default docker registry, e.g. a private one. @@ -126,8 +126,8 @@ hostAliases: [] image: repository: ghcr.io/bcgov/nr-openmetadata-opensearch # Abi: previously "opensearchproject/opensearch" # override image tag, which is .Chart.AppVersion by default - tag: security-context-changes # Abi: was previously defaulting to version # 2.7.0 - pullPolicy: "Always" + tag: security-context-changes # Abi: previously defaulting 2.7.0 + pullPolicy: "Always" # Abi: prevously "IfNotPresent" podAnnotations: {} # iam.amazonaws.com/role: es-cluster @@ -190,11 +190,11 @@ podSecurityPolicy: persistence: enabled: true # Set to false to disable the `fsgroup-volume` initContainer that will update permissions on the persistent disk. - enableInitChown: false # Abi: disables line 234 on the statefulset.yaml, as permissions are now handled with custom dockerfile for random UID + enableInitChown: false # Abi: disables line 234 on the statefulset.yaml # override image, which is busybox by default - image: ghcr.io/bcgov/nr-openmetadata-opensearch-busybox # Abi: set to main branch after PR merge + image: ghcr.io/bcgov/nr-openmetadata-opensearch-busybox # Abi: previously busybox:latest # override image tag, which is latest by default - imageTag: security-context-changes # will become main + imageTag: security-context-changes # Abi: set to main branch after PR merge labels: # Add default labels for the volumeClaimTemplate of the StatefulSet enabled: false @@ -296,8 +296,8 @@ updateStrategy: RollingUpdate # of your pods to be unavailable during maintenance # maxUnavailable: 1 # Abi: removed PDB for now -podSecurityContext: # Abi: removed runAsUser: 0 from initContainer in statefulset.yaml - # fsGroup: 1000 # Abi: allow for GID=0 +podSecurityContext: + # fsGroup: 1000 # Abi: allow for group ID 0 # runAsUser: 1000 # Abi: allow for random UID securityContext: @@ -375,8 +375,7 @@ readinessProbe: ## schedulerName: "" -imagePullSecrets: - - name: "artifactory-pull" +imagePullSecrets: [] nodeSelector: {} tolerations: [] From ae2c7d569dd3df9b702886cddcd5105620c0c276 Mon Sep 17 00:00:00 2001 From: abimichel Date: Wed, 31 Jan 2024 14:23:23 -0800 Subject: [PATCH 20/29] corrected auto-label policy --- auto-label.yaml | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/auto-label.yaml b/auto-label.yaml index da0ca48..508659a 100644 --- a/auto-label.yaml +++ b/auto-label.yaml @@ -8,18 +8,20 @@ metadata: spec: background: false failurePolicy: Ignore + validationFailureAction: audit rules: - - match: - any: - - resources: - kinds: - - Pod - - resources: - kinds: - - Pod - mutate: - patchStrategicMerge: - metadata: - labels: - DataClass: Medium - name: add-data-class-label \ No newline at end of file + - match: + any: + - resources: + kinds: + - Pod + selector: + matchLabels: + app.kubernetes.io/instance: mysql + app.kubernetes.io/app: opensearch + mutate: + patchStrategicMerge: + metadata: + labels: + DataClass: Medium + name: add-data-class-label \ No newline at end of file From 82317752fe4fc7d7c17676d1825b558823bf3f4b Mon Sep 17 00:00:00 2001 From: abimichel Date: Thu, 1 Feb 2024 09:13:21 -0800 Subject: [PATCH 21/29] added openmetadata and airflow auto-label --- oc/auto-label-airflow.yaml | 26 +++++++++++++++++++ oc/auto-label-mysql.yaml | 26 +++++++++++++++++++ .../auto-label-openmetadata.yaml | 3 +-- oc/auto-label-opensearch.yaml | 26 +++++++++++++++++++ 4 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 oc/auto-label-airflow.yaml create mode 100644 oc/auto-label-mysql.yaml rename auto-label.yaml => oc/auto-label-openmetadata.yaml (85%) create mode 100644 oc/auto-label-opensearch.yaml diff --git a/oc/auto-label-airflow.yaml b/oc/auto-label-airflow.yaml new file mode 100644 index 0000000..e7cd4f7 --- /dev/null +++ b/oc/auto-label-airflow.yaml @@ -0,0 +1,26 @@ +apiVersion: kyverno.io/v1 +kind: Policy +metadata: + annotations: + policies.kyverno.io/subject: Label + policies.kyverno.io/title: Add Labels to all OpenMetadata pods + name: add-airflow-labels +spec: + background: false + failurePolicy: Ignore + validationFailureAction: audit + rules: + - match: + any: + - resources: + kinds: + - Pod + selector: + matchLabels: + app: airflow + mutate: + patchStrategicMerge: + metadata: + labels: + DataClass: Medium + name: add-data-class-label \ No newline at end of file diff --git a/oc/auto-label-mysql.yaml b/oc/auto-label-mysql.yaml new file mode 100644 index 0000000..7a0ac44 --- /dev/null +++ b/oc/auto-label-mysql.yaml @@ -0,0 +1,26 @@ +apiVersion: kyverno.io/v1 +kind: Policy +metadata: + annotations: + policies.kyverno.io/subject: Label + policies.kyverno.io/title: Add Labels to all OpenMetadata pods + name: add-mysql-labels +spec: + background: false + failurePolicy: Ignore + validationFailureAction: audit + rules: + - match: + any: + - resources: + kinds: + - Pod + selector: + matchLabels: + app.kubernetes.io/name: mysql + mutate: + patchStrategicMerge: + metadata: + labels: + DataClass: Medium + name: add-data-class-label \ No newline at end of file diff --git a/auto-label.yaml b/oc/auto-label-openmetadata.yaml similarity index 85% rename from auto-label.yaml rename to oc/auto-label-openmetadata.yaml index 508659a..f56c4be 100644 --- a/auto-label.yaml +++ b/oc/auto-label-openmetadata.yaml @@ -17,8 +17,7 @@ spec: - Pod selector: matchLabels: - app.kubernetes.io/instance: mysql - app.kubernetes.io/app: opensearch + app.kubernetes.io/name: openmetadata mutate: patchStrategicMerge: metadata: diff --git a/oc/auto-label-opensearch.yaml b/oc/auto-label-opensearch.yaml new file mode 100644 index 0000000..7073784 --- /dev/null +++ b/oc/auto-label-opensearch.yaml @@ -0,0 +1,26 @@ +apiVersion: kyverno.io/v1 +kind: Policy +metadata: + annotations: + policies.kyverno.io/subject: Label + policies.kyverno.io/title: Add Labels to all OpenMetadata pods + name: add-opensearch-labels +spec: + background: false + failurePolicy: Ignore + validationFailureAction: audit + rules: + - match: + any: + - resources: + kinds: + - Pod + selector: + matchLabels: + app.kubernetes.io/name: opensearch + mutate: + patchStrategicMerge: + metadata: + labels: + DataClass: Medium + name: add-data-class-label \ No newline at end of file From 1fc61eafda0e11c6d1afefd5d1194d1840bff71d Mon Sep 17 00:00:00 2001 From: abimichel Date: Thu, 1 Feb 2024 09:14:29 -0800 Subject: [PATCH 22/29] deleted openmetadata poddisruptionbudget.yaml --- .../templates/poddisruptionbudget.yaml | 20 ------------------- 1 file changed, 20 deletions(-) delete mode 100644 charts/openmetadata/templates/poddisruptionbudget.yaml diff --git a/charts/openmetadata/templates/poddisruptionbudget.yaml b/charts/openmetadata/templates/poddisruptionbudget.yaml deleted file mode 100644 index f0f1668..0000000 --- a/charts/openmetadata/templates/poddisruptionbudget.yaml +++ /dev/null @@ -1,20 +0,0 @@ -{{- if .Values.podDisruptionBudget.enabled }} -apiVersion: policy/v1 -kind: PodDisruptionBudget -metadata: - name: {{ include "OpenMetadata.fullname" . }}-poddisruptionbudget - labels: - {{- include "OpenMetadata.labels" . | indent 4 }} -spec: - {{- with .Values.podDisruptionBudget.config }} - {{- if .minAvailable }} - minAvailable: {{ .minAvailable }} - {{- end }} - {{- if .maxUnavailable }} - maxUnavailable: {{ .maxUnavailable }} - {{- end }} - {{- end }} - selector: - matchLabels: - {{- include "OpenMetadata.selectorLabels" . | nindent 6 }} -{{- end }} \ No newline at end of file From 835799c2e4254af9effb35461d1f9b74ad154bdd Mon Sep 17 00:00:00 2001 From: abimichel Date: Thu, 1 Feb 2024 09:17:46 -0800 Subject: [PATCH 23/29] name service account and increase startup probe timeout --- charts/openmetadata/values.yaml | 5 +++-- service-account.yaml | 6 ------ 2 files changed, 3 insertions(+), 8 deletions(-) delete mode 100644 service-account.yaml diff --git a/charts/openmetadata/values.yaml b/charts/openmetadata/values.yaml index 7b51c09..0208f4c 100644 --- a/charts/openmetadata/values.yaml +++ b/charts/openmetadata/values.yaml @@ -274,7 +274,7 @@ networkPolicy: # callbackUrl: "" image: - repository: docker.getcollate.io/openmetadata/server + repository: artifacts.developer.gov.bc.ca/docker-remote/openmetadata/server # Overrides the image tag whose default is the chart appVersion. tag: "" pullPolicy: "Always" @@ -304,7 +304,7 @@ serviceAccount: annotations: {} # The name of the service account to use. # If not set and create is true, a name is generated using the fullname template - name: "" + name: openmetadata-admin automountServiceAccountToken: true podSecurityContext: {} # fsGroup: 2000 @@ -400,6 +400,7 @@ readinessProbe: path: / port: http startupProbe: + timeoutSeconds: 10 # Abi: Added this value, as it was timing out after 1 second periodSeconds: 60 failureThreshold: 5 successThreshold: 1 diff --git a/service-account.yaml b/service-account.yaml deleted file mode 100644 index 30a4feb..0000000 --- a/service-account.yaml +++ /dev/null @@ -1,6 +0,0 @@ -kind: ServiceAccount -apiVersion: v1 -metadata: - name: openmetadata-admin -imagePullSecrets: - - name: artifactory-pull \ No newline at end of file From 42e316b006d4474a68fa0fce791cccb0eaf296b2 Mon Sep 17 00:00:00 2001 From: abimichel Date: Thu, 1 Feb 2024 09:19:54 -0800 Subject: [PATCH 24/29] fixed airflow scc --- README.md | 5 ++++- charts/deps/charts/airflow-8.8.0/values.yaml | 9 +++++++-- charts/deps/values.yaml | 16 ++++++++-------- oc/airflow-mysql-secret.yaml | 7 +++++++ oc/net-pol-airflow.yaml | 16 ++++++++++++++++ oc/net-pol-mysql.yaml | 16 ++++++++++++++++ oc/net-pol-openmetadata.yaml | 16 ++++++++++++++++ oc/net-pol-opensearch.yaml | 16 ++++++++++++++++ oc/openmetadata-mysql-secret.yaml | 7 +++++++ 9 files changed, 97 insertions(+), 11 deletions(-) create mode 100644 oc/airflow-mysql-secret.yaml create mode 100644 oc/net-pol-airflow.yaml create mode 100644 oc/net-pol-mysql.yaml create mode 100644 oc/net-pol-openmetadata.yaml create mode 100644 oc/net-pol-opensearch.yaml create mode 100644 oc/openmetadata-mysql-secret.yaml diff --git a/README.md b/README.md index beac82e..c349f40 100644 --- a/README.md +++ b/README.md @@ -108,4 +108,7 @@ helm install openmetadata open-metadata/openmetadata --values charts/openmetadat -create Pod mysql-0 in StatefulSet mysql failed error: pods "mysql-0" is forbidden: unable to validate against any security context constraint: [provider "trident-controller": Forbidden: not usable by user or serviceaccount, provider "anyuid": Forbidden: not usable by user or serviceaccount, provider "pipelines-scc": Forbidden: not usable by user or serviceaccount, provider restricted-v2: .spec.securityContext.fsGroup: Invalid value: []int64{1001}: 1001 is not an allowed group, provider restricted-v2: .containers[0].runAsUser: Invalid value: 1001: must be in the ranges: [1001900000, 1001909999], provider restricted: .spec.securityContext.fsGroup: Invalid value: []int64{1001}: 1001 is not an allowed group, provider restricted: .containers[0].runAsUser: Invalid value: 1001: must be in the ranges: [1001900000, 1001909999], provider "nonroot-v2": Forbidden: not usable by user or serviceaccount, provider "nonroot": Forbidden: not usable by user or serviceaccount, provider "hostmount-anyuid": Forbidden: not usable by user or serviceaccount, provider "elasticsearch-scc": Forbidden: not usable by user or serviceaccount, provider "log-collector-scc": Forbidden: not usable by user or serviceaccount, provider "machine-api-termination-handler": Forbidden: not usable by user or serviceaccount, provider "hostnetwork-v2": Forbidden: not usable by user or serviceaccount, provider "hostnetwork": Forbidden: not usable by user or serviceaccount, provider "hostaccess": Forbidden: not usable by user or serviceaccount, provider "trident-node-linux": Forbidden: not usable by user or serviceaccount, provider "node-exporter": Forbidden: not usable by user or serviceaccount, provider "privileged": Forbidden: not usable by user or serviceaccount] \ No newline at end of file +create Pod mysql-0 in StatefulSet mysql failed error: pods "mysql-0" is forbidden: unable to validate against any security context constraint: [provider "trident-controller": Forbidden: not usable by user or serviceaccount, provider "anyuid": Forbidden: not usable by user or serviceaccount, provider "pipelines-scc": Forbidden: not usable by user or serviceaccount, provider restricted-v2: .spec.securityContext.fsGroup: Invalid value: []int64{1001}: 1001 is not an allowed group, provider restricted-v2: .containers[0].runAsUser: Invalid value: 1001: must be in the ranges: [1001900000, 1001909999], provider restricted: .spec.securityContext.fsGroup: Invalid value: []int64{1001}: 1001 is not an allowed group, provider restricted: .containers[0].runAsUser: Invalid value: 1001: must be in the ranges: [1001900000, 1001909999], provider "nonroot-v2": Forbidden: not usable by user or serviceaccount, provider "nonroot": Forbidden: not usable by user or serviceaccount, provider "hostmount-anyuid": Forbidden: not usable by user or serviceaccount, provider "elasticsearch-scc": Forbidden: not usable by user or serviceaccount, provider "log-collector-scc": Forbidden: not usable by user or serviceaccount, provider "machine-api-termination-handler": Forbidden: not usable by user or serviceaccount, provider "hostnetwork-v2": Forbidden: not usable by user or serviceaccount, provider "hostnetwork": Forbidden: not usable by user or serviceaccount, provider "hostaccess": Forbidden: not usable by user or serviceaccount, provider "trident-node-linux": Forbidden: not usable by user or serviceaccount, provider "node-exporter": Forbidden: not usable by user or serviceaccount, provider "privileged": Forbidden: not usable by user or serviceaccount] + +## To apply the pod labels policy: +oc apply -f auto-label.yaml \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/values.yaml b/charts/deps/charts/airflow-8.8.0/values.yaml index 3712eb0..5fd6fa7 100644 --- a/charts/deps/charts/airflow-8.8.0/values.yaml +++ b/charts/deps/charts/airflow-8.8.0/values.yaml @@ -13,7 +13,10 @@ airflow: tag: 2.6.3-python3.9 pullPolicy: IfNotPresent pullSecret: "" - uid: 50000 + + # Abi this causes the error: runAsUser: Invalid value: 50000 + # uid: 50000 + gid: 0 ## the airflow executor type to use @@ -205,7 +208,9 @@ airflow: defaultSecurityContext: ## sets the filesystem owner group of files/folders in mounted volumes ## this does NOT give root permissions to Pods, only the "root" group - fsGroup: 0 + + # Abi: this causes error: securityContext.fsGroup: Invalid value: []int64{0}: 0 is not an allowed group + # fsGroup: 0 ## extra annotations for airflow Pods ## diff --git a/charts/deps/values.yaml b/charts/deps/values.yaml index 8cd49f8..68ed64a 100644 --- a/charts/deps/values.yaml +++ b/charts/deps/values.yaml @@ -15,7 +15,7 @@ mysql: primary: extraFlags: "--sort_buffer_size=10M" persistence: - size: 50Gi + size: 500Mi # Abi: previously 50Gi service: nodePort: 3306 initdbScripts: @@ -40,7 +40,7 @@ opensearch: imagePullPolicy: Always opensearchJavaOpts: "-Xmx1g -Xms1g" persistence: - size: 30Gi + size: 500Mi # Abi: previously 30Gi protocol: http config: opensearch.yml: | @@ -60,7 +60,7 @@ airflow: enabled: true airflow: image: - repository: docker.getcollate.io/openmetadata/ingestion + repository: artifacts.developer.gov.bc.ca/docker-remote/openmetadata/ingestion tag: 1.2.5 pullPolicy: "IfNotPresent" executor: "KubernetesExecutor" @@ -77,9 +77,9 @@ airflow: - username: admin password: admin role: Admin - email: spiderman@superhero.org - firstName: Peter - lastName: Parker + email: NRM.DataFoundations@gov.bc.ca + firstName: NRM + lastName: Data Foundations web: extraVolumes: - name: pod-template @@ -130,7 +130,7 @@ airflow: enabled: true # NOTE: "" means cluster-default storageClass: "" - size: 1Gi + size: 500Mi # Abi: previously 1Gi accessMode: ReadWriteMany logs: persistence: @@ -138,4 +138,4 @@ airflow: # empty string means cluster-default storageClass: "" accessMode: ReadWriteMany - size: 1Gi + size: 500Mi # Abi: previously 1Gi diff --git a/oc/airflow-mysql-secret.yaml b/oc/airflow-mysql-secret.yaml new file mode 100644 index 0000000..d7f711e --- /dev/null +++ b/oc/airflow-mysql-secret.yaml @@ -0,0 +1,7 @@ +kind: Secret +apiVersion: v1 +metadata: + name: airflow-mysql-secrets +data: + airflow-mysql-password: YWlyZmxvd19wYXNz +type: Opaque \ No newline at end of file diff --git a/oc/net-pol-airflow.yaml b/oc/net-pol-airflow.yaml new file mode 100644 index 0000000..394861a --- /dev/null +++ b/oc/net-pol-airflow.yaml @@ -0,0 +1,16 @@ +kind: NetworkPolicy +apiVersion: networking.k8s.io/v1 +metadata: + name: allow-openmetadata-airflow +spec: + podSelector: + matchLabels: + app: airflow + ingress: + - {} + egress: + - {} + policyTypes: + - Ingress + - Egress +status: {} \ No newline at end of file diff --git a/oc/net-pol-mysql.yaml b/oc/net-pol-mysql.yaml new file mode 100644 index 0000000..ec50ab1 --- /dev/null +++ b/oc/net-pol-mysql.yaml @@ -0,0 +1,16 @@ +kind: NetworkPolicy +apiVersion: networking.k8s.io/v1 +metadata: + name: allow-openmetadata-mysql +spec: + podSelector: + matchLabels: + app.kubernetes.io/name: mysql + ingress: + - {} + egress: + - {} + policyTypes: + - Ingress + - Egress +status: {} \ No newline at end of file diff --git a/oc/net-pol-openmetadata.yaml b/oc/net-pol-openmetadata.yaml new file mode 100644 index 0000000..2aaf22e --- /dev/null +++ b/oc/net-pol-openmetadata.yaml @@ -0,0 +1,16 @@ +kind: NetworkPolicy +apiVersion: networking.k8s.io/v1 +metadata: + name: allow-openmetadata +spec: + podSelector: + matchLabels: + app.kubernetes.io/name: openmetadata + ingress: + - {} + egress: + - {} + policyTypes: + - Ingress + - Egress +status: {} \ No newline at end of file diff --git a/oc/net-pol-opensearch.yaml b/oc/net-pol-opensearch.yaml new file mode 100644 index 0000000..3a936e5 --- /dev/null +++ b/oc/net-pol-opensearch.yaml @@ -0,0 +1,16 @@ +kind: NetworkPolicy +apiVersion: networking.k8s.io/v1 +metadata: + name: allow-openmetadata-opensearch +spec: + podSelector: + matchLabels: + app.kubernetes.io/name: opensearch + ingress: + - {} + egress: + - {} + policyTypes: + - Ingress + - Egress +status: {} \ No newline at end of file diff --git a/oc/openmetadata-mysql-secret.yaml b/oc/openmetadata-mysql-secret.yaml new file mode 100644 index 0000000..254123b --- /dev/null +++ b/oc/openmetadata-mysql-secret.yaml @@ -0,0 +1,7 @@ +kind: Secret +apiVersion: v1 +metadata: + name: mysql-secrets +data: + openmetadata-mysql-password: b3Blbm1ldGFkYXRhX3Bhc3N3b3Jk +type: Opaque \ No newline at end of file From f094540e5040e3a51ec05e13d535fbfc85946469 Mon Sep 17 00:00:00 2001 From: abimichel Date: Thu, 1 Feb 2024 09:57:13 -0800 Subject: [PATCH 25/29] update steps in README.md --- README.md | 46 +++++++++++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index c349f40..1cfa86f 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ helm uninstall openmetadata-dependencies ``` ## Deploying to OpenShift -To deploy to OpenShift, using OC commands make sure Helm cli is installed on your local machine. +To deploy to OpenShift, use OC commands and make sure Helm cli is installed on your local machine. ``` brew install helm ``` @@ -79,36 +79,44 @@ Source: https://github.com/open-metadata/openmetadata-helm-charts/tree/main/char ``` oc create secret generic airflow-mysql-secrets --from-literal=airflow-mysql-password=airflow_pass ``` -#### Add Helm repo to OS +#### Deploy dependencies to OpenShift +Navigate to the 'deps' chart folder then: ``` -helm repo add open-metadata https://helm.open-metadata.org -``` -#### Deploy dependencies to OS -``` -helm install openmetadata-dependencies open-metadata/openmetadata-dependencies +helm install openmetadata-dependencies . ``` If you see the below error then get admin access to the dev namespace Issues: User "vikas.grover@gov.bc.ca" cannot get resource "roles" in API group "rbac.authorization.k8s.io" in the namespace "a1b9b0-dev" ### Install OpenMetadata +Source: https://github.com/open-metadata/openmetadata-helm-charts/tree/main/charts/openmetadata + #### Create default Secrets ``` oc create secret generic mysql-secrets --from-literal=openmetadata-mysql-password=openmetadata_password oc create secret generic airflow-secrets --from-literal=openmetadata-airflow-password=admin ``` -#### Deploy Helm chart +## Apply the pod label policies under 'oc' folder: ``` -helm install openmetadata-dependencies open-metadata/openmetadata-dependencies --values charts/deps/values.yaml -helm install openmetadata open-metadata/openmetadata --values charts/openmetadata/values.yaml +oc apply -f [auto-label].yaml +``` +## Apply the network policies under 'oc' folder: +``` +oc apply -f [net-pol].yaml +``` +#### Deploy OpenMetadata to OpenShift: +Navigate to the 'openmetadata' chart folder then: +``` +helm install openmetadata . +``` +#### Port Forward OpenMetadata to view UI +``` +oc port-forward service/openmetadata 8585:http ``` -## Creating OpenShift ConfigMaps - -## Errors encountered - - - -create Pod mysql-0 in StatefulSet mysql failed error: pods "mysql-0" is forbidden: unable to validate against any security context constraint: [provider "trident-controller": Forbidden: not usable by user or serviceaccount, provider "anyuid": Forbidden: not usable by user or serviceaccount, provider "pipelines-scc": Forbidden: not usable by user or serviceaccount, provider restricted-v2: .spec.securityContext.fsGroup: Invalid value: []int64{1001}: 1001 is not an allowed group, provider restricted-v2: .containers[0].runAsUser: Invalid value: 1001: must be in the ranges: [1001900000, 1001909999], provider restricted: .spec.securityContext.fsGroup: Invalid value: []int64{1001}: 1001 is not an allowed group, provider restricted: .containers[0].runAsUser: Invalid value: 1001: must be in the ranges: [1001900000, 1001909999], provider "nonroot-v2": Forbidden: not usable by user or serviceaccount, provider "nonroot": Forbidden: not usable by user or serviceaccount, provider "hostmount-anyuid": Forbidden: not usable by user or serviceaccount, provider "elasticsearch-scc": Forbidden: not usable by user or serviceaccount, provider "log-collector-scc": Forbidden: not usable by user or serviceaccount, provider "machine-api-termination-handler": Forbidden: not usable by user or serviceaccount, provider "hostnetwork-v2": Forbidden: not usable by user or serviceaccount, provider "hostnetwork": Forbidden: not usable by user or serviceaccount, provider "hostaccess": Forbidden: not usable by user or serviceaccount, provider "trident-node-linux": Forbidden: not usable by user or serviceaccount, provider "node-exporter": Forbidden: not usable by user or serviceaccount, provider "privileged": Forbidden: not usable by user or serviceaccount] +## OpenSearch Dockerfile and Use of GHCR +OpenSearch requires a modified Dockerfile to work within the OpenShift restricted security context. The Dockerfile can be found under charts/containers/opensearch. The image is built automatically and pushed to the GHCR any time there is a push or PR to the **main** branch. -## To apply the pod labels policy: -oc apply -f auto-label.yaml \ No newline at end of file +Usage example: +```sh +docker pull ghcr.io/bcgov/nr-openmetadata-opensearch:main +``` \ No newline at end of file From d4f44bc1b562a8ed20a82d9a07dbdf5f708ae9d3 Mon Sep 17 00:00:00 2001 From: abimichel Date: Thu, 1 Feb 2024 10:16:21 -0800 Subject: [PATCH 26/29] final code clean up --- .github/workflows/docker-publish-busybox.yml | 70 ------------------- .../workflows/docker-publish-opensearch.yml | 14 ++-- README.md | 7 +- charts/deps/charts/airflow-8.8.0/values.yaml | 2 +- charts/deps/charts/mysql-9.7.2/values.yaml | 14 ++-- .../opensearch-2.12.1/containers}/Dockerfile | 0 .../deps/charts/opensearch-2.12.1/values.yaml | 28 ++++---- .../containers/opensearch/busybox/Dockerfile | 5 -- charts/deps/values.yaml | 8 +-- charts/openmetadata/values.yaml | 2 +- oc/airflow-mysql-secret.yaml | 7 -- oc/openmetadata-mysql-secret.yaml | 7 -- 12 files changed, 39 insertions(+), 125 deletions(-) delete mode 100644 .github/workflows/docker-publish-busybox.yml rename charts/deps/{containers/opensearch => charts/opensearch-2.12.1/containers}/Dockerfile (100%) delete mode 100644 charts/deps/containers/opensearch/busybox/Dockerfile delete mode 100644 oc/airflow-mysql-secret.yaml delete mode 100644 oc/openmetadata-mysql-secret.yaml diff --git a/.github/workflows/docker-publish-busybox.yml b/.github/workflows/docker-publish-busybox.yml deleted file mode 100644 index bf268fb..0000000 --- a/.github/workflows/docker-publish-busybox.yml +++ /dev/null @@ -1,70 +0,0 @@ -name: Push to GHCR - -on: - push: - branches: [ "security-context-changes" ] - -env: - # Abi: pull ghcr.io/bcgov/nr-openmetadata-busybox:security-context-changes - REGISTRY: ghcr.io - DOCKERFILE_PATH: charts/deps/containers/opensearch/busybox - IMAGE_NAME: ${{ github.repository }}-busybox - -jobs: - build: - runs-on: ubuntu-latest - permissions: - contents: read - packages: write - id-token: write - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - # Abi: delete before merging PR - with: - ref: security-context-changes - - - name: Install cosign - if: github.event_name != 'pull_request' - uses: sigstore/cosign-installer@6e04d228eb30da1757ee4e1dd75a0ec73a653e06 #v3.1.1 - with: - cosign-release: 'v2.1.1' - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0 - - - name: Log into registry ${{ env.REGISTRY }} - if: github.event_name != 'pull_request' - uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0 - with: - registry: ${{ env.REGISTRY }} - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: Extract Docker metadata - id: meta - uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0 - with: - images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} - - - name: Build and push Docker image - id: build-and-push - uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0 - with: - # Abi: to help the action find the Dockerfile to build from - context: ${{ env.DOCKERFILE_PATH }}/ - push: ${{ github.event_name != 'pull_request' }} - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - cache-from: type=gha - cache-to: type=gha,mode=max - - - name: Sign the published Docker image - if: ${{ github.event_name != 'pull_request' }} - env: - # https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable - TAGS: ${{ steps.meta.outputs.tags }} - DIGEST: ${{ steps.build-and-push.outputs.digest }} - - run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST} \ No newline at end of file diff --git a/.github/workflows/docker-publish-opensearch.yml b/.github/workflows/docker-publish-opensearch.yml index 00695ed..0c62084 100644 --- a/.github/workflows/docker-publish-opensearch.yml +++ b/.github/workflows/docker-publish-opensearch.yml @@ -2,12 +2,15 @@ name: Push to GHCR on: push: - branches: [ "security-context-changes" ] + branches: [ "main" ] + pull_request: + branches: [ "main" ] + env: - # Abi: pull ghcr.io/bcgov/nr-openmetadata-opensearch:security-context-changes + # NOTE: pull ghcr.io/bcgov/nr-openmetadata-opensearch:main REGISTRY: ghcr.io - DOCKERFILE_PATH: charts/deps/containers/opensearch + DOCKERFILE_PATH: charts/opensearch-2.12.1/containers IMAGE_NAME: ${{ github.repository }}-opensearch jobs: @@ -21,9 +24,6 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v3 - # Abi: delete before merging PR - with: - ref: security-context-changes - name: Install cosign if: github.event_name != 'pull_request' @@ -52,7 +52,7 @@ jobs: id: build-and-push uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0 with: - # Abi: to help the action find the Dockerfile to build from + # NOTE: to help the action find the Dockerfile to build from context: ${{ env.DOCKERFILE_PATH }}/ push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} diff --git a/README.md b/README.md index 1cfa86f..3a014c6 100644 --- a/README.md +++ b/README.md @@ -114,9 +114,12 @@ oc port-forward service/openmetadata 8585:http ``` ## OpenSearch Dockerfile and Use of GHCR -OpenSearch requires a modified Dockerfile to work within the OpenShift restricted security context. The Dockerfile can be found under charts/containers/opensearch. The image is built automatically and pushed to the GHCR any time there is a push or PR to the **main** branch. +OpenSearch requires a modified Dockerfile to work within the OpenShift restricted security context. The Dockerfile can be found under charts/opensearch-2.12.1/containers. The image is built automatically and pushed to the GHCR any time there is a push or PR to the **main** branch. Usage example: ```sh docker pull ghcr.io/bcgov/nr-openmetadata-opensearch:main -``` \ No newline at end of file +``` + +## Helm chart modifications +To review all Helm chart modifications (i.e. differences between the OpenMetadata default config and this config), search this repo for "NOTE:" annotations. \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/values.yaml b/charts/deps/charts/airflow-8.8.0/values.yaml index 5fd6fa7..61d2483 100644 --- a/charts/deps/charts/airflow-8.8.0/values.yaml +++ b/charts/deps/charts/airflow-8.8.0/values.yaml @@ -209,7 +209,7 @@ airflow: ## sets the filesystem owner group of files/folders in mounted volumes ## this does NOT give root permissions to Pods, only the "root" group - # Abi: this causes error: securityContext.fsGroup: Invalid value: []int64{0}: 0 is not an allowed group + # NOTE: this caused error: securityContext.fsGroup: Invalid value: []int64{0}: 0 is not an allowed group # fsGroup: 0 ## extra annotations for airflow Pods diff --git a/charts/deps/charts/mysql-9.7.2/values.yaml b/charts/deps/charts/mysql-9.7.2/values.yaml index c426ad0..74b33aa 100644 --- a/charts/deps/charts/mysql-9.7.2/values.yaml +++ b/charts/deps/charts/mysql-9.7.2/values.yaml @@ -296,7 +296,7 @@ primary: ## @param primary.podSecurityContext.fsGroup Group ID for the mounted volumes' filesystem ## podSecurityContext: - enabled: false # Abi: OCP security context is set to 'restricted' by default + enabled: false # NOTE: OCP security context is set to 'restricted' by default fsGroup: 1001 ## MySQL primary container security context ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container @@ -305,7 +305,7 @@ primary: ## @param primary.containerSecurityContext.runAsNonRoot Set MySQL primary container's Security Context runAsNonRoot ## containerSecurityContext: - enabled: false # Abi: OCP security context is set to 'restricted' by default + enabled: false # NOTE: OCP security context is set to 'restricted' by default runAsUser: 1001 runAsNonRoot: true ## MySQL primary container's resource requests and limits @@ -440,7 +440,7 @@ primary: - ReadWriteOnce ## @param primary.persistence.size MySQL primary persistent volume size ## - size: 500Mi # Abi: previously 8Gi + size: 500Mi # NOTE: previously 8Gi ## @param primary.persistence.selector Selector to match an existing Persistent Volume ## selector: ## matchLabels: @@ -676,7 +676,7 @@ secondary: ## @param secondary.podSecurityContext.fsGroup Group ID for the mounted volumes' filesystem ## podSecurityContext: - enabled: false # Abi: OCP security context is set to 'restricted' by default + enabled: false # NOTE: OCP security context is set to 'restricted' by default fsGroup: 1001 ## MySQL secondary container security context ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container @@ -685,7 +685,7 @@ secondary: ## @param secondary.containerSecurityContext.runAsNonRoot Set MySQL secondary container's Security Context runAsNonRoot ## containerSecurityContext: - enabled: false # Abi: OCP security context is set to 'restricted' by default + enabled: false # NOTE: OCP security context is set to 'restricted' by default runAsUser: 1001 runAsNonRoot: true ## MySQL secondary container's resource requests and limits @@ -820,7 +820,7 @@ secondary: - ReadWriteOnce ## @param secondary.persistence.size MySQL secondary persistent volume size ## - size: 500Mi # Abi: previously 8Gi + size: 500Mi # NOTE: previously 8Gi ## @param secondary.persistence.selector Selector to match an existing Persistent Volume ## selector: ## matchLabels: @@ -1060,7 +1060,7 @@ metrics: ## @param metrics.containerSecurityContext.runAsNonRoot Set MySQL metrics container's Security Context runAsNonRoot ## containerSecurityContext: - enabled: false # Abi: OCP security context is set to 'restricted' by default + enabled: false # NOTE: OCP security context is set to 'restricted' by default runAsUser: 1001 runAsNonRoot: true ## MySQL Prometheus exporter service parameters diff --git a/charts/deps/containers/opensearch/Dockerfile b/charts/deps/charts/opensearch-2.12.1/containers/Dockerfile similarity index 100% rename from charts/deps/containers/opensearch/Dockerfile rename to charts/deps/charts/opensearch-2.12.1/containers/Dockerfile diff --git a/charts/deps/charts/opensearch-2.12.1/values.yaml b/charts/deps/charts/opensearch-2.12.1/values.yaml index 37be863..fb865dc 100644 --- a/charts/deps/charts/opensearch-2.12.1/values.yaml +++ b/charts/deps/charts/opensearch-2.12.1/values.yaml @@ -19,10 +19,10 @@ roles: - data - remote_cluster_client -replicas: 1 # Abi: previously 3 +replicas: 1 # NOTE: previously 3 # if not set, falls back to parsing .Values.imageTag, then .Chart.appVersion. -majorVersion: 2.7.0 # Abi: Chart.appVersion = 2.7.0 +majorVersion: 2.7.0 # NOTE: Chart.appVersion = 2.7.0 global: # Set if you want to change the default docker registry, e.g. a private one. @@ -124,10 +124,10 @@ hostAliases: [] # - "bar.local" image: - repository: ghcr.io/bcgov/nr-openmetadata-opensearch # Abi: previously "opensearchproject/opensearch" + repository: ghcr.io/bcgov/nr-openmetadata-opensearch # NOTE: previously "opensearchproject/opensearch" # override image tag, which is .Chart.AppVersion by default - tag: security-context-changes # Abi: previously defaulting 2.7.0 - pullPolicy: "Always" # Abi: prevously "IfNotPresent" + tag: main # NOTE: previously defaulting 2.7.0 + pullPolicy: "Always" # NOTE: previously "IfNotPresent" podAnnotations: {} # iam.amazonaws.com/role: es-cluster @@ -142,7 +142,7 @@ opensearchJavaOpts: "-Xmx512M -Xms512M" resources: requests: - cpu: "100m" # Abi: lowered + cpu: "100m" # NOTE: lowered memory: "100Mi" initResources: {} @@ -190,11 +190,11 @@ podSecurityPolicy: persistence: enabled: true # Set to false to disable the `fsgroup-volume` initContainer that will update permissions on the persistent disk. - enableInitChown: false # Abi: disables line 234 on the statefulset.yaml + enableInitChown: false # NOTE: disables line 234 on the statefulset.yaml # override image, which is busybox by default - image: ghcr.io/bcgov/nr-openmetadata-opensearch-busybox # Abi: previously busybox:latest + # image: busybox:latest # override image tag, which is latest by default - imageTag: security-context-changes # Abi: set to main branch after PR merge + # imageTag: labels: # Add default labels for the volumeClaimTemplate of the StatefulSet enabled: false @@ -208,7 +208,7 @@ persistence: # storageClass: "-" accessModes: - ReadWriteOnce - size: 500Mi # Abi: previously 8Gi + size: 500Mi # NOTE: previously 8Gi annotations: {} extraVolumes: [] @@ -294,11 +294,11 @@ updateStrategy: RollingUpdate # This is the max unavailable setting for the pod disruption budget # The default value of 1 will make sure that kubernetes won't allow more than 1 # of your pods to be unavailable during maintenance -# maxUnavailable: 1 # Abi: removed PDB for now +# maxUnavailable: 1 # NOTE: removed PDB for now podSecurityContext: - # fsGroup: 1000 # Abi: allow for group ID 0 - # runAsUser: 1000 # Abi: allow for random UID + # fsGroup: 1000 # NOTE: allow for auto fsGroup + # runAsUser: 1000 # NOTE: allow for random UID securityContext: capabilities: @@ -306,7 +306,7 @@ securityContext: - ALL # readOnlyRootFilesystem: true runAsNonRoot: true - # runAsUser: 1000 # Abi: allow for random UID + # runAsUser: 1000 # NOTE: allow for random UID securityConfig: enabled: true diff --git a/charts/deps/containers/opensearch/busybox/Dockerfile b/charts/deps/containers/opensearch/busybox/Dockerfile deleted file mode 100644 index 5219039..0000000 --- a/charts/deps/containers/opensearch/busybox/Dockerfile +++ /dev/null @@ -1,5 +0,0 @@ -# based on line 230 of statefulset.yaml -FROM busybox:latest - -# the statefulset init container is supposed to change ownership of the /usr/share/opensearch/data directory: chown -R 1000:1000 /usr/share/opensearch/data -RUN chmod -R g+rwX /usr \ No newline at end of file diff --git a/charts/deps/values.yaml b/charts/deps/values.yaml index 68ed64a..44f172f 100644 --- a/charts/deps/values.yaml +++ b/charts/deps/values.yaml @@ -15,7 +15,7 @@ mysql: primary: extraFlags: "--sort_buffer_size=10M" persistence: - size: 500Mi # Abi: previously 50Gi + size: 500Mi # NOTE: previously 50Gi service: nodePort: 3306 initdbScripts: @@ -40,7 +40,7 @@ opensearch: imagePullPolicy: Always opensearchJavaOpts: "-Xmx1g -Xms1g" persistence: - size: 500Mi # Abi: previously 30Gi + size: 500Mi # NOTE: previously 30Gi protocol: http config: opensearch.yml: | @@ -130,7 +130,7 @@ airflow: enabled: true # NOTE: "" means cluster-default storageClass: "" - size: 500Mi # Abi: previously 1Gi + size: 500Mi # NOTE: previously 1Gi accessMode: ReadWriteMany logs: persistence: @@ -138,4 +138,4 @@ airflow: # empty string means cluster-default storageClass: "" accessMode: ReadWriteMany - size: 500Mi # Abi: previously 1Gi + size: 500Mi # NOTE: previously 1Gi diff --git a/charts/openmetadata/values.yaml b/charts/openmetadata/values.yaml index 0208f4c..04f955c 100644 --- a/charts/openmetadata/values.yaml +++ b/charts/openmetadata/values.yaml @@ -400,7 +400,7 @@ readinessProbe: path: / port: http startupProbe: - timeoutSeconds: 10 # Abi: Added this value, as it was timing out after 1 second + timeoutSeconds: 10 # NOTE: Added this value, as it was timing out after 1 second periodSeconds: 60 failureThreshold: 5 successThreshold: 1 diff --git a/oc/airflow-mysql-secret.yaml b/oc/airflow-mysql-secret.yaml deleted file mode 100644 index d7f711e..0000000 --- a/oc/airflow-mysql-secret.yaml +++ /dev/null @@ -1,7 +0,0 @@ -kind: Secret -apiVersion: v1 -metadata: - name: airflow-mysql-secrets -data: - airflow-mysql-password: YWlyZmxvd19wYXNz -type: Opaque \ No newline at end of file diff --git a/oc/openmetadata-mysql-secret.yaml b/oc/openmetadata-mysql-secret.yaml deleted file mode 100644 index 254123b..0000000 --- a/oc/openmetadata-mysql-secret.yaml +++ /dev/null @@ -1,7 +0,0 @@ -kind: Secret -apiVersion: v1 -metadata: - name: mysql-secrets -data: - openmetadata-mysql-password: b3Blbm1ldGFkYXRhX3Bhc3N3b3Jk -type: Opaque \ No newline at end of file From 9d0fddd6cbfd45e97fcaf77007cbd645911fa5bc Mon Sep 17 00:00:00 2001 From: abimichel Date: Thu, 1 Feb 2024 12:41:58 -0800 Subject: [PATCH 27/29] flag all Helm changes with DF-NOTE --- .../workflows/docker-publish-opensearch.yml | 4 ++-- README.md | 5 ++-- charts/deps/charts/airflow-8.8.0/values.yaml | 2 +- charts/deps/charts/mysql-9.7.2/values.yaml | 14 +++++------ .../deps/charts/opensearch-2.12.1/values.yaml | 24 +++++++++---------- charts/deps/values.yaml | 8 +++---- charts/openmetadata/values.yaml | 2 +- 7 files changed, 30 insertions(+), 29 deletions(-) diff --git a/.github/workflows/docker-publish-opensearch.yml b/.github/workflows/docker-publish-opensearch.yml index 0c62084..f9ccbf0 100644 --- a/.github/workflows/docker-publish-opensearch.yml +++ b/.github/workflows/docker-publish-opensearch.yml @@ -8,7 +8,7 @@ on: env: - # NOTE: pull ghcr.io/bcgov/nr-openmetadata-opensearch:main + # DF-NOTE: pull ghcr.io/bcgov/nr-openmetadata-opensearch:main REGISTRY: ghcr.io DOCKERFILE_PATH: charts/opensearch-2.12.1/containers IMAGE_NAME: ${{ github.repository }}-opensearch @@ -52,7 +52,7 @@ jobs: id: build-and-push uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0 with: - # NOTE: to help the action find the Dockerfile to build from + # DF-NOTE: to help the action find the Dockerfile to build from context: ${{ env.DOCKERFILE_PATH }}/ push: ${{ github.event_name != 'pull_request' }} tags: ${{ steps.meta.outputs.tags }} diff --git a/README.md b/README.md index 3a014c6..d4c153e 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,8 @@ helm install openmetadata . ``` #### Port Forward OpenMetadata to view UI ``` -oc port-forward service/openmetadata 8585:http + + ``` ## OpenSearch Dockerfile and Use of GHCR @@ -122,4 +123,4 @@ docker pull ghcr.io/bcgov/nr-openmetadata-opensearch:main ``` ## Helm chart modifications -To review all Helm chart modifications (i.e. differences between the OpenMetadata default config and this config), search this repo for "NOTE:" annotations. \ No newline at end of file +To review all Helm chart modifications (i.e. differences between the OpenMetadata default config and this config), search this repo for "DF-NOTE:" annotations. \ No newline at end of file diff --git a/charts/deps/charts/airflow-8.8.0/values.yaml b/charts/deps/charts/airflow-8.8.0/values.yaml index 61d2483..e24e16e 100644 --- a/charts/deps/charts/airflow-8.8.0/values.yaml +++ b/charts/deps/charts/airflow-8.8.0/values.yaml @@ -209,7 +209,7 @@ airflow: ## sets the filesystem owner group of files/folders in mounted volumes ## this does NOT give root permissions to Pods, only the "root" group - # NOTE: this caused error: securityContext.fsGroup: Invalid value: []int64{0}: 0 is not an allowed group + # DF-NOTE: this caused error: securityContext.fsGroup: Invalid value: []int64{0}: 0 is not an allowed group # fsGroup: 0 ## extra annotations for airflow Pods diff --git a/charts/deps/charts/mysql-9.7.2/values.yaml b/charts/deps/charts/mysql-9.7.2/values.yaml index 74b33aa..de9e59a 100644 --- a/charts/deps/charts/mysql-9.7.2/values.yaml +++ b/charts/deps/charts/mysql-9.7.2/values.yaml @@ -296,7 +296,7 @@ primary: ## @param primary.podSecurityContext.fsGroup Group ID for the mounted volumes' filesystem ## podSecurityContext: - enabled: false # NOTE: OCP security context is set to 'restricted' by default + enabled: false # DF-NOTE: OCP security context is set to 'restricted' by default fsGroup: 1001 ## MySQL primary container security context ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container @@ -305,7 +305,7 @@ primary: ## @param primary.containerSecurityContext.runAsNonRoot Set MySQL primary container's Security Context runAsNonRoot ## containerSecurityContext: - enabled: false # NOTE: OCP security context is set to 'restricted' by default + enabled: false # DF-NOTE: OCP security context is set to 'restricted' by default runAsUser: 1001 runAsNonRoot: true ## MySQL primary container's resource requests and limits @@ -440,7 +440,7 @@ primary: - ReadWriteOnce ## @param primary.persistence.size MySQL primary persistent volume size ## - size: 500Mi # NOTE: previously 8Gi + size: 500Mi # DF-NOTE: previously 8Gi ## @param primary.persistence.selector Selector to match an existing Persistent Volume ## selector: ## matchLabels: @@ -676,7 +676,7 @@ secondary: ## @param secondary.podSecurityContext.fsGroup Group ID for the mounted volumes' filesystem ## podSecurityContext: - enabled: false # NOTE: OCP security context is set to 'restricted' by default + enabled: false # DF-NOTE: OCP security context is set to 'restricted' by default fsGroup: 1001 ## MySQL secondary container security context ## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-container @@ -685,7 +685,7 @@ secondary: ## @param secondary.containerSecurityContext.runAsNonRoot Set MySQL secondary container's Security Context runAsNonRoot ## containerSecurityContext: - enabled: false # NOTE: OCP security context is set to 'restricted' by default + enabled: false # DF-NOTE: OCP security context is set to 'restricted' by default runAsUser: 1001 runAsNonRoot: true ## MySQL secondary container's resource requests and limits @@ -820,7 +820,7 @@ secondary: - ReadWriteOnce ## @param secondary.persistence.size MySQL secondary persistent volume size ## - size: 500Mi # NOTE: previously 8Gi + size: 500Mi # DF-NOTE: previously 8Gi ## @param secondary.persistence.selector Selector to match an existing Persistent Volume ## selector: ## matchLabels: @@ -1060,7 +1060,7 @@ metrics: ## @param metrics.containerSecurityContext.runAsNonRoot Set MySQL metrics container's Security Context runAsNonRoot ## containerSecurityContext: - enabled: false # NOTE: OCP security context is set to 'restricted' by default + enabled: false # DF-NOTE: OCP security context is set to 'restricted' by default runAsUser: 1001 runAsNonRoot: true ## MySQL Prometheus exporter service parameters diff --git a/charts/deps/charts/opensearch-2.12.1/values.yaml b/charts/deps/charts/opensearch-2.12.1/values.yaml index fb865dc..2843ce4 100644 --- a/charts/deps/charts/opensearch-2.12.1/values.yaml +++ b/charts/deps/charts/opensearch-2.12.1/values.yaml @@ -19,10 +19,10 @@ roles: - data - remote_cluster_client -replicas: 1 # NOTE: previously 3 +replicas: 1 # DF-NOTE: previously 3 # if not set, falls back to parsing .Values.imageTag, then .Chart.appVersion. -majorVersion: 2.7.0 # NOTE: Chart.appVersion = 2.7.0 +majorVersion: 2.7.0 # DF-NOTE: Chart.appVersion = 2.7.0 global: # Set if you want to change the default docker registry, e.g. a private one. @@ -124,10 +124,10 @@ hostAliases: [] # - "bar.local" image: - repository: ghcr.io/bcgov/nr-openmetadata-opensearch # NOTE: previously "opensearchproject/opensearch" + repository: ghcr.io/bcgov/nr-openmetadata-opensearch # DF-NOTE: previously "opensearchproject/opensearch" # override image tag, which is .Chart.AppVersion by default - tag: main # NOTE: previously defaulting 2.7.0 - pullPolicy: "Always" # NOTE: previously "IfNotPresent" + tag: main # DF-NOTE: previously defaulting 2.7.0 + pullPolicy: "Always" # DF-NOTE: previously "IfNotPresent" podAnnotations: {} # iam.amazonaws.com/role: es-cluster @@ -142,7 +142,7 @@ opensearchJavaOpts: "-Xmx512M -Xms512M" resources: requests: - cpu: "100m" # NOTE: lowered + cpu: "100m" # DF-NOTE: lowered memory: "100Mi" initResources: {} @@ -190,7 +190,7 @@ podSecurityPolicy: persistence: enabled: true # Set to false to disable the `fsgroup-volume` initContainer that will update permissions on the persistent disk. - enableInitChown: false # NOTE: disables line 234 on the statefulset.yaml + enableInitChown: false # DF-NOTE: disables line 234 on the statefulset.yaml # override image, which is busybox by default # image: busybox:latest # override image tag, which is latest by default @@ -208,7 +208,7 @@ persistence: # storageClass: "-" accessModes: - ReadWriteOnce - size: 500Mi # NOTE: previously 8Gi + size: 500Mi # DF-NOTE: previously 8Gi annotations: {} extraVolumes: [] @@ -294,11 +294,11 @@ updateStrategy: RollingUpdate # This is the max unavailable setting for the pod disruption budget # The default value of 1 will make sure that kubernetes won't allow more than 1 # of your pods to be unavailable during maintenance -# maxUnavailable: 1 # NOTE: removed PDB for now +# maxUnavailable: 1 # DF-NOTE: removed PDB for now podSecurityContext: - # fsGroup: 1000 # NOTE: allow for auto fsGroup - # runAsUser: 1000 # NOTE: allow for random UID + # fsGroup: 1000 # DF-NOTE: allow for auto fsGroup + # runAsUser: 1000 # DF-NOTE: allow for random UID securityContext: capabilities: @@ -306,7 +306,7 @@ securityContext: - ALL # readOnlyRootFilesystem: true runAsNonRoot: true - # runAsUser: 1000 # NOTE: allow for random UID + # runAsUser: 1000 # DF-NOTE: allow for random UID securityConfig: enabled: true diff --git a/charts/deps/values.yaml b/charts/deps/values.yaml index 44f172f..8dbfa4d 100644 --- a/charts/deps/values.yaml +++ b/charts/deps/values.yaml @@ -15,7 +15,7 @@ mysql: primary: extraFlags: "--sort_buffer_size=10M" persistence: - size: 500Mi # NOTE: previously 50Gi + size: 500Mi # DF-NOTE: previously 50Gi service: nodePort: 3306 initdbScripts: @@ -40,7 +40,7 @@ opensearch: imagePullPolicy: Always opensearchJavaOpts: "-Xmx1g -Xms1g" persistence: - size: 500Mi # NOTE: previously 30Gi + size: 500Mi # DF-NOTE: previously 30Gi protocol: http config: opensearch.yml: | @@ -130,7 +130,7 @@ airflow: enabled: true # NOTE: "" means cluster-default storageClass: "" - size: 500Mi # NOTE: previously 1Gi + size: 500Mi # DF-NOTE: previously 1Gi accessMode: ReadWriteMany logs: persistence: @@ -138,4 +138,4 @@ airflow: # empty string means cluster-default storageClass: "" accessMode: ReadWriteMany - size: 500Mi # NOTE: previously 1Gi + size: 500Mi # DF-NOTE: previously 1Gi diff --git a/charts/openmetadata/values.yaml b/charts/openmetadata/values.yaml index 04f955c..af58398 100644 --- a/charts/openmetadata/values.yaml +++ b/charts/openmetadata/values.yaml @@ -400,7 +400,7 @@ readinessProbe: path: / port: http startupProbe: - timeoutSeconds: 10 # NOTE: Added this value, as it was timing out after 1 second + timeoutSeconds: 10 # DF-NOTE: Added this value, as it was timing out after 1 second periodSeconds: 60 failureThreshold: 5 successThreshold: 1 From 31cd44547f4885f7c401126783cd31fdc99bacf0 Mon Sep 17 00:00:00 2001 From: abimichel Date: Thu, 1 Feb 2024 12:55:46 -0800 Subject: [PATCH 28/29] modified: docker-publish-opensearch.yml --- .github/workflows/docker-publish-opensearch.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/docker-publish-opensearch.yml b/.github/workflows/docker-publish-opensearch.yml index f9ccbf0..5e1d9d0 100644 --- a/.github/workflows/docker-publish-opensearch.yml +++ b/.github/workflows/docker-publish-opensearch.yml @@ -24,6 +24,9 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@v3 + # DF-NOTE: delete after merging PR + with: + ref: security-context-changes - name: Install cosign if: github.event_name != 'pull_request' From ac3f2cd626b5d987437e3a4600b3f4558565bd9e Mon Sep 17 00:00:00 2001 From: abimichel Date: Thu, 1 Feb 2024 14:25:33 -0800 Subject: [PATCH 29/29] modified: docker-publish-opensearch.yml --- .github/workflows/docker-publish-opensearch.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker-publish-opensearch.yml b/.github/workflows/docker-publish-opensearch.yml index 5e1d9d0..03938d1 100644 --- a/.github/workflows/docker-publish-opensearch.yml +++ b/.github/workflows/docker-publish-opensearch.yml @@ -10,7 +10,7 @@ on: env: # DF-NOTE: pull ghcr.io/bcgov/nr-openmetadata-opensearch:main REGISTRY: ghcr.io - DOCKERFILE_PATH: charts/opensearch-2.12.1/containers + DOCKERFILE_PATH: charts/deps/charts/opensearch-2.12.1/containers IMAGE_NAME: ${{ github.repository }}-opensearch jobs: