From 700143950b76a2b7a7ee14e8162d7a81bddc1a8d Mon Sep 17 00:00:00 2001 From: Micah Nagel <micah.nagel@defenseunicorns.com> Date: Tue, 7 Feb 2023 13:56:14 -0700 Subject: [PATCH 1/5] update --- .gitignore | 1 - CHANGELOG.md | 276 ++ CODEOWNERS | 2 +- CONTRIBUTING.md | 22 + README.md | 189 +- bb_prod/.sops.yaml | 7 - bb_prod/apps/gitlab-runners/README.md | 12 - .../gitlab-runners/bigbang/kustomization.yaml | 12 - .../gitlab-runners/bigbang/namespace.yaml | 8 - .../bigbang/privileged-runner-hr.yaml | 116 - .../apps/gitlab-runners/gitlab-runners.yaml | 75 - .../graduated/kustomization.yaml | 12 - .../gitlab-runners/graduated/namespace.yaml | 8 - .../graduated/privileged-runner-hr.yaml | 116 - .../incubating/kustomization.yaml | 12 - .../gitlab-runners/incubating/namespace.yaml | 8 - .../incubating/privileged-runner-hr.yaml | 116 - .../release/generic-runner-hr.yaml | 96 - .../gitlab-runners/release/kustomization.yaml | 12 - .../gitlab-runners/release/namespace.yaml | 8 - .../gitlab-runners/sandbox/kustomization.yaml | 12 - .../gitlab-runners/sandbox/namespace.yaml | 8 - .../sandbox/privileged-runner-hr.yaml | 116 - bb_prod/bigbang/base/kustomization.yaml | 11 - bb_prod/bigbang/base/values.yaml | 67 - bb_prod/bigbang/ci-hr/Chart.yaml | 6 - .../ci-hr/templates/alertmanager-egress.yaml | 25 - .../ci-hr/templates/kyverno-secret-copy.yaml | 86 - .../bigbang/ci-hr/templates/reg-creds.yaml | 8 - .../bigbang/ci-hr/templates/reg1-creds.yaml | 8 - .../bigbang/ci-hr/templates/runner-token.yaml | 9 - bb_prod/bigbang/ci-hr/values.yaml | 13 - bb_prod/bigbang/prod.yaml | 32 - bb_prod/bigbang/prod/bbci.enc.yaml | 22 - .../bigbang/prod/certs-bbci-secret.enc.yaml | 22 - bb_prod/bigbang/prod/ci-hr.yaml | 35 - bb_prod/bigbang/prod/docker-daemon.json | 1 - .../prod/environment-bbci-secret.enc.yaml | 22 - bb_prod/bigbang/prod/kustomization.yaml | 58 - .../nothingtoseehere.jpg.png.pdf.enc.exe.txt | 22 - bb_prod/bigbang/prod/values.yaml | 401 --- bb_staging/.sops.yaml | 4 - chart/Chart.lock | 12 + chart/Chart.yaml | 42 + chart/charts/gluon-0.3.2.tgz | Bin 0 -> 3152 bytes chart/charts/minio-instance-4.5.4-bb.2.tgz | Bin 0 -> 20002 bytes chart/charts/postgresql-10.3.5.tgz | Bin 0 -> 40072 bytes ...ermost-performance-monitoring-v2_rev2.json | 2272 +++++++++++++++++ .../deps/postgresql}/.helmignore | 2 - chart/deps/postgresql/Chart.lock | 6 + chart/deps/postgresql/Chart.yaml | 29 + chart/deps/postgresql/Kptfile | 11 + chart/deps/postgresql/README.md | 770 ++++++ .../deps/postgresql/ci/commonAnnotations.yaml | 3 + chart/deps/postgresql/ci/default-values.yaml | 1 + .../ci/shmvolume-disabled-values.yaml | 2 + chart/deps/postgresql/files/README.md | 1 + chart/deps/postgresql/files/conf.d/README.md | 4 + .../docker-entrypoint-initdb.d/README.md | 3 + chart/deps/postgresql/templates/NOTES.txt | 59 + chart/deps/postgresql/templates/_helpers.tpl | 919 +++++++ .../deps/postgresql/templates/configmap.yaml | 27 + .../templates/extended-config-configmap.yaml | 22 + .../deps/postgresql/templates/extra-list.yaml | 4 + .../templates/initialization-configmap.yaml | 25 + .../templates/metrics-configmap.yaml | 14 + .../postgresql/templates/metrics-svc.yaml | 26 + .../postgresql/templates/networkpolicy.yaml | 39 + .../templates/podsecuritypolicy.yaml | 38 + .../postgresql/templates/prometheusrule.yaml | 23 + chart/deps/postgresql/templates/role.yaml | 20 + .../postgresql/templates/rolebinding.yaml | 20 + chart/deps/postgresql/templates/secrets.yaml | 22 + .../postgresql/templates/serviceaccount.yaml | 12 + .../postgresql/templates/servicemonitor.yaml | 33 + .../templates/statefulset-readreplicas.yaml | 411 +++ .../postgresql/templates/statefulset.yaml | 618 +++++ .../postgresql/templates/svc-headless.yaml | 28 + chart/deps/postgresql/templates/svc-read.yaml | 43 + chart/deps/postgresql/templates/svc.yaml | 41 + chart/deps/postgresql/values.schema.json | 103 + chart/deps/postgresql/values.yaml | 840 ++++++ .../ci-hr => chart}/templates/_helpers.tpl | 25 +- .../bigbang/mattermost-dashboards.yaml | 12 + .../bigbang/mattermost-gossip-svc.yaml | 20 + .../networkpolicies/allow-dns-egress.yaml | 22 + .../networkpolicies/allow-egress-tempo.yaml | 21 + .../networkpolicies/allow-elastic-egress.yaml | 24 + .../allow-external-postgres.yaml | 20 + .../bigbang/networkpolicies/allow-in-ns.yaml | 18 + .../networkpolicies/allow-istio-egress.yaml | 21 + .../networkpolicies/allow-istio-ingress.yaml | 24 + .../allow-mattermost-egress.yaml | 20 + .../allow-minio-monitoring.yaml | 22 + .../allow-minio-operator-egress.yaml | 21 + .../allow-minio-operator-ingress-upgrade.yaml | 24 + .../allow-minio-operator-ingress.yaml | 21 + .../allow-monitoring-ingress.yaml | 24 + .../allow-sidecar-monitoring-ingress.yaml | 22 + .../networkpolicies/allow-test-egress.yaml | 26 + .../bigbang/networkpolicies/deny-default.yaml | 14 + chart/templates/db-credentials.yaml | 16 + chart/templates/default-bucket.yaml | 71 + chart/templates/env-secret.yaml | 52 + chart/templates/license.yaml | 15 + chart/templates/mattermost.yaml | 246 ++ chart/templates/role-binding.yaml | 21 + chart/templates/role.yaml | 24 + chart/templates/service-monitor.yaml | 24 + chart/templates/tests/test-ui.yaml | 13 + chart/templates/virtualservice.yaml | 32 + chart/tests/cypress/cypress.json | 6 + chart/tests/cypress/mattermost-health.spec.js | 126 + chart/values.yaml | 279 ++ docs/AFFINITY.md | 44 + docs/DEVELOPMENT_MAINTENANCE.md | 45 + docs/keycloak.md | 104 + docs/overview.md | 16 + renovate.json | 83 +- storageclass/ebs-gp3-storage-class.yaml | 14 - tests/dependencies.yaml | 7 + tests/policy/mattermost-minio-ib.rego | 35 + tests/policy/postgres-ib.rego | 35 + tests/test-values.yml | 22 + tests/wait.sh | 27 + 125 files changed, 8671 insertions(+), 1728 deletions(-) delete mode 100644 .gitignore create mode 100644 CHANGELOG.md create mode 100644 CONTRIBUTING.md delete mode 100644 bb_prod/.sops.yaml delete mode 100644 bb_prod/apps/gitlab-runners/README.md delete mode 100644 bb_prod/apps/gitlab-runners/bigbang/kustomization.yaml delete mode 100644 bb_prod/apps/gitlab-runners/bigbang/namespace.yaml delete mode 100644 bb_prod/apps/gitlab-runners/bigbang/privileged-runner-hr.yaml delete mode 100644 bb_prod/apps/gitlab-runners/gitlab-runners.yaml delete mode 100644 bb_prod/apps/gitlab-runners/graduated/kustomization.yaml delete mode 100644 bb_prod/apps/gitlab-runners/graduated/namespace.yaml delete mode 100644 bb_prod/apps/gitlab-runners/graduated/privileged-runner-hr.yaml delete mode 100644 bb_prod/apps/gitlab-runners/incubating/kustomization.yaml delete mode 100644 bb_prod/apps/gitlab-runners/incubating/namespace.yaml delete mode 100644 bb_prod/apps/gitlab-runners/incubating/privileged-runner-hr.yaml delete mode 100644 bb_prod/apps/gitlab-runners/release/generic-runner-hr.yaml delete mode 100644 bb_prod/apps/gitlab-runners/release/kustomization.yaml delete mode 100644 bb_prod/apps/gitlab-runners/release/namespace.yaml delete mode 100644 bb_prod/apps/gitlab-runners/sandbox/kustomization.yaml delete mode 100644 bb_prod/apps/gitlab-runners/sandbox/namespace.yaml delete mode 100644 bb_prod/apps/gitlab-runners/sandbox/privileged-runner-hr.yaml delete mode 100644 bb_prod/bigbang/base/kustomization.yaml delete mode 100644 bb_prod/bigbang/base/values.yaml delete mode 100644 bb_prod/bigbang/ci-hr/Chart.yaml delete mode 100644 bb_prod/bigbang/ci-hr/templates/alertmanager-egress.yaml delete mode 100644 bb_prod/bigbang/ci-hr/templates/kyverno-secret-copy.yaml delete mode 100644 bb_prod/bigbang/ci-hr/templates/reg-creds.yaml delete mode 100644 bb_prod/bigbang/ci-hr/templates/reg1-creds.yaml delete mode 100644 bb_prod/bigbang/ci-hr/templates/runner-token.yaml delete mode 100644 bb_prod/bigbang/ci-hr/values.yaml delete mode 100644 bb_prod/bigbang/prod.yaml delete mode 100644 bb_prod/bigbang/prod/bbci.enc.yaml delete mode 100644 bb_prod/bigbang/prod/certs-bbci-secret.enc.yaml delete mode 100644 bb_prod/bigbang/prod/ci-hr.yaml delete mode 100644 bb_prod/bigbang/prod/docker-daemon.json delete mode 100644 bb_prod/bigbang/prod/environment-bbci-secret.enc.yaml delete mode 100644 bb_prod/bigbang/prod/kustomization.yaml delete mode 100644 bb_prod/bigbang/prod/secrets/nothingtoseehere.jpg.png.pdf.enc.exe.txt delete mode 100644 bb_prod/bigbang/prod/values.yaml delete mode 100644 bb_staging/.sops.yaml create mode 100644 chart/Chart.lock create mode 100644 chart/Chart.yaml create mode 100644 chart/charts/gluon-0.3.2.tgz create mode 100644 chart/charts/minio-instance-4.5.4-bb.2.tgz create mode 100644 chart/charts/postgresql-10.3.5.tgz create mode 100644 chart/dashboards/mattermost-performance-monitoring-v2_rev2.json rename {bb_prod/bigbang/ci-hr => chart/deps/postgresql}/.helmignore (95%) create mode 100644 chart/deps/postgresql/Chart.lock create mode 100644 chart/deps/postgresql/Chart.yaml create mode 100644 chart/deps/postgresql/Kptfile create mode 100644 chart/deps/postgresql/README.md create mode 100644 chart/deps/postgresql/ci/commonAnnotations.yaml create mode 100644 chart/deps/postgresql/ci/default-values.yaml create mode 100644 chart/deps/postgresql/ci/shmvolume-disabled-values.yaml create mode 100644 chart/deps/postgresql/files/README.md create mode 100644 chart/deps/postgresql/files/conf.d/README.md create mode 100644 chart/deps/postgresql/files/docker-entrypoint-initdb.d/README.md create mode 100644 chart/deps/postgresql/templates/NOTES.txt create mode 100644 chart/deps/postgresql/templates/_helpers.tpl create mode 100644 chart/deps/postgresql/templates/configmap.yaml create mode 100644 chart/deps/postgresql/templates/extended-config-configmap.yaml create mode 100644 chart/deps/postgresql/templates/extra-list.yaml create mode 100644 chart/deps/postgresql/templates/initialization-configmap.yaml create mode 100644 chart/deps/postgresql/templates/metrics-configmap.yaml create mode 100644 chart/deps/postgresql/templates/metrics-svc.yaml create mode 100644 chart/deps/postgresql/templates/networkpolicy.yaml create mode 100644 chart/deps/postgresql/templates/podsecuritypolicy.yaml create mode 100644 chart/deps/postgresql/templates/prometheusrule.yaml create mode 100644 chart/deps/postgresql/templates/role.yaml create mode 100644 chart/deps/postgresql/templates/rolebinding.yaml create mode 100644 chart/deps/postgresql/templates/secrets.yaml create mode 100644 chart/deps/postgresql/templates/serviceaccount.yaml create mode 100644 chart/deps/postgresql/templates/servicemonitor.yaml create mode 100644 chart/deps/postgresql/templates/statefulset-readreplicas.yaml create mode 100644 chart/deps/postgresql/templates/statefulset.yaml create mode 100644 chart/deps/postgresql/templates/svc-headless.yaml create mode 100644 chart/deps/postgresql/templates/svc-read.yaml create mode 100644 chart/deps/postgresql/templates/svc.yaml create mode 100644 chart/deps/postgresql/values.schema.json create mode 100644 chart/deps/postgresql/values.yaml rename {bb_prod/bigbang/ci-hr => chart}/templates/_helpers.tpl (69%) create mode 100644 chart/templates/bigbang/mattermost-dashboards.yaml create mode 100644 chart/templates/bigbang/mattermost-gossip-svc.yaml create mode 100644 chart/templates/bigbang/networkpolicies/allow-dns-egress.yaml create mode 100644 chart/templates/bigbang/networkpolicies/allow-egress-tempo.yaml create mode 100644 chart/templates/bigbang/networkpolicies/allow-elastic-egress.yaml create mode 100644 chart/templates/bigbang/networkpolicies/allow-external-postgres.yaml create mode 100644 chart/templates/bigbang/networkpolicies/allow-in-ns.yaml create mode 100644 chart/templates/bigbang/networkpolicies/allow-istio-egress.yaml create mode 100644 chart/templates/bigbang/networkpolicies/allow-istio-ingress.yaml create mode 100644 chart/templates/bigbang/networkpolicies/allow-mattermost-egress.yaml create mode 100644 chart/templates/bigbang/networkpolicies/allow-minio-monitoring.yaml create mode 100644 chart/templates/bigbang/networkpolicies/allow-minio-operator-egress.yaml create mode 100644 chart/templates/bigbang/networkpolicies/allow-minio-operator-ingress-upgrade.yaml create mode 100644 chart/templates/bigbang/networkpolicies/allow-minio-operator-ingress.yaml create mode 100644 chart/templates/bigbang/networkpolicies/allow-monitoring-ingress.yaml create mode 100644 chart/templates/bigbang/networkpolicies/allow-sidecar-monitoring-ingress.yaml create mode 100644 chart/templates/bigbang/networkpolicies/allow-test-egress.yaml create mode 100644 chart/templates/bigbang/networkpolicies/deny-default.yaml create mode 100644 chart/templates/db-credentials.yaml create mode 100644 chart/templates/default-bucket.yaml create mode 100644 chart/templates/env-secret.yaml create mode 100644 chart/templates/license.yaml create mode 100644 chart/templates/mattermost.yaml create mode 100644 chart/templates/role-binding.yaml create mode 100644 chart/templates/role.yaml create mode 100644 chart/templates/service-monitor.yaml create mode 100644 chart/templates/tests/test-ui.yaml create mode 100644 chart/templates/virtualservice.yaml create mode 100644 chart/tests/cypress/cypress.json create mode 100644 chart/tests/cypress/mattermost-health.spec.js create mode 100644 chart/values.yaml create mode 100644 docs/AFFINITY.md create mode 100644 docs/DEVELOPMENT_MAINTENANCE.md create mode 100644 docs/keycloak.md create mode 100644 docs/overview.md delete mode 100644 storageclass/ebs-gp3-storage-class.yaml create mode 100644 tests/dependencies.yaml create mode 100644 tests/policy/mattermost-minio-ib.rego create mode 100644 tests/policy/postgres-ib.rego create mode 100644 tests/test-values.yml create mode 100755 tests/wait.sh diff --git a/.gitignore b/.gitignore deleted file mode 100644 index d47317927..000000000 --- a/.gitignore +++ /dev/null @@ -1 +0,0 @@ -**/.terra* \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..dd868621c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,276 @@ +# Changelog + +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). + +--- +## [7.5.1-bb.4] - 2022-01-17 +### Changed +- Update gluon to new registry1 location + latest version (0.3.2) + +## [7.5.1-bb.3] - 2022-01-11 +### Changed +- Add support for istio injection via network policies / pod annotation value support +- Disable update job for MM to prevent upgrade issues + +## [7.5.1-bb.2] - 2022-01-11 +### Changed +- Changed minio subchart to utilize OCI +- Updated minio subchart to latest 4.5.4-bb.2 + +## [7.5.1-bb.1] - 2022-12-15 +### Changed +- Set capabilities to drop all + +## [7.5.1-bb.0] - 2022-11-18 +### Changed +- ironbank/opensource/mattermost/mattermost updated from 7.4.0 to 7.5.1 + +## [7.4.0-bb.0] - 2022-10-18 +### Changed +- ironbank/opensource/mattermost/mattermost updated from 7.3.0 to 7.4.0 + +## [7.3.0-bb.1] - 2022-10-05 +### Updated +- updated minio and gluon dependencies + +## [7.3.0-bb.0] - 2022-09-27 +### Changed +- ironbank/opensource/mattermost/mattermost updated from 7.2.0 to 7.3.0 + +## [7.2.0-bb.1] - 2022-09-28 +### Changed +- Change default SSO auth endpoints to use direct Keycloak endpoints. + +## [7.2.0-bb.0] - 2022-08-23 +### Changed +- Upgraded MM to 7.2.0 + +## [7.1.2-bb.1] - 2022-08-09 +### Added +- Added grafana dashboard configmap and dashboard json when `monitoring.enabled` and `enterprise.enabled` + +## [7.1.2-bb.0] - 2022-07-29 +### Changed +- Upgraded MM to 7.1.2 + +## [7.0.1-bb.1] - 2022-07-11 +### Changed +- Upgraded cypress test to reduce inconsistent/flaky behavior with a conditional check + +## [7.0.1-bb.0] - 2022-07-06 +### Changed +- Upgraded MM to 7.0.1 +- Updated tests to resolve cypress test failures from updated application html/css + +## [6.7.0-bb.0] - 2022-05-19 +### Changed +- Upgraded MM to 6.7.0 + +## [6.6.0-bb.0] - 2022-04-22 +### Changed +- Upgrade Mattermost to application version 6.6.0 + +## [0.7.0-bb.1] - 2022-03-28 +### Added +- NetworkPolicy Template to allow metrics scraping of minio tenants + +## [0.7.0-bb.0] - 2022-03-03 +### Changed +- Updated MM to 6.4.1 + +## [0.6.0-bb.1] - 2022-02-28 +### Changed +- Updated Minio to 4.4.10 + +## [0.6.0-bb.0] - 2022-02-22 +### Changed +- Upgrade Mattermost to application version 6.3.4 + +## [0.5.0-bb.0] - 2022-02-07 +### Changed +- Upgrade Mattermost to application version 6.3.3 + 1. v5 -> v6 has some major migrations and upstream notes long migration times, they have provided analysis on the time these take - [see here](https://docs.mattermost.com/upgrade/important-upgrade-notes.html#:~:text=Longer%20migration%20times,70%2B%20million%20posts.) + 2. v5 servers cannot run with v6 servers, i.e. upgrade WILL have downtime - [see here](https://docs.mattermost.com/upgrade/important-upgrade-notes.html#:~:text=The%20field%20type,a%20large%20extent.) + 3. v6.1 has additional schema changes, MM has provided analysis on these changes as well - [see here](https://docs.mattermost.com/upgrade/important-upgrade-notes.html#:~:text=Please%20refer%20to%20the%20schema%20migration%20analysis%20when%20upgrading%20to%20v6.1.) + 4. v6.2 has modified autocomplete to include private channels, which requires re-indexing if using Elastic - [see here](https://docs.mattermost.com/upgrade/important-upgrade-notes.html#:~:text=Channel%20results%20in,in%20autocomplete%20results.) + +## [0.4.0-bb.4] - 2022-02-15 +### Changed +- Update mino dependency chart to 4.4.3-bb.3 + +## [0.4.0-bb.3] - 2022-02-14 +### Changed +- Fixed bug with ENV for SITE_URL not being set in certain cases + +## [0.4.0-bb.2] - 2022-02-02 +### Updated +- Update minio dependency chart to 4.4.3-bb.2 + +## [0.4.0-bb.1] - 2022-01-31 +### Updated +- Update Chart.yaml to follow new standardization for release automation +- Added renovate check to update new standardization + +## [0.4.0-bb.0] - 2022-01-25 +### Changed +- Relocated `bbtests` to `values.yaml` + +## [0.3.0-bb.0] - 2021-12-06 +### Changed +- Added a conditional addition of tolerations to mattermost.yaml +- Added a spot for tolerations in values.yaml + +## [0.2.4-bb.0] - 2021-11-02 +### Changed +- Disabled ingress by default +- Disabled readiness container by default, added default initcontainer with IB postgres image + +## [0.2.3-bb.0] - 2021-10-27 +### Changed +- Fixed minio operator ingress networkpolicy labels + +## [0.2.2-bb.0] - 2021-09-30 +### Changed +- Updated Mattermost to 5.39.0 + +## [0.2.1-bb.0] - 2021-09-29 +### Changed +- Updated Minio and Minio-operator to 4.2.3-bb.2 + +## [0.2.0-bb.2] - 2021-09-14 +### Changed +- Updated Minio, and Minio-operator + +## [0.2.0-bb.1] - 2021-09-01 +### Fixed +- Missing appVersion update in Chart.yaml + +## [0.2.0-bb.0] - 2021-08-27 +### Changed +- Updated to latest IB 5.38.2 +- Modified tests to remove buggy "site name" test and accomodate the new "tutorial" setup + +## [0.1.8-bb.0] - 2021-08-19 +### Fix +- Fixes issue with default bucket creation already existing + +## [0.1.8-bb.0] - 2021-08-18 +### Added +- default user value set to null +- Set replica count to 1 +- Resource requests and limits for all containers +- Updated to latest Minio and Minio Operator dependency +- Updated Gluon test library + +## [0.1.7-bb.1] - 2021-07-23 +### Changed +- Updated to latest IronBank image 5.37.0 +- Updated to latest Minio 4.1.2 package as dependency +- Moved to Gluon test library +- Pulled in changes from main-minio2 branch + +### Added +- Added BigBang networkPolicies + +## [0.1.7-bb.0] - 2021-05-17 +### Changed +- Updated to latest Minio package as dependency + +## [0.1.6-bb.8] - 2021-07-21 +### Changed +- Add openshift toggle, conditionally add port 5353 egress. Changing "openshift:" to true in values.yaml will enable. + +## [0.1.6-bb.7] - 2021-07-08 +### Changed +- Update Mattermost to version 5.36.1 + +## [0.1.6-bb.6] - 2021-06-22 +### Changed +- Update Mattermost to version 5.36.0 + +## [0.1.6-bb.5] - 2021-06-21 +### Fixed +- NetworkPolicy blocking an init container, added policy to allow postgres egress for the init container +- Redo of test egress +- Move around DNS policy + +## [0.1.6-bb.4] - 2021-06-07 +### Added +- Ability to pass volumes / volumeMounts to MM pods + +## [0.1.6-bb.3] - 2021-06-04 +### Added +- Add IPS with new operator +- Switch to the IB image being used directly + +## [0.1.6-bb.2] - 2021-06-02 +### Changed +- Restricted test policy to just cluster + +## [0.1.6-bb.1] - 2021-06-01 +### Changed +- Moved tests to gluon library +### Added +- Default NetworkPolicies added + +## [0.1.6-bb.0] - 2021-05-11 +### Changed +- Migrated Cypress tests to Helm tests +- Added additional testing of file storage and settings modification + +## [0.1.5-bb.0] - 2021-05-06 +### Changed +- Updated to 5.34.2 +- Cleaned up values and test-values + +## [0.1.4-bb.0] - 2021-04-23 +### Added +- Added Elastic Search declarative configuration. + +## [0.1.3-bb.2] - 2021-04-19 +### Changed +- Pulled in official BB Minio via kpt +- Refactored the Minio connection secret + +## [0.1.3-bb.1] - 2021-04-15 +### Added +- Added Minio security context + +## [0.1.3-bb.0] - 2021-04-08 +### Added +- Values passthroughs for secret env values +- Moved all envs to a secret that chart creates + +## [0.1.2-bb.0] - 2021-04-05 +### Changed +- Modified the way affinity is passed to simplify and standardize + +## [0.1.1-bb.3] - 2021-03-25 +### Fixed +- Minio virtualservice disabled by default + +## [0.1.1-bb.2] - 2021-03-25 +### Changed +- Updated Cypress test to handle upgrades +- Added a test to make sure chats can send + +## [0.1.1-bb.1] - 2021-03-24 +### Changed +- Refactored the Minio dependency to use the BB upstream with kpt + +## [0.1.1-bb.0] - 2021-03-15 +### Changed +- Bumped Mattermost image to 5.32.1 +- Added a ENV to set S3 connection to insecure when using the built-in minio (due to an operator change) + +## [0.1.0-bb.2] - 2021-02-26 +### Fixed +- Bumped Minio image version to the newest IB image to fix an issue + +## [0.1.0-bb.1] - 2021-02-25 +### Fixed +- Fixed issue with the dependency listing in the chart, Flux did not properly install + +## [0.1.0-bb.0] - 2021-02-24 +### Added +- Initial chart built from operator v1.12.0 using Ironbank images diff --git a/CODEOWNERS b/CODEOWNERS index befe5dec7..1448d092e 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1 +1 @@ -* @brandencobb @micah.nagel @michaelmcleroy @ryan.thompson.44 @runyontr @ryan.j.garcia +* @micah.nagel @trkdashin @blloyd diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..e9f2890f0 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,22 @@ +# Contributing + +Thanks for contributing to this repository! + +This repository follows the following conventions: + +* [Semantic Versioning](https://semver.org/) +* [Keep a Changelog](https://keepachangelog.com/) +* [Conventional Commits](https://www.conventionalcommits.org/) + +Development requires the Kubernetes CLI tool as well as a local Kubernetes cluster. [KIND](https://github.com/kubernetes-sigs/kind) is recommended as a lightweight local option for standing up Kubernetes clusters. + +To contribute a change: + +1. Create a branch on the cloned repository with a descriptive name, prefixed with your name. For example, `gd/add-ingress` is an appropriate branch name. +2. Make the changes in code (review the [development/maintenance doc](./docs/DEVELOPMENT_MAINTENANCE.md), especially for new MM versions). +3. Write tests using [KUTTL](https://kuttl.dev) and [Conftest](https://conftest.dev) +4. Make commits using the [Conventional Commits](https://www.conventionalcommits.org/) format. This helps with automation for changelog. Update `CHANGELOG.md` in the same commit using the [Keep a Changelog](https://keepachangelog.com). Depending on tooling maturity, this step may be automated. +5. Open a merge request using one of the provided templates. If this merge request is solving a preexisting issue, add the issue reference into the description of the MR. +6. During this time, ensure that all new commits are rebased into your branch so that it remains up to date with the `development` branch. +7. Wait for a maintainer of the repository (see CODEOWNERS) to approve. +8. If you have permissions to merge, you are responsible for merging. Otherwise, a CODEOWNER will merge the commit. diff --git a/README.md b/README.md index c642e5647..de9c7497b 100644 --- a/README.md +++ b/README.md @@ -1,72 +1,141 @@ -# bigbang-ci +# mattermost -### Storage Class +   -By default, Big Bang will use the cluster's default `StorageClass` to dynamically provision the required persistent volumes. This means the cluster must be able to dynamically provision persistent volume claims (PVCs). Since we're on AWS, the simplest method is to use the [AWS EBS](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/AmazonEBS.html) Storage Class using Kubernetes' in tree [AWS cloud provider](https://kubernetes.io/docs/concepts/storage/storage-classes/#aws-ebs). +Deployment of mattermost -> Without a default storage class, some Big Bang components, like Elasticsearch, Jaeger, or Twistlock, will never reach the running state. +## Learn More +* [Application Overview](docs/overview.md) +* [Other Documentation](docs/) -```shell -kubectl apply -k "github.com/kubernetes-sigs/aws-ebs-csi-driver/deploy/kubernetes/overlays/stable/?ref=release-1.5" -kubectl apply -f ./storageclass/ebs-gp3-storage-class.yaml -``` -### Deploy bigbang -```shell -# Create namespace and setup git repo-creds -kubectl create ns bigbang -kubectl create secret -n bigbang generic repository-credentials --from-literal=username="bigbang-ci" --from-literal=password="***" - -install_flux.sh +## Pre-Requisites -# Deploy bigbang -kubectl apply -f bb_prod/bigbang/prod.yaml +* Kubernetes Cluster deployed +* Kubernetes config installed in `~/.kube/config` +* Helm installed -# Deploy gitlab-runners -kubectl apply -f bb_prod/apps/gitlab-runners/gitlab-runners.yaml +Kubernetes: `>=1.12.0-0` -# Deploy cluster-autoscaler -kubectl apply -f bb_prod/apps/cluster-autoscaler/prod.yaml -``` - -### Vault configuration: -Vault hostname must be resolvible on the cluster. For bigbang-ci the following was added to the coredns configmap: -```shell -NodeHosts: 10.0.11.186 vault.ci.bigbang.dev -``` -This is temporary until the cluster gets its own hostname. +Install Helm +https://helm.sh/docs/intro/install/ -To get vault to initialize: -```shell -kubectl exec -it pod/vault-0 -n vault -- bash +## Deployment -vault operator init +* Clone down the repository +* cd into directory +```bash +helm install mattermost chart/ ``` -To get monitoring to work with vault: -```shell -kubectl exec -it pod/vault-0 -n vault -- bash - -METRICS_POLICY_NAME="prometheus-metrics" -METRICS_ROLE_NAME="prometheus" -MONITORING_SERVICE_ACCOUNT_NAME="monitoring-monitoring-kube-prometheus" -MONITORING_NAMESPACE="monitoring" -export VAULT_ADDR=https://vault-vault-internal.vault.svc.cluster.local:8200 -export VAULT_TOKEN="<root token from vault initialization>" - -vault login $VAULT_TOKEN -vault auth enable kubernetes -vault write auth/kubernetes/config \ -kubernetes_host="https://$KUBERNETES_PORT_443_TCP_ADDR:443" \ -token_reviewer_jwt="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \ -kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt \ -issuer="https://kubernetes.default.svc.cluster.local" -vault policy write $METRICS_POLICY_NAME - << EOF -path "/sys/metrics" { - capabilities = ["read"] -} -EOF - -vault policy read $METRICS_POLICY_NAME -vault write auth/kubernetes/role/$METRICS_ROLE_NAME bound_service_account_names=$MONITORING_SERVICE_ACCOUNT_NAME bound_service_account_namespaces=$MONITORING_NAMESPACE policies=$METRICS_POLICY_NAME ttl=15m -``` +## Values + +| Key | Type | Default | Description | +|-----|------|---------|-------------| +| hostname | string | `"bigbang.dev"` | | +| istio.enabled | bool | `false` | Toggle istio integration | +| istio.chat.enabled | bool | `true` | | +| istio.chat.annotations | object | `{}` | | +| istio.chat.labels | object | `{}` | | +| istio.chat.gateways[0] | string | `"istio-system/main"` | | +| istio.chat.hosts[0] | string | `"chat.{{ .Values.hostname }}"` | | +| istio.injection | string | `"disabled"` | | +| ingress | object | `{"annotations":{},"enabled":false,"host":"","tlsSecret":""}` | Specification to configure an Ingress with Mattermost | +| monitoring.enabled | bool | `false` | | +| monitoring.namespace | string | `"monitoring"` | | +| networkPolicies.enabled | bool | `false` | | +| networkPolicies.ingressLabels.app | string | `"istio-ingressgateway"` | | +| networkPolicies.ingressLabels.istio | string | `"ingressgateway"` | | +| networkPolicies.controlPlaneCidr | string | `"0.0.0.0/0"` | | +| sso.enabled | bool | `false` | | +| sso.client_id | string | `"platform1_a8604cc9-f5e9-4656-802d-d05624370245_bb8-mattermost"` | | +| sso.client_secret | string | `"nothing"` | | +| sso.auth_endpoint | string | `"https://login.dso.mil/auth/realms/baby-yoda/protocol/openid-connect/auth"` | | +| sso.token_endpoint | string | `"https://login.dso.mil/auth/realms/baby-yoda/protocol/openid-connect/token"` | | +| sso.user_api_endpoint | string | `"https://login.dso.mil/auth/realms/baby-yoda/protocol/openid-connect/userinfo"` | | +| image.name | string | `"registry1.dso.mil/ironbank/opensource/mattermost/mattermost"` | | +| image.tag | string | `"7.5.1"` | | +| image.imagePullPolicy | string | `"IfNotPresent"` | | +| global.imagePullSecrets[0].name | string | `"private-registry"` | | +| replicaCount | int | `1` | | +| users | string | `nil` | | +| enterprise.enabled | bool | `false` | | +| enterprise.license | string | `""` | | +| nameOverride | string | `""` | | +| updateJob.disabled | bool | `true` | Must be disabled when Istio injected | +| updateJob.labels | object | `{}` | | +| updateJob.annotations | object | `{}` | | +| resources.limits.cpu | int | `2` | | +| resources.limits.memory | string | `"4Gi"` | | +| resources.requests.cpu | int | `2` | | +| resources.requests.memory | string | `"4Gi"` | | +| affinity | object | `{}` | | +| nodeSelector | object | `{}` | | +| tolerations | object | `{}` | | +| mattermostEnvs | object | `{}` | | +| existingSecretEnvs | object | `{}` | | +| volumes | object | `{}` | | +| volumeMounts | object | `{}` | | +| podLabels | object | `{}` | Pod labels for Mattermost server pods | +| podAnnotations | object | `{}` | Pod annotations for Mattermost server pods | +| securityContext | object | `{}` | securityContext for Mattermost server pods | +| containerSecurityContext | object | `{"capabilities":{"drop":["ALL"]}}` | containerSecurityContext for Mattermost server containers | +| minio.install | bool | `false` | | +| minio.bucketCreationImage | string | `"registry1.dso.mil/ironbank/opensource/minio/mc:RELEASE.2022-08-23T05-45-20Z"` | | +| minio.service.nameOverride | string | `"minio.mattermost.svc.cluster.local"` | | +| minio.secrets.name | string | `"mattermost-objstore-creds"` | | +| minio.secrets.accessKey | string | `"minio"` | | +| minio.secrets.secretKey | string | `"minio123"` | | +| minio.tenant.metrics.enabled | bool | `false` | | +| minio.tenant.metrics.port | int | `9000` | | +| minio.containerSecurityContext.capabilities.drop[0] | string | `"ALL"` | | +| postgresql.install | bool | `false` | | +| postgresql.image.registry | string | `"registry1.dso.mil/ironbank"` | | +| postgresql.image.repository | string | `"opensource/postgres/postgresql11"` | | +| postgresql.image.tag | string | `"11.18-1"` | | +| postgresql.image.pullSecrets[0] | string | `"private-registry"` | | +| postgresql.postgresqlUsername | string | `"mattermost"` | | +| postgresql.postgresqlPassword | string | `"bigbang"` | | +| postgresql.postgresqlDatabase | string | `"mattermost"` | | +| postgresql.fullnameOverride | string | `"mattermost-postgresql"` | | +| postgresql.securityContext.fsGroup | int | `26` | | +| postgresql.containerSecurityContext.runAsUser | int | `26` | | +| postgresql.containerSecurityContext.capabilities.drop[0] | string | `"ALL"` | | +| postgresql.volumePermissions.securityContext.capabilities.drop[0] | string | `"ALL"` | | +| postgresql.postgresqlConfiguration.listen_addresses | string | `"*"` | | +| postgresql.pgHbaConfiguration | string | `"local all all md5\nhost all all all md5"` | | +| database.secret | string | `""` | | +| database.readinessCheck.disableDefault | bool | `true` | | +| database.readinessCheck.image | string | `"registry1.dso.mil/ironbank/opensource/postgres/postgresql12:12.13"` | | +| database.readinessCheck.command[0] | string | `"/bin/sh"` | | +| database.readinessCheck.command[1] | string | `"-c"` | | +| database.readinessCheck.command[2] | string | `"until pg_isready --dbname=\"$DB_CONNECTION_CHECK_URL\"; do echo waiting for database; sleep 5; done;"` | | +| database.readinessCheck.env[0].name | string | `"DB_CONNECTION_CHECK_URL"` | | +| database.readinessCheck.env[0].valueFrom.secretKeyRef.key | string | `"DB_CONNECTION_CHECK_URL"` | | +| database.readinessCheck.env[0].valueFrom.secretKeyRef.name | string | `"{{ .Values.database.secret \| default (printf \"%s-dbcreds\" (include \"mattermost.fullname\" .)) }}"` | | +| fileStore.secret | string | `""` | | +| fileStore.url | string | `""` | | +| fileStore.bucket | string | `""` | | +| elasticsearch.enabled | bool | `false` | | +| elasticsearch.connectionurl | string | `"https://logging-ek-es-http.logging.svc.cluster.local:9200"` | | +| elasticsearch.username | string | `""` | | +| elasticsearch.password | string | `""` | | +| elasticsearch.enableindexing | bool | `true` | | +| elasticsearch.indexprefix | string | `"mm-"` | | +| elasticsearch.skiptlsverification | bool | `true` | | +| elasticsearch.bulkindexingtimewindowseconds | int | `3600` | | +| elasticsearch.sniff | bool | `false` | | +| elasticsearch.enablesearching | bool | `true` | | +| elasticsearch.enableautocomplete | bool | `true` | | +| openshift | bool | `false` | | +| resourcePatch | object | `{}` | | +| bbtests.enabled | bool | `false` | | +| bbtests.cypress.artifacts | bool | `true` | | +| bbtests.cypress.envs.cypress_url | string | `"http://mattermost.mattermost.svc.cluster.local:8065"` | | +| bbtests.cypress.envs.cypress_mm_email | string | `"test@bigbang.dev"` | | +| bbtests.cypress.envs.cypress_mm_user | string | `"bigbang"` | | +| bbtests.cypress.envs.cypress_mm_password | string | `"Bigbang#123"` | | + +## Contributing + +Please see the [contributing guide](./CONTRIBUTING.md) if you are interested in contributing. diff --git a/bb_prod/.sops.yaml b/bb_prod/.sops.yaml deleted file mode 100644 index 4a1bc21ce..000000000 --- a/bb_prod/.sops.yaml +++ /dev/null @@ -1,7 +0,0 @@ -creation_rules: - - encrypted_regex: '^(data|stringData)$' - path_regex: .*/prod/.* - kms: 'arn:aws-us-gov:kms:us-gov-west-1:141025336883:key/955de827-66e6-4133-aa6e-e60f7d307ee2' - - encrypted_regex: '^(data|stringData)$' - path_regex: .*/gitlab-runners/.* - kms: 'arn:aws-us-gov:kms:us-gov-west-1:141025336883:key/955de827-66e6-4133-aa6e-e60f7d307ee2' \ No newline at end of file diff --git a/bb_prod/apps/gitlab-runners/README.md b/bb_prod/apps/gitlab-runners/README.md deleted file mode 100644 index e4a96d6ce..000000000 --- a/bb_prod/apps/gitlab-runners/README.md +++ /dev/null @@ -1,12 +0,0 @@ -# Gitlab Runners - -These are deployed out of band of BigBang for several reasons relating to how we test things. - -Since BigBang spends a _lot_ of time spinning up clusters in ci, we needs runners that have special elevated permissions to run `privileged` containers. We limit the blast radius by keeping these runners separate from the generic runners, and require special gitlab permissions to run. - -All in all we currently deploy 2 types of runners: - -* `generic`: general runners assigned to the `Platform One/BigBang` group that handle generic workloads and can run untagged jobs -* `privileged`: specially configured runners assigned to the `Platform One/BigBang` group that strictly handle workloads deploying to cluster's in cluster, only runs tagged jobs - -What makes the privileged runner special is the configuration of `privileged` and `DNSPolicy`. Without the `privileged` tag, and without using `DNSPolicy: Default`, the CI workflows we have for running clusters in cluster will _not_ work. \ No newline at end of file diff --git a/bb_prod/apps/gitlab-runners/bigbang/kustomization.yaml b/bb_prod/apps/gitlab-runners/bigbang/kustomization.yaml deleted file mode 100644 index 3da26464c..000000000 --- a/bb_prod/apps/gitlab-runners/bigbang/kustomization.yaml +++ /dev/null @@ -1,12 +0,0 @@ -bases: - - namespace.yaml - - privileged-runner-hr.yaml - -configMapGenerator: - - name: docker-daemon - namespace: bigbang-runner - files: - - ../../../bigbang/prod/docker-daemon.json - -generatorOptions: - disableNameSuffixHash: true \ No newline at end of file diff --git a/bb_prod/apps/gitlab-runners/bigbang/namespace.yaml b/bb_prod/apps/gitlab-runners/bigbang/namespace.yaml deleted file mode 100644 index 94f4a9afa..000000000 --- a/bb_prod/apps/gitlab-runners/bigbang/namespace.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: bigbang-runner - labels: - istio-injection: enabled - app.kubernetes.io/name: bigbang-runner - app.kubernetes.io/component: "ci" diff --git a/bb_prod/apps/gitlab-runners/bigbang/privileged-runner-hr.yaml b/bb_prod/apps/gitlab-runners/bigbang/privileged-runner-hr.yaml deleted file mode 100644 index c7f734521..000000000 --- a/bb_prod/apps/gitlab-runners/bigbang/privileged-runner-hr.yaml +++ /dev/null @@ -1,116 +0,0 @@ ---- -apiVersion: helm.toolkit.fluxcd.io/v2beta1 -kind: HelmRelease -metadata: - name: bigbang-runner - namespace: bigbang -spec: - targetNamespace: bigbang-runner - chart: - spec: - chart: "./chart" - interval: 5m - sourceRef: - kind: GitRepository - name: gitlab-runner - namespace: bigbang - interval: 5m - test: - enable: false - install: - remediation: - retries: 3 - upgrade: - remediation: - retries: 3 - remediateLastFailure: true - cleanupOnFail: true - rollback: - timeout: 5m - cleanupOnFail: false - dependsOn: - - name: bigbang - namespace: bigbang - - name: istio - namespace: bigbang - - name: kyvernopolicies - namespace: bigbang - - name: monitoring - namespace: bigbang - - name: bbci - namespace: bigbang - values: - probeTimeoutSeconds: 10 - nodeSelector: - node-role.kubernetes.io/master: "true" - imagePullSecrets: - - name: "reg1-creds" - gitlabUrl: https://repo1.dso.mil - concurrent: 20 - checkInterval: 15 - unregisterRunners: true - runners: - tags: "bb-prod-ci, bigbang-runner, privileged" - runUntagged: false - secret: bigbang-group-runner-token - protected: false - - # Don't set limits/requests for now, monitor it first and actually make an informed decision - config: | - [[runners]] - output_limit = 102400 - - [runners.kubernetes] - privileged = true - image = "registry1.dso.mil/ironbank/redhat/ubi/ubi8:8.7" - helper_image = "registry1.dso.mil/ironbank/gitlab/gitlab-runner/gitlab-runner-helper:v15.6.0" - image_pull_secrets = ["bigbang-group-registry-read-credentials", "reg1-creds"] - - cpu_request = "1" - service_cpu_request = "10" - memory_request = "2Gi" - helper_cpu_request = "1" - helper_memory_request = "1Gi" - - poll_timeout = 500 - - [[runners.kubernetes.services]] - name = "registry1.dso.mil/bigbang-ci/docker-amd:20.10.23-dind" - command = ["--default-ulimit","nofile=65535:65535"] - - [[runners.kubernetes.volumes.empty_dir]] - name = "docker-certs" - mount_path = "/certs/client" - medium = "Memory" - - [[runners.kubernetes.volumes.empty_dir]] - name = "cypress" - mount_path = "/cypress" - medium = "Memory" - - [[runners.kubernetes.volumes.host_path]] - name = "machine-id" - host_path = "/etc/machine-id" - mount_path = "/etc/machine-id" - read_only = true - - [runners.kubernetes.pod_labels] - "job_id" = "${CI_JOB_ID}" - "job_name" = "${CI_JOB_NAME}" - "pipeline_id" = "${CI_PIPELINE_ID}" - - [runners.kubernetes.pod_annotations] - "traffic.sidecar.istio.io/kubevirtInterfaces" = "${CI_JOB_ID}" - - [runners.kubernetes.affinity] - [runners.kubernetes.affinity.node_affinity] - [[runners.kubernetes.affinity.node_affinity.preferred_during_scheduling_ignored_during_execution]] - weight = 1 - [runners.kubernetes.affinity.node_affinity.preferred_during_scheduling_ignored_during_execution.preference] - [[runners.kubernetes.affinity.node_affinity.preferred_during_scheduling_ignored_during_execution.preference.match_expressions]] - key = "type" - operator = "In" - values = ["privileged"] - - rbac: - create: true diff --git a/bb_prod/apps/gitlab-runners/gitlab-runners.yaml b/bb_prod/apps/gitlab-runners/gitlab-runners.yaml deleted file mode 100644 index fd0b2f154..000000000 --- a/bb_prod/apps/gitlab-runners/gitlab-runners.yaml +++ /dev/null @@ -1,75 +0,0 @@ ---- -apiVersion: kustomize.toolkit.fluxcd.io/v1beta1 -kind: Kustomization -metadata: - name: bigbang-runner - namespace: bigbang -spec: - interval: 10m - sourceRef: - kind: GitRepository - name: environment-repo - path: ./bb_prod/apps/gitlab-runners/bigbang - prune: true - decryption: - provider: sops ---- -apiVersion: kustomize.toolkit.fluxcd.io/v1beta1 -kind: Kustomization -metadata: - name: graduated-runner - namespace: bigbang -spec: - interval: 10m - sourceRef: - kind: GitRepository - name: environment-repo - path: ./bb_prod/apps/gitlab-runners/graduated - prune: true - decryption: - provider: sops ---- -apiVersion: kustomize.toolkit.fluxcd.io/v1beta1 -kind: Kustomization -metadata: - name: incubating-runner - namespace: bigbang -spec: - interval: 10m - sourceRef: - kind: GitRepository - name: environment-repo - path: ./bb_prod/apps/gitlab-runners/incubating - prune: true - decryption: - provider: sops ---- -apiVersion: kustomize.toolkit.fluxcd.io/v1beta1 -kind: Kustomization -metadata: - name: sandbox-runner - namespace: bigbang -spec: - interval: 10m - sourceRef: - kind: GitRepository - name: environment-repo - path: ./bb_prod/apps/gitlab-runners/sandbox - prune: true - decryption: - provider: sops ---- -apiVersion: kustomize.toolkit.fluxcd.io/v1beta1 -kind: Kustomization -metadata: - name: release-runner - namespace: bigbang -spec: - interval: 10m - sourceRef: - kind: GitRepository - name: environment-repo - path: ./bb_prod/apps/gitlab-runners/release - prune: true - decryption: - provider: sops diff --git a/bb_prod/apps/gitlab-runners/graduated/kustomization.yaml b/bb_prod/apps/gitlab-runners/graduated/kustomization.yaml deleted file mode 100644 index 16e095c46..000000000 --- a/bb_prod/apps/gitlab-runners/graduated/kustomization.yaml +++ /dev/null @@ -1,12 +0,0 @@ -bases: - - namespace.yaml - - privileged-runner-hr.yaml - -configMapGenerator: - - name: docker-daemon - namespace: graduated-runner - files: - - ../../../bigbang/prod/docker-daemon.json - -generatorOptions: - disableNameSuffixHash: true \ No newline at end of file diff --git a/bb_prod/apps/gitlab-runners/graduated/namespace.yaml b/bb_prod/apps/gitlab-runners/graduated/namespace.yaml deleted file mode 100644 index c74b1e3c6..000000000 --- a/bb_prod/apps/gitlab-runners/graduated/namespace.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: graduated-runner - labels: - istio-injection: enabled - app.kubernetes.io/name: graduated-runner - app.kubernetes.io/component: "ci" diff --git a/bb_prod/apps/gitlab-runners/graduated/privileged-runner-hr.yaml b/bb_prod/apps/gitlab-runners/graduated/privileged-runner-hr.yaml deleted file mode 100644 index 52205ceee..000000000 --- a/bb_prod/apps/gitlab-runners/graduated/privileged-runner-hr.yaml +++ /dev/null @@ -1,116 +0,0 @@ ---- -apiVersion: helm.toolkit.fluxcd.io/v2beta1 -kind: HelmRelease -metadata: - name: graduated-runner - namespace: bigbang -spec: - targetNamespace: graduated-runner - chart: - spec: - chart: "./chart" - interval: 5m - sourceRef: - kind: GitRepository - name: gitlab-runner - namespace: bigbang - interval: 5m - test: - enable: false - install: - remediation: - retries: 3 - upgrade: - remediation: - retries: 3 - remediateLastFailure: true - cleanupOnFail: true - rollback: - timeout: 5m - cleanupOnFail: false - dependsOn: - - name: bigbang - namespace: bigbang - - name: istio - namespace: bigbang - - name: kyvernopolicies - namespace: bigbang - - name: monitoring - namespace: bigbang - - name: bbci - namespace: bigbang - values: - probeTimeoutSeconds: 10 - nodeSelector: - node-role.kubernetes.io/master: "true" - imagePullSecrets: - - name: "reg1-creds" - gitlabUrl: https://repo1.dso.mil - concurrent: 20 - checkInterval: 15 - unregisterRunners: true - runners: - tags: "bb-prod-ci, graduated-runner, privileged" - runUntagged: false - secret: bigbang-group-runner-token - protected: false - - # Don't set limits/requests for now, monitor it first and actually make an informed decision - config: | - [[runners]] - output_limit = 102400 - - [runners.kubernetes] - privileged = true - image = "registry1.dso.mil/ironbank/redhat/ubi/ubi8:8.7" - helper_image = "registry1.dso.mil/ironbank/gitlab/gitlab-runner/gitlab-runner-helper:v15.6.0" - image_pull_secrets = ["bigbang-group-registry-read-credentials", "reg1-creds"] - - cpu_request = "1" - service_cpu_request = "5" - memory_request = "2Gi" - helper_cpu_request = "1" - helper_memory_request = "1Gi" - - poll_timeout = 500 - - [[runners.kubernetes.services]] - name = "registry1.dso.mil/bigbang-ci/docker-amd:20.10.23-dind" - command = ["--default-ulimit","nofile=65535:65535"] - - [[runners.kubernetes.volumes.empty_dir]] - name = "docker-certs" - mount_path = "/certs/client" - medium = "Memory" - - [[runners.kubernetes.volumes.empty_dir]] - name = "cypress" - mount_path = "/cypress" - medium = "Memory" - - [[runners.kubernetes.volumes.host_path]] - name = "machine-id" - host_path = "/etc/machine-id" - mount_path = "/etc/machine-id" - read_only = true - - [runners.kubernetes.pod_labels] - "job_id" = "${CI_JOB_ID}" - "job_name" = "${CI_JOB_NAME}" - "pipeline_id" = "${CI_PIPELINE_ID}" - - [runners.kubernetes.pod_annotations] - "traffic.sidecar.istio.io/kubevirtInterfaces" = "${CI_JOB_ID}" - - [runners.kubernetes.affinity] - [runners.kubernetes.affinity.node_affinity] - [[runners.kubernetes.affinity.node_affinity.preferred_during_scheduling_ignored_during_execution]] - weight = 1 - [runners.kubernetes.affinity.node_affinity.preferred_during_scheduling_ignored_during_execution.preference] - [[runners.kubernetes.affinity.node_affinity.preferred_during_scheduling_ignored_during_execution.preference.match_expressions]] - key = "type" - operator = "In" - values = ["privileged"] - - rbac: - create: true diff --git a/bb_prod/apps/gitlab-runners/incubating/kustomization.yaml b/bb_prod/apps/gitlab-runners/incubating/kustomization.yaml deleted file mode 100644 index 558c6c86e..000000000 --- a/bb_prod/apps/gitlab-runners/incubating/kustomization.yaml +++ /dev/null @@ -1,12 +0,0 @@ -bases: - - namespace.yaml - - privileged-runner-hr.yaml - -configMapGenerator: - - name: docker-daemon - namespace: incubating-runner - files: - - ../../../bigbang/prod/docker-daemon.json - -generatorOptions: - disableNameSuffixHash: true \ No newline at end of file diff --git a/bb_prod/apps/gitlab-runners/incubating/namespace.yaml b/bb_prod/apps/gitlab-runners/incubating/namespace.yaml deleted file mode 100644 index 266846cfe..000000000 --- a/bb_prod/apps/gitlab-runners/incubating/namespace.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: incubating-runner - labels: - istio-injection: enabled - app.kubernetes.io/name: incubating-runner - app.kubernetes.io/component: "ci" diff --git a/bb_prod/apps/gitlab-runners/incubating/privileged-runner-hr.yaml b/bb_prod/apps/gitlab-runners/incubating/privileged-runner-hr.yaml deleted file mode 100644 index 30d4405be..000000000 --- a/bb_prod/apps/gitlab-runners/incubating/privileged-runner-hr.yaml +++ /dev/null @@ -1,116 +0,0 @@ ---- -apiVersion: helm.toolkit.fluxcd.io/v2beta1 -kind: HelmRelease -metadata: - name: incubating-runner - namespace: bigbang -spec: - targetNamespace: incubating-runner - chart: - spec: - chart: "./chart" - interval: 5m - sourceRef: - kind: GitRepository - name: gitlab-runner - namespace: bigbang - interval: 5m - test: - enable: false - install: - remediation: - retries: 3 - upgrade: - remediation: - retries: 3 - remediateLastFailure: true - cleanupOnFail: true - rollback: - timeout: 5m - cleanupOnFail: false - dependsOn: - - name: bigbang - namespace: bigbang - - name: istio - namespace: bigbang - - name: kyvernopolicies - namespace: bigbang - - name: monitoring - namespace: bigbang - - name: bbci - namespace: bigbang - values: - probeTimeoutSeconds: 10 - nodeSelector: - node-role.kubernetes.io/master: "true" - imagePullSecrets: - - name: "reg1-creds" - gitlabUrl: https://repo1.dso.mil - concurrent: 20 - checkInterval: 15 - unregisterRunners: true - runners: - tags: "bb-prod-ci, incubating-runner, privileged" - runUntagged: false - secret: bigbang-group-runner-token - protected: false - - # Don't set limits/requests for now, monitor it first and actually make an informed decision - config: | - [[runners]] - output_limit = 102400 - - [runners.kubernetes] - privileged = true - image = "registry1.dso.mil/ironbank/redhat/ubi/ubi8:8.7" - helper_image = "registry1.dso.mil/ironbank/gitlab/gitlab-runner/gitlab-runner-helper:v15.6.0" - image_pull_secrets = ["bigbang-group-registry-read-credentials", "reg1-creds"] - - cpu_request = "1" - service_cpu_request = "5" - memory_request = "2Gi" - helper_cpu_request = "1" - helper_memory_request = "1Gi" - - poll_timeout = 500 - - [[runners.kubernetes.services]] - name = "registry1.dso.mil/bigbang-ci/docker-amd:20.10.23-dind" - command = ["--default-ulimit","nofile=65535:65535"] - - [[runners.kubernetes.volumes.empty_dir]] - name = "docker-certs" - mount_path = "/certs/client" - medium = "Memory" - - [[runners.kubernetes.volumes.empty_dir]] - name = "cypress" - mount_path = "/cypress" - medium = "Memory" - - [[runners.kubernetes.volumes.host_path]] - name = "machine-id" - host_path = "/etc/machine-id" - mount_path = "/etc/machine-id" - read_only = true - - [runners.kubernetes.pod_labels] - "job_id" = "${CI_JOB_ID}" - "job_name" = "${CI_JOB_NAME}" - "pipeline_id" = "${CI_PIPELINE_ID}" - - [runners.kubernetes.pod_annotations] - "traffic.sidecar.istio.io/kubevirtInterfaces" = "${CI_JOB_ID}" - - [runners.kubernetes.affinity] - [runners.kubernetes.affinity.node_affinity] - [[runners.kubernetes.affinity.node_affinity.preferred_during_scheduling_ignored_during_execution]] - weight = 1 - [runners.kubernetes.affinity.node_affinity.preferred_during_scheduling_ignored_during_execution.preference] - [[runners.kubernetes.affinity.node_affinity.preferred_during_scheduling_ignored_during_execution.preference.match_expressions]] - key = "type" - operator = "In" - values = ["privileged"] - - rbac: - create: true diff --git a/bb_prod/apps/gitlab-runners/release/generic-runner-hr.yaml b/bb_prod/apps/gitlab-runners/release/generic-runner-hr.yaml deleted file mode 100644 index 8ab229643..000000000 --- a/bb_prod/apps/gitlab-runners/release/generic-runner-hr.yaml +++ /dev/null @@ -1,96 +0,0 @@ ---- -apiVersion: helm.toolkit.fluxcd.io/v2beta1 -kind: HelmRelease -metadata: - name: release-runner - namespace: bigbang -spec: - targetNamespace: release-runner - chart: - spec: - chart: "./chart" - interval: 5m - sourceRef: - kind: GitRepository - name: gitlab-runner - namespace: bigbang - interval: 5m - test: - enable: false - install: - remediation: - retries: 3 - upgrade: - remediation: - retries: 3 - remediateLastFailure: true - cleanupOnFail: true - rollback: - timeout: 5m - cleanupOnFail: false - dependsOn: - - name: bigbang - namespace: bigbang - - name: istio - namespace: bigbang - - name: kyvernopolicies - namespace: bigbang - - name: monitoring - namespace: bigbang - - name: bbci - namespace: bigbang - values: - probeTimeoutSeconds: 10 - nodeSelector: - node-role.kubernetes.io/master: "true" - imagePullSecrets: - - name: "reg1-creds" - gitlabUrl: https://repo1.dso.mil - concurrent: 20 - checkInterval: 15 - unregisterRunners: true - runners: - tags: "bb-prod-ci, release-runner, release" - runUntagged: false - secret: bigbang-group-runner-token - protected: true - config: | - [[runners]] - output_limit = 102400 - cache_dir = "/tmp/gitlab-runner/cache" - - [runners.kubernetes] - privileged = false - image = "registry1.dso.mil/ironbank/redhat/ubi/ubi8:8.7" - helper_image = "registry1.dso.mil/ironbank/gitlab/gitlab-runner/gitlab-runner-helper:v15.6.0" - image_pull_secrets = ["bigbang-group-registry-read-credentials", "reg1-creds"] - - cpu_request = "1" - cpu_limit = "4" - memory_request = "2Gi" - memory_limit = "8Gi" - service_cpu_request = "2" - service_cpu_limit = "4" - service_memory_request = "2Gi" - service_memory_limit = "8Gi" - helper_cpu_request = ".5" - helper_cpu_limit = "1" - helper_memory_request = "500Mi" - helper_memory_limit = "2Gi" - - [runners.kubernetes.pod_labels] - "job_id" = "${CI_JOB_ID}" - "job_name" = "${CI_JOB_NAME}" - "pipeline_id" = "${CI_PIPELINE_ID}" - - [runners.kubernetes.affinity] - [runners.kubernetes.affinity.node_affinity] - [[runners.kubernetes.affinity.node_affinity.preferred_during_scheduling_ignored_during_execution]] - weight = 1 - [runners.kubernetes.affinity.node_affinity.preferred_during_scheduling_ignored_during_execution.preference] - [[runners.kubernetes.affinity.node_affinity.preferred_during_scheduling_ignored_during_execution.preference.match_expressions]] - key = "type" - operator = "In" - values = ["non-privileged"] - rbac: - create: true diff --git a/bb_prod/apps/gitlab-runners/release/kustomization.yaml b/bb_prod/apps/gitlab-runners/release/kustomization.yaml deleted file mode 100644 index 2326e73a1..000000000 --- a/bb_prod/apps/gitlab-runners/release/kustomization.yaml +++ /dev/null @@ -1,12 +0,0 @@ -bases: - - namespace.yaml - - generic-runner-hr.yaml - -configMapGenerator: - - name: docker-daemon - namespace: release-runner - files: - - ../../../bigbang/prod/docker-daemon.json - -generatorOptions: - disableNameSuffixHash: true \ No newline at end of file diff --git a/bb_prod/apps/gitlab-runners/release/namespace.yaml b/bb_prod/apps/gitlab-runners/release/namespace.yaml deleted file mode 100644 index b381030eb..000000000 --- a/bb_prod/apps/gitlab-runners/release/namespace.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: release-runner - labels: - istio-injection: enabled - app.kubernetes.io/name: release-runner - app.kubernetes.io/component: "ci" diff --git a/bb_prod/apps/gitlab-runners/sandbox/kustomization.yaml b/bb_prod/apps/gitlab-runners/sandbox/kustomization.yaml deleted file mode 100644 index eb7448705..000000000 --- a/bb_prod/apps/gitlab-runners/sandbox/kustomization.yaml +++ /dev/null @@ -1,12 +0,0 @@ -bases: - - namespace.yaml - - privileged-runner-hr.yaml - -configMapGenerator: - - name: docker-daemon - namespace: sandbox-runner - files: - - ../../../bigbang/prod/docker-daemon.json - -generatorOptions: - disableNameSuffixHash: true \ No newline at end of file diff --git a/bb_prod/apps/gitlab-runners/sandbox/namespace.yaml b/bb_prod/apps/gitlab-runners/sandbox/namespace.yaml deleted file mode 100644 index 73d165d92..000000000 --- a/bb_prod/apps/gitlab-runners/sandbox/namespace.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: sandbox-runner - labels: - istio-injection: enabled - app.kubernetes.io/name: sandbox-runner - app.kubernetes.io/component: "ci" diff --git a/bb_prod/apps/gitlab-runners/sandbox/privileged-runner-hr.yaml b/bb_prod/apps/gitlab-runners/sandbox/privileged-runner-hr.yaml deleted file mode 100644 index a48c4ae66..000000000 --- a/bb_prod/apps/gitlab-runners/sandbox/privileged-runner-hr.yaml +++ /dev/null @@ -1,116 +0,0 @@ ---- -apiVersion: helm.toolkit.fluxcd.io/v2beta1 -kind: HelmRelease -metadata: - name: sandbox-runner - namespace: bigbang -spec: - targetNamespace: sandbox-runner - chart: - spec: - chart: "./chart" - interval: 5m - sourceRef: - kind: GitRepository - name: gitlab-runner - namespace: bigbang - interval: 5m - test: - enable: false - install: - remediation: - retries: 3 - upgrade: - remediation: - retries: 3 - remediateLastFailure: true - cleanupOnFail: true - rollback: - timeout: 5m - cleanupOnFail: false - dependsOn: - - name: bigbang - namespace: bigbang - - name: istio - namespace: bigbang - - name: kyvernopolicies - namespace: bigbang - - name: monitoring - namespace: bigbang - - name: bbci - namespace: bigbang - values: - probeTimeoutSeconds: 10 - nodeSelector: - node-role.kubernetes.io/master: "true" - imagePullSecrets: - - name: "reg1-creds" - gitlabUrl: https://repo1.dso.mil - concurrent: 50 - checkInterval: 15 - unregisterRunners: true - runners: - tags: "bb-prod-ci, sandbox-runner, privileged" - runUntagged: false - secret: bigbang-group-runner-token - protected: false - - # Don't set limits/requests for now, monitor it first and actually make an informed decision - config: | - [[runners]] - output_limit = 102400 - - [runners.kubernetes] - privileged = true - image = "registry1.dso.mil/ironbank/redhat/ubi/ubi8:8.7" - helper_image = "registry1.dso.mil/ironbank/gitlab/gitlab-runner/gitlab-runner-helper:v15.6.0" - image_pull_secrets = ["bigbang-group-registry-read-credentials", "reg1-creds"] - - cpu_request = "1" - service_cpu_request = "5" - memory_request = "2Gi" - helper_cpu_request = "1" - helper_memory_request = "1Gi" - - poll_timeout = 300 - - [[runners.kubernetes.services]] - name = "registry1.dso.mil/bigbang-ci/docker-amd:20.10.23-dind" - command = ["--default-ulimit","nofile=65535:65535"] - - [[runners.kubernetes.volumes.empty_dir]] - name = "docker-certs" - mount_path = "/certs/client" - medium = "Memory" - - [[runners.kubernetes.volumes.empty_dir]] - name = "cypress" - mount_path = "/cypress" - medium = "Memory" - - [[runners.kubernetes.volumes.host_path]] - name = "machine-id" - host_path = "/etc/machine-id" - mount_path = "/etc/machine-id" - read_only = true - - [runners.kubernetes.pod_labels] - "job_id" = "${CI_JOB_ID}" - "job_name" = "${CI_JOB_NAME}" - "pipeline_id" = "${CI_PIPELINE_ID}" - - [runners.kubernetes.pod_annotations] - "traffic.sidecar.istio.io/kubevirtInterfaces" = "${CI_JOB_ID}" - - [runners.kubernetes.affinity] - [runners.kubernetes.affinity.node_affinity] - [[runners.kubernetes.affinity.node_affinity.preferred_during_scheduling_ignored_during_execution]] - weight = 1 - [runners.kubernetes.affinity.node_affinity.preferred_during_scheduling_ignored_during_execution.preference] - [[runners.kubernetes.affinity.node_affinity.preferred_during_scheduling_ignored_during_execution.preference.match_expressions]] - key = "type" - operator = "In" - values = ["privileged"] - - rbac: - create: true diff --git a/bb_prod/bigbang/base/kustomization.yaml b/bb_prod/bigbang/base/kustomization.yaml deleted file mode 100644 index 55631ecf7..000000000 --- a/bb_prod/bigbang/base/kustomization.yaml +++ /dev/null @@ -1,11 +0,0 @@ -# When updating the version of BigBang, make sure to update -# both the bases reference and the GitRepository reference -namespace: bigbang -resources: - - git::https://repo1.dso.mil/platform-one/big-bang/bigbang.git//base?ref=1.51.0 - -configMapGenerator: - - name: common - behavior: merge - files: - - values.yaml=values.yaml diff --git a/bb_prod/bigbang/base/values.yaml b/bb_prod/bigbang/base/values.yaml deleted file mode 100644 index 667232faf..000000000 --- a/bb_prod/bigbang/base/values.yaml +++ /dev/null @@ -1,67 +0,0 @@ -networkPolicies: - enabled: true -istio: - enabled: true -istiooperator: - enabled: true -jaeger: - enabled: false -kiali: - enabled: true -clusterAuditor: - enabled: false -gatekeeper: - enabled: false -kyverno: - enabled: true -kyvernopolicies: - enabled: true -kyvernoreporter: - enabled: true -logging: - enabled: false -eckoperator: - enabled: false -fluentbit: - enabled: true -promtail: - enabled: false -loki: - enabled: true -tempo: - enabled: true -monitoring: - enabled: true -twistlock: - enabled: true -addons: - argocd: - enabled: false - authservice: - enabled: true - minioOperator: - enabled: false - minio: - enabled: false - gitlab: - enabled: false - gitlabRunner: - enabled: true - nexus: - enabled: false - sonarqube: - enabled: false - anchore: - enabled: false - mattermostoperator: - enabled: false - mattermost: - enabled: false - velero: - enabled: false - keycloak: - enabled: false - vault: - enabled: false - metricsServer: - enabled: auto diff --git a/bb_prod/bigbang/ci-hr/Chart.yaml b/bb_prod/bigbang/ci-hr/Chart.yaml deleted file mode 100644 index 8a06af2d2..000000000 --- a/bb_prod/bigbang/ci-hr/Chart.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: v2 -name: bbci -description: A deployment of resources for bigbang ci environment, anything that depends on BB namespaces -type: application -version: 1.0.1 -appVersion: "1.0.1" diff --git a/bb_prod/bigbang/ci-hr/templates/alertmanager-egress.yaml b/bb_prod/bigbang/ci-hr/templates/alertmanager-egress.yaml deleted file mode 100644 index 45ea761e9..000000000 --- a/bb_prod/bigbang/ci-hr/templates/alertmanager-egress.yaml +++ /dev/null @@ -1,25 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - annotations: - meta.helm.sh/release-name: monitoring-monitoring - meta.helm.sh/release-namespace: monitoring - labels: - app.kubernetes.io/managed-by: Helm - helm.toolkit.fluxcd.io/name: monitoring - helm.toolkit.fluxcd.io/namespace: bigbang - name: allow-egress-alertmanager-notifications - namespace: monitoring -spec: - egress: - - ports: - - port: 443 - protocol: TCP - to: - - ipBlock: - cidr: {{ .Values.notifications.ip }} - podSelector: - matchLabels: - app.kubernetes.io/name: alertmanager - policyTypes: - - Egress diff --git a/bb_prod/bigbang/ci-hr/templates/kyverno-secret-copy.yaml b/bb_prod/bigbang/ci-hr/templates/kyverno-secret-copy.yaml deleted file mode 100644 index 3c1e5752d..000000000 --- a/bb_prod/bigbang/ci-hr/templates/kyverno-secret-copy.yaml +++ /dev/null @@ -1,86 +0,0 @@ -apiVersion: kyverno.io/v1 -kind: ClusterPolicy -metadata: - name: reg1-credential-sync - annotations: - policies.kyverno.io/title: Sync Secrets - policies.kyverno.io/subject: Secret - policies.kyverno.io/description: >- - This policy will copy a Secret called `reg1-creds` which exists in the `gitlab` Namespace to new Namespaces with the component label ci when they are created. It will also push updates to the copied Secrets should the source Secret be changed. -spec: - generateExistingOnPolicyUpdate: true - rules: - - name: sync-secret - match: - resources: - kinds: - - Namespace - selector: - matchLabels: - app.kubernetes.io/component: "ci" - generate: - kind: Secret - name: reg1-creds - namespace: {{`"{{request.object.metadata.name}}"`}} - synchronize: true - clone: - namespace: gitlab - name: reg1-creds ---- -apiVersion: kyverno.io/v1 -kind: ClusterPolicy -metadata: - name: bigbang-registry-credential-sync - annotations: - policies.kyverno.io/title: Sync Secrets - policies.kyverno.io/subject: Secret - policies.kyverno.io/description: >- - This policy will copy a Secret called `bigbang-group-registry-read-credentials` which exists in the `gitlab` Namespace to new Namespaces with the component label ci when they are created. It will also push updates to the copied Secrets should the source Secret be changed. -spec: - generateExistingOnPolicyUpdate: true - rules: - - name: sync-secret - match: - resources: - kinds: - - Namespace - selector: - matchLabels: - app.kubernetes.io/component: "ci" - generate: - kind: Secret - name: bigbang-group-registry-read-credentials - namespace: {{`"{{request.object.metadata.name}}"`}} - synchronize: true - clone: - namespace: gitlab - name: bigbang-group-registry-read-credentials ---- -apiVersion: kyverno.io/v1 -kind: ClusterPolicy -metadata: - name: bigbang-runner-token-sync - annotations: - policies.kyverno.io/title: Sync Secrets - policies.kyverno.io/subject: Secret - policies.kyverno.io/description: >- - This policy will copy a Secret called `bigbang-group-runner-token` which exists in the `gitlab` Namespace to new Namespaces with the component label ci when they are created. It will also push updates to the copied Secrets should the source Secret be changed. -spec: - generateExistingOnPolicyUpdate: true - rules: - - name: sync-secret - match: - resources: - kinds: - - Namespace - selector: - matchLabels: - app.kubernetes.io/component: "ci" - generate: - kind: Secret - name: bigbang-group-runner-token - namespace: {{`"{{request.object.metadata.name}}"`}} - synchronize: true - clone: - namespace: gitlab - name: bigbang-group-runner-token diff --git a/bb_prod/bigbang/ci-hr/templates/reg-creds.yaml b/bb_prod/bigbang/ci-hr/templates/reg-creds.yaml deleted file mode 100644 index 700c76271..000000000 --- a/bb_prod/bigbang/ci-hr/templates/reg-creds.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -kind: Secret -metadata: - name: bigbang-group-registry-read-credentials - namespace: gitlab -type: kubernetes.io/dockerconfigjson -data: - .dockerconfigjson: {{ .Values.gitlab.reg.contents}} diff --git a/bb_prod/bigbang/ci-hr/templates/reg1-creds.yaml b/bb_prod/bigbang/ci-hr/templates/reg1-creds.yaml deleted file mode 100644 index c688b533f..000000000 --- a/bb_prod/bigbang/ci-hr/templates/reg1-creds.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -kind: Secret -metadata: - name: reg1-creds - namespace: gitlab -type: kubernetes.io/dockerconfigjson -data: - .dockerconfigjson: {{ .Values.gitlab.reg1.contents}} diff --git a/bb_prod/bigbang/ci-hr/templates/runner-token.yaml b/bb_prod/bigbang/ci-hr/templates/runner-token.yaml deleted file mode 100644 index 10644321b..000000000 --- a/bb_prod/bigbang/ci-hr/templates/runner-token.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: v1 -kind: Secret -metadata: - name: bigbang-group-runner-token - namespace: gitlab -type: Opaque -data: - runner-registration-token: {{ .Values.gitlab.token.contents}} - runner-token: "" diff --git a/bb_prod/bigbang/ci-hr/values.yaml b/bb_prod/bigbang/ci-hr/values.yaml deleted file mode 100644 index 0c2f6e025..000000000 --- a/bb_prod/bigbang/ci-hr/values.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# Add values here if needed -gitlab: - reg1: - contents: | - reg1 docker creds - reg: - contents: | - reg docker creds - token: - contents: | - Secret Token -notifications: - ip: 15.200.226.88/32 diff --git a/bb_prod/bigbang/prod.yaml b/bb_prod/bigbang/prod.yaml deleted file mode 100644 index 3d75d1fe8..000000000 --- a/bb_prod/bigbang/prod.yaml +++ /dev/null @@ -1,32 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: bigbang ---- -apiVersion: source.toolkit.fluxcd.io/v1beta2 -kind: GitRepository -metadata: - name: environment-repo - namespace: bigbang -spec: - interval: 10m - url: https://repo1.dso.mil/platform-one/big-bang/customers/bigbang-ci.git - ref: - branch: master - secretRef: - name: repository-credentials ---- -apiVersion: kustomize.toolkit.fluxcd.io/v1beta1 -kind: Kustomization -metadata: - name: environment - namespace: bigbang -spec: - interval: 10m - sourceRef: - kind: GitRepository - name: environment-repo - path: ./bb_prod/bigbang/prod - prune: true - decryption: - provider: sops diff --git a/bb_prod/bigbang/prod/bbci.enc.yaml b/bb_prod/bigbang/prod/bbci.enc.yaml deleted file mode 100644 index f27e2ceee..000000000 --- a/bb_prod/bigbang/prod/bbci.enc.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: v1 -kind: Secret -metadata: - name: bbci - namespace: bigbang -stringData: - values.yaml: ENC[AES256_GCM,data:dxrocnTRNvKQ5Oxq1r0uLBnmbzUKmTGEj/0xVGwJo9GVRkyVsw2wvOdJ0KBzKADk7wyj6HcqCSjsq81zKacuuMdgOjJhsCJz6GVWBeSpD5Jg3LW2FO1sBeEOOSWj98Inf1ZX+TysC5vOpS5ljWnf6LNVjVtTCY+Ifins7LslRWaaROqBzfXJF6VL0cIQSemlL1LFn5mosNkK8H3Y2HKmGdlpHkE8AawY8hiLj1wUUZFPAkykZ91qGYzbIyGVAiv3zzvvpGUP1IWutpxEix59erFiQkaHF3YCSaH6R8btrFG9nHpMbzx9kASFzNJeUypiU6j4moHY/hoJqgtl3jNtYeF0CCHGbYnudclt84hl9K8NnXw9NUE73NEswIJbLULsDRyHp3E/sdudFzb8rRcbSKv0kFN0T5qZ9P/l5aBt4gESyzeHQEmf193lzv9eMuWsNEfq4us7gaGMNrk/QsvrC7czHHOnaBJh+1foyUo6ed9YhWIIpaW/kQ35TcxuwNpom9YSC9ww0qGiz5fWLUdWP6Fcu11cIqURYZ8r9CYZ9ien3tofFCondxbdNHON2VhrGWrldhhMsZPE1lzj6menASSm9nmLS03VrAr3R8wkm0pSS975oWgK+QGTnkcPO6hg4nEvHHJY2TIvfDUf0SWioUMSJBzjW7bQrN3oAGRRKMzRcPLFF82WAbsNA4aHmJsjUNf1eVkQDesPF2d+eL5SoaC/1REjoGPdricU8qB8IeckoOB89sHoOBGozWqDhNNcRcBFPxVNL694cEytCHjPU+HgtxjU9uN2DMZNIiWPkvqrNOSFyvXT1HuddDsYzb1Me17irkCIsSnlvN+A73d2fnzoX5v/26xqdibKoKIS1NE50mt63fQfHj2YS/CvVtKDucl2/EAbjvxq+JiMmyR4SXq9c9e6jguOBhVy+JAbnQQQ7T+PVqVrFDp6ieH9J/YpYJRWzT4XHhQ=,iv:cDw6cRVEibrVKLaYvj2eCV+PpTG0iX/p+O3R0fTckng=,tag:wQc23vBEej2R0ab4S9IuiA==,type:str] -sops: - kms: - - arn: arn:aws-us-gov:kms:us-gov-west-1:141025336883:key/955de827-66e6-4133-aa6e-e60f7d307ee2 - created_at: "2023-01-12T18:33:54Z" - enc: AQICAHjR4kw0AUxX6hoCyMBpPAhVJbqN+noP3nXNZJn2tJTsMQHqtEiAFD9z7GxXzwJvQhPnAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMNKeclgQXFEHyp5jtAgEQgDuA4GRHykRNl0ypM2iC13eEd+lfJEr/ub7VTrFCVM7o3gfEWnK6VxEhPgLGHsaKcwa5DShbEEzPjC7lHw== - aws_profile: "" - gcp_kms: [] - azure_kv: [] - hc_vault: [] - age: [] - lastmodified: "2023-01-12T18:33:54Z" - mac: ENC[AES256_GCM,data:Ipqvqja+HPamtfCvYc8e2F0dfpnsbEFVAP88+qlEIShQKDmzs//ejIe0A/p/CbPNmtd/54f3VktQmtjFwUn5zaaAC3e7y0q/aNsGNh/CSWJgxG69dGUssjx7M+aA9ohi8tvHP6+cfZQtu3Q7TohSdc599YEUhEJGbDkJPXS+9/g=,iv:yD7vwakXokCQvh8s4RmYzLuolLAoa8EPZiqJQ0bbkUg=,tag:yRQkHysSK9S95avsYR1FFQ==,type:str] - pgp: [] - encrypted_regex: ^(data|stringData)$ - version: 3.7.1 diff --git a/bb_prod/bigbang/prod/certs-bbci-secret.enc.yaml b/bb_prod/bigbang/prod/certs-bbci-secret.enc.yaml deleted file mode 100644 index 696a373b0..000000000 --- a/bb_prod/bigbang/prod/certs-bbci-secret.enc.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: v1 -kind: Secret -metadata: - name: certs-bbci - namespace: bigbang -stringData: - values.yaml: ENC[AES256_GCM,data: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,iv:pq8P6QAAsr1Nju3ygcKStKGQQaZ4kf6KN67Rz7YvBWU=,tag:KK93F7XvZEIdHxtRZhLJSw==,type:str] -sops: - kms: - - arn: arn:aws-us-gov:kms:us-gov-west-1:141025336883:key/955de827-66e6-4133-aa6e-e60f7d307ee2 - created_at: "2023-01-27T16:05:27Z" - enc: AQICAHjR4kw0AUxX6hoCyMBpPAhVJbqN+noP3nXNZJn2tJTsMQExEHgn8fI9JIHt+vytcSFHAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMmVXEf4xv5GTWNNzyAgEQgDsI6wuzLkMJh35fhLv0KvSjC3dfkfmRYVZMu35CzVlrfHzRMEDr4InhlyBpsf5Wyjza2wLCQHwMcFzzZg== - aws_profile: "" - gcp_kms: [] - azure_kv: [] - hc_vault: [] - age: [] - lastmodified: "2023-01-27T16:05:27Z" - mac: ENC[AES256_GCM,data:FlD2+4xBnhpG5BwKL/dn7A77EjfYwpv63CICS9QU+O7R2NzPUmaaXpqu+dXc0ny3VWpXDolLUs59yHUtC8tZvhToW0oUdwBH6xUyZuwR1ajkX4Ki7K+wwTqlUT1IOapyDySB59Z6C3+fzOhkY9o4uklL1wInb++W0yzsNbca1Ks=,iv:hxl6ioajs1S5T0NpEIutRW965BsDPEkG59Ed91o6xoM=,tag:7gexeyzdgSUygYAl25plLQ==,type:str] - pgp: [] - encrypted_regex: ^(data|stringData)$ - version: 3.7.3 diff --git a/bb_prod/bigbang/prod/ci-hr.yaml b/bb_prod/bigbang/prod/ci-hr.yaml deleted file mode 100644 index 101ae23d4..000000000 --- a/bb_prod/bigbang/prod/ci-hr.yaml +++ /dev/null @@ -1,35 +0,0 @@ -apiVersion: helm.toolkit.fluxcd.io/v2beta1 -kind: HelmRelease -metadata: - name: bbci - namespace: bigbang -spec: - targetNamespace: bigbang - releaseName: bbci - interval: 10m - chart: - spec: - chart: ./bb_prod/bigbang/ci-hr - sourceRef: - kind: GitRepository - name: environment-repo - install: - remediation: - retries: -1 - upgrade: - remediation: - retries: 5 - remediateLastFailure: true - cleanupOnFail: true - rollback: - timeout: 10m - cleanupOnFail: false - # Ensure namespaces exist before reconciling - dependsOn: - - name: bigbang - namespace: bigbang - - name: kyverno - namespace: bigbang - valuesFrom: - - kind: Secret - name: bbci diff --git a/bb_prod/bigbang/prod/docker-daemon.json b/bb_prod/bigbang/prod/docker-daemon.json deleted file mode 100644 index 9e26dfeeb..000000000 --- a/bb_prod/bigbang/prod/docker-daemon.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/bb_prod/bigbang/prod/environment-bbci-secret.enc.yaml b/bb_prod/bigbang/prod/environment-bbci-secret.enc.yaml deleted file mode 100644 index 7ce80dad5..000000000 --- a/bb_prod/bigbang/prod/environment-bbci-secret.enc.yaml +++ /dev/null @@ -1,22 +0,0 @@ -apiVersion: v1 -kind: Secret -metadata: - name: environment-bbci - namespace: bigbang -stringData: - values.yaml: ENC[AES256_GCM,data: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,iv:vdPa4v5sqqHGeuqdgihzFppepZIG7mywLQTAR2Q33wg=,tag:8lgUieV6EU/Hc4ewNS3fRw==,type:str] -sops: - kms: - - arn: arn:aws-us-gov:kms:us-gov-west-1:141025336883:key/955de827-66e6-4133-aa6e-e60f7d307ee2 - created_at: "2022-10-25T18:51:20Z" - enc: AQICAHjR4kw0AUxX6hoCyMBpPAhVJbqN+noP3nXNZJn2tJTsMQEGZ/mHP0TO6I6+vXskhbCUAAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMn9Fo8teh8FzidwFjAgEQgDus6GhGx420Ph/bapI1uqazsmPpJYSGK4EtF7jwP3VakwNxh8Qn4GWU4gVcD3Lqcx8NFbhw08ojkc3HUg== - aws_profile: "" - gcp_kms: [] - azure_kv: [] - hc_vault: [] - age: [] - lastmodified: "2022-10-25T18:51:20Z" - mac: ENC[AES256_GCM,data:qU7N2Xg13V/rKYAHtspuWReHee+VMmmsWGxspfTiahnch3NX/08z1L1bNG8iKlhRZMpzbJNiu5U6nwjpDlRQSO3iIDmkx0+rDNFlBDZGG+GtcGeJjjDwWjkYjwYMd2K8onbsmMMQMNKugVhE4j4OCbX8/uzuaAa5Xq+6JKc6EIM=,iv:r/cfGFU6fgCP5PlCcc4/AaOcbvipshLV8ZGdS3b2jtw=,tag:uhua3euAZfDhpYaeznM7XA==,type:str] - pgp: [] - encrypted_regex: ^(data|stringData)$ - version: 3.7.1 diff --git a/bb_prod/bigbang/prod/kustomization.yaml b/bb_prod/bigbang/prod/kustomization.yaml deleted file mode 100644 index d0d0833b8..000000000 --- a/bb_prod/bigbang/prod/kustomization.yaml +++ /dev/null @@ -1,58 +0,0 @@ -resources: - - ../base - - ci-hr.yaml - -configMapGenerator: - - name: environment - behavior: merge - files: - - values.yaml=values.yaml - - name: docker-daemon - namespace: gitlab - files: - - docker-daemon.json - -generatorOptions: - disableNameSuffixHash: true - -secretGenerator: - - name: certs-bbci - namespace: bigbang - behavior: create - - name: environment-bbci - namespace: bigbang - behavior: create - - name: bbci - namespace: bigbang - behavior: create - -patchesStrategicMerge: - - certs-bbci-secret.enc.yaml - - environment-bbci-secret.enc.yaml - - bbci.enc.yaml - - |- - apiVersion: source.toolkit.fluxcd.io/v1beta2 - kind: GitRepository - metadata: - name: bigbang - spec: - ref: - $patch: replace - tag: "1.51.0" - - |- - apiVersion: helm.toolkit.fluxcd.io/v2beta1 - kind: HelmRelease - metadata: - name: bigbang - spec: - valuesFrom: - - kind: Secret - name: common-bb - - kind: ConfigMap - name: common - - kind: Secret - name: environment-bbci - - kind: ConfigMap - name: environment - - kind: Secret - name: certs-bbci \ No newline at end of file diff --git a/bb_prod/bigbang/prod/secrets/nothingtoseehere.jpg.png.pdf.enc.exe.txt b/bb_prod/bigbang/prod/secrets/nothingtoseehere.jpg.png.pdf.enc.exe.txt deleted file mode 100644 index 441e04327..000000000 --- a/bb_prod/bigbang/prod/secrets/nothingtoseehere.jpg.png.pdf.enc.exe.txt +++ /dev/null @@ -1,22 +0,0 @@ -{ - "data": "ENC[AES256_GCM,data:D6kooCImj7YYaiuPH4wIWlQoYuTzlPI7hYY2AFvBIKE/RzVkRG641WWcHtE5mg6K72uQVEzF9g07X9euimYfLN0IFdM2WhwyqVcT7OdeB3PYROuFrVJQ0OQsg1LGR/qlcGfeEmc7VD+PAqucG+/BM28pzhrA6XUZ7TVAqcjLDkZH/UuIFOwQe+R7xtI8Acb0bdfeZ7se+x0IT4oHA/61WasJksEHpNifSMSbctyyUMMJIr42T5r77JlbLF+i3V2PPrWWkg/4/FjfoO+GVkO1Maas/+8iQnWFTk0XmUOreftlHah7nrTZQwe4DxqDeGomVj+7hL9psuN4YAY+mIrsYAXfsvarNXScpa9pgDa/j4UGxUc9XeHq3Neekaak4PcneQKKql+PSATtktgelhCjycvUyHLuzsJ2/gyCpio7Z75UlzDD8g809qKm2oa2v81wtYzoVgDRqJb7SEdwntmQD+wViQChXwBojZ/CwmLHYo2mRvV8GXyDVmfLD7jSY+bH4sDPWoz/EgBMGi3otXgVnBdVNbG/6RnxKTChoZqaf2Iiq5maONECoSa0yB/cOb+JSDYEnqWL9ykYbUPdObkjRMiyU8qukmQA1+HYj2Kc55Gw7K2ZSNNTjWxSiVc=,iv:Rqy6TDhYMqgCG6caNHmGZQHZ2FoZF/xmYaJVHVRl5x0=,tag:jK6FQJhBr1ya0bPXG3LwHw==,type:str]", - "sops": { - "kms": [ - { - "arn": "arn:aws-us-gov:kms:us-gov-west-1:141025336883:key/955de827-66e6-4133-aa6e-e60f7d307ee2", - "created_at": "2022-09-13T21:07:02Z", - "enc": "AQICAHjR4kw0AUxX6hoCyMBpPAhVJbqN+noP3nXNZJn2tJTsMQFrPG57N/0gnqPGUdG9uDi5AAAAfjB8BgkqhkiG9w0BBwagbzBtAgEAMGgGCSqGSIb3DQEHATAeBglghkgBZQMEAS4wEQQMrnBzi6bMPCQopmnWAgEQgDtXBxg4t5tzFTaabxXnyIgBn5NOqLr7ckE5C+gGoMgopfTvsBaLU24yW5rVFS4cI7jlbfonCbMKAeB7/g==", - "aws_profile": "" - } - ], - "gcp_kms": null, - "azure_kv": null, - "hc_vault": null, - "age": null, - "lastmodified": "2022-09-13T21:07:02Z", - "mac": "ENC[AES256_GCM,data:wlM7FsknMjOk2/qQ0FWhT7JvIRRYiFeojtF1y5jBKUjoNUdFQBn2ds6tJTqdLVm5iYdf3FdaaXMJiDfZV19LigbgCN3fGqOJFH/8woW++I//E3VyAZJDOXDxE+aVY/sWBRSUTGcB+1xqA4sT48GyLMcoFaSZfv1lZdHwb5lI7H8=,iv:g+b7Uod+rQLLU62igEjqVIcT4EsuLsAEtfT98seUkHo=,tag:/CpZh4IdoYmWhFzS9qVbkg==,type:str]", - "pgp": null, - "encrypted_regex": "^(data|stringData)$", - "version": "3.7.1" - } -} \ No newline at end of file diff --git a/bb_prod/bigbang/prod/values.yaml b/bb_prod/bigbang/prod/values.yaml deleted file mode 100644 index 7f6224212..000000000 --- a/bb_prod/bigbang/prod/values.yaml +++ /dev/null @@ -1,401 +0,0 @@ -domain: ci.bigbang.dev - -# Authservice can't trust keycloak's cert so we include that here -sso: - certificate_authority: '-----BEGIN CERTIFICATE-----\nMIIH0zCCBrugAwIBAgIQHeg1retyhPnWuzryBJeBvTANBgkqhkiG9w0BAQsFADCB\nujELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsT\nH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAy\nMDEyIEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEuMCwG\nA1UEAxMlRW50cnVzdCBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eSAtIEwxSzAeFw0y\nMDEyMTUwMzE1MDJaFw0yMjAxMTQwMzE1MDJaMHMxCzAJBgNVBAYTAlVTMREwDwYD\nVQQIEwhDb2xvcmFkbzEZMBcGA1UEBxMQQ29sb3JhZG8gU3ByaW5nczEeMBwGA1UE\nChMVRGVwYXJ0bWVudCBvZiBEZWZlbnNlMRYwFAYDVQQDEw1sb2dpbi5kc28ubWls\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAymUXk7STDlepS5HJu0ca\nB57S5dfLp7zxYmcsGjo10YkHy3m9LASQCTyiioDrlwo2b+n8oZ7esGLv3RgggMwf\nxvLVyx1+lZDswxdQoXmjArTdbqpcSoq3Y1rvVp33/jGb3slBjQtcMt2QvaFv3fxy\ncwwINvJFEqsQS7zGUgpolJ3smKdcVpUSGZmzpYposuDlPUGeOJaQRMAACW5arWiT\nVkDhJD+OVOYEHW8uCQfghD3JJXu6Xp9SwlWe6UNOdxo9cq3s/XE4ZwEgffdLXP2A\nwuJF/7B7CFdZjIMptmOODyCeatC344iyubU0MiGCOm4W4wn0pQ0XJtAzWeYFKATL\n9BquNOzPUR6pMSFMvIEiS96zbVFuOYt2XKgPryWEYji3Oky082WWYOcXt0NnqnCj\nSafVU+2fQi4jQ0att5YXagEEPz83lQZdSKb2+grDeFg78VrEZAe+Y0mVu4/G93he\nUOqfZ9jdCnFXq8sEMG9bJJFKeOXkb1Da8Y0amfOw4hFd4UslrbvC5ZCUZNh6roOk\n8kast9QWtWFIGPC3f+Uq3gvx3GBHzIG9QPOq1CjSSAF3tWKuMTxK4zaS33mriJo0\nDv1CMX3FCmjT/qG3422guBL02hbGHveDSWk0/saY7ZWFifxnvKEdOi4ItnpMuQhE\nzx6/+t7FWuzBTPAeVqV1l2sCAwEAAaOCAxkwggMVMAwGA1UdEwEB/wQCMAAwHQYD\nVR0OBBYEFCLwpnkje7QKLWok+nWIeBEnIGfmMB8GA1UdIwQYMBaAFIKicHTdvFM/\nz3vU981/p2DGCky/MGgGCCsGAQUFBwEBBFwwWjAjBggrBgEFBQcwAYYXaHR0cDov\nL29jc3AuZW50cnVzdC5uZXQwMwYIKwYBBQUHMAKGJ2h0dHA6Ly9haWEuZW50cnVz\ndC5uZXQvbDFrLWNoYWluMjU2LmNlcjAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8v\nY3JsLmVudHJ1c3QubmV0L2xldmVsMWsuY3JsMCcGA1UdEQQgMB6CDWxvZ2luLmRz\nby5taWyCDWxvZ2luLmRzb3AuaW8wDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQG\nCCsGAQUFBwMBBggrBgEFBQcDAjBMBgNVHSAERTBDMDcGCmCGSAGG+mwKAQUwKTAn\nBggrBgEFBQcCARYbaHR0cHM6Ly93d3cuZW50cnVzdC5uZXQvcnBhMAgGBmeBDAEC\nAjCCAX4GCisGAQQB1nkCBAIEggFuBIIBagFoAHUAVhQGmi/XwuzT9eG9RLI+x0Z2\nubyZEVzA75SYVdaJ0N0AAAF2ZGTpIwAABAMARjBEAiAK+W9ukx92DJPFV87LexEg\n/qDFTjtkiLh/z+mLmDtOwQIgUD4YrMuo22sV9MeJ8JmzraCQVdUUIprw4K4HN+eO\n6W0AdwDfpV6raIJPH2yt7rhfTj5a6s2iEqRqXo47EsAgRFwqcwAAAXZkZOlKAAAE\nAwBIMEYCIQDRpvbR/GroWSGlCIh1q0RUITb8RfI4skqqBa/FeU811AIhAPlRY4lv\nDC2u9MFSEiCVeaFYJRU0xvAwmHQMtrl+IE4iAHYARqVV63X6kSAwtaKJafTzfREs\nQXS+/Um4havy/HD+bUcAAAF2ZGTrYAAABAMARzBFAiEAifP8Y0nXFBykaTyzpWpv\nE3FDi8NCQeJFRMJqD7loTjMCIHVDio7r+zANTbIdRLRRzHoNzo//xfJ0JUqejNRA\naCpZMA0GCSqGSIb3DQEBCwUAA4IBAQB/wtYjDQiPLe99tZq98IyxOSJCli2mtlV9\ngSC67aj4rgW6g+C8P1bSoB5PamMq6rON5q0SXL3CQiQ7vegxCQnleDh0LWeKPFS2\njjSIl3CvrYfBlNBzw4H1uAa/yw+enr0So8oX8kdSTBFGnU4KoK646lFZRXSifFIU\nzzQ9QYYedmiP0iKs5LDYGAOsB/w/O94+zv6qGKXA1fVzBXAD54MddqGk9mHZTSyL\n6nsSTx4r8vCGQir7d2QuIGLD48zaYQz0TFcGKnBV3/9CB27RxJkRdMwUbMvNdp3C\nV+C2+jdR8xA/0qCnvSxHc1lTZgXxVkcu/wpqIBn3af5Ha8ddd0DU\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIFDjCCA/agAwIBAgIMDulMwwAAAABR03eFMA0GCSqGSIb3DQEBCwUAMIG+MQsw\nCQYDVQQGEwJVUzEWMBQGA1UEChMNRW50cnVzdCwgSW5jLjEoMCYGA1UECxMfU2Vl\nIHd3dy5lbnRydXN0Lm5ldC9sZWdhbC10ZXJtczE5MDcGA1UECxMwKGMpIDIwMDkg\nRW50cnVzdCwgSW5jLiAtIGZvciBhdXRob3JpemVkIHVzZSBvbmx5MTIwMAYDVQQD\nEylFbnRydXN0IFJvb3QgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkgLSBHMjAeFw0x\nNTEwMDUxOTEzNTZaFw0zMDEyMDUxOTQzNTZaMIG6MQswCQYDVQQGEwJVUzEWMBQG\nA1UEChMNRW50cnVzdCwgSW5jLjEoMCYGA1UECxMfU2VlIHd3dy5lbnRydXN0Lm5l\ndC9sZWdhbC10ZXJtczE5MDcGA1UECxMwKGMpIDIwMTIgRW50cnVzdCwgSW5jLiAt\nIGZvciBhdXRob3JpemVkIHVzZSBvbmx5MS4wLAYDVQQDEyVFbnRydXN0IENlcnRp\nZmljYXRpb24gQXV0aG9yaXR5IC0gTDFLMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A\nMIIBCgKCAQEA2j+W0E25L0Tn2zlem1DuXKVh2kFnUwmqAJqOV38pa9vH4SEkqjrQ\njUcj0u1yFvCRIdJdt7hLqIOPt5EyaM/OJZMssn2XyP7BtBe6CZ4DkJN7fEmDImiK\nm95HwzGYei59QAvS7z7Tsoyqj0ip/wDoKVgG97aTWpRzJiatWA7lQrjV6nN5ZGhT\nJbiEz5R6rgZFDKNrTdDGvuoYpDbwkrK6HIiPOlJ/915tgxyd8B/lw9bdpXiSPbBt\nLOrJz5RBGXFEaLpHPATpXbo+8DX3Fbae8i4VHj9HyMg4p3NFXU2wO7GOFyk36t0F\nASK7lDYqjVs1/lMZLwhGwSqzGmIdTivZGwIDAQABo4IBDDCCAQgwDgYDVR0PAQH/\nBAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwMwYIKwYBBQUHAQEEJzAlMCMGCCsG\nAQUFBzABhhdodHRwOi8vb2NzcC5lbnRydXN0Lm5ldDAwBgNVHR8EKTAnMCWgI6Ah\nhh9odHRwOi8vY3JsLmVudHJ1c3QubmV0L2cyY2EuY3JsMDsGA1UdIAQ0MDIwMAYE\nVR0gADAoMCYGCCsGAQUFBwIBFhpodHRwOi8vd3d3LmVudHJ1c3QubmV0L3JwYTAd\nBgNVHQ4EFgQUgqJwdN28Uz/Pe9T3zX+nYMYKTL8wHwYDVR0jBBgwFoAUanImetAe\n733nO2lR1GyNn5ASZqswDQYJKoZIhvcNAQELBQADggEBADnVjpiDYcgsY9NwHRkw\ny/YJrMxp1cncN0HyMg/vdMNY9ngnCTQIlZIv19+4o/0OgemknNM/TWgrFTEKFcxS\nBJPok1DD2bHi4Wi3Ogl08TRYCj93mEC45mj/XeTIRsXsgdfJghhcg85x2Ly/rJkC\nk9uUmITSnKa1/ly78EqvIazCP0kkZ9Yujs+szGQVGHLlbHfTUqi53Y2sAEo1GdRv\nc6N172tkw+CNgxKhiucOhk3YtCAbvmqljEtoZuMrx1gL+1YQ1JH7HdMxWBCMRON1\nexCdtTix9qrKgWRs6PLigVWXUX/hwidQosk8WwBD9lu51aX8/wdQQGcHsFXwt35u\nLcw=\n-----END CERTIFICATE-----\n-----BEGIN CERTIFICATE-----\nMIIEPjCCAyagAwIBAgIESlOMKDANBgkqhkiG9w0BAQsFADCBvjELMAkGA1UEBhMC\nVVMxFjAUBgNVBAoTDUVudHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50\ncnVzdC5uZXQvbGVnYWwtdGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3Qs\nIEluYy4gLSBmb3IgYXV0aG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVz\ndCBSb290IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IC0gRzIwHhcNMDkwNzA3MTcy\nNTU0WhcNMzAxMjA3MTc1NTU0WjCBvjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUVu\ndHJ1c3QsIEluYy4xKDAmBgNVBAsTH1NlZSB3d3cuZW50cnVzdC5uZXQvbGVnYWwt\ndGVybXMxOTA3BgNVBAsTMChjKSAyMDA5IEVudHJ1c3QsIEluYy4gLSBmb3IgYXV0\naG9yaXplZCB1c2Ugb25seTEyMDAGA1UEAxMpRW50cnVzdCBSb290IENlcnRpZmlj\nYXRpb24gQXV0aG9yaXR5IC0gRzIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK\nAoIBAQC6hLZy254Ma+KZ6TABp3bqMriVQRrJ2mFOWHLP/vaCeb9zYQYKpSfYs1/T\nRU4cctZOMvJyig/3gxnQaoCAAEUesMfnmr8SVycco2gvCoe9amsOXmXzHHfV1IWN\ncCG0szLni6LVhjkCsbjSR87kyUnEO6fe+1R9V77w6G7CebI6C1XiUJgWMhNcL3hW\nwcKUs/Ja5CeanyTXxuzQmyWC48zCxEXFjJd6BmsqEZ+pCm5IO2/b1BEZQvePB7/1\nU1+cPvQXLOZprE4yTGJ36rfo5bs0vBmLrpxR57d+tVOxMyLlbc9wPBr64ptntoP0\njaWvYkxN4FisZDQSA/i2jZRjJKRxAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAP\nBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRqciZ60B7vfec7aVHUbI2fkBJmqzAN\nBgkqhkiG9w0BAQsFAAOCAQEAeZ8dlsa2eT8ijYfThwMEYGprmi5ZiXMRrEPR9RP/\njTkrwPK9T3CMqS/qF8QLVJ7UG5aYMzyorWKiAHarWWluBh1+xLlEjZivEtRh2woZ\nRkfz6/djwUAFQKXSt/S1mja/qYh2iARVBCuch38aNzx+LaUa2NSJXsq9rD1s2G2v\n1fN2D807iDginWyTmsQ9v4IbZT+mD12q/OWyFcq1rca8PdCE6OoGcrBNOTJ4vz4R\nnAuknZoh8/CbCzB428Hch0P+vGOaysXCHMnHjf87ElgI5rY97HosTvuDls4MPGmH\nVHOkc8KT/1EQrBVUAdj8BbGJoX90g5pJ19xOe4pIb4tF9g==\n-----END CERTIFICATE-----' -tls: - cert: {} - key: {} - -networkPolicies: - controlPlaneCidr: 192.168.0.0/22 - -istiooperator: - values: - operator: - resources: - limits: - cpu: 200m - memory: 2Gi - requests: - cpu: 200m - memory: 2Gi - -istio: - ingressGateways: - public-ingressgateway: - type: "LoadBalancer" - kubernetesResourceSpec: - serviceAnnotations: - # Ensure mission apps have internal load balancer only - service.beta.kubernetes.io/aws-load-balancer-internal: "true" - # Enable cross zone load balancing - service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true" - # Set to Network ELB for AWS - service.beta.kubernetes.io/aws-load-balancer-type: nlb - nodePortBase: 30000 - passthrough-ingressgateway: - type: "LoadBalancer" - kubernetesResourceSpec: - serviceAnnotations: - # Ensure mission apps have internal load balancer only - service.beta.kubernetes.io/aws-load-balancer-internal: "true" - # Enable cross zone load balancing - service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true" - # Set to Network ELB for AWS - service.beta.kubernetes.io/aws-load-balancer-type: nlb - nodePortBase: 30100 - gateways: - public: - selector: - app: "public-ingressgateway" - istio: "ingressgateway" - passthrough: - ingressGateway: "passthrough-ingressgateway" - hosts: - - "vault.ci.bigbang.dev" - tls: - mode: "PASSTHROUGH" - -kiali: - sso: - enabled: true - client_id: platform1_a8604cc9-f5e9-4656-802d-d05624370245_bb8-kiali - values: - cr: - spec: - external_services: - grafana: - url: "https://grafana.ci.bigbang.dev" - tracing: - url: "https://tracing.ci.bigbang.dev" - deployment: - resources: - requests: - cpu: 200m - memory: 512Mi - limits: - cpu: 200m - memory: 512Mi - -kyverno: - values: - resources: - limits: - memory: 1Gi - requests: - memory: 1Gi - -kyvernopolicies: - values: - policies: - disallow-privileged-containers: - exclude: - any: - - resources: - namespaces: - - bigbang-runner - - graduated-runner - - incubating-runner - - sandbox-runner - names: - - runner-* - restrict-host-path-mount: - exclude: - any: - - resources: - namespaces: - - bigbang-runner - - graduated-runner - - incubating-runner - - sandbox-runner - names: - - runner-* - restrict-volume-types: - exclude: - any: - - resources: - namespaces: - - bigbang-runner - - graduated-runner - - incubating-runner - - sandbox-runner - names: - - runner-* - -fluentbit: - values: - securityContext: - runAsUser: 0 - readOnlyRootFilesystem: true - privileged: false - seLinuxOptions: - type: spc_t - storage: - total_limit_size: "2G" - -loki: - strategy: scalable - objectStorage: - endpoint: "s3.us-gov-west-1.amazonaws.com" - region: "us-gov-west-1" - bucketNames: - chunks: "bigbang-ci-cluster-loki" - ruler: "bigbang-ci-cluster-loki" - values: - loki: - read: - replicas: 5 - persistence: - size: 50Gi - write: - replicas: 3 - persistence: - size: 50Gi - resources: - limits: - cpu: 300m - memory: 3Gi - requests: - cpu: 300m - memory: 3Gi - -tempo: - sso: - enabled: true - client_id: platform1_a8604cc9-f5e9-4656-802d-d05624370245_bb8-tempo - values: - fullnameOverride: "tracing-tempo" - istio: - tempoQuery: - hosts: - - tempo.{{ .Values.domain }} - -monitoring: - sso: - enabled: true - prometheus: - client_id: platform1_a8604cc9-f5e9-4656-802d-d05624370245_bb8-prometheus - alertmanager: - client_id: platform1_a8604cc9-f5e9-4656-802d-d05624370245_bb8-alertmanager - grafana: - client_id: platform1_a8604cc9-f5e9-4656-802d-d05624370245_bb8-grafana - scopes: "Grafana" - values: - prometheusOperator: - tls: - enabled: false - admissionWebhooks: - enabled: false - patch: - enabled: false - kube-state-metrics: - resources: - limits: - cpu: 100m - memory: 512Mi - requests: - cpu: 100m - memory: 512Mi - prometheus: - prometheusSpec: - resources: - limits: - cpu: 800m - memory: 10Gi - requests: - cpu: 800m - memory: 10Gi - alertmanager: - config: - global: - resolve_timeout: 5m - route: - group_by: ['alertname'] - group_wait: 30s - group_interval: 5m - repeat_interval: 120h - receiver: 'bb-notifications' - routes: - - match: - alertname: Watchdog - receiver: 'null' - - match: - severity: 'info' - receiver: 'null' - - match: - severity: 'none' - receiver: 'null' - - match: - alertname: InfoInhibitor - receiver: 'null' - - match: - severity: 'critical' - receiver: 'bb-notifications' - receivers: - - name: 'null' - - name: 'bb-notifications' - slack_configs: - - api_url: https://chat.il2.dso.mil/hooks/7b4qsct6x3dyjjqegy4swz6zay - channel: 'bb-notifications' - username: 'bb-8' - templates: - - '/etc/alertmanager/config/*.tmpl' - -twistlock: - sso: - enabled: true - client_id: "platform1_a8604cc9-f5e9-4656-802d-d05624370245_bb8-twistlock" - cert: |- - -----BEGIN CERTIFICATE----- - MIICoTCCAYkCBgFyLIEqUjANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAliYWJ5LXlvZGEwHhcNMjAwNTE5MTAzNDIyWhcNMzAwNTE5MTAzNjAyWjAUMRIwEAYDVQQDDAliYWJ5LXlvZGEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCGIwvWSPD6zDbkGBpkjUDJ+BwJEE8ko8T7DC22prs03Gm2v/lEIEa4leoPKjenW+NxtvwqN0qdcjuRZ6vxvY/P9E/Wb9Bw+so7FKW6TsZkXwBGxzoU8ZvNiCLyjxwFVKxqaXodnOk3dmcfgMVnbuJ8z5SX8/IzFnZrB6iEhqLNen6uAXtGqlq/k1dTCZxLIfws/3Y1Ui4WUPcdhMMaixVt8D+78fblnhcIYpb+8sNM2uXw9wDceoigP681q/Kx3ECr8o3DuIstzQouyMVhJ0kv/ngftC5uwZHQDIissu6sluoC2+20YkrfyTldhYojBga27qKInCNJvtS2idV0+HxXAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAIVkoDYkM6ryBcuchdAL5OmyKbmmY4WDrMlatfa3uniK5jvFXrmVaJ3rcu0apdY/NhBeLSOLFVlC5w1QroGUhWm0EjAA4zyuU63Pk0sro0vyHrxztBrGPQrGXI3kjXEssaehZZvYP4b9VtYpus6oGP6bTmaDw94Zu+WrDsWdFs+27VEYwBuU0D6E+ENDGlfR+9ADEW53t6H2M3H0VsOtbArEutYgb4gmQcOIBygC7L1tGJ4IqbnhTYLh9DMKNklU+tq8TMHacps9FxELpeAib3O0J0E5zYXdraQobCCe+ao1Y7sA/wqcGQBCVuoFgty7Y37nNL7LMvygcafgqVDqw5U= - -----END CERTIFICATE----- - values: - resources: - limits: - memory: 2Gi - cpu: 250m - requests: - memory: 2Gi - cpu: 250m - -addons: - gitlabRunner: - values: - probeTimeoutSeconds: 10 - nodeSelector: - node-role.kubernetes.io/master: "true" - imagePullSecrets: - - name: "reg1-creds" - gitlabUrl: https://repo1.dso.mil - concurrent: 20 - checkInterval: 15 - unregisterRunners: true - runners: - tags: "bb-prod-ci, generic" - runUntagged: true - secret: bigbang-group-runner-token - protected: false - config: | - [[runners]] - output_limit = 102400 - cache_dir = "/tmp/gitlab-runner/cache" - - [runners.kubernetes] - privileged = false - image = "registry1.dso.mil/ironbank/redhat/ubi/ubi8:8.7" - helper_image = "registry1.dso.mil/ironbank/gitlab/gitlab-runner/gitlab-runner-helper:v15.4.0" - image_pull_secrets = ["bigbang-group-registry-read-credentials", "reg1-creds"] - - cpu_request = "1" - cpu_limit = "4" - memory_request = "2Gi" - memory_limit = "8Gi" - service_cpu_request = "2" - service_cpu_limit = "4" - service_memory_request = "2Gi" - service_memory_limit = "8Gi" - helper_cpu_request = ".5" - helper_cpu_limit = "1" - helper_memory_request = "500Mi" - helper_memory_limit = "2Gi" - - [runners.kubernetes.pod_labels] - "job_id" = "${CI_JOB_ID}" - "job_name" = "${CI_JOB_NAME}" - "pipeline_id" = "${CI_PIPELINE_ID}" - - [runners.kubernetes.affinity] - [runners.kubernetes.affinity.node_affinity] - [[runners.kubernetes.affinity.node_affinity.preferred_during_scheduling_ignored_during_execution]] - weight = 1 - [runners.kubernetes.affinity.node_affinity.preferred_during_scheduling_ignored_during_execution.preference] - [[runners.kubernetes.affinity.node_affinity.preferred_during_scheduling_ignored_during_execution.preference.match_expressions]] - key = "type" - operator = "In" - values = ["non-privileged"] - - rbac: - create: true - vault: - ingress: - gateway: "passthrough" - values: - # disable autoInit. It should not be used for operations. - autoInit: - enabled: false - global: - # this is a double negative. Put "false" to enable TLS for passthrough ingress - tlsDisable: false - injector: - certs: - secretName: vault-tls - server: - # disable the Vault provided ingress so that Istio ingress can be used. - ingress: - enabled: false - # Extra environment variable to support high availability - extraEnvironmentVars: - # the istio gateway domain - VAULT_API_ADDR: https://vault.ci.bigbang.dev - VAULT_SKIP_VERIFY: "true" - VAULT_LOG_FORMAT: "json" - ha: - # enable high availability. - enabled: true - replicas: 3 - # raft is the license free most simple solution for a distributed filesystem - raft: - enabled: true - setNodeId: true - config: | - ui = true - - listener "tcp" { - tls_disable = 0 - address = "[::]:8200" - cluster_address = "[::]:8201" - tls_cert_file = "/vault/tls/tls.crt" - tls_key_file = "/vault/tls/tls.key" - telemetry { - unauthenticated_metrics_access = true - } - } - - storage "raft" { - path = "/vault/data" - - retry_join { - leader_api_addr = "https://vault-vault-0.vault-vault-internal:8200" - leader_client_cert_file = "/vault/tls/tls.crt" - leader_client_key_file = "/vault/tls/tls.key" - leader_tls_servername = "vault.ci.bigbang.dev" - } - - retry_join { - leader_api_addr = "https://vault-vault-1.vault-vault-internal:8200" - leader_client_cert_file = "/vault/tls/tls.crt" - leader_client_key_file = "/vault/tls/tls.key" - leader_tls_servername = "vault.ci.bigbang.dev" - } - - retry_join { - leader_api_addr = "https://vault-vault-2.vault-vault-internal:8200" - leader_client_cert_file = "/vault/tls/tls.crt" - leader_client_key_file = "/vault/tls/tls.key" - leader_tls_servername = "vault.ci.bigbang.dev" - } - } - - seal "awskms" { - region = "us-gov-west-1" - kms_key_id = "955de827-66e6-4133-aa6e-e60f7d307ee2" - endpoint = "https://kms.us-gov-west-1.amazonaws.com" - } - - telemetry { - prometheus_retention_time = "24h" - disable_hostname = true - } - - service_registration "kubernetes" {} diff --git a/bb_staging/.sops.yaml b/bb_staging/.sops.yaml deleted file mode 100644 index 97cc81f05..000000000 --- a/bb_staging/.sops.yaml +++ /dev/null @@ -1,4 +0,0 @@ -creation_rules: - - encrypted_regex: '^(data|stringData)$' - path_regex: .*/prod/.* - kms: 'arn:aws-us-gov:kms:us-gov-west-1::key/staging-key' diff --git a/chart/Chart.lock b/chart/Chart.lock new file mode 100644 index 000000000..9105a8a6c --- /dev/null +++ b/chart/Chart.lock @@ -0,0 +1,12 @@ +dependencies: +- name: postgresql + repository: file://./deps/postgresql + version: 10.3.5 +- name: minio-instance + repository: oci://registry1.dso.mil/bigbang + version: 4.5.4-bb.2 +- name: gluon + repository: oci://registry1.dso.mil/bigbang + version: 0.3.2 +digest: sha256:4826e20ab1e1cdbe95e2ccbddd969329c7e8646f276a95d2dbf1850882652de1 +generated: "2023-01-17T12:00:42.140338-07:00" diff --git a/chart/Chart.yaml b/chart/Chart.yaml new file mode 100644 index 000000000..98aa72cbf --- /dev/null +++ b/chart/Chart.yaml @@ -0,0 +1,42 @@ +--- +apiVersion: v2 +name: mattermost +type: application +version: 7.5.0-bb.4 +appVersion: 7.5.0 +description: "Deployment of mattermost" +keywords: + - Mattermost + - Instance +kubeVersion: ">=1.12.0-0" +dependencies: + - name: postgresql + version: 10.3.5 + alias: postgresql + condition: postgresql.install + repository: file://./deps/postgresql + - name: minio-instance + version: 4.5.4-bb.2 + alias: minio + condition: minio.install + repository: oci://registry1.dso.mil/bigbang + - name: gluon + version: 0.3.2 + repository: oci://registry1.dso.mil/bigbang +annotations: + bigbang.dev/applicationVersions: | + - Mattermost: 7.5.0 + helm.sh/images: | + - name: mattermost + image: registry1.dso.mil/ironbank/opensource/mattermost/mattermost:7.5.0 + - name: minio + condition: minio.install + image: registry1.dso.mil/ironbank/opensource/minio/minio:RELEASE.2022-11-17T23-20-09Z + - name: mc + condition: minio.install + image: registry1.dso.mil/ironbank/opensource/minio/mc:RELEASE.2022-08-23T05-45-20Z + - name: postgresql11 + condition: postgresql.install + image: registry1.dso.mil/ironbank/opensource/postgres/postgresql11:11.18-1 + - name: postgresql12 + image: registry1.dso.mil/ironbank/opensource/postgres/postgresql12:12.13 diff --git a/chart/charts/gluon-0.3.2.tgz b/chart/charts/gluon-0.3.2.tgz new file mode 100644 index 0000000000000000000000000000000000000000..dbc09aef2326a0bb33e9df8340a09e3db4c4b486 GIT binary patch literal 3152 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 literal 0 HcmV?d00001 diff --git a/chart/charts/minio-instance-4.5.4-bb.2.tgz b/chart/charts/minio-instance-4.5.4-bb.2.tgz new file mode 100644 index 0000000000000000000000000000000000000000..26eeec62dc5a2252ac5161f421a8af2e2c1c41f8 GIT binary patch literal 20002 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 literal 0 HcmV?d00001 diff --git a/chart/charts/postgresql-10.3.5.tgz b/chart/charts/postgresql-10.3.5.tgz new file mode 100644 index 0000000000000000000000000000000000000000..7f724ea8f422a69026caf14d122613d5dd462688 GIT binary patch literal 40072 zcmV(_K-9k<iwG0|00000|0w_~VMtOiV@ORlOnEsqVl!4SWK%V1T2nbTPgYhoO;>Dc zVQyr3R8em|NM&qo0PMYMciT9!C_10@D{z$bj-4?n*-7kf-JN}pW4qH{?T5DIr1zYj zJ~l)`62=t420%NS#P_$~g%=4vMai<0%!q5IW063iP$(1%g+f(C7#~x_{tP+?6G*u` zgVW$It9`oNZg=<fYx!@t+s*&G{rc6;U$$Sr+TDJ$*WG@#^Ox@St5<uwe*xVU<5Bv= zj6?dD?t|OPckVlRU>Fh(IVK_NwE*xSM`J=~J#Yv)972X#Fv1@ZWtfCLaJ$ojQI!1L z-gdXQ-EPZA5ej`2dKj@@%K;&rq8{+bbV|Y&02D=pVJ^W=IFDGb)A0neF?Wc02&dTf z$h4#XZUJzcG~j-6Kec?sJc=VOUr8<nBSJx6vF_l%PqqLI0U7=SdEB8Wklld+@O2l! z6ozn&rYPiq%@{}1762RP7l-do2b<sy^NFaBrqKj4%m4`i`ovMl@DK->&j1X4@se>E z`jGm}@yIj^FbqB9wkAaM)pTo&`6M3N-O)YmaEef83K>T<`IJt+7WPEv3>cd%kTJwr zhjHlLFsn)jPsi3zM;35)cDkQ-UcGjsaNN2<vpYh4IV4t7>5rC^(5U4A^^Kw^z@8*w z%K=^x3yfP+h(iu>h^T~yrVs~Wgw4zUZDHI3ngQPF0Zq#zpJMNS`-ll}Q%E@u-4RVH zWInXiI}33}V(KXltD(34*YSyWgQ%0~xT$t`cju-bTtdg4R?2i;_m%sm^|fc_(>w+F z9}w?m#cj-y|J_%w-W25j?whsze~RZ3MfYp5GT<>{ya(6>?!4LU!O@Sf`{PfqJ-9pS z{`3=ivxkNw^pl5n-oQ6q|0lHf=Jk&dLHOp?aQ7#_`(_(?-uCW~+y1V<w>KQN#wbJ- za^&|wd#Agz?R0mXot?|=-QLb_@AaO${pP1`clWQ|?OwOr{x_Mz4|=Tme+z>cu}4q< z>*xQQy}j-2{QTd2y|<qKPw_l^2Hpo`2m^2^*Gqt>aEw3%DV!pXC~G}?2KoVl3~hms za0K`Sa=<5;fjb-ofZQTVu`k}ELgg)hLobMZ9FBo4X|{ywFv8=QA|G%9Vy5bk6)KVw zMZ*U%r9x|iw-5(#7$hYsknO2A7if$br%H2^Z~Ac%43I|=7aeLTC_>>)`M(D~-?yFt zkOk2L)0z4E3{Fv6!UEZoZvk+u!oQ-K`kK^YoKQGM2LWVEfE`%BTcWpGU$PWi-9ar> zMHz}lsU#Mvk5Y4_6fRRNI~<N#NAy{O>PeYyx~A)5l9Ti$`Y8Y$juVaF@zD^&(An-f z^rsd8&z^xnggiW&0hm&!PXg@C)aOGqf^oo^80{DP!5y5j7XXiR2RR%A%)pBPa>V$H zEr0@s07G0BKOUVC-lvG6kiSsi5;F8Uott=wXoxssE+!qHc&rmMM4fSrebkW+u+FoH zA}5N20Ec5oy_1jx3nZxafZdd8={!=qiC_kp>0$^VrEmtwsK5%YriS_jrZMASD0d*S zgyB&nmM?%q$?#&iWf2q`iEO`EdB-?pH>{IP9w#EclW5@`(G%4<C!+Wka^#r7?wH;_ zll@H?SG=fWT+<yn5}d|J4BCvu;Eu$B4+4CH#21E;*fdcHa5y4#D)$d!(-e@gA~ZPW zfX6gs;NAY<7dbP}_D_$2kB0GhECd7&!4!wsM?;Gib(+2|?Z}2%=a~TSkkA=IHgOWT zrsMKO4~$^IkeqG<PDQhv2m!*e9G*m)DGkV#<KvFvAP|26j;4{|QLrIppV<}3p`=Jk zNjapOV!}O>f1YcIy<D3DX5;o0IP*FKQb0WpF}F5jwm=l0Z%GhOQ6JGMW=!t%#V$G= zE165~LPP~Ciyx7YG>q#iU`pbUM+Ap*_%GscpM7A6_D1YIC2=I0F9L4?fI=axeM4<6 zF~yfE@l!5<Qhw0#XQ;NL;`bbxvvR4kO1Yz1GAnx&ex{`*<j(Sv=kaAv(&K{snJ0mi z@W^nu=wwVWpMe7sa`Z`y>NCPoPj=3mkh{?Ev=2E1BNF&RVPL=~p%z&Rh6dqM!`%v) z5KtlE62cG#<gPf4+&LhAo1hRR??l_0x8#5tJqNU5%t>3QN|`=N2_uvkPJlPLBXZaj zV7o`008!r;(|w9V<O^kr{>NC%&n*x$A#Q|lfuYZO;2QhDiLZO|-}m4P@L~=ezYW^J z8SPvv@CqS;FVdI>GXZrJ2h#9%9-(kB!6OdtCfJ*R31onIp0cF*Gcj{Li3Ro<*bs#5 z3SfeoFbK#U@;CKz_cs!QDV%{js3eTi12J2K)^82Eq3GujhidH+$f!ml;ui*63#hkJ zKFP6Wlj%p<sOwFp#Lt#6Ye)`^7HElB1YIfQKS+=O1{~2)tQO3hAU_Te-2%uRyPz#p z!JD>QQ0OXW9rT4L6td6JCOK;&PUC>%C_qlqj?(y^r45Ujq*kCl@r%>v!L#2GM!rIy zxNN_KTFJs7REZV<dLik7?QVBl;&)Iz$sY&H<nv%Yy4FxjgaPni1f`i++D`;K_>@HI zFCo?TPfjed(_8Ezu<v<dU43|e()Ub(cAx^S$uHmO)=HOUAvA@aK}L#UDT4kCpih`_ zAvZJxprN`#50oF&9F@|`$4mfaQc#MSD0DwBUsbI>n1piTD=Slx9rfbffG!>hJrr@) ziDZ?Ipi45k`kkOb#N=40wa-@m4&)mKC<%}hYJ&{5M3D>hXN<)LRUS4B`T`yuLazj% zWi&6Pb1Yd#l)8%5@(@AB?g;huTk}(H#Grqn;ISUqP8D!A3%v;?A&Hrk=$L~k@lg+u za0`&pXbVs@B^+JBC<<oDuRDsl5^(|XU{JEpD6xi1BLC#>c4!=OJVlOTHRlcn?u1W+ zXYW58IKS<mI44IRk4}VSOp!Z~xb?sbG8*aa+Rv0smDUkYhf3SLw)=Svd^D1a7aWpX zw51lxOnE*bOdB8M$~b|yC_@ta;6ics`Q=ehY&$%8$R(7a|H@Fe1qA#-3L_<ZSAn=l zVS)oCcY&a;c_;YT74qIJjj|hJh8US_qXi^m;&kU}fsz6mOlMctJ3T=XV}M))63zh| zF+)DM6H3N)I($Mp3AC=wlAC<H-Ym?lMUGE8!+;FU3D(8Y{^9A7JN2Kbi5-p|=$Nx4 zN2<XjbVism?$9^{8)AbP5{~}YX6u<{bkV(IeKQ^MeHNg=hSV_P2vPY{=$AM^W5k3m zAL|Z_{yuVFnG$~vpCE@4!W}afna)bNQy7o*BrD&=WbS`ej5DKBH5v);DZSEKD1^E$ z3Y%9-f$ybXB&bul7FYn)X8;+1V1xq%aL71<zL@c%G1*FZizx}^@$D_7Sjg7aGcoQ4 z&!2HnBHRH?PdA^4@#~0c4%7=omD;?I=_4SSWkrF(A*MZ0W+xO_NX+)lE>r9*d|#V& z$_7E&hJPj$pigib1*oSOfKzxB-pU_B15(WY^ZB`Y0hHd*1MMNCZAGNCPVyJ{{SS#v zqMisXaD>OFFfw~2#eo_*ffUO^`v@sJIMsF+y@fV5D$M;MfOO0riRkvgAIsME>6m44 zfxwFyCsVmD%k@F5AY#?Cn1fRoGU&<A0U19Ty1&6duu7g+jmD|gC#y4BPskRq3{h%| z``F{jrW8(5a3IeW#HL69``l&|NRj_89*q!X?H*{q>2^=wweKZk>irbHMMAxqkyxzk zAd0#8n_69P1r{I<<^Fhrq-0?oF~^X{2`y@vRT_||&L29A3#B%v3`s>OPrU=X{{`|% zzY-t$MLO;?qxuYE4ulpU3tmaH>jX(tV@a(!Bzaj*3GvePa|Bt2qn^qAyrWkc=5XQ+ zkrNQ;Bi|WOGIeBw4&f6-9X^4fT#0;Tma_l}jPj&B(!>%Z?U}uy+V228FvdUS1rtia z6w;a8cqp^TP}w)7+s-igfI}d$F;ykwT_(p_vO~b5gfUYhqrwS2I-=xO+H5nWCU`tC z2UXH<NJ9P_i51`wsiUrB&jzCLFGHB&m&CTN%^_+Zp>|s%1i7dsjVw}=FU3!9JzOdI z6-oBlK#<_wtb7Xy4C#I$jQ28c`h@bL2net|l8gBSh1|4TDH!BdJ5(N9(fDdIgsvWJ zW1Z~*pGZwrZL0F6=1%*xc<q!gp?42_apcjQq9;`2FE;y(%l8w)c=B2nl5cVarnA)3 zY{sODy8Y9RyS@9P+jYC0o!4OMzp)rttYLB(>jbOXy<8U0Su13X;><!9ThFqf6AXHD z5ULVY$p=w22TiZ5%3F`wTp$ELCZU+1WMm5HNh<aSkOQeWfgvHh$*3C;`4QJ53Wc1` z<iUlbkaGQuv}+k;2MUfl!JW>o#M>(c++~v<_@a+?!L$F>8RD?RCguh5CIqw(31DQ3 z_ymVzw_Qfl2A!=O>kN*0oO8ri8h4{M!G>5&1iz*hX9gGw85RV$&+s;L_=!QdBlHHq zfXFjD3n4&g1%`Rl_3j{}4&@Y|WLqcs+Uz@oQ2d0vm}`H79K-1s(n6Q~%I;?NV;^&1 zR;=Y0J^==YLLqC(zkAAJa6-mLLJHX+Pg%@%KOo~@^t+4+PSzn|h~yk$X319zmR0#6 zEmNrPV`hP{DjjA%Wa^zqNXrg+h#=^qRI_NTsI;LMYOyW}0nHHKOis;JnG-VZfp**K z2;?vz<1D0Nr2|#02ijybvZ@E@7AaGTqGHk&_45>mr-(5)Myv-Ar9>aKT$+PfQByP@ z;1Gd`q7nX7+l_#XPjHC(vShX=Le={x36WLwQooV~CG$#sRWu}&9uVLmg%r!3k+tzs zrTVJSi7C?q?Px5?u3(+QPk0(n0ViB6?$&5#fStlmHX~%J+FWEd6Q-t|QCU#N`jWys z!BfoV!!&1!MUaPb@|6O-Jm`ZPgd!N=Tf_hkIij~P$kxB~qF<9T$GS*)eLp~Tb^QR9 z)-(3qs)`57TCPs(Ay;nWf)_}ftCoDeR9{u43p6FS87)RKBU6%Du2e)QnaRPN!;sh~ z33^75i)BxWrH#~VsSJIhy52Ebz&CS4I-U%nLg8l|C>Ma1b({?RMAr-fLmmlzrrE@o zI;s*s@+?uzr!j>dlD-O!POB2TyjUXyx3f+})PFk93{*@)hm0KYK`q7BhxekrKBAcT z1BG1=yi&(%Cx`p}f}Ye0d>ED6+-cAQFJ8z~J0VE*k4WgW`cFOSRiSv4{t&v{;hFh? zLqGE)vDWt_a7ew$737>I&B-?-Nq+Orn;;?e<qsY(>1>u$tj&>0p^N&1USax#iJ`V@ z@L7~dDe-sD7^{!_QlIzCuO2v40!>lj&Ffb?t*rZ&G`bTX3DsYp@R&(|LZJT+1~Bvx zRsK7soQ(%-=*g6DimuF@5OKjZI2@aIoo51~Bk&Ojr!Gte!=A>zuXkiHoB`;Im09w- zb-6_!RZrneYDD^I#|1~Ck8<uw1BG$8aT620bw5UoS?Ugzr-)4iF^8ml6IhT?Or8}I z#Xy!DZD*(vNU0N8XdU+x#RU2wMOIy<nA86T=hB{s7;~&ci<0wcFCek+=v^~)Mwp^I z7zCYX6fsV*#~lr3M5yeO>5@6tEbrPt_Td7CW27C~9I)MW)&In{%gAub6YGFrTZ*8+ z!8V1&6aNUgN@5NIu~KPfAt7LsDVU-up-S+*n*pOmC}mKBN{(WE<>wF)#_%wh852Wt zmaQ#EOsdneC2-yG(QSuKrq_a7u6JLbUaMmg=_0@<(rEp=ZVWRPqdzuER8_5-D$Qe- zSadtG2J5`u-rba_8h{qc)j-oi_MaN$tKB!RH@5^ZHK#o32{@GJI{<|w9#4d(mDS~y zQ(6$9KyDupWnhBfEmrGFEAhVQr8De0b;;1eT~UhD7|Fk+cN%~oM0bEsqmgkN5#Oj> z0u?Z6KS_r-ncMl({nXJhZR1*cgMhugJ&=x<*lo+rMdGZW=ZoATkw~qt4B&A{gnEca z;JWD1l5`3)a4lD@>qOZVjVdRCERtnM2YuzUC)5;0E}%V&fpRT=_6!`4%mM3G#tm7W zI{v5q>c4b(?W)vv+GSPR?JYg}$@#x={E_oq^ib`N1T3*hSI4^>(jiMtq)oA3)Xju( z5Nv^v6q=I0QUEh`c)$?f0^WoWro0dmSZS+>x&Um9qa86y`@apgl6!kV8CC#z|Lf6~ z_yqn#xiWF+-6%E6P*N?nCH%3U`qQaPg?<V7#dJFeAf9U9HH-NDj|Yioo>8I`h(k0Y z6d5L#mVhAwLlTF+VK1cDWXu36m)u)`kuAAq>r<Jn#NAJB-jy4^AdY*XJyr<?uDtw} z%2{)aC%a)*S<!q1wGx}dQqDzzi?r{{^Kf}Gt+s(a9*q!9e5&&f?kt&-5=<ZRN$DY# zg$Ij=i9N?S`lYj9Nd`_5T(Ic)t$m8cQa3*mBGCURX_g$@n*}(*jKxER*NYbtjhs7w z+YviQwT_(<-#7y~-~vJaH;Oqr59LC}@IO%x{P-SMDHjX{vRiVrjPft~1i7a!s+v>R zl=BqhW$SB63RJ}tI79(U-bvqs9+=L=(k*pXUCHRBapXge23+h*#<SdgTeUG7#eoo) z@{R>(a#>~em*Ux4vkik-aVQC-8ynN$7~-903MYptr1UO3i)b&=fG>#jx&0tN8%JrY z3w^B-WNx|`lX<Ver%7?*12`H9;Xe}_2!{Bgs-5!5!{OLLx5_NqfsEmCD80LcYO7T| z`P($+z|ePK$Z_&UOC;q{spJCd0Y*lA3rryO6Qc|9_jMKN&dindqPB;2JKs?^Lyq%Z z6$Pt1E6{j~@f81ExJRN=t~<t8`z(XE-v7-9YbJ*@bESX+AsoOBnk90^d_Ho=iGTPF zn)N_COA54u9QaR1tS)Wa%UhK#YenHz+mJ@x{1j4DwX<D(wPUwx`uzJJdc+O&^71Q6 zd7S|@Skq*6YCQ6pje2p+=drYMSJ$Yxg;~0l`BdQSEqxTJiWFHTS(%LzDwku-Uq~<X zWP7XYD7l{-E4EUyS$8AkY*0&%pq$O`gUmvtmU|LtJN0T2su)M1&~rIvJZU&VF@2S= znIe^5zl$q+MEn!GgdEI5A&QA~Jx(?37ut35DDhaaZupo_Qo&2KHtqUyY;R1`CEs}B z_{2$0tWtGAUC;r;n9YXdQ~F+C9q57IU-;z37I=}=eep-Kk1A=^nuDo5rex$kS{IF2 z2B7*PJ;oHc#?Z>(WI+IiedIx!oV7&6x5$|BX;Mm+E?=PLjKox5dkK@<Di%T0@3~L? zvYS7;Ktw3Fb+xoP@g>`i8YE43X%hq6JFi|RYO6s!9guVT#%`(6Q61JRnx<DDlo3+v zscIp~8RN3H#uBBAO4q(Hm>HjiocfjVeJ(88Y3+YqEiBi^e|0@9?e1C=`?u7@GW`Cv zbunA~lqqC|?-g2Errgu0W+@%N9qsI)IITiKdrbZ8Us^$vgQr(%wuqXxD9~zcZLO{? zpsoqhK15fOZT&Vu^581<J*^zP+|fg_`e#k(h4Ty1Y^FyPsk6w6E+q}hn{i15I2AD9 z&loXti5%$Dx*phhvwMp5J1>gGi|$nZmS;c>%V+(K6D&=m>O!4_*IFnpfJdfLEHB6F z&UBRGYjrK#u_Bswr|d4(m2BymWFLuhqTF5OF-jDW8S+yatE;ioDGBr9u%*wW^>omp zPO*!*<_X9MoY}=vz%Do!OTuwDrid}XsaWVdfEiF86Z*nsNbZUUb^3}AFsA?-_LTT2 zkll%*VCJfe_xfZ>R@t&tjKmGcBa|XfuSMEtPiaQ!1w-9cZP-u656#?SlE;gJ=@W@f zLXCys`YpSx@-p|t!j^TdqGT%l7ZjNkn2kKLz(<UmEA9e~&D2vtsGCQhlxu)B-JStF z0_c;NMOa2ukWn9)S;__S@<=iqTjz0~?={IZUHU}22|*4ZMM($=dw^fCG^9j&v?8mh zlM8($PBBLmLv<=eLZrb;YReE%g!pP0w&V#2MbJ|uIDDs1S>MH6Io;wZVq4%2X@_}P zM*F;~0mIULo)}+3MP!9Yf~*WurOt>wc^l<U9h8|^1gcG`BP2AF;UmP~qEUu2!0aGd zH8cWjBG1iXz$C(EgaYZof_&Fn;EpnjTlHAZIr5=IUX!EJBj3-IJ^CbeTuJ;ERlxzD z<?cb$+X6&-iwfQEINVAVd;{)}`*a<z#Lt{ArGiym$Wlk<N`Yh?QXWTJAi%dM6tm8# z)L8zCC>f$gXG={l&zpvvq!C9W;i$$s&nzIO`Iauz7ca_#-h`NA7#yMi&TMBkxk}m& zZrg8!=Bk_slb7P%2;v~7=yF04n~=cof$gq5i<I1LzizcmFN*l+<Ll<=bQ`0yTZK(R z%Ei!m^ElYZP;%09a-arHagki(<okg=mI;@tDkp{yb9GKK1kY8vKsLLkl`gV$(Y2(T z>dBz!`p5MIHtVY>q*u(+%RI8IrNx=Rdddaz1yilyXd<ssBY7zAAqFT^H6~Drb|o-h z$m4HSZXh8X$f_)LiI5GNOPZplzJ?$LU~;uUf<%<#5%wULD!={HTozU~Zwc}s{VZ>G zXf0aQ`dR<1EdMK-<?H;4X64oPt6GwN8`@{hFImCU>rZl2I)lMUvdj!;Mu*dGDpLUM z4omQjLokS@9I>Y(K2vuNRi6&_lP)Fw5pV4Ku`tuU3v_F)nx0_BWgLqj1(KD_Q^YCu zm=NcLa^>f(-<3+cWwrC|?qb@xE^j%-mN~s<xB9w%MCINX#d3{c9)+UX3&G1TN{eV` z;h(mD+*1XLu9X^(Qxamm0+-MRSYD|aYl8`fBG<_tm?|hrjTGOEle`erIy4wicpIN{ zu-(-vik3Y6qz4zV(td`>I3T5(^O9~vs#+sIqRM;99-)Y^PD*LVs20zZj5M62f@x$f zv_zTCElzEV)-RHvZkJMr6N>FWV#ZB}8K7|>6uL<D4*D?SYBF=8E}=<(`NUXy3U3f# zV(O@Co{Uq7wO%SgneBtQyqZpU!$oN<Hc>ufcZC5Gy!cBzMpjgk6F5!_N!5$?Ks3Ix zu7q9%aQs=rf7|~2ISpdiE)6=^1}?b2cl1<m--8Z#wcG82{{q`~QzN0yZBOl2L85S4 z<_?B#5tU|4q55MLfhPH0n50R)OwfP(hO6MoroG5-(x8eoIh+Xyf)>veL*L)GMd=BN z=?2?$b3KTs(kCcu3rP`L9Iy@>7NmBrCF3I+4ssG94NRoOuKQy)PV1}PokGaLT)0zZ zZ(0W)v;q$vO2KJgD$rMO^!%t7b$67+h=nLfM(WCp)H4Ax96KSpGjq+k#n6E!)RNFe zl$B04c$zM~0_|vg<tG6})GGk~jFI4u_TcE`=-?9gt`DX85$q2n*|)&K{@@7w_RG<k z0HCkxxf93|U{^_SosFdW7I<-Res*?raQR{rT;?iUh+HxJPpjG{I64^|fi5^YI}{j* zuUEqvM-2RYaei6>1jyqMD#JEP+(W_z>ukI*Grs#`3nV|b#ScT=7n|*b7Sk-fEUT>b zMm#Z-mf!Zt{=1`-cJ5<F3bk!L*WlaEG?4bOs+sruAKo9;H#5NhEHooXAmO%+u2&PO zMFvC31sf=l6w+C=Jb6hS<62I%P8tfP)DjOe!C3SwV;t|lzdHJ#{`tk_(Z$u_@nHYm z$<fu}(a-xIPA;!bk1j8c4+e%zCS;7z)Y`2a)buQ>G`8UiL`*SmAgGB4S)z{=;|4yi z`J&2fv8gr18uLrN)kXKaU~4FD?`t;r<y-vnO@7rjzha{gK+*8<t>7kRtemdWsH-SH zgHvg^R`z$xHZ60pRNc-JTQOPxP*#&l#FFv<i}@eqRYmCjgOPAWIR@tBe|f$8x|_@Y z@MgQa&j0l!&*v5Z?dNLD+da_MXINtN9nA@ZjyvklXYDOfT<o?*S(R~1y^E;W#Bq#R zy9YkYNzg6^!ArhbP?!vfE}^LRm<RI1z6^I5GepymBSNQbYaZWk<(o3m9u{gUsWiW# z^nA0d1&buAc#$mfz0eafZ>q7(VOqTGB|v2gTexT5Qxy7$$|eHrTVz!z$#<tXZ1=!+ z*%$b!Etjv7Zy!R;1>E<zoqW7++5Z~)C03#))JuAugkrr&swagk-ANR!IkPPt-MK@R z)UFt=9MW~L<Tqv5D!i{}fVy#=Kekz7o=OB{cw5AHv0#P~x>cVgH|%s@|5#DLu*9jw z5+G%Sq_h{sUy&9E{SPZ5M7OGdA;dIChgR9&Rg)&MHZ++wW!!B|rPOk9h&aRnn=h`k z0#!8yhJ*wNh7}@9A6-}tSBk5Y4qNsFOugx<Fu);4<Ge&mFp!QU35Z6ra)Nyyg~iz` zm87=T?Q^UgK|WE<B|YsJ$}KS3g`2_;S!k9Sxhk6EM^q+dNL|XZK=NXFB?(97tuDrB z`NRZE#YyGqNduP8_NXQPfB)zy?CeZYdUwG(XXlqk1DAi|t7)TN|KIMuF6#f?y><S# zCwX4J)Y;v@P+snq-adB|9H4X`3NQmhq;t3_H?fy5TP-_!p}MN9oNka0ZZT9&QW-<N zb-*b;Y7@OmY+?mPIy_U>O69F1w!}D8=PlY_QF<$a#)kmz=g-L*m76%Fh!5=6b4?gf z6#`P-*Y3y*+m?G)>+@#^;1O^?sy(tL;Y}C^aDTrLR?=FtIBMD}D~lNlW%CBhkZ`bp z{sbE;`CgXtCazu^xSQ#R_9l=JE(sXx;5JFjy%C{SK|teB4}g+wpnpENe1CB?xY|EF zJwChY?+*sQonIWjeZFxM50S?M2~tP;cAV4$s;cvqT*DIMyyhK;0I^dQ3-8<ZXV-@u zTF2-~yuKvr`F$IFk-?pIUjxVSkw<*AnIT~%0jhAa=qXQos>$;$5vG<Nf(V+!lB3SV z4o&0yd^H~Es@JA#qFm#-puG$ohfaaxa6SVL7Qc}6v3mRB4BaC7q8W`Gbr@7hEBV!s z$wA@m+jjqbIyQyjY6Cs3VCaJlBLOmgeMSasf(<eE%FAT@GdH!ka2$1Ly1>D#qSaez z3I{3c0>{w<0UQTJ?9Tu=jto!R_^{OZAgQXcFIsP886V_iVKKtJ=G58(67fKlH3jU> zL=Bb>A@Ck$VAO4(0S-Ns^J2!#7?TSw#maSfxnVRHf}rtNO=q8UOwEK})?ur2Qmb;p zDxcSwGvje@!baPHR1342^^)Zz@#1QmfN$-DlE!aA20gpK&j`M=^TVU7<9<%ug~Ug! z;Y`j7G0f4F{oeh<WgPODxzP8iP&ec|{o}s9SwNwGesP&YVwvVyggnW7vORN=;=Y}P zifKIdn?_N<qw+P{h-Xn;F3gsIYL`nW%?kFsf361rIF2O7^Mo4T&`eicCckG`%?_<s z%Wg@QpDCOH4sQ^EV1(|#6o)ZKtUP+*ao@O!o5aFyvJd-BdaLWm@jO<uy9Zum*6dra zArIX(=86}Y$ul^*_;`F!GJhI3-CXQDX}Qmz#X>~oUVQ_HKKcaAbhBOGT&!<zbT{P) zyW&53MBDdf5;o@WWf?prlHu_N`;Bj(_Y*(HIdayph3K3!BJ>VYzZq|^Td&iYKjzc4 z*MRK*@gyv<y|d?b-LAX+4GnX1WhS?iD<RS?46qM5QrL)^PNBFS%R5Lz9FCbwRV1uS zI8OOVsmMv)=Z5$n6?{y>BT5Nnp!c=__%<oS+}k`=l3`?Ru;F8mgSOh7w5?IE;<iok zNqSPW!Sl`PuF1gP%#EWlg+5A~bkQ{8v!vnMTxZ%~Bfvs#RQ0x`{jY7GdF-du{&zJ& zL4+uCc@(Umje7gv-kZIhy!~&t+kL&Z|2@U?`E%zbxW&_+y!ki60TN5h+o>RnH$gq{ zvU7jm5~W&4pCTPdR+;Ct1z)+Uv~)Q~yPD@FxtickT0{ozRNmrpX-fBIR)TZz1#lXN z9@u>)|HRXQjM5F-P6|wjOf4xoFIzhGo3dLb`6F%-Wim$!Q{fbcGt0UuH*WJfAd9Ar zMihs91lm6^=LaE%?T)CwKk6pGNMB*G?zaEh2JNf%5}o=raSls^nJT+MCD!V*Yl<t# zFpAu)qnYx=6RUEmY)-X+)&vDpmrXj75H(dap=6z6dpHyrd8*BfZc<)e2U&H7vuq8M zO1G3;%IDu)e<GnaTdN(MiFD~&cO+E;=vyyaVjgWR01qecqNFPJ`a+(1GXV7^job~a zB(V1f>4@4YL5fGgt+8**6Lhcvl9e<@srDeY#4aZ;pCI)ryKdg!SCX#Sgp=6Kr6MMz z&oN8R{ef%z=#%}KD9EG0d~tQlz=ET7u|ky%UFQqn<Zo~qY?jwnI#{J_fr<Q*c@e;$ zF%0ksBOkyhQYm3uzacfrWqB^9x|kOX43P%~Q!+9|z?#<TyaS`aXYwM7ybGn`zDfho zhL%|RY((bVjF7kKwvI=D>iah;#;+N1O9bLH<ct)5D&1xr1RC3V(U&S{Mmkvvv-y~a zY*VJPKqK)d2GIO?F8k1X+kDxu@Jz^|pk(J!f@|jO!U*OUgA6p)?jkb3>Tfg6c}$fF zUL9ep8yRvW%O<rBu&k}#W<txK7liQgNv%M(#{n6_VBVb0wvddPxrA}1N6$^#bj>Hb zu({k)THJh0D{wf@l@f2n#2!e=k>OR<50I5K`mKgwFfvl3*oC(hHJqI2C>ugN$^k2I zPUi>K6U>t>mZyvC_8cBGU}`EZz+8ta|G=?ih@{W@lCQKW)G(cO;U>p=$zH6I`?j~L zzwK<MylD!l-%S`bgOwURwFge$3)Yh8DN{=|A1O;&hB2(WQ3-F9wD1<F9G}^@ZYeuu zf3-4At4?LA=`Ya}A%aKhbY(;CzB6Dp<#^=D(#v6*X;`*mu80WDr(|iKvrcO+hBD$_ zCUzf4uatr^zSS|;Fj8ULm@6P8_JkWWYlHUB=jUy$5hYyQez$+o7XSS}?M-DjsN%tj z7DN?w&z>UxFcuPQkVG>)R$=1Q>mzw;Ba_PtI;EHRlZ(?O@jTSys9=DCs5fY4D@Y&{ z-NSlq2LQCSDa~R!gkT1ej2mK&n_Vp#KLG9!9#41=Y}XCoMC^1b2C&iiP2}F^F~@I` z?5<2wq2|M9ht#`7bebd$_zuRfX4D=qV98iXe>>1tv*S@l&X)an1kj&AS=V)&wzAMn zO1QR&)JFXjs^XJHpsE^fiQt_l@~qI6om`m8=Hn3&KPUe@GL1-xLY|C8-dfs#_7>Pg znM(lMT7J7Nc4y|p@&YoYg_vSy8U%Jcr*v-Efs-W33r6d*&A-`2Zh|i$#Gx+`-tJ~6 zPsTS(EtE;`HuFUjb*^X@NGkP!RP<636`Hf8trhtoBTU33*T|Qg6nz1I#)Mm2X-*;% zkn!wSA%n7bnQW&E3is8qYDUzrj#X3nz6h%>z7{y;EV(*{{4sA4X~c~W=fxZ+7Wr^N zE^NYsm0V|UZL(RiN;phs_;Ec^Al<;&SHMd5C{}@{d+1zK>A{?xWk_y#E#PkEW66fs zDc|uHs@X%8ZSie=rJ6-aZF);;nJokQQ(8YVMK*IA6~lVP1Z012|GnMLFSrfsN?xtN ztg<&*cKS^nt^znDvB{dO&shP7<2oyA?YV4Wt9wBRuVb82X!a0D=eX3jz(fvd1Iw(U z-Qza$Hk2=bqA~jPGY);wcIe;#?*H}q7dC-AZ+3fawYU3i|KjZU?0u4NN%S80h$D|9 zA8d%3ZJBSBtAK3g{(dtFt&@gK#M0TEaZ19m%n~5Fsq)Zs0^lvdev#S?V6mP=l=!i9 zinjB7xvjtAa72=e_CE2LtErI9b;vpsa_4Z;kvZk%$%&&|aYQSQRkCncU$MrmY<YVc z#aOkp0=?MM@>Zu7TkCN%tyrJ$=b{+vtE7q7fHb(v+?8S>ted*adOoym|Liu4hx(9A zULq@;o%A7vYAfg@&f&mVHNi!ay4H?HqRXksDOOx5y}1)T6ao+VTL8J^p6yeTID<1s z<EvWMSON#UQ+J+#lf(vclNf_mTbL!M3rS@e$d|j|P-#jgCPo!2HYTuTwlnGQv`w<a zVI}K7BY<Y>WZCJtxzN!JlD;mM1tCBFie~-7we)6CZ_q4x%lejMRBap94a=iN`XE}f zZnWgvOJ#vvOT<jc5q(px<_Y0cn3m0qmhjxnyM8k5jE&VX{%qNDrKGctBYzd5ua45m z^wZZPKwBQLwM&`5JrCQ|Oj`U_hjg}gnnd;`+(p>T9;SBAMyqwIvL^BaQbX*EA|EuQ zdkOb78Ud?IC8M#r5h?3gnI2oY5|s!ilP)ypcxD=Jm!X!5`*fGK&|;=#x{R|XCgTtl zRxTwTQcuAH5{|lEF_0Vik!%~4Q=joyELzFH>3%rfu4br;*0xvM+IG@flX0mU4(<O} zMw_xOY!=q$+$gB@ReG&9y)0j?)8x6tMADR4hhr0qGfX3FGIs{2fmy4kvy6Xig<_Dd z&6SEldAkgZQHUty$nRv81le$fejtZve*3DJ?wdx&ha8qS(OyI|$WOj>5^2kJqUt#{ z`eg}VvA-zH>6C}vWS}eMo2$BQanF)--8l2qP4@X+s_aU83Mq#E&~m<>Gb5)n$1a<w zvi6_rIrFr0Ca10!paUn{JySGEQeZ@1x+xMpjHCiTs&)^WJ9YBfYMHJRa~v>tjJTA$ zKT|T5oM|7~Sre{g?M&N9I@Ws&<ZV7W1MN20NPQz*F;#V`O=J1A-ek00spu-aGrHj| zq{bsjOb5M^Wr~auKTtt_Kbn5G3$AWLn>K<&3yj<`y=ke6YO>NLQGpO`eXs(Jw~0{< zSofSwIARrK@$cc^L!)^8!2TYV`0cC473N~(jCbH>h0j3N9;d*+_<_ABN;NCkvJYN3 z4nrLLz<ywFpNrxqe6qTs=t^I!gf-I9x84~77AVm~2=@iT0Edjz*h@0z2&zFdE0uJu z5ycgxDz^`)<bA2E)o*1T$GmLlXj##)rLZMTyZ~?M%V{dUvAIUm>^^%F`@Srw>sMT2 z7bVnOxxcT!&7ThByBG()iHoW{BFFd^g#ap1J9qqYv`~Xrx+2aUksK&XLdq|KDuXD% zQid_{$B8uNIqj^7kTv2cLi7MK^mC!Ay|K0&ZE|g7WL6clbemOGVgtx8^4?a3mk{^| z!J+hiuY^)M2j3%}dNj+M%-T|}MsV)P)vhbaW<>p3Xl4}vCo@Zqnvg!(5fn?$4Vq=P zOqrEIzLfWG`1$Z3;zOb|nm(IidNh-kG6q|@Y^89@M6edJ6y0KUCuJ%MIi_0KA|oZ8 zrHx10){SNPx$MCg8TPbfq%%wXTd)Dt1yT!{EQeSh-r6f|UMwf;_pKPe^vqRNkq|+p z5uSk&rm3T$%&jW#>qC<iL30<Ox};W<<P;LibP#}$gpNs}(17KBMKi${V&H9#OKtNO z<85d8aHn0|bk60(OnV70-6$T^-52fK{<gP>GO<WXJDmQhOJ%vtF1jLpu54ojEKLrN zN)<2Bp|{2<scP-cFpS_?J?Y9gWIz6d11!|UQ5>q1BN>a=!#)z~zqD|h1Q|dds%lNQ zh7XG7yO<%@90X@1&<&b7WaOA{bw>d=Rl6i9NA-R7G@FNxo1`F{okPy<UCCMXJ;;cg zoQ$eEdM+(xwUbj__0{vawX{G|3dDSm)ut?9RAY+A6K+-?8Qa{Z-(ozFKXXVtRT|Cp z`S2{cs_68wYKktvpZP|rjF%Qaq&rb-IE>WZ$PcN7OYR^?kLqT&JGryOMr|w#cvZ}z zt>9J{7h@Q6wDe$3F@UlYw{)QFwYF}obl+-*BeQDD=sWpgF|m18PkQlANQS~0+F~P6 zaSUg6Z>hygMe(UU$o(3}sf~N|D6*p@{C5NSA=TI7#H?Xk05Sm;@+WZFTY+VjiY3EJ z&MquL<t;vf9LWS7Tt<*&4-da%CNAyI>SSA4aL0ynE)R7Ms1-{)yR~R(fmiAY@EvnY zWuNA7Oa(_oxk+xbLtVMbvAU|1Z91~n18ju5E8CPL3wzHZ7+ojNS(6JV8GxzRDO z32~whfggSbmTXxKQr+$YkB$=zw;v0`?bRGNKQ#DEaYTwFC^>k($osA3&(X4RdrEhT z(h@~RR%xK_*)zR;q|#)vB_@RO#8jMzIk+R?3n6_dxl^3#%684>-7)EwGVS%DJXkP7 zv2xPRBWc#SR0vR{Y>VnJAV&pzio?(^sb4TmAF~e%kF4=Q;;3obdkfls4^1el0XVw2 zIKSwD8Hv?&)M;aN&|;JAF`2&B99QRy`U3wCtX;OUWSTB^pwb*yWf7B2a!AE;Vuc{k zF;XfA$%7Fb;y}4#-o!(sBb=0m1t`C9o!!P;FzkXgrJRZw51%tJWo$fuO=~9+Sxf_! zkZt<uR#wwO<I#apL`g)kbODI4fgu)q70X0<Ha8k7t3nDYGiVG=ZxKBp(+E-o+W)!j zZohWAB?o`iEZ}Ztjc=fhK5-Pv%Zr`c?IGfDyDoZyyj>zn)`vq_giwq_jz)37P*Y0T zHJ*}Gy6azqNIH&VLjsx76~sw`%jw{d)t@W~NvQ@!jQjZ4Vc7D9e|m7kIvOz!$L`IK z@*YzoUMQ=UWsEh|mZ921rqji{{ev_Sdc#>ohtOL<-t0Y?bgIFaPYA{TRJ)Z$NLW{W zb)-DFIBZHlkNOMH@8PTmBb>7RLZqvx*_>)j=8TbnvL-s1(=RBPCIRj+1Fzgye|1~? z;7%om^(M%>sVIF1nNF|E<=rXdgE-_kND91iU%6d%T$<H-1Q<=BEpRu%-Xx8*1VcMr za{KfBeH#GfL=#iwwk{`_X|yo2+m>Oe<c+ga8G{*zjLVc4BON!k5(jX9pM=(ukcFyj zn?~%iI0XOs%6%o~X&i8Uc}!-Iq9f?3t3V?Xhko1abY8x^)Z3#exkbKG;H4{)%3u6~ zfhi90G!Dx~<K@eS>PH;O@ZuJ(8D6$1)Be11`+L{<>5t8qzyII+KVIJ7KW~GL40-H( zeAdybWS{xmwqUGB%U1H#ksiOTXl!+qi71{zIxC5%wtR%M=LVQvdt-h;vd#Gc+~%aa zDW0{<wo-)Jy1xwrUX};5vH>m|L#-;bm31l53ZYgvf|(F!^CGQP23pGnS1So%wIKE; z*I2_|=;ABtO$OS7dzE$pY#8~CL!1P%L{w*rbD}gqo~pH3+v!45y%lx80`7we(ID>H zTu$xh1D!RE)K(X$O?9t)^yd(zK5APfRV+?7J(O926N_5C)YX5w*}G-sCaLmcJ^fd5 zYpmK>)vSJge^fk!Dy@=lvuO)XHZ+{SA4C&)7Ei%;x4GQ32cZ$!m!|fzR9=9(4XOGh z)U?*tW|S>EWNJv|l4G!DWG<^%Mdvd7N-14g@X=^(a<$dcH%;iaAm>_{B^Kh`q+Vs; zYqD58l;s+c#2%>y$zxR&I4{g@veI0_<Pls>t`W-TEG2db_}Xskd@X%Kd9yCt73Ox= zWP^z^r5EU`C-+W%k|eCQ+osUM6T-@nve1M`>dhDBnWELxr3kT-Snu=MAN3uYS`yi( z$&HAVb4e_7ClQ*LjC0-;$n(ugg5^p&P1>6Sy%I))EHBM>Yuea#hX9+9IPhgee;5`V zapws1qY__PJw}8!r*H#uHngwg`j=cNQ(3A_XNSnl)Y=ZJG9`VJL7gg+z~)JkyS>-2 zA7-|AI`OqyR8$)5Qy5jnm$#9(YaX1I&Yx_V1bCUbAj85H^~zW|XUfRbToqFrVo4LQ zkNgKv&44&)Ks~$c0>mq?)_``V;^PppFmqHZQy5vZMy8S1mmo6ho{dqxkwieKqb{Lf z!~Gcth`H|rG6e0B_`8$i?Dl4Psqxih2u+FNdX;H7{XYI>2+KcJaQgBSTE^Uhfj-~? zDwjW7h(`qoe>hMzD|j<ora5y~)N8<<qNby}v+9XmK{UNgEJ(4uJa0yelGVBtRbw+; zWI%Ntdnhf!>aWYuG>aTX(8nQS3`CR+lN;Z2_9(heUsO3zH58%__+t33bTv#BTvwlz zpijsH(fA580d}U73C>BUZeL_}e9-oX;=|jDidLiQ=5rboK#waA5?ANUI}cHQurGv) z$z#Y^AYN8A#RGPLl0=M*1vh9mr>qKeVp1Ebyq>_B*!dM3B{lq<@zi=SSsz3tF(!{l zsdh@Lr74v<ZDbOHT0uK)D{MBhW($DdfinUf67i0yVW;<-MjbbiP{;>=fG=Nkm6GDw zqH_e`0hvZPK=Q7viAV}?$cfNwrJpXCBF5ktZOOs&M8OfnWog%HvRx*pUK&_7hFsx` zxwoFYE1mt>M%=)MQD%)jMSMd1jMRE~D-nxU43W#Y&lM1b9z<epy=)h$nYz;cJ#1S= z9Xp_}DVtva{vLkZKlyMp03Y{Hjt}=QkI&C~e-GsWo~dFvs@8K;KGGQ(fxdN76daF$ z7$rBR>6HOKL7{ZO^`)b7h}1hk<Q)TsIF~#*B2?C0fbFU|!qR7Ki6n6|B55OunP{TT z*rj3`Y;IP6W{BP*x~U@VrU>?u?r#CBoK*GxMq=Q>P(WmegCXG)aBYfTOFXZwI@dsl z0AfN&ay-?Eu=^@PfN^AoAP(`LF-n|DG&vABIo$87DyM`B4QNCJNE2=M?(WXsPZK(p z0eCywDY^q=K5-;I6Fv<d&KLbb-^$Ui{ISw)fqBgdS%d-PWUUzNh(&t;tuHwmi{ub` z`a@y#(mGqfDkrH}fiW(04TpeGA5qED;u|O6j?kN$!N>+nSK~lJ4@F$;TLSFK7{v}l zUQ98cIr`l*<LiaPv3iGP)~AR2ctylFjj6UumxFwn=B{2{r`xV^*M5(A-otcB=VeW) zV%Y~0NP8>R5GVD!QAPWU(CEM3Zo99Y?iTpZZFg@WT9r{KV|mQA^nVv_pwv<GK?36P zWB?rSn^-8spXprM0*t7O8$$#>=FhGdkrX^&m6F0%R5PNYcv_y^jLr!T3&a+vtR@9O zNjKB`De6TL0)PzMp&$^l1cgk<ccCrg5%wTQOr{5BU;=NItV-`9TFw}5bH=wr`-yfW z{CX}=6m$bTiSgJzNHLXwROU;Tmbgnu5`r{rb8gF!Y#6>Is!f$bEsC?w53I^Tt*)mx zXx6;9nfOBG9sX6rTdvc;qS@p3`(S^OLEg^}b90<(_JE6_dvVeb)r*r-)0RYl28Gs_ z>`DowvPrgR{%?DOnB;#?OsX=Z=7V#L*XMul?rgu>&F6oA)$OkHzdyyJ9INFf<(sf* zZ)VrVa=Z4X%9w3boF2H{Zr$L}@1^F<))aB*Lk?x0J(El_HNsSxCc*uE_S~YSbA`!^ zwx3G^2p2M~yH?Vr*O{PJX|F`heX&bZcbZixn7-(hHq|&mcd-HMJ~7xVM=(j!lmu=p zrXE)j8}IL1inPY6+K6qWq-s{y-PcBa@C9f@ck<Vn=JA;9N)B>QTY`0Hvl}$qvO<{| z|7l6mM&M;=!{;gn6${}F@|3&7TNNQwXD-i~_Oe#K`ID)JZg#naXjVK)QshbjDpJ+e zNSb+F=$ZNJYfHEFGw&%~|8pm}&I9#<IqU!StG&JK`oFXN>h<1w{eOzb+Nf430AloA zzw@$saX+$L1#$~`sTaZKqb}d&{s-%Ie9k(qZu!j{el6c?7n@`iP#|mBMPW+x<)a3R zw7Z&>cfCNVr%L`)=mePYm23d#$p7y4?wkDn@Aa$gwfujIC!J`P#HR321`QE2#BrRm zdqX86EGxkZ5scQe?pu6H=YQ6Z>7nZ1T>Wo%=S@NX+ga;>Px3U-|9p8B(Lo`nvxwl3 zJIdosDg~d}WQ+Y7i2u9ef8JZ_Va6>jrxljF?>(|v$M*bNYlEfQV4WUV%LZ!rrFJ;P zA*8dzM6c9pTm<=K3p^jrxN=EJ!nn%}9_l-|zn928H_aoiKYRhh-Pb7e>}25O8r^(_ zwh2GqFQn)?Sxq;?-5L%je}mINa#0PKr(YjS<-d02e6Zlx&pi3x-Ok(p_x4_|?f*~m zlpIu>JKcG&O7q@}87I?I?LhoskzMX!ti{7xNLOvy0Z`*${I2B`RixFTrgqrM<>=Su z#`W{mPp$lCx85oY0Q2Pko4xJ){{PM1?pppo#gh|{dD&kj-3RIh@<Y^d-PB5iye+;P z%lz`@o=DK^OIhWj+T8E9x>W?%WT{{g@&rNnEYig)S`r07K3MVw`m?@5`-DLM9SmUT zA!=>?@`VGd*l}O4?3s79$dy&k5L1Rt>q)9QnhhtMM;42zfbNv{z)#yh?&%jQ26A7% z&Xs2<KprP_4pUAc_a-Ou?^3=5fRX_9fDVQA4KB5%@;~c#`rs8{p8wD5y`8-Nzw>5o z|9z4tCn~dE#MUaH#QvH6IkPfUsr1!u$x_2#e*IW)4r(QSMErpz!vL^ERj+dKn<v(G z6(TxBvx4&s><0n412`O#IFy&#L!|QMYKd%#=k!H2%UGA<tb}kXnn5|ALV>{1Cz<m( zNb8L#nP!{K(ZoCj+N4i&r?CK|koR<Al$H&lZian|0_<U)j3F}^rri)vGW2N&iaWi= zpc5O^_D;5$oQkq6*_L6g?blgU(#4Y^=(kGoo3Fgu3>r}~MSOx{2AD@-gy!&PqA7fJ zBi$oDrR#t3&dsCR|BB~7-R<@H@6$ZFDP&z|%WeI#x1j6pWVxxn&=;aZ71TPzdcl9J z1)t(u9H24sO^Pg_FZd)oqZgjB$-XdeRM?^9XC%guaTIcO4eB6(c$$2bH&hQXO^Z|V z59D#=i#HR(xYiF-48+Uh{z3Ld|M)O{LE~_rePBqhEi@J)|HXxPyC2S8$i3JJ4&zVN z-{U9>&=iFn2Jb0}BbF(88Z$17E41ZtR3hyiFvVdHY`4-i?NblzzIpTNO$&e#lkipr zPAp{SVKBQOMBX!EGse->km4IU|4CO$(dh?o0O!g7*RKobKd*OQt>ynyJWHJaq}*{K zxTC^UI3*$GgyL}QdW0e(Wnm{VAoepr9<nSHj$bSA3~i}dkgYl8ZdGed6U5SF2}&<c zYg>oLb<4!}w5X}&swruN_|fQ(Ey?c8IY(67@jTs7NI0r|6o3sw*G=%;LiWdR;z0h1 zpHlfR_Fb#F`X`@x@_%P1FaO`{?7dmb|EG9zvcn2Tk!tZ5BtVsWfkqjP*0#RKQ2muu zD|Riri4)=yMp$dc;CCx@L;DY%wpJ&s5?}LsJB&=RTSSMlNX$^KrbH`Mf)^Oqe>Zvi ztv!|Ue~3e0C~r@2{{QOLUjF=l_tjeef0C!E{C}s|VZ97|gUbL3&;=R^qFC$o62vvD zL=RM^_-nCPMyS?lr!&qV`<_SQkhjcvg={;?Ni{bpDA>%$x7Pq&JE=lbFXr_^((Eaf z|H>-JR<#9~YyW>;@c-&|*ZTjHJfCag;%4K@yVch-RFSo0U$tacZd>OO{4++0+iyBR zj``Cu$tv>4G<8|9+|o-GS@?6o&V`oOFG?~A=3BMO%*UWS8&VxMd9&F@W$ez(OQ8>6 znHDzlIEx}#WUDLB$L%2jofdp5<iA{hp<aI<+5X?%+sVuSy;pD6_dlNGvE4l@)UPBO zVdhMn18`(f*REsRwr!_lO>En?Z95a&wkO8KwvCBx+<f1E@2$UTRrNVt-KTb~uDyG& zcfad_(T|K%YZEkK6ZJ0eXy4TLsCZq`rb=D$I6v+M40v9=_BJKQe>ZkNdwF;Nq{ck* zUSJBpER#2t_Z!zFAf+nqzAeUdp#x;<?VvaGto9|l-V$+q20BZJtUN>^aKhcQG2LSz ze>QE!D|v9Bd~&dtd&|`9MSG)RvCDFRcX%C~a>C4lDsNbtPmJu?@@Vp$0!p=yTHpDX zPegR<*Bv{xccjs8gi-7F>9gFs$Grbm@JQ}dK|R|h^wvm~`rkV)?(X{N2ff{Tbw4fM zY#VveN;BY;Iu>&6OcG)F&lnzH5D}iEK}o4D$qQoVO`ZpfTp6i3R2ngwM@~GmVh|+| z6>i4<eske&ojn2uuHlM@X3C5nCOC--dYB2*m*DQ{aKOPPEsU|GiSa!hhqj}2?SmE9 z0J_2E0`o-@gl4Y{KJ5#Pb1)$R!5FD$xll(l<W|a*t(p;CV(D7>^sCd->WoD0I?K** z4EcnPw}u<A*SVe>X8)uV9heI9?1&f2N9D4~gDMQYG5q~a7wg5i#SONN-&6tr(}~L8 zgwPXGYTm6@SY0Xpr+m>gY-F07E7W)~K3EX@mYowlqoQ76RoR7YfcTg-n&M6N;8U<d zgW8@9Riic~2VmYfZF(cMDj8!&pVSGHT0rZYx;tvFh#rM82@r#GgqCr1#BDxGQVRWY zTggR8TH*3TgQIUp9AY5@Sj7}lZ9+-lRthWZaP%ftJmIKy2(eDdS9_AH_EZkubJ&5O z4*&6a+CZO<l`)1tkywaxKq1OZ^IV=lcN?@j+w0zqkaMK!?=G7a@fB_8iWe2$x%#Es zFsZeTw9D5O<GmGlX7JTnI19r9jutqEc29$ppHuAiYLuE+BB^Hg+Q!_}n^&_wX3UVQ z*7yW>Fq&R6LVOHKV@^>>=UuCwK_?~7FM~%p^+<_miuyv%C0}}Gq|(oo>32(^yiXZU zk(JfDHY(24_TrDano3@^4wkXe_e(jY0nJobqC9cATem`9<<BwZ9|~HNhjFkN1?k1^ z{z<;-fIV@k+l#SEPZ*_X8xhtLI{Q_W2`jBJEz;wiLI{d*$EU((P#zIHw^g#|<y*kf zwh#5D^i1bR>W%OoPfKm^k3A<K8MurKFE|#b@t9oINgdI~E{G$B+Y(dzmg0~BY-JTv z&Sxw6y9ZJ~0Mj`m5VJXub-kl?WI<f-SogY0BZ2(Xyrt778AEhtvQfws@#@;U6(onY z%>Jt)dr+D<lhFTG#@<K96|n2c#A~D%$n%_ihxRxxTc!v~04=f9K0XHj)VXyHyoQYM zNnsGcitfy@k6I6(P5oi=)haAQ7t=#(#XT?8P-ArH-^wDSO{j}85Fra?Z>|R!a@!jx zi8p@qeywjh4{`%N&%D4o){)NZk2^C3ayXJvDqCi78NSKVb@Qgl9-R$a-_{1n1L(Z0 zA9E2(`L%qQ6h=bC>Qi;$$Vl51P<5^EXlT)=9Zjm=M#qOUK_6(=lE0-I)T&}WZk}lm z@_HNY^2K0kN1C70tkmgDty&Euq8laYjF;J(RaPJ5LxT|}{E`K(%37p)qGEYVR;6wa zEfz97xL*0@-D3n^b^II!{!e46s&KT)OO*yK%TR$7Np=Hmo7)X$1BVagpC-)c-J=au zogCMqyBqPn%-S-iw&R*ePREo=&g~>C7u%J#D08ARdq9k7>|UsRhjN;k0?evM=%wed z?fq*Rslk_7``+?O>jS1+XGOzKq1mveZLqJM*c7;_@!n{I(HwnF_dRARjmM)~y?A=j z4M(q-deQgQ%bm^l!^_#t=~no2hb1Fr9Dx~*FwcVGuAp|9o8^i4jKrXaj$NVT#(5qh z!;U^4{MY;8*}>~r_(D<D;6e1A+XlQ^*i)DI+X-fxx;dfT4*pUz3#Fd^A2}^fttOPA zZNe-4EA=GqzzhRNC~m%?=BP?^*k8E_>X;Ir$p^uo69@NARSQ3cBp9W7pzT7hBr`!@ zEEUC;{+6(G=u)Nml2UJs3gfW1x5oY%uJj2bY?b%-;C}lbe2KaMVV;U>ua)h>^Islk z24}RjVi|w?5L4_R0rLSsro`S8ZRWqKq}MV)VML{`8pXksbFf*r=ZZ$^{#bA_V(>&! zbk(Qd_nFOivylRg4}w9;M~?0r4G!;Cn(e0(puHN|DY_<TX=)Ok=l4oR%4kI%!ktnu z+dqxKYGo?Ihpu15ZJeA<7*EAE`roFdsvW<n%uY{H&G5nx9+o$cqbRN4My$-O_E^@E z7McTgIQb-CeeF77GkUo-Jv7w$G7<5Ht_>~-h?Kv75_)R$U(@K(m_ZK<r${yYI<aM% z9e-BM7(7Q6hFcnCaMjoF+>8XKdT>#6{Lc&I$ftfBL#jAOp#|B4saNzVa2$ov_p%Ns z`Y9fsuFf*k3EqKfu$WNW{rTKZs(tCExEc$Z5$=ybV`jE$pvgwIs*lq234_Ac=Ip_y zfwmT%Oy1DVr3trWhI)1Q(XB#@Q^Caa#Ow~r@)gR&z`K>LDJBAWVs0)0DXdzZ%N*K@ zFh6<(%i-?9r%^d9GTHSSd%Q|q;Tt5fSY{C-z%F`!_;Mi`_50x4GOlQ8+$gJxrkU{; z?K3$<*3#S4$Lqm=dIn=Rqc{+K7dQ5KxR=so6KLz}{Sz^KBn{*;Y*lNQu+@30)z7uw zzoQop12SPSA15P84N)#Ge)>O_+L>Cpc!=Q7X9?L?eX&V_;XP_2xVSlzWV@S*pF9=p z9wpy@4pt=}xMRL!j=*QiiESBxP~W|yfrGtKdPouvFL52lVne4Zzl^2g-YW`hedmP^ zDVr)I?qBUMQ-J#hm8EkUwwr`!x}2OHzC5&hovd~}qPt!>P`P<VyI&6Oo(j{cApX<x z+@L*MEy4VZQUC+>iC%Twi!(GGVL*IS?<K&0{85Pn2;ghuW*Fib09Sc*vo&|AgeBTv zAHK>`X$aOXxfJxZ<+OJs^Jt-64oi)Vl))396Gh6za>Ir0Uxvtbv~15<q)vkS^->Z} zB2Erpdp3zZDa{=;hMc)@d*GQ#+CPp2kK_6uTf^sbZ`Vd|Cl?R@w+ETZ*+i$WMiB?* zKUoIe$XL>65=vXja3v?nd40MKIq1(Ju8@cZ0Qq3l!M02yUF)f`*8;g%?GP@CikB$J zCAy}E=84m>2tv35K`)w(15AD&oah1VB-IbZfs3#bF7AlK<6rYW;&6ByIyyaBN|kp1 z#3%>c;*|ohaiE9hl8RXwJ<Lx2T6W`@8~0HchXaLyxB1Lvr>?s;FD=IjUUVYuX1|)? z6QrVI%o?duqb=`uO+=CyFeT$s-!2lkgPNNvF|V=ouM=SL-)#hq`Gt49h-Z6N|1uEC zwV4pZhJV^K|70r+QIOqO106*l3&j#47)50UjAPa~@HfV{n2H~v;{M7}@R{Y$Xl$*# zvY>YqW~3m=CEaf_&6WGh+b47s)cdfzD1IkckTAj4`sdVUT=7I`ff~|!?KK7TJ^=c6 z@IyZK@8Fh;l+MwBilYH^nMxgN#0*teK;T3+!W8e7#i65_TzINNc5hGo&hsh1<^sKN ze;BQ^!2gqQwYTjYKBuMCw+UD2)<p4q_k(_af})fT2`x{6CT~;$GiG?j{en_sbudMU zA;ZHC!Ni)4%H7yAQ{9o4j;*5*(U8rVoQipPe@P>z`#v<Sb)pmOG^K7mY?#4I_5TqB z*mN}!9dL@sc@$iTJ}t&Z8plH?+**JtL!FuLguvC{-baH|0)0Y4Ah{<f6s-A8U_~d3 z&uI(HrwZq6`W0w}+zfxoZ>MqPFzy9kTXe20)G;k2UqotxEc0@Bgz_3A7Va3+qruXn zNFxu38)64dqDI5>ec{#%i248h;RrQ9KT;Y?4DMm+<iZ8O;~Jzcw`b)mr1rs)*^PQC zhg4K-4j#t{igC)|OcT6phB(B!binX4Skj0zQQtZ8T?Q(%k-MalQ+`n<o#64;e-dyW zI`dXLME0b?@?}3>GSccT*HzoKlR;N4lcv@!IN%$r&Lyyv4<#aU<2b-@<D5fr(6Drs zq?^H31W@DTHzXDc2)%<;<bPduMnQv9LxbSO35=5PBmEVc1u2QHvW=ouUduwrwR+`{ z1$bKqp=285zK*Kqa}DI(8)jdG!R!q!CP!zaf+Ruf_22&pRv<G+g@|7E(N9jAAe%cA zLUTuFhgy4~_=qq-RGfQdLo-#NFgf#aC8~|16<9f@<_P3?dD}2!7aiX*$P@OK*p!Pp z_S9R1d;X~erN6;m7xHx&1L$IrO@OV_T7cFM0^kf2{8W0$ztyJp;KIl|p4NU9-lw$A zQ<ct}(>{HljL64vFn*mJoP4i)ceg#$<b9jko8y0fOXX6=_Go&33Elp5A($$q&sVeu z?PhguH6o=zlo)iRbnW;;$t`?eGrb)@wbion%Wlr3boAcN=6SlbKrGtqr=ELfda@!5 z{&W^!@LrEQp}%wh3t`&d@FK!xLd8i$L2Ctk%Z7rsXckfYEoFFm=oe(I)+U5>Va8L( zfLSzHrGv4M{WlMk&wfOR@xB1@k!~m;z2WImEd%=G80?$c(inIDdX0JF3@Cge*O_8F z$IAQvv7E8Z^Y^3+VwoAR!VFk2$3JlX-?8-7wNsRJ_Phu5agB4>WVex3({vH3s0$m9 zyw2Ld&w#ts1RMZ@@J#~D-#hKs>R|J_6>^JqW}#@1_I`&|m6}a<pDhbZLgj8a2Fw`4 zI*ZvS>=DdIh1t~Bsy`b|4rUnXeePpIA>zS40PLmxZ_NI6eOls7J0`C4t*=BLC5T<1 z6Ix3c<$ch%Aun^`wd$bhU}Df+{T=ffvm@S)y}nTU5oX_>iEF5tdj1FYqN3CUVY@XN zj5LChv<*3l>lt{pySv}+&n;OPG&$Va87jsk6C}$JW~zJ9gF;uOWjPAAW1zC*)NPyx z&jt7C-c)M%f-@{!V<X!85?bE@%!&~i##7e~D3FGj7(G;@H4F4(Vz(BdW#l$#LC&a( zg(#gzV~MknssSezs~hC~2}TtDWsQQG`XLU9dd*W=Drl)mgi#fXNm~}27S0nzhnu#h zV10(^nP~yIT7+qN`FKWaqjdG5Xol7;1ys}^+TcKfnb*w7Egvj-NNNbF#tTSsSN}<< z^{KE98lC2h*DN<AtNQ)d-Z-pJTk#kMB%?jo!`qcOSB*0XIB8w?bS=#8O2M>2_un?G zgfGD<)|PyHF!s{X^#3SQ`)shHif+NA2mSK!uAWb_sG|y+K?%iSA-<+nGUD_5WJW%6 z|Hw$q%KUw>gVDTT&g_r1azpv;LHOacXyB>p;f<=&lYJaH5=^1^Mh5bGZ*yZqy5bdj zEfZW;Eu;!QRY$55QGnAfaJYzE%cD(ztCNS%dO0KkA>2Gxh%gVI12R^m7ixV}xP)1j z6hOP&$uiKAu2O{2bZxMP<Xf%Fiz6NP86B)&`^3nI8;$s1oIzIu+?;K>WIAjMOVTZ4 zR_pzqk)MthG=FL6Y}?O4#WZNc{|aWv5m$nUpU6~O@g!RQUvYP5ngeO{6X2diG;no< z#HNsC;G8vunuAgYPrq^4m6FBLR9cjhDE74{)%&@M`QogMGB(JU*J%DHddym*CVhMr zTt4B7O^wgZ1Nev<{UD};_s`6U=y6s34S*{W!w`8oX)P^7HEEVTBbJkV>DPKG$9kqk zigR5F3e8aba>6bC8>hGTNUFt{H~*pG1q;iWBli=#FXAI-IPJ1#*)#}%-uk<L%y`qj z2hgzHP{nac-@~T()iyR!;fy}vl|~&?XdeR{*$bstLs_6OMDAyV78I`tCJeQe+^^VU z{TJYbVLR*nE;luIeBW~G{GY$|=_?1G6VS?_>j1h4)iN<b;7t;$E4mDhDS>T`*^1x+ zvI}BINLeIu4eD9w5||BMJd{a+fgK))Ju*|Cw{J9FESxSB7H9_fAe$t&;F-F?m?gQm zsIaHqEq74(|2<~vi<E;kfdHM9gcA<&xs4PX9W25QAOdbu^dnUBYhtjj%*M1c!_8yt zUDMlSBYz*#U?T6iP}<ihe_w{MC1jl|aB?5Li|kIRDOMrE|0zcF;im<3@bH%O&!VUh zUZI*PLvGtD@X1J0dO!OinMEEh5#dg2&jd;|#XmiR&FuXRl7i3^51O}3oSXXj>q3f4 z#FV4u!K-63%3LPT80J^m-W?qI(oCRRyaZly2v@(e=F73U=eZMIyeon?!f5igy1%{* zZd4iRj}oZ|4{#j;`iIj>)U-=m+O%Tx9OcW1zK!jglyfDjshi$u1OBVSMZa^kWVqtD zPnwp2k*H;9ztsqGqG{wDhx6?9S(-HpmRj)+8<$I+ED)J^euMh_0Q~SwKv5S-v&4HL zNNeT|G=c96s+)O0&A^9#GcD{xO!+@99`1B|4#U+nQstiU3w5tG*f5xRE3BHY7|;Y4 z2kvadah!Dc|BaZ&BSv1=&ab(2$w)QjX@4TT|D$~Xu12j2127P<{ccyMM_TA;5S8gN zIufUN64tMB;_yc-%|+qu=lQ;E%lvK+^!PeX3h*Z#k(r81PsLfH`xPC3oSj@2DwmUr zzlwsltl3djwrJJll!jGtVw%iQGf$T20s*{%l*UQKsit^bNhvK$18)}j?;-Y(i;>t- zSLtH1?FSx)7iW5Ru5{4^sVk-X#Vnh-m5ilQ+E;5M5|{4bnc4vhl$(|<fE6FfJ+^`C zLd@!HGkbrkp2M|$mCun>>l{_*93{Y3*1k1zEl|=v`E7IQRP(XZrkAAU4i4RSe(&$@ z?&j78``X*KQ+@9*Fl>DC-Lu7^*`+$(<B8pcYi1_r44&5DpUk$Mhg&kkP`vf|tD_m% z0|LPG!(U{Dr4A4Q@+F+;<p%Z%0($U(14SAh1Zo<?-ou1?Yx3Rs0oc^&0YFJ(KnUR! z1+V&$JegAa65-zwAOn$dk+Db$P?gC{1S9|kAt44>&Wfa<h^$P-hY(UjSQ#V^Rak+* zEFirU6=~j`%OUp>k32cd$H^X47K>8%tQ-DDe~pHC?EN;kwk!kLvWj7YEkK;5a$IQ? z4u=X{2HX`+C+LaK|1sUdQlO}7_8<nOJBr4?(-+L0_ImHegakp&^w~pg_9uX1ff11a zP}z{kTJBb&nKS#erL1IzXDfu5i$Z;vNK}MT*|EEXf*khk2Ph+g{7AG^QBN^K4I;!t zex)~IqDgIR+Q2f=r4rmK4Jc2cFo_ECz(Lss_Q$#q2W*QDj-;CRBY7Huk#n_y({R%? z0Bb#UtZ!t@8Kp6E7aI@#eyjzWnfB5ng`rW-?v+!kd2FgIbNa-rZVqbsX1rhdR`>Si zeSPeGFa4wUowrYo$AUrHYh82OA$4d?AKl&7N6I8PDU#v*3$=YsR<u`@daXLm$Jr@X zcba6e3q6B~4^gjYa*&^R)*liS%ge~nVp}+&Ce5DqxubR|a1%v0qGkoe1+J24(J+ZR zeS}(*I*T-st~^w2f$`OBIwIc0F6eJfNU3;F;$g7GLBoT3;QTL_m2Bkv44QZJ7WG(z zH5+}f7C1B0J{q@LQ!4t}FW;~1-$ZUR2B+$gf$$IJt$G^_v&$R-7i2SRsln<}x`Bh6 ze~*8~f^a;o9L2ypZ|E4TTD@mR;8zt@P$fOmcW;q6)+&?^52vi7TehSMl@nc{K#7}~ zb}G@O{+s%>XliLi3Vb%!E!6*^T=88a`ItPz_i6tu%USl_jr?8x*h}ewF?W&l4ijIp zc!aA_0Z4=V3aQl5U63yrKHAXl07<jotva)_Zn3RttyyaI*KE1mk+xn)dYqfgP5nBN zQ(ziCNKj~A=Quv~-{Rb`cY52Y+PXS#`Fcj1VVw=nCdFatgH|Z2%PtLLM)1Ll69VhT z<r1H?<p)heodk*|<u(P*$|nN)+3W@$n&ZW_0f_-0zF>@HfiHVwz15f#i5j~awG_-3 z6X|!Te$>fgW{3B~B`7STQ3XONBJCn|&_6pku<wQxF0(oiQKGzfFaZHdtB>NCuSdUW z9<;A+@|sD%t@UL7;@=Y9CvS!2pv@wAaXNw(TN|C1Ok5Q%OS?naYcc8qVzn5*dzV>n zkd<VNAmR|?H6~)IULo&1CWZk)_^NS9{yy>MsLLPU?E66wRrbc)AO0<A0^d7p8{RGd zp|7s;yvPpe5^lNKwH-;-%#E_^r&t^=N8w1sD1((%uyFqE0w3~U8owH|N#ftV6twq# zhWBZDM*VPH+<HumcMJPNdOLH<e&-fO3a0=4AYKzlMbh1vZ0;;Im?h}UJ~*Pn(A6{# z1ctL&4yiJ~a_v|P_-j1%9bNP}xIpTDR@WK3{`&p)xLtL*HSpp8e}S;9b}+%;0LvCX zdH;_F!_uv-tsH#afLTX#RLK<#!7w9ym&GJ`VXWdg*ixntW9$gLdh7!W7>;R1gq<7& zj-?<(k%0e;`VH(N=>IS2_wYD4r0-%j;~>!z&7h&qQM)D&8oc+yK2Xw*3Rnc{gCWN@ zq+Rm177Y_$4*86VTY-co8+y~GCHLp&ru?4S6RUFd^}S3?NCaxh1uY<*a07bq&8MYA z`PJ|KX#K}h89h8^kO*iYBR_MfS5!k+Utbz7PDCz}aH1KwCRL@!1l2$i$~=1@0vPI- zL{0JYo4$m8ujIxC7$6FSCAmmU7?|bpTam~+NA9nq`NGA$;ABYBC?(X#7c5LEHAG=A zLzWE=xKb0Ct9Ffhrd^a?UobHi2@-1&{P?jUS_9g8tRJa&GqiZfx+H8^16N#3Kp!|B z)e9HfDhsBDO12<G7*uEg>0ed6I9_3UGz9bO_?a(Ayzn4V8|iH(2N%TFKB@@CQQtMY zyd>&<R5WSvV=@*y!QS5Y7XQRcA<@Wdqxm(Lqn9V@Bu;d!<A@fmxFa|4)~(B&-d4P& zMD?rI4O{N~Z)DMwO``?_QY$vcgKo5NKw-a33?zaC8jGB9{0N$*SyHvRxb%Ffs}xEq z8Y8-{$@j4QU?B^_i0gRMN_V4W1CYIbRI7@DD1Xc(ifB?Cs)_QTL)KMAyf_mA*QgF7 zs&RDRwXftJT4ev1@quQ_$10f2(GC!>S+*3@KlMJ!1YB`8g+#=UbQ-7e+6ptRnr{$B zeZBqDqX`18ewmKMe+;br#I@v7qRLh@{!otCpTj^y$I2rT!29DJGo`J=|4KqumgaH9 zNR%_&1Atj)IV{K6NQhy$ll_8z=tzuM0WrvkB6emb<9Jr1Aeu}2$m(vxXM)_>JQOIX zLkEGtC|h2AQm1bmJG{&g<{_laP~nAP#MM?BUZOaX6_mLec|az+2;hORZ<*4_HwKeO z2=ctAuc8RNEB9=BsA5aV&>&8fxFLf;%+Vz%ad;5eU-K;eMt3RM<AXmm*w%*@E&X{n zq2v=-F3~f$^xeD1dtCt|CUgNh&ECgh%?r)ukGI#byl{l$v}H_(8j<KRNxT2@j+@S{ z!h9K{gGDOzf{Ybm6?AQ1BkTQo2JXRi!nw-v&R-wtpDJQy+;dtj=wmJ6_?)7th5<&q zoyB*@m2ZFi)ZX}b-hF0o2glb&L@}!j@`~>=eS4-(5?4obVAWPW>YC_tq5X(S4PY_^ zL9?OUlAa)<2W52NQw<w*K&_wNcxH;E2~h(|blwF0qXt7nXiOA9jHbwT*6$@Rinb(v zu!ejWaPN}Axl?R^-Q|2VnkG2E`B2_9IGgVFPhi_61%4fs>AbC`{V8nuc;(1bZInkT z)ukq@#<TJ}$hBE(fgt<j0#1SSOHgXxp70->!F2e1CtNWYq>LC_<(qw0ZTCYO7Rnz{ z;qk5z0Y9AB_$LmxdQn7Ljt7>YT8~h_CV>gbvKz&dpB3s5)%`H~>vZY-1lLcE#8>4Q zl4r)iLaW&&uVw1y-LEIW`bcNjs1Xj;bd{0>&Y2}V@Ykw@mf!M>`->0oidc9c_{*pf zA;O3(O=>-2_&NL8yH8KR`ApjTwJAsXJNZ!jFu-!XP^=_=v61BWt;MI0{Lf&0`FKdX z<dH=T`z2X>{Zf6co^?ux1LfXbbQWh<W}Y9hmOR&L(uM&5oWJm?bt%3#P@_ES?ojp6 zk*1?ueY1@SWalSRd5pVWV4&xMK67tDz9DDcweWt<z`nqs3^};mz{tgTgGA}T#*APk zoYf0eY2%T&OyEW$of~3kVv?^-raBTU9DgZ2)EZqm4>ltrZ6Myh*Sz14TOEt_UB2;s zVRTL^aCxog7(MZKvV3d*K25eSOZm`KHhDKHBmdy$m&y8o1-aLk)N<+^d$wQ6%4UHi z6$<r?79xKesZWWfLLo5<uyx{Y!Ry|%?pmG-DbNG4Shg}oNS23~6f(r>sCFy6SlJB~ z$5ig$VV+0xvlN_{il<<x9?R`&?2%l++9I|K61N-d#6(CnX7DcQ^(K{5vMeWFlOzA2 z<uBnc2;Ej<xNJ5!b#yrK&Z8?*;cW;2(lL?BE^2>cskaz}N*bu_AeM}Hbl~z}jQ8Je zEa{>UbNLnwg|TBLzb6En(Ez{y4n6KIXrK}DS-uDctkJvImE2UYQPd*l{VjaiwT$x0 zd4Z5m)p}3;T^BZU!TuuewT-(Ygz^F+aAjzs%)bV9$MLNYQg5@M<MI?$LRM{0gwqKx zt9ZTB#$7);Q3pvV|CvGZ$;iVCEC3%HfsJm1>jVVv0A-lqP!#-k`cF#zRRjXsK8Pus zF!mp_kFZM;dfhDAs8)y47qucGU-rzx?JxXU<6Tzgy2HD-(o=PBR&l$fFlmdxDI&Oz z6e6?CU8wA{HLTbM3j}rolBL4-5oEcv3@@{7i%Y->Wll6-q>u=%6#Ia=qtEmt?h4d0 zum!py4?Mp@<?tEY&DfCFk_<e@wtj9A09)y=Oc3Jg6od|0X=nW1b5VCUa@yHJqM3Z} z^<x7W^83pUVS`f86RVit_s^y2sPYR_egnz=^N#=bCxf@6h`#t^zXS@ip2T-vVH2!7 zZhC4^#*r8fz)T=GDoC5NqM%OVv7fRg;=Iz2lnl{KbDna5oafG==(#?DRa|&=8pmr? zh4dL(f=p8+ER)2Wofs{dpYqNE%oR7cNmGT;N2M$GUqir*5JFDXJp&0HS~7+GqM2fB z7Uto;Ui3Q}gYop*gpMh!VHpuD_5?9zr1YrU9fr+ZiddVSmB^!VY?eC5kMDQ4S!hOF zIVTenWA8LstxEj`*Dd?Fh8S2KmWXdCMT$L`q-r`V(|&6x>?+Y}B%ZmCuEGK4)sUWI zazPFPq}JHUFLUP`4i3`XN;KW60v*ST>t|?h8cE4*=NEcu*|cqhiG~>??anH@Le2f- zpk+9FBAU$vK62Y6045+ul-T{hPYbhVirqJ_Dbo~aRbEBBxr|Mj5K_33Eq!JCwO^*@ zrB9<<w65nXoDa4Ysif|NfD9eSJJm#^>H=AE-q$>}>9@kWrW?%;U-}7!mD#Yw;29iY zY1dcH7pNf8==d|`7-}!;cxgz7*zS!XliPv<_#$dNp{;@sx~RjDP||+vnM3AHcCI|r z7HPCQf+lyA!Vn2E!5pq*PO`@Se2MZAO?&lTlD#OO0ZSbDu3mdERnc$!d!!ML+!REJ z*z1{<f`9lD-Ut9X=}vGqnpMRNXw6p8#PNi8gOHJ8Fos^Ihg4dpkhncH&qYcLDkwFI z`3!o)u&KbWPX&5{`>$%tCj)`rVO2joWfOZriR~f>HRSx~B)CSbMK8QqW>_f|r3Es@ zs7Ax4F@ChnLOqt(L)k-2Bk0|x+O{U&1Nv)9sEZ6pKHh=(&NOP9nw1q3rE%#UhT_gF zWO#{)&a6AW1LY-MoMWg^8eO8oU|{4YlD{8@-P&)Ajf5xLP#0`7WJ<qTdvy}qRMeJK zYnuDEFIBN5xN2(0bd@UF{n<p3%nf{4gKZ1*%SA=~2Rs#GCj3wqjlXXH4*O;PN$ne% zNrHu$?9TFfqoGXAv{C!wQ!<kvA4)<K;Hpo@Y}eLs%havb%Pf=!X^NmcH6XM@9wN4( zbL1JIla9zQkgd6|==8w`IJ^H->2^GJ!*9k90xNQy;<CnM{UR5%G6L%b_e}Pu#bBH8 zUFP|d9w=}8xu#K>@jj^wrvCW1vT6@!U9wX1Y3_!*KzeG``9=sC-J<WHoO^hc>nB;m zzd!k#>0A6vMD6Jme}xn)A!8%~kt{IQ<6NRurlGu~#ICqxT-QSfkwnxpqi6Ty-y74F zcYJsbu)2Uks~daC%J9fqG6r*wqsA|*!sB$RH?9<I_H-fUA!>{sa9UemVc`#M?ViHR z<sj6ezd~r?im==3na5O??;sBj2YFcD7v6M%!(j~41!}+Jt|N6~zI=oo41*{@1DM+& zOdW_&)1~kRc9`u^7Hxr+;0T*=O^qUdI-M9;OD%*qmt6zF<swLAY4WCuGWVR{G?6h9 zB}t8mpB)kD`{!CC!C2KE!IZ<=M;AE40phsMdu_#DyR08A@hb*=Pq7Id(|DvMgYrcH z1U4eOt&p%DeC(He+bokUJi?cz&L)y0os4%E35|qP;e_;q)9fd3441#T{9?z%DVb#o z7X%_3#PTScsbs?-DkbT^mg>_WW%_s>FsA;v)@b&?<#E2;H%K)Kb88>Eyg>MuN~IDF zqsQqgVMtDH%!bYJ=d@#D@szO=giq`qfOzPH#)^D`mUSZ1zy17GN%u7o_?RJV<9d~= zu=>;vJpAZuT&2b&GwCEoF$bD2I{5ne=O5Ifm&WW5r3G~|&oFuAG%NRfK85YvjynpS z@-KMz)%&H3+%F+-4S$}B3hag12g|DJNuUkSq9{VlRnIojju44Wb33&$ao*fi+K$=r z9&gE|UeZ6{uzikyKU4G6)?Wp{Bh!k3fl_(^yXFV(M$QfMy_4wZ&(4bEYwe<%Dcu%V z)hlRv5qtP|v=#jMU=s*UC?)&lI;YK9Qcx$)K5&v92TiI}+b0k1W2F6KQz%u8z3Cw1 zDy+Iy1}5D5#qqMnR0$4nI7J@vd7(|S63a%&ER;hlU@FyeBUl5O=Z5|3vpUL=CDKy- zI=0tz)&)B?KzTgKVH#03l)cEwv`$xv37}L7t3qOfB+R8=1F~SP5PvBijQe`dR@W)g z@hS?F$7aBx$#CiDvlWZ*Q41NP2;BlL#W5uuRM=K{3X_Zsj~-Af8~sAaj~q)M!Vz)` z0T<#_iH$1GfaiCqRCfWC#pqPG9~ec|+LXt@>rc(B0n4Qzo79fAyJq3MS*bAk)LICC z4X?pWP4KG@#1=v)!cbZsu(b-Q#BYtbJ97YL>>WC1RRR8mt5sdAIZCEPeA3OfjZxge zi8?5ohDw{0y!zK{{lU+IEq&o0x9C-~%J78hgD`mPNFD|%us$-?7My5os<iiAGIP-2 z!vxXL`=FQ>G_wD+Ig1PIg?=*p>4jZ*a{o>hH)@VDAS+861oA2Y=bi9DiV*f~74R7F zlaT5}obj%PH!IkTqyz-2NF-DJh|#bf^rc?UtzRy=YyHP!M|WSVqy2~>XW=uIAC<}` zxlF4W{qTqGZm&w@hDkI$%cnRFiCEa{!n8wl@`y0(^kB@G@NZYqR)O(kMlvEO<x-t* z55`ennU9eMqR5e*rDXHu_FG|LJ=n5}Go}9kf7&0Xs1N|um{q~5(uGY7j(@^9ACN4f zyk_$F`McQPsr$okBXp{R6v&?)$?Z2{!g}DBIz9VUQ9U~TARcl|5@t;i+go?KI^(=` zvWa#3f0u=~VoJu-E@-Fh{<L9#h8Rx{kbCc;{U#J~NhpF4C8ANh-t;b>+azRI8u#_l zaq*-Oc8Xl9K7n|7ha#!T3<Vcj)f)hlIy~Z9gt`M(k&wXcL~P7Zy&m-)K#puok2sTd zG21z1sTvTYs3RMXF0!o^zw#nKm)I$O_8e6~e8wgx^vEVRdGu}Hj%_ECS~?7YVGC;{ zg18hj>7tZ?{z!z$G++({h5LT%x9tdK@F@PaJT;s}drTR%yKLftEpkY?vEWW85~CP^ zp$mtC$xma{=V?s=wah}m#w43(QJNvT42lT(RbhI)kFRCI;@1XW*~7C0LMZV3@%HTG zT~a-GKOBCr(fXfh{Wv(H_vD6WoT;M9D~1^qZz+moHoWA}4XYl`5;K{y10J`S&BCWr zNvSHlG=&+x_4lDJ!2^?}83+)A7J_@f7dBT|rMS!>085zhm=*{bC)eaB3J#6V8D~ro z;H1DVxeoQSpIlzntrMs2mv9UW_qtAuW~sJ9F0&>Ox!L@2<QP>HK+3n+>v6$Frr3Kl zb8SlxmVw3-f9indPL)uf`F4?Uy~!$wZ8s$N?9t2KZ-#$acM$0v1JVxTV<eXZEm(gx z%yoVRu1<NXBW3pzWk?MRrx#h0Pp1!0M<?xwkwR2@NtQ*{gv=|s<qj%Jd$8~X*ysed zRmi_-Tb}<~yug=ZyoB&cah+Emv@%clqDlA$IhZic^*hFpB>nXfCLxOmxmS;D1vf-S zhBqR>SsL`6BvwthD`&-05MMeJM$)`|)p_D+9?_<bM`{&QgfRtmInL2)S8osBtp~|H zTQ&CHmAiv_BqO9c_{TTEdQ0fO$a=yvfu_5?H=JV{)q34FqWmlZpJU2La+Is)L8b1d zmX@8P<uF^(*5*2GBW7MVW>R`LoD=JqH*}gQ6Sd{qom;!J{(f1F{cju7WjST^{m<26 z?Z(<4oh<D$o4Jq@MwbKTszgs%4H7zX{7hcw^CI52xv41|_ZGSrj=aCzu&eyfZPU{< z1b-G~r5CEv;(b4-w}&Mv;0|05o2fS+Cn2HIO4#wl7%($cG!lg{tW&#m(=WFa*yh&; z3$%{dn%33D|7v|`Y|jdsCs(*}`<K+VLwp0PZRDGymfL+~*O>@Syvl>-Oci?Fl2T!+ z$nLJXCH{Fr(_J08xUJo%N<Pky+jRi5@(9j{FYt=<p!XX!C%?fiI$IT8QQtZ=A`zt` zqll^rw*7J$jNypAvJflpi|5Ai9As6?Ij9UF^iGA9lY;l+NVtL(puO8myImkMH?C0o zki<zuQn%GA;dCpMEE+Z}@AB3B*z6I5_m!>06gVf#$jD5_v?LW~iaIqA1T(C}GryFm zV1H1kP<!>PVI!8lNv4FhtZ$-rVSIs3JL1&}%Iner&6h3Uxm`%q4b^;DFtLm{K>Oq% zp+Lzay(fZU-2**spxdt^Px|R-19Rgfi8)#ix!V{)#*DaS<Sw@_XQ(_9&*%PFW%N60 zyd$|q7J8$E2gh|o)WpO$qXMPw3`vB*LrlI3|6XMjOwDlr><qF-6AjM04zTKvC*ta4 z6HK7ZA(sZIC%gqWc@*)HpLG72)OFHy=NwdTT-;JcNQUkFBeb1V)v4{2p|iX-b(5yE zw3W1Qcy<MFi62vPSMQKzcuJ?EKZLws<>ve|fk&D4CbR7wZ+(45V?V&?Oh!j2`NC)= zM^1pSv>&G^tVnR)^imoU{p@WoIWSCy2q!!c(lg$jPN5<-v<3d)rTY3f0rV+;fCcY> z_w49Vleoevv*^!`Zvl@lW+I3kiDUTYSK)_Nb0?#_LET~~f-CY^Wbxbo%vMJk7mDAe z6BCX8QR!0{atT5ZSMWKY5WS~T|Dtq#_q1R6^Y4HvL6y6snk~>HQw2s^o+ldpt#)_! zgjH}s_^;=vLRb7e+z%VBrT9WsKy~c<*|Wrd9*|qDzdv-Q?vtj+;nJ(p>(V`*=c=o| z3?Y;y@*G|$rdMOl+swhVj@1d-mXFTqq0-Tk3PidI`K=ZoM!>S<<Q`5!QUHg*>W9ol zhAFB*;3_q$$wp~9IDGIHu9ILEA}Ze4`iR7~xCGb8W#@BF$hI^TlA5<O)8&{m_z}ht zR5~hih<t_*@V^ava(lcO(0~7p`8}D+nPwk`jpyu8I%<y2r<uEcgnwa>dEFkAzmq&O zPLWbV7t;ilowcsQqi#u+kW@c?#p%LXc{FN+0idnzZhB6gGAt{IE%LIH%?_Lq{37v4 zWe7t2h?thUTYQZrcU1ma?;zjbA>D)J?X<3Hf2hY(w2Tj?+Ue@mYok5fODX*Rz&@3a z?teV#`{hM&g<EX&VE0J>N5LpzqW%o+bw}s2vKTB+otntZ;WZp(Z54KU5;J#x3zcPV z{`^lN)%j$tjoVq^Xj7>92DT@23r35x&y}06fD{2J2=3rua&Rs}@wp|ptnKY3tR$!C zaXQ23y``6f+7I4u%-1AhZ>Cm>UQ0lB9UOiHkGE{+BMhJM-F_>v+X@KlAzvEs9lz;! zoV1q{pe4bo1v*B{ad^@Q%*lWW8c0VewGphr2p68lf|liy5JAkcLe4R7pE+SGC06x{ zPohSMq!YW9Q-u6vKS%A4AVCqb3HL|Nr6u^W)C(gvbdj(j@d<H~;>01DiU<{p{@U#i z2%|dG#0&H>F}}v_^sgK_tNx!YXYX5m_lGzok9UdPPQdHgInwb5f}%M3@GW%YZSfhF zN7OFmA|)sTRMfw8rr2{d9taFt-M~)r!m`tUU-a*d`6B-sQEfdA>OLTD14(4+PEsO( zF(a98mYFn_U2Z<)j!t-rErdNvF`l4&_|sQ_b@O{_Y@@Cg^P&*onPORmYQ=*_k>gJ= z7SOs2Vkk5V+pMGpWagNKT2&mAYcA<D6d!{Zb?__lNRbCX#lUd(n9B(@u~1MlG_I+6 z%CVgkx>NL5iM6d<<1d1P=@_2JAJ}H`JEi?)!Z!!>qNk2@YE{nUVyV>i2W$1OW??wo zXLM={>ZDpOs7+t0S5>Y&<t<OGmFV9^umQBKH{iTnGdLn^$BOiKN<VLam3pV9;k;$C zi7{TO`4IqUhZekl%UyF|!TVA#6~g{h{GGKd3Fjb3O72!-B0CXQb6sY;O*)U6b6t0h zv1cuTD$y6Lpm|4?>P4Qu5-QQh?EnA9g>cin<7~-7ZwGm)&~vKqf8A7Q)bbfUZCkL{ zRo&ZNnG-d!9Tl$iaKu`1udUojfny`hg`QW$S@KkNwc<=2LyeL#k0lmt8bhbU6mYSy zqhO#!f^m?GuR>G?xGx?2O+Ld_<{tw#!VVFo;>jmg6LpiKI=0KE-ahT}VZO)rbw!Si z=j$jQBiLQ>4)^15VN{7us|%i)VWXJfACh$n0714eh~Df3hWC~3JXqp3efjt;qefZ| zmuJmlNz~k-LhBR9YkiYP&2y!~k`I&v-`vAqiX7?oN$X8C3jH6MhQ+jY`j7Y4Rmbl@ zvNL%dC)94#N@OXdr{`|*lAlRqe~)sj<&Bw1@|lh5Ad|~MD4S+7dC4AkqQWu08eFQi zn~X$m(O!B75Yr6}71nUh6g@Y{5It;K-pB&lk}c9gbBD}R&9J(}?FRZ?o0s4EvtCf2 z;WPf(^)vNk<a2*jeI3UA14E1B^(xvSXwS_G9VY=M!JIfhe8I8)CQ_=!$p}f>^`S+_ z=-BJeo(d6PQ?WXknJtBwSz7*VFAoJxwUb=XN6js){maC}+WlCazed91d?Z+%vF&T2 zDolRT@c}iC8J6}QH_cz%B>*h4e6m6LSBD(=S&ldORX#a~3hx%=v*#eTst#R3aalR+ zu_=tEMa`79W0v`llt?Rr^SK?d(R~V3b+7%Y(5!F7Gy-5ahv@6kr(|kkYZTHPJ-q-R zltG4^<bxq$@cs`KO%eN!8L#J=63~eA2B9MD@(l8FDD=tNKYOhNoBIJQngRc+KN^PP z72%SXg=rcUackq1M12<I5V|>IVB7ky&TTJ!@qii`dzVg4GIhct5o%RMWGCec6<{`A z7jte^=TQ_I|7iJ%@~}z*??C{w1d}H$@n>>h44Bt+PE&e%J5IG$h$HD9v`kCv1p8%E z)+JX=iIi7Buu|0}H*khzt|KAcUR-ptDf=S~iQB28QRitki~vJ6wvI|Jg0o74<hpkG z=qRmE!h%U<$kR83&~Jx;`uF&c-r1<jvS0AfvmV_*qI4OW4S&jnYEZa6$c!A-xJOxT zWk1rfh?{a*=pZ^^&Gy&P%w6!tW=IN$vj^8ziJsGk=VcZQQHp3Z;TJgtY>q0we6F%P zmt$!(iTcCSrR8bWJlZ1(fgUQdg5Npmg62!Hj@42Xj^M=Z!V8`qmaJl(Jo+H?mh-#n zMvi)|Yt0&;4;lf3fTKPfVQ4t}Q%!+4B|VS6tKaKG>;3Tq2Z;-~&pch6tlK){F`$#` zR~FnFu88s?FK*Y-nJ|*nYT<4s`BBxLv9K!*X?JOP)=#}i0WgutmMikH$Kh_;a~q26 z;0BdIF1$yr;);^QxrdHa7$>!~U!m%!oGjiS!@XbG*U8J>8c7+|Wye`(s$5xlZTgRZ zB(N(+TuLFSDG&yQtK6?CoGxrkM|Cv<rcilneOJkOHG`N_z2=Y=<Cq<2iXa`5NFs$} zET*>oqhKGm{57qg;`jPlZ!YQ^ey*b#10TCpao3?L*K}<j*}bH%=d6L64OJ%iy$}G_ zrfK$#<NgvV(rT|(FLaWQtA`vPapkF-VM{lf3H$Y9wlDaK^d0b3drd^chRGW@yoFj$ zxN3vuLQ#VhZF}ybEr%Q~H=4A6KG3!TP*+3DsY+MC_SQb`AXr)-wmMg4Kb7dm{$cc$ zX+2u8)m=we{q|iRXW04ga#-nE^INxPuB(T%Q~pZ1#*003<>z@;djA>Ie?L3d)yOsw z<?_2P3J$Yl6w}@ZF`m>i&wdk@GwnZ<mOan~kj58Bjhi{PrFD-*u$w&2#>C8G{iA0Z zqbhp!lP<cJU17q9a3orBJ7W6c)yC%G{d|D}stF44A*xyYoNp`iHA*GPt&<;p6}sJH z*WRx8&vS8>^GB-z)eEEC9q&Z&;St{bkr2R+kgmUzLwb1WIt#48?&67<`;+LN%&bF> z?4{k^)oi(E+=B*@e>L<$&ym<t*oN6HTDOra{?smdBAX{8TOSA4{2{|kpz*51iuR)W zV2TvzaR+(<e#Zmg;a9C%-Fos9vwRda-@;$XR2j|q^Ivnqqg{1SDcyZa+YZRx9B~^J zpU!O_`(U)2BS~Fy6qloZVU<XkvBqzt%kY*hwc=oV07C12fD{|%?_E>?i652J4Ei}S zUKpP-Ovj2y_yqkKcrK|2pODI0pJun&of~s@0@Pzy=~Ix}IpgW&pQ;7Yhq%xolYA%% z4M7f+lj5E#+Ty<@{6|Xjel~Z?F-*9be=Hp`|1K6U`ON2YYbBO6BTYg}1vnHFO@ zQbY#ynGA_-Q+8ONe5#{R0(ah>r<SaL{;EMS$*gFok0wDuE#WMK{uP;^LgQd{>{z<f z?t3eAm2Ly|8F`Bx+;gUhRBYRUr241R_G&%HY2^(&RyB(yq$q?1Ca8UR5ACEu)Ft2s zYXR%o7^PV&N8{i08RY&L!@%iv4(Cb2%yzMPLOmE2^TIVaXE4HPYKV&^p3arG3+ue9 z2HKw^5Zw;+rw!2eD}$h94`Qe9pj^I=!hgw>5~tUE!QvgvPS~~pug8y`%g=@dD}Jw# zxX%Q8EY}U3dofY8(uT!&fG}Z=LJYuVPBX~_#zzdDz{~L%8PU`r3pa#sZ9*65Xggx3 z8GUC$Lo`GsUCh>y#^#)7eLI=AW*K`|o2Gw_DVIp-VVz7SzNI@LGBq=R4d}r~R1-?} zS?3hgr9>Us5Jg59HTM-s%+n!4jB6TAzP_+Pvm=<qM}Lp_5DcV-h$V|u92vhaR(@PG z6|81i-V8{F1Cg@~i`mI|51#$Bp!gStUJ^vj&8h|?`t-wc^-iwAc+`noVzCIszxxd9 zmJoCl7??}(13EL&T|jKbyhRvoI0$R}2vVx>cgarPf25a&bNKV`{&ku=?7{YRd)G2) zDG;rixF86c+`{x2DY%N?VA(iFzco4rDEy*F16cm_-L;*PFP~Vkt>jDJn^~qRic0<p zfv+z34&rF%G!in?J5{xo;bQe&>dd_|5FKF~f3VRm-u*fK078T308j|eM!eAVdrh8f z<z7}EhUKaBtgs*gZt@RK9Dm<X<yUzS%?QxX5g752VCqm|=gZM~UiH;EmTLPJ1uAZt z+v&qY8|(7_DNPBZB{hqbGO`-g2UjxO#^XhZDpE;k_<|@>U^Iu<=8t(*=;t~eYe<&J z*l8pp<WkJNUaJWCOYKu5ouqKh^)|wcEKNBev|Vju&)WuDz0|o+wU>zDZN}l#24?6S zB!0qsJH(=G2~GiLliE4U5*iLJfoJ;J2eE7INA+}^@tSL9;LY2Ue<b$n<d2*u<!!F# zXWQ;||Mw~PO=-ohRuCwW^i``gEr~+}Qefje7i_x$i6vx}g+@Lm@4Cppn}zcDDSfZH z6fK({DjmFB3z%5R+Qh|GjlT7@8dyv_xb^bvR7BRhU{{TVde!p@70buk-|38;j3RoE z%Wav3)`zaX1Ff2+U`$0J;o(s2F@<e%)nCnvW@u?$w+UMl?y8*8@s(X@lf7nSwf%NU zeAVgEKqp7AkZ5>f<&tPeX>{>Hl!hJ#R9?Psn*9XjZMKNm;^@&9<=KlE`yoRk8cDjM zL52Ze-Q@q#^UGuy4LV#yT63?jdtWWj1u}zE)~mVdz40=~=96-$4kMks9|0Hab!4&@ zicc{|Tdkn<(EADKDqcmP4#eXo34woy)L7<NO<Fa(-y~Ga6WuCXJSMT{oQlbQ9Tln5 zB(7*_soQsu#OW(fSpCw`-@*b&a2LA=KW!|{&v}P>7d3ydA8Sa{fEp6hT{ceG2e}Nc zWeK^|E%D&?^@(QltEDD7Q(AaUm=lKq-Qf&o4<~1fg^F7Ectg<^p{gM_+z9h8U~N1Z zeg3l#wy<C8wATI6AzKgAYP+ZkZQ9Gizr>|k++Z*b>vmHqvM-fI#b!9b*??9(C-w9; z!00s(^?{6y?O&akP~(9&Q6^ZqTA2W>xk8y@#%xHUaWNEq!bWU|@p9$#0qQnRB9#w- zGET0x?lu1nvukV2!FFT!vBreo*kk18A1r8W>{*byo6SHu%tK-(wWj2GQt8y*>{Zl= z1AAcP87gM$u}IQ;Kr;)RkbKL(AK)f!v!IoYlAOkyy`)qZ5est@FGh!;9ktRXP|V>c z%1oGl-GOUfnL|&#>FVAO!lkY7ed)be0Y)LBTQzBCHLNUis?o72sD946ZsEwFlhKxW zOhFk?-c#6ZUcw?9Esa@<=+nIS?VRX44P!-d8pv2|lm^%dv}!NGofbEizCVzk1MUY# z3|{JTb#;zSB;qr>-Pb3dmOR^*I(e>-R}{2mxH)Ahmq3Nzxfy4|^@mwbUs-}1gaZo3 zbehau<w@fa+wBA_e8rF%ee+Gl#Xsc1<s%~~TbXZ!dn^1l;<ReqUOBEvzZ-3}$pk6G z$}%UQVrn+ftJ@`?PoLC}tyZ}a7(l=AUt?V*U>VqSRAzh4?=ALRGg*(cbNl=jt@wom zl!=#WrAe*X3{K_k%n_v=ZyA}2iZHMk`I{2I?s?9W6^3gmv?mgnNGvpPqFbX(H@>#* z&f)87&VBJO{*?tOsQA_Jvfr~W&vdq)PV2-%ZA_&ppm$bo*xC4Fijt`e&^Xn5)`*%B z+}5jnIT29SwP8HCRjG{1=6dS5mwf!;?0rhKW?7@@tP=Pbc3_RQ>>d}Gd^#A#9KYDh zq9d!@7aH<iHa(2L%(njBKu&yHsPfkaEgv;G5{q?6(|hzWz`t0@tk{XOXZjj&FEDWL zbp1L(h?sXpBowC67ioAaS<%wj<Q4U(at|!IC~kDl00+xKhtaA#A?W42fS{g;RfI=k zOhRE*j9Vb~OMz+ZBqbzDG%j*JB1B_UM;Jmnv`)=4saVa31!7{{iH2#n1dqeYKC@Mb ze_*n11f0e>&<KEcCeDrTS+>ecQfnH>JRF8Cz0s9xTKq+^S|WubUwt$Y#qDXqh9F`W zwgWS??fZWMA{*W0b>)l<0a%YGIEh=2z!os0g<6wJ%l>=RLddmBe{REh!x6l{4Ki3; ze*w^jKmDJIZ!hwETSur9-{6Ua-$1uNf{(6Z{Z(FL(e?|t1?k}M>9gu)TjQc^`Sza& z*m&Rr8i_UskP!;Z?#$Ets0V~Rdt>F?4POH;(8nV4>VgEVJ7&YqOS1^1P_zwO?~ezs zUhcTlA6>K5;-@iM&|=vX21r+r7FI$Y$L43yeLO+<`jvD>Mk}94XcBy)35_vGphh{; zf>^&0Q9jyD&d*eBbLp3KoJt<$C8G5{_G8}POR#Q^mdD{_(Ilp|@A98+7o01YB=L?F z*z#ewx<-+~eI_*%jD^PmG%Wwfh@e`Q=n^wP$9kq8OVsmDQF=3(Z($xlCy13B8Eaib zrCwBy#Esw?k&MK-q3e??{>bRYZANa|w$<%1#ckX+0}R{rj>)sY!CnI=H<ow4H=KJI z^FEG?j6$GDz~dp;t^bT<NEjBB%UyIsP=Y6DgvYZRyb{D%uSu$vQ5I5Nm0li|C(D0s zm*~&cPfcf!qbrO586h(d#?$Z5mGw@S)m!<F(nEQL#Vc2oSn}mVFEOqvQSs%t_2-IV z`Sbi(&3)CXnt|CiO)I|A1-)$|FWAlc?a*OhI`h?`EqJ>(&O#N<!?)DO9D2DUfg0(> z0d0|}Phx&!mx-47>>)BSr*=X)C<o|QexpU8T_m%<EqnmH<Pa92JCq4IV<ZHNpf@sD zq8k#&Wn&14Csp;ix~>_&nca%Z{sA0mrQd9Az*)?pDcF~t)@V=Y-I~nxm<8&99w*PD zR^i#9h*bK&mTUPz@P!2q*DX-_Snfa)^$%9Hq4L=UuP;3da5*z)yDUrkMELwv=~-*r zX`@VUS(3=zO7{XY;&Yw}Ay0@aO5p@OqJseqvseYF!w@cvQ#N+gc(=P#Ki_m>gn`+; zL!-`2vYS`)#JYpO?CJTj0yESNp6t%G&Y75VFAq4ej$?Opo-Y8{3eko0LAf`(rc_*z zIrpImE9``WLLQRHifFxxW5FIuu^PF4<PvGnYF!kLG)4(U$1$dfk?`QWO0RE1QX{Rw zt3$!1^xPgi+n(kG%sGeZ;rr$IbQg7g>+V8}6UwNTuY#77gg##6c>8_(Q31XkBuVIu zlDR#}48@0Itxv%jvV>_$i<t;=oesxZ39wQJG@LSU|H;p%2bjiy)Cmi%X3V1g1kS1L zJp^$Ko?KMPT_*>@vJhloc;m;AN7efB%2_sY>8qEA&j;indKo?4Cw=lN`Vk+zAU__w zd>-vTAH2lTkB2Yz!yljS{TRI1i%6eh*8Xu%aIOY&FQ$DD^>Xy+{Q1GF_WASYFLs>a zGiTDqy27Oyj9ru(UlO<*29T4$82k|%1t0+h!8l=+UGUI=GAc(_n?`Sm(vxjeBRB-N zYC1DNxG)Cr9L`akvmo>lyC;Fg$Jr1x0jzWzVgdl0Rc1@?1ckPk0#AN-_fse=ujQU| z>LQ|Tm6N@QgfSMRZ5z-wO?_mdm)jGbwfkg9*^WW4=4d_Uzq$yb&o4!_raG^&<AdNi z`oHx*FFacYSkzS}nBL9H{h6_iTU+M|VH5w>kNgH`V;Mj4tm1?XxCn_q@}F=Ve}aUx zPSMJ%I-2n$a|S{C2LGxNca*iohzj~d;h;R)<#Bxd?|*Djrok!yv2~>Xklc8K{bTFs zA6s6({;~C+|7;od{F~l{%Hv@Nc{jItJ4PMTgersXf9f#>$UcAe^w3TM=p(5hA(d`0 zcB^I)bh=Y}tnu2IlPym(rwlBf7{$+wU@n?~mDKn$#Z>c9xSwgEcomq<$2{cS&y>b^ zQfR<DQ)#BaTI~mg+4W);-HG*{35gaB)eRvG&F?<$c#K8p;|Z`I^iB^T#qmO~l27@z zVRxZcs|_ZEnF{7#lRLjT!@hI%0*qR2lwv8tgVX!5GDq#M#j}$r=#X(iB4f^g38~I( zYhPY?fzGzuj)@@X;$8RZm&?=cr}y2{%iZG7lb^glE;`-rZ|^QoysysQo?V^%^r>_C zOLzX;-#X_fXO}SM7r&fzt~vqg*n=@a5=<G`04tjgZfC}8G;|icMUxuXSy?#gVANr4 zy;10quq)T1I=3hTpVjjsGOJT?>n_%tNz`bLql~|z;m0K_w2>@~Vb&f8GIJJo?4*re zfI4u*(I<=ys4|79oH@Iu((`xYk&_IIOrm|Fm%6~A0)Ic)t)yP&S|5s>y_Ped2S-#Y zLHn7Jl%RbhETU3+QmF){db0snrgte}-4PuqJ<^()<X`N9E~9lRLgA#}8e?IKo!J_i zHITmuo~4CdHlBcQr4kd<b7Nnb=ot*Hc63y6sIf3-=Ibq-DZaJ4ASbI~WxETF?B&Pg zC@~^n5RMWam47wH9}^DZJWoNGU&0p74Ey+5aOchC9O0e?`@z2RJomlMjgXRi$K{N6 z74r#NKM_f_*fw*r$;gc>T--Hdjv?^SwZI5F)<34&g%1SC+%*L%uwYM&13#xs1?VR% zjkE9Wq6)P5sF`Hn0LCkAtJh_zLL=-PO!O|$_q<|YpPaou=e6V7t-_b@U_W@#Dyu># zRJ`8PP1#F_`k$6BIzwA7uo~G%3Nv-riX#_1dMuINgc(&6t4ldYJ>LgIZ?e7*hAZMb zC;aVie+%6AaP~|<^xu{{0s5J0Z9%UN$_CsX#^XNE)Jz+4v$1#Jw7@!AAsB|7g(OvS z_P#PH>5#Qk9<{YDu=mKlt&LgK#!S(+`(`I-wK@_`eF{Ce>=j^nV=9Oql^LZ?%Vuzt zWb4~CfBCO4!l!OX(d$A*wVd*!Q#Z7_vcM*sKLSeZo)UyOvv|3*>o3(pfDJvkD(pa3 zv4;svdDO9uf}1r|Z@2h?DLVHf7v^`}pP3&$+k=sEt_08Y&N$*+!>`pU!$OpBG2%BS z+6>2rZ<xLyA_NzD%Zss<744q+zyIg|k`R-Ea{CDSXs&53zZVTN>MMD3kqJf&j9plQ zU%ahVCu4wVo3qj#)3^8OP~Zr9b*3Im#E2I3u-+MX?%8AfyR5%L?sQ_EGi-JoaL-^U zgZhYf5L);I7|}v=ol?93WyaN5U>5Nto_OMH{;mLBl7xU+X;HhZbGC(fZ{AX4Jz0El zFb{4j`<xKhkCI`^C@6g_M=s9Ab=He>nKydaSgnPDUmu<5_Wz&fQr_>SLRBq({qgej z;{5Emb9MIaZBfoe(HK?C*Slrbmx?OqO$P3;Bk&QE=u>4IKA9E&srpTyie1r-d>^*= z;v7y16S<P>dZkDl#x)BxjQxtEgMGajw^|?HG4z&?&0HTm-9?A{hx@x`fs7Axq5cMp zbs~WSN@oeEWH)-IEU$Sa6;_#S^FdpUNIMn$pCnW}R>n{8Ke@<d11hbs3<qBpReAie z4N)<k=w^WR@6bwPIkuao6(}eRxk$NiPBKN<!GK@|K|DB$jPhl=`qW4VMS2cn9m@wD zp0YhaR7nyKJimwhiSZ#ieRm$TT2G$*1|CMX;WrS{!W4uTdEo%1LHO?{Pqf-~;hfp* z*D@EGz5asZG*0OU^UM(%AN0_+n{ho1{fUWOfkKDSy{B&My^8eAI{u*eOr3}Gbwbgn zpo{GU=;N<Mcsx>1ejrEI{=!qLcU!aXJkk)rDYczy4=9PFoqWq2EaCCMV2XD25R}yt z`Wrqb#VO*XR2G(nF8aj6$QUgrIEkM)06l>*FyFP>r~`H{kugx$jQz{2-3(K27hzX0 z=Hc}d<1kI7Zx=AKw}S?_j+phCIRk4R#bPJB+)Aj}DtjJyb-$&NDOm18=Xb)iMN5%= zLhNA<i4cy{5$=;bB#j}AMm*uV#rowpc#>N_ef_h*2|)VT=B=?(%q-{T23&TA4VbL~ zz`3D&`<4H!7b=RzhZdynDA{=`6gob55qPB|P9%F=m>}Fz#B8D=RW!q5nMCEWclE$( zFA7J{Y`Lu6UWuo<aWyv+gNVkR_5zPBkW#A_&iL@t`01yF4y}CpDF4{bBx#3OYC+8` z5&IJx8rZu&D2~QY^~}AOWRS%urtDgpiCHMts7!G|aV+-)kvtv~5M~bH|4+e?-7Xqp z9l^Z%*6B&b<NcTq_skQ-xDA2`H9l-BZa+TVgWc;9`biKBW@F1;ovlg{k)WQDZF|Kc zu)!$K!eO18tau`0JSIK+lkY#&Luw>=HXJz)WF=zAlwqQs;f%qw!S2HO%l*1FS1rF( zzo!>~)!YMC&+N%PPy`FNT5y~eq)?$QJ{-&ZkVeZCl}abK0TwyP3s>G;rZ+~GTvo{C z2XpMaT|S1*9aN-*$m`r(<76?onw3gl7VPaD72)U?@L2A=icuej*IBA}Hxp6oqS|d5 zQOAcdo0uJtOM-CdVrSlg{4;l)=X?!qK>FYoLl$NN*sbl@tfky|)SwC(?)86dJ2U!_ zD3m+R&YRLbd9}^aKP6|W*1)&^9q5*LCz)+0f&OO;O_3g+7tc_;9g&bnWT(0{AC6}l zl8!^vj<xhOW4U)>5U-0D?g}20*9OP4ZKH}L^5g#RK|&OUhU%XEXKTmE!6znCe>SvD zVWpXmBnr+fsErG=ZUMcv=42s`h&914I+(C|0KDpCfYa2!2EFYMYq?s}!nYRfZq<RS z=<Ewix`tO{g|4ws%lNMfbhxKI+l$kt;Pkaz=)<vT^55OLrSYTUZX8O=nD4{Mqhbw^ z8-Kh&M&Wj=&R3y+h%>T$)OD?u<J{gi&HR}%V*u0JtYu!(p7zENu5FkKWix?i);%@m z!?}rYZP34=8AS6)t`F(?r@&2n7W6LGd2Q4OLEu^1(Eh8V{pUvqhr#n7(b?Od-y!t+ z^<S*@bY_-}(}hJs1nfswrvJ;$Z&w`sXq2=wR+c(E=MAhi8Jt?_-DBt=0+=(Wvgv#m z=H=tXC3?Dlc(9JiD9z${Pd^N9rC6baU>h`{S@;bW5%~XMMIm3n#7S(!3=7N@Aq$oK zT%al`Le{zl%M%NpvB>k9u7JOf@$FK!nKElZgBTADcARO%R2S{IO()-vtJ7~-Gqnk` z!N^!rbTbDrzkx6oYb?hM>21c%*!HX7Z^7SomSyi)#c5nsuY!a9;J|tlx6wQAEs$Em zgzB5Uv+8}ZY`yKgIW-<@Kj@F$i_Y=s50|GO&bnvs-v03Z;-qtRdQ#=r=hF|}tIpN? zE<!&X|Ju2{D*x8GxIiCHFLiz13i-ipf9MC7XYq%_{loqC{;T%>Gj#Cu=;@C~&z}V^ zUp;;HYX9)n3-ssx{r!FP{_6M#m$~K#kN4Q2_~GCnc%|1#!7?0=@VYunm#3YRzaxZP zH}p5%tIp+>{@nTHw6%cWA%qV0O!F_Tgo2Ql^7!Qo>E`Cj+iI*j5M!lRqn7Eme*{dF zxbgtg_}1G|>r@CXj?nvyUoJZ*r|9R-+4<?okvn@xu?k0MtFl{Lh9g~TExc=2Q<tbC z{|~d<E-DXM-eEl)fx+-dubV!NBEpV}14Le0t8h!C);q0Y>xIxhc250|chO^)BkZv; zkv%SF*nA9&dEnmW2H%rLymx-7a52L|syD%wFXJp7CChGH8!P3J+t2@3Tj8tM_tWb2 z{p|2)|5dPmxc~Ik^H;B)tyteb1_!~RTitfZej~tsuW23#?F{9&6ns|5o6960{af`+ zxA%*=ei&@&;77}roEpaRz*@cZb35DIo(V=omqcaTA5}ftDUWtw{pI7^2bTvvwlO88 z;<@*WyDJ+HwGNkCS$jU%doG<?E)Kmi;?RX41NzzdBWUf%D0{9Ns<mFOc*tWDvcVyG z3VuG5ih`fcSSyT1lXM9a%5K(0y)IXQBq~dHQG%}tGG{H^a<<%&PLM$i4nX#ZltDTt z+x(Zofo^^$igJe);C|;Pos04bp<f2NY|PK*=toONG3Q!!c8SGo(jcuKLI<dlXb4US z+nOEi>=uH^_`v$L0k7Xa$^AKZY$_n*%!wsb0-g-k?)q1%<uESLFCYFIn2b11()#7Y zUy*U?aCxoqj6~QM1YZ+j%p`@2#$$U3&If2r$qo37TS-rESz8tgTUPt`@=Lm94zFfy zF!2QF6pNS=Q8Jxdi*-aJlkU#DUcwGz$b~tBw;85JSRzSTNQ?nVgAK&%1a?r^QA{-5 z8AHpDK#c5s@bd80!MqvUIT(9ON)X925o$2c+2vEYH$Lcr)i-0LSTs1LkHIll5J*n2 zYfn)iJs7K+2a{y%YW@_apCzfw4Mt=&ieCiJgO_tU$!^J`JdWXjVpieFEdvl`W@F&< zyQFQ>CqHWvl)5PPJon?l{w_K&@dyWp8hNhP!{B*<&QxLi)4hjG>J@<ne)ti{?!Yzr z*}(hQ4qI)|2I0eyaA|)0XFeX!D|Iw89#5X+nHP;(;*H3`0f_ztM;ZwJ5BpD^1bIF_ zI2chbg<F(+09y9|lfcd$Gg*6B(M2%$9-)s8r8hS>zQ;Tl!#yw{?=f;C_YPkD_-wDx z2-kRc$G91;EMbq`608LMyv{4i1$LZkC!p2Z<z#u>cC8)Lrl|mno>W}mAu&gnCLv&E zSY@8;QWwjl*(2c8mkr$p<mGwLDzoGXaef8hELI+BEz;ZZpEqXwMq-GlnDOuP#JSuQ z(&}b?6CG*OC_sCi1l^yY8_k9E8*k%&u4_-g|4C-njm{1=oVF6FAf(_{p6#a2R#)$$ znN5>8ASCMJ@ER5A7Ko7lpLgKUY8AGUCr`A(A!cn_u<@cNPg?ndAGD%@Z9)f|n1?do z3i<>QHb!GCWDt07$kWu!yuulugVNKpnatpH&6o|82`xvzm_t94i*cs(OxCmYOMZ#X zs3>*p<rniEZ3|}$@8NT9Ofls&GUW;(2ZyOG(bA%IZ`K>D_f%K$G%dHaT+Ndw^ONa( z2>vfUt*a66<Vg#mj|ab_pF3Bb^CMIo7T9!haB%Fly4dcHCaan)d2)KyInzb2Tu4jd z*rzw!B1;fB5mzMg)4Prl5UhP`_I)KI-N8Zd)LQf0-VlP|?O90efmab_lOJ01IPxuI z#Az!Y!@;ab)#u`?l}#I23a~crO*XP-DepDzapw4AFE`f(BbCIDj8)<6?Jw;QXPx%N zFDLKcTy)OQcf2;clg`p1XURP>SHAN3i?qn$eBfQDcac^gY|=@HhXS8X=8r@nkvuay z@RjdDY9)}u2YQiTwU;5m;SM_Frm=UhAG{3q+v3G57aib!(ruJ>*C}NgiusU+rW(d0 z68zIRbz(WPHB@A1H4V>Qv`cA{=G@YIZY<!4QMv%mUHx?)z?o;@DF;&ZJho&Kv;}bQ zz71lZJrtMa2I_@|qcOE-Y0fS|lY~SRD-ut%Sp4SV-Q`v1?bQ*=lhMI~nR5fk>-9|A z%+s)R**wRRHxqNCmj}0@cWOk$4RtFGpfDs>C4=qT_|j@NP<2XE<)sg45t39*wE6?S z2t);_Yh5BMOLWf9-~IOK^v%W9-;49XiLrs<+)7`d&uGqoK_{*DW6Z97r#u&L=O;6U zpa>INJ0>wtbt_6|2v5D$kxNc6!nB<G+qwA_{w5l{T@nM6-1Xd0!BZ+GzBj8qFob|9 zjY%+b1g$e0@?`XN%#7UOPT~vZV~PiZERG>V5*m>>)uUo9e=z90Phd}UPrb8gz=9=y z0X=B7nw0zjOxkp<RoV9z4#~yAeetEq<5YBbd0nLkz3Tk$p+<jnmseSS(yO!dL%|Ra zbCo1#25!aW%(}N9{4Mxf5A_9RTB+rzcL3j%HYo2v$(-LwMN*?IdFSsgWiEICZ1=jS zmmkiKPd~lwygBU|4I$5TzukLxep2wU9k+lzGhQJQoAZ}BO{nCnpt$vfG*%n;9{cy( zcUPy~Kz&xb^9^-bl${@Cf3o1%q;#e~4xJ_P%pTlq8y8y`-<^Di%JT!y8*>Foo#ALn z3im}h+uK^sb5UmQZ7Gk?wo_KT@qmsN9YeiYOC)$`RQ8s9CLwB5g}_z`ez@+)NvoeR z9}Yph@Hch6mxH96!z|xU@(tf8!g?3dkN0-15eqAHLmceFau4%LX2CJrc3UZGkB|>S z)PA2AMJJuB&QG1L{pBxcPbKM|aYYI8LC!q^XD)W|mWIjRpTN;8_|E0vv!{dw7G=2? zX2)B&By7B(-n{Mo2=G2gT@5;@qKWf*G>T^~Kf(Y@`Y+8A?L6rg=P6jM$Bmw%LbvrK zESr$t^)}aXA$KD&N1WvWhqK9rqx=6>kM6uY3}OWYJ$iYpVjg1uTy)pw&Mbzmm<nG+ z-Yz+n8o@DV1UrSn*GVI807|3ijuMJt4M!xwMZ6<$to$Ag-;aMlg;YN41g7c9<S>-M zilNL6{jVRPNbt01NokD#AOAVK64!q+lSH8R-P6nYCm^x~2_BQ;0hcVo4*%ny&aU2e z-kd=wWscy-rD}#kk1`x^3p%DT84^feSKUTEe9M}%pcKEcB9vEFBkfNOeIa?~oX`aZ zb@HHd<34@YckJ))@4tBV4F28U-=F<=|KRD1KOH=K`r_dE%l(6=hkx2Xc=7D%i$9_L zdb+rrGA)AtY5&gen(f?%(A<n4wGeVnx+Bi7kI>dXwo*abR%F@L`&!$IKmXX;nt8l^ zL+Eg%j?h;7V5?b=51z{F-`Y;vUc$N>-m!B1Ki{8O|Ic4Md)}=7uR)K{IWf|ryZpk~ zJ^TPetkCh97LLu`S&=ly_|jNIs*~+g=3ht^G@RgpQhEK$QDWlV+cxP^R0k_qwf^^? zJ}a;PLtUU*|6hahbP#&qx49sWka3~^)@^I({>fYa<|>QJ)6U78(;$iJd&kQC|M2O| z{{L+Muv!0Kg^qbTL2w<Oi5{Cd0LLua<D%j|FL$S+GWVF+6y^y|o2`DMXm0)68=LB6 z1FP2m%V%@)|Ka{&v;MyZt$BIaU2hBv%WS6l-msu`1@{t_uL7g&pC<qO45Km5OgM`2 z>%YhnedOC+{`JNsImnY3E%c~DX4Ty;(deU6hfR9-)y#Nq{V%@C>ef7A)%t(7e>fxm z9~``V*{uJsL4U7apw>&Qd{Y${#YI(y2e`Y<upsU-wFpJUjhE(Rq|bSt;>F!~`~|Dx tmdk;9o_}o&_aL}f(H$>KT=1=DprM8uYUtb0{|Nv9|Njpd{l5Uh1OTf02owMS literal 0 HcmV?d00001 diff --git a/chart/dashboards/mattermost-performance-monitoring-v2_rev2.json b/chart/dashboards/mattermost-performance-monitoring-v2_rev2.json new file mode 100644 index 000000000..069318415 --- /dev/null +++ b/chart/dashboards/mattermost-performance-monitoring-v2_rev2.json @@ -0,0 +1,2272 @@ +{ + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "7.2.1" + }, + { + "type": "panel", + "id": "graph", + "name": "Graph", + "version": "" + }, + { + "type": "datasource", + "id": "prometheus", + "name": "Prometheus", + "version": "1.0.0" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "gnetId": 15582, + "graphTooltip": 1, + "id": null, + "iteration": 1642655786188, + "links": [], + "panels": [ + { + "collapsed": true, + "datasource": "${DS_PROMETHEUS}", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 6, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 1 + }, + "hiddenSeries": false, + "id": 12, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.2.1", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(mattermost_http_requests_total{instance=~\"$server\"}[1m])", + "interval": "", + "legendFormat": "{{instance}}", + "refId": "A" + }, + { + "expr": "sum(rate(mattermost_http_requests_total{instance=~\"$server\"}[1m]))", + "interval": "", + "legendFormat": "Total", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "HTTP Requests per second", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:1128", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:1129", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 1 + }, + "hiddenSeries": false, + "id": 20, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.2.1", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(mattermost_db_store_time_count{instance=~\"$server\"}[5m])) by (instance)", + "interval": "", + "legendFormat": "{{instance}}", + "refId": "A" + }, + { + "expr": "sum(rate(mattermost_db_store_time_count{instance=~\"$server\"}[5m]))", + "interval": "", + "legendFormat": "Total", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "DB Calls per second", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:1772", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:1773", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 9 + }, + "hiddenSeries": false, + "id": 16, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.2.1", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(mattermost_api_time_bucket{instance=~\"$server\"}[1m])) by (instance,le))", + "interval": "", + "legendFormat": "p99-{{instance}}", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.50, sum(rate(mattermost_api_time_bucket{instance=~\"$server\"}[1m])) by (instance,le))", + "interval": "", + "legendFormat": "p50-{{instance}}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "API Latency", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:1298", + "format": "s", + "label": null, + "logBase": 2, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:1299", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 9 + }, + "hiddenSeries": false, + "id": 18, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.2.1", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(mattermost_db_store_time_bucket{instance=~\"$server\"}[1m])) by (instance,le))", + "interval": "", + "legendFormat": "p99-{{instance}}", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.50, sum(rate(mattermost_db_store_time_bucket{instance=~\"$server\"}[1m])) by (instance,le))", + "interval": "", + "legendFormat": "p50-{{instance}}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Store latency", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:1381", + "format": "s", + "label": null, + "logBase": 2, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:1382", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 17 + }, + "hiddenSeries": false, + "id": 28, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.2.1", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(increase(mattermost_db_store_time_count{instance=~\"$server\",method=~\"$top_db_count\"}[5m])) by (method)", + "interval": "", + "legendFormat": "{{method}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Top 10 DB Calls by Count", + "tooltip": { + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:2313", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:2314", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 17 + }, + "hiddenSeries": false, + "id": 30, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.2.1", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(increase(mattermost_api_time_count{instance=~\"$server\",handler=~\"$top_api_count\"}[5m])) by (handler)", + "interval": "", + "legendFormat": "{{handler}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Top 10 API Requests by Count", + "tooltip": { + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:2422", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:2423", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 25 + }, + "hiddenSeries": false, + "id": 32, + "legend": { + "alignAsTable": true, + "avg": true, + "current": false, + "max": true, + "min": true, + "rightSide": true, + "show": true, + "sort": "avg", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null as zero", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.2.1", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(increase(mattermost_db_store_time_sum{instance=~\"$server\",method=~\"$top_db_latency\"}[5m])) by (method) / sum(increase(mattermost_db_store_time_count{instance=~\"$server\",method=~\"$top_db_latency\"}[5m])) by (method)", + "interval": "", + "legendFormat": "{{method}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Top 10 DB calls by duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:2505", + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:2506", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 25 + }, + "hiddenSeries": false, + "id": 34, + "legend": { + "alignAsTable": true, + "avg": true, + "current": false, + "max": true, + "min": true, + "rightSide": true, + "show": true, + "sort": "avg", + "sortDesc": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null as zero", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.2.1", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(increase(mattermost_api_time_sum{instance=~\"$server\",handler=~\"$top_api_latency\"}[5m])) by (handler) / sum(increase(mattermost_api_time_count{instance=~\"$server\",handler=~\"$top_api_latency\"}[5m])) by (handler)", + "interval": "", + "legendFormat": "{{handler}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Top 10 API requests by duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:2667", + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:2668", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 33 + }, + "hiddenSeries": false, + "id": 24, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null as zero", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.2.1", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile (\n 0.99,\n sum by (le, instance)(\n rate(mattermost_api_time_bucket{instance=~\"$server\",handler=\"getPostsForChannelAroundLastUnread\"}[5m])\n )\n)", + "interval": "", + "legendFormat": "p99-{{instance}}", + "refId": "A" + }, + { + "expr": "histogram_quantile (\n 0.50,\n sum by (le, instance)(\n rate(mattermost_api_time_bucket{instance=~\"$server\",handler=\"getPostsForChannelAroundLastUnread\"}[5m])\n )\n)", + "interval": "", + "legendFormat": "p50-{{instance}}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Channel Load Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:2118", + "format": "s", + "label": null, + "logBase": 2, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:2119", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 33 + }, + "hiddenSeries": false, + "id": 26, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null as zero", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.2.1", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile (\n 0.99,\n sum by (le, instance)(\n rate(mattermost_api_time_bucket{instance=~\"$server\",handler=\"createPost\"}[5m])\n )\n)", + "interval": "", + "legendFormat": "p99-{{instance}}", + "refId": "A" + }, + { + "expr": "histogram_quantile (\n 0.50,\n sum by (le, instance)(\n rate(mattermost_api_time_bucket{instance=~\"$server\",handler=\"createPost\"}[5m])\n )\n)", + "interval": "", + "legendFormat": "p50-{{instance}}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "CreatePost duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:2216", + "format": "s", + "label": null, + "logBase": 2, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:2217", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 41 + }, + "hiddenSeries": false, + "id": 14, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.2.1", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "mattermost_http_websockets_total{instance=~\"$server\"}", + "interval": "", + "legendFormat": "{{instance}}", + "refId": "A" + }, + { + "expr": "sum(mattermost_http_websockets_total{instance=~\"$server\"})", + "interval": "", + "legendFormat": "Total", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Number of connected devices (WebSocket Connections)", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:1215", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:1216", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 41 + }, + "hiddenSeries": false, + "id": 22, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.2.1", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(mattermost_db_master_connections_total)", + "interval": "", + "legendFormat": "master", + "refId": "A" + }, + { + "expr": "sum(mattermost_db_read_replica_connections_total)", + "interval": "", + "legendFormat": "replica", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "DB Connections", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:1932", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:1933", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "description": "This needs to be configured in config.json for it to work", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 49 + }, + "hiddenSeries": false, + "id": 44, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.2.1", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "mattermost_db_replica_lag_time{instance=~\"$server\"}", + "interval": "", + "legendFormat": "{{instance}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Replica Lag", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:3692", + "format": "none", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:3693", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "title": "Application Metrics", + "type": "row" + }, + { + "collapsed": true, + "datasource": "${DS_PROMETHEUS}", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 1 + }, + "id": 36, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "description": "Lower numbers are better, and zero means \"totally healthy\".", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 2 + }, + "hiddenSeries": false, + "id": 38, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.2.1", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "mattermost_cluster_cluster_health_score{instance=~\"$server\"}", + "interval": "", + "legendFormat": "{{instance}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Cluster Health", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:2865", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:2866", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "description": "", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 12, + "y": 2 + }, + "hiddenSeries": false, + "id": 40, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.2.1", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "histogram_quantile(0.99, sum(rate(mattermost_cluster_cluster_request_duration_seconds_bucket{instance=~\"$server\"}[5m])) by (le,instance))", + "interval": "", + "legendFormat": "p99-{{instance}}", + "refId": "A" + }, + { + "expr": "histogram_quantile(0.50, sum(rate(mattermost_cluster_cluster_request_duration_seconds_bucket{instance=~\"$server\"}[5m])) by (le,instance))", + "interval": "", + "legendFormat": "p50-{{instance}}", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Cluster Request Duration", + "tooltip": { + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:2948", + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:2949", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 10 + }, + "hiddenSeries": false, + "id": 42, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.2.1", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(mattermost_cluster_cluster_request_duration_seconds_count{instance=~\"$server\"}[5m])) by (instance)", + "interval": "", + "legendFormat": "{{instance}}", + "refId": "A" + }, + { + "expr": "sum(rate(mattermost_cluster_cluster_request_duration_seconds_count{instance=~\"$server\"}[5m]))", + "interval": "", + "legendFormat": "Total", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Cluster Requests Per Second", + "tooltip": { + "shared": true, + "sort": 2, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:3241", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:3242", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "title": "Cluster Metrics", + "type": "row" + }, + { + "collapsed": true, + "datasource": "${DS_PROMETHEUS}", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 2 + }, + "id": 48, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 19 + }, + "hiddenSeries": false, + "id": 46, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.2.1", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "mattermost_jobs_active{instance=~\"$server\"}", + "interval": "", + "legendFormat": "{{instance}}", + "refId": "A" + }, + { + "expr": "sum(mattermost_jobs_active{instance=~\"$server\"})", + "interval": "", + "legendFormat": "Total", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Active Jobs", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:4113", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:4114", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "title": "Job Server", + "type": "row" + }, + { + "collapsed": true, + "datasource": "${DS_PROMETHEUS}", + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 3 + }, + "id": 4, + "panels": [ + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 0, + "y": 20 + }, + "hiddenSeries": false, + "id": 2, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.2.1", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "irate(mattermost_process_cpu_seconds_total{instance=~\"$server\"}[5m])* 100", + "interval": "", + "legendFormat": "{{instance}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "CPU Utilization Percentage", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:99", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:100", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 9, + "w": 12, + "x": 12, + "y": 20 + }, + "hiddenSeries": false, + "id": 8, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.2.1", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "go_memstats_heap_inuse_bytes{instance=~\"$server\"}", + "interval": "", + "legendFormat": "{{instance}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Heap Utilization", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:288", + "format": "decbytes", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:289", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "${DS_PROMETHEUS}", + "fieldConfig": { + "defaults": { + "custom": {} + }, + "overrides": [] + }, + "fill": 1, + "fillGradient": 0, + "gridPos": { + "h": 8, + "w": 12, + "x": 0, + "y": 29 + }, + "hiddenSeries": false, + "id": 10, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "nullPointMode": "null", + "options": { + "alertThreshold": true + }, + "percentage": false, + "pluginVersion": "7.2.1", + "pointradius": 2, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "go_goroutines{instance=~\"$server\"}", + "interval": "", + "legendFormat": "{{instance}}", + "refId": "A" + }, + { + "expr": "sum(go_goroutines{instance=~\"$server\"})", + "interval": "", + "legendFormat": "Total", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Goroutines", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "$$hashKey": "object:1021", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "$$hashKey": "object:1022", + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "title": "System Metrics", + "type": "row" + } + ], + "refresh": "1m", + "schemaVersion": 26, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "allValue": null, + "current": {}, + "datasource": "${DS_PROMETHEUS}", + "definition": "label_values(instance)", + "hide": 0, + "includeAll": false, + "label": "server", + "multi": true, + "name": "server", + "options": [], + "query": "label_values(instance)", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": null, + "current": {}, + "datasource": "${DS_PROMETHEUS}", + "definition": "query_result(topk(10, sum(increase(mattermost_db_store_time_count{instance=~\"$server\"}[${__range_s}s])) by (method)))", + "hide": 0, + "includeAll": true, + "label": null, + "multi": true, + "name": "top_db_count", + "options": [], + "query": "query_result(topk(10, sum(increase(mattermost_db_store_time_count{instance=~\"$server\"}[${__range_s}s])) by (method)))", + "refresh": 2, + "regex": ".*method=\"(.*?)\".*", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": null, + "current": {}, + "datasource": "${DS_PROMETHEUS}", + "definition": "query_result(topk(10, sum(increase(mattermost_db_store_time_sum{instance=~\"$server\"}[${__range_s}s])) by (method) / sum(increase(mattermost_db_store_time_count{instance=~\"$server\"}[${__range_s}s])) by (method)))", + "hide": 0, + "includeAll": true, + "label": null, + "multi": true, + "name": "top_db_latency", + "options": [], + "query": "query_result(topk(10, sum(increase(mattermost_db_store_time_sum{instance=~\"$server\"}[${__range_s}s])) by (method) / sum(increase(mattermost_db_store_time_count{instance=~\"$server\"}[${__range_s}s])) by (method)))", + "refresh": 2, + "regex": ".*method=\"(.*?)\".*", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": null, + "current": {}, + "datasource": "${DS_PROMETHEUS}", + "definition": "query_result(topk(10, sum(increase(mattermost_api_time_count{instance=~\"$server\"}[${__range_s}s])) by (handler)))", + "hide": 0, + "includeAll": true, + "label": null, + "multi": true, + "name": "top_api_count", + "options": [], + "query": "query_result(topk(10, sum(increase(mattermost_api_time_count{instance=~\"$server\"}[${__range_s}s])) by (handler)))", + "refresh": 2, + "regex": ".*handler=\"(.*?)\".*", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allValue": null, + "current": {}, + "datasource": "${DS_PROMETHEUS}", + "definition": "query_result(topk(10, sum(increase(mattermost_api_time_sum{instance=~\"$server\"}[${__range_s}s])) by (handler) / sum(increase(mattermost_api_time_count{instance=~\"$server\"}[${__range_s}s])) by (handler)))", + "hide": 0, + "includeAll": true, + "label": null, + "multi": true, + "name": "top_api_latency", + "options": [], + "query": "query_result(topk(10, sum(increase(mattermost_api_time_sum{instance=~\"$server\"}[${__range_s}s])) by (handler) / sum(increase(mattermost_api_time_count{instance=~\"$server\"}[${__range_s}s])) by (handler)))", + "refresh": 2, + "regex": ".*handler=\"(.*?)\".*", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "current": { + "selected": false, + "text": "Prometheus", + "value": "Prometheus" + }, + "hide": 0, + "includeAll": false, + "label": "Datasource", + "multi": false, + "name": "DS_PROMETHEUS", + "options": [], + "query": "prometheus", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "type": "datasource" + } + ] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": {}, + "timezone": "", + "title": "Mattermost Performance Monitoring v2", + "uid": "im7xNX17k", + "version": 9, + "description": "A dashboard to monitor complete Mattermost application performance" +} \ No newline at end of file diff --git a/bb_prod/bigbang/ci-hr/.helmignore b/chart/deps/postgresql/.helmignore similarity index 95% rename from bb_prod/bigbang/ci-hr/.helmignore rename to chart/deps/postgresql/.helmignore index 691fa13d6..f0c131944 100644 --- a/bb_prod/bigbang/ci-hr/.helmignore +++ b/chart/deps/postgresql/.helmignore @@ -14,10 +14,8 @@ *.swp *.bak *.tmp -*.orig *~ # Various IDEs .project .idea/ *.tmproj -.vscode/ \ No newline at end of file diff --git a/chart/deps/postgresql/Chart.lock b/chart/deps/postgresql/Chart.lock new file mode 100644 index 000000000..2be1d0932 --- /dev/null +++ b/chart/deps/postgresql/Chart.lock @@ -0,0 +1,6 @@ +dependencies: +- name: common + repository: https://charts.bitnami.com/bitnami + version: 1.4.0 +digest: sha256:af8a0894ca6f099e57ebfe9ce25a50d9e7548aeaa53b69d051ecc1681d6d77bb +generated: "2021-02-22T16:26:47.159006+01:00" diff --git a/chart/deps/postgresql/Chart.yaml b/chart/deps/postgresql/Chart.yaml new file mode 100644 index 000000000..53e0c06d7 --- /dev/null +++ b/chart/deps/postgresql/Chart.yaml @@ -0,0 +1,29 @@ +annotations: + category: Database +apiVersion: v2 +appVersion: 11.11.0 +dependencies: + - name: common + repository: https://charts.bitnami.com/bitnami + version: 1.x.x +description: Chart for PostgreSQL, an object-relational database management system (ORDBMS) with an emphasis on extensibility and on standards-compliance. +engine: gotpl +home: https://github.com/bitnami/charts/tree/master/bitnami/postgresql +icon: https://bitnami.com/assets/stacks/postgresql/img/postgresql-stack-220x234.png +keywords: + - postgresql + - postgres + - database + - sql + - replication + - cluster +maintainers: + - email: containers@bitnami.com + name: Bitnami + - email: cedric@desaintmartin.fr + name: desaintmartin +name: postgresql +sources: + - https://github.com/bitnami/bitnami-docker-postgresql + - https://www.postgresql.org/ +version: 10.3.5 diff --git a/chart/deps/postgresql/Kptfile b/chart/deps/postgresql/Kptfile new file mode 100644 index 000000000..250659e6f --- /dev/null +++ b/chart/deps/postgresql/Kptfile @@ -0,0 +1,11 @@ +apiVersion: kpt.dev/v1alpha1 +kind: Kptfile +metadata: + name: postgresql +upstream: + type: git + git: + commit: c2ac165a579a8f06dede2b6fede2f4ec2bfea495 + repo: https://github.com/bitnami/charts + directory: /bitnami/postgresql + ref: master diff --git a/chart/deps/postgresql/README.md b/chart/deps/postgresql/README.md new file mode 100644 index 000000000..1ccc0c1d4 --- /dev/null +++ b/chart/deps/postgresql/README.md @@ -0,0 +1,770 @@ +# PostgreSQL + +[PostgreSQL](https://www.postgresql.org/) is an object-relational database management system (ORDBMS) with an emphasis on extensibility and on standards-compliance. + +For HA, please see [this repo](https://github.com/bitnami/charts/tree/master/bitnami/postgresql-ha) + +## TL;DR + +```console +$ helm repo add bitnami https://charts.bitnami.com/bitnami +$ helm install my-release bitnami/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 3.1.0 +- 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 bitnami/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 but PVC's associated with the chart and deletes the release. + +To delete the PVC's associated with `my-release`: + +```console +$ kubectl delete pvc -l release=my-release +``` + +> **Note**: Deleting the PVC's will delete postgresql data as well. Please be cautious before doing it. + +## 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 common.names.fullname template with a string (will prepend the release name) | `nil` | +| `fullnameOverride` | String to fully override common.names.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.*` | Other container security context to be included as-is in the container spec | `{}` | +| `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 against 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.readReplicas` | Number of read replicas 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.readReplicas`. | `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-password` which is the password for `postgresqlUsername` when it is different of `postgres`, `postgresql-postgres-password` which will override `postgresqlPassword`, `postgresql-replication-password` which will override `replication.password` and `postgresql-ldap-password` which will be used to authenticate on LDAP. The value is evaluated as a template. | `nil` | +| `postgresqlPostgresPassword` | PostgreSQL admin password (used when `postgresqlUsername` is not `postgres`, in which case`postgres` is the admin username). | _random 10 character alphanumeric string_ | +| `postgresqlUsername` | PostgreSQL user (creates a non-admin user when `postgresqlUsername` is not `postgres`) | `postgres` | +| `postgresqlPassword` | PostgreSQL user 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. The value is evaluated as a template. | `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)` | +| `postgresqlSharedPreloadLibraries` | Shared preload libraries (comma-separated list) | `pgaudit` | +| `postgresqlMaxConnections` | Maximum total connections | `nil` | +| `postgresqlPostgresConnectionLimit` | Maximum total connections for the postgres user | `nil` | +| `postgresqlDbUserConnectionLimit` | Maximum total connections for the non-admin user | `nil` | +| `postgresqlTcpKeepalivesInterval` | TCP keepalives interval | `nil` | +| `postgresqlTcpKeepalivesIdle` | TCP keepalives idle | `nil` | +| `postgresqlTcpKeepalivesCount` | TCP keepalives count | `nil` | +| `postgresqlStatementTimeout` | Statement timeout | `nil` | +| `postgresqlPghbaRemoveFilters` | Comma-separated list of patterns to remove from the pg_hba.conf file | `nil` | +| `customStartupProbe` | Override default startup probe | `nil` | +| `customLivenessProbe` | Override default liveness probe | `nil` | +| `customReadinessProbe` | Override default readiness probe | `nil` | +| `audit.logHostname` | Add client hostnames to the log file | `false` | +| `audit.logConnections` | Add client log-in operations to the log file | `false` | +| `audit.logDisconnections` | Add client log-outs operations to the log file | `false` | +| `audit.pgAuditLog` | Add operations to log using the pgAudit extension | `nil` | +| `audit.clientMinMessages` | Message log level to share with the user | `nil` | +| `audit.logLinePrefix` | Template string for the log line prefix | `nil` | +| `audit.logTimezone` | Timezone for the log timestamps | `nil` | +| `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` | +| `initdbUser` | PostgreSQL user to execute the .sql and sql.gz scripts | `nil` | +| `initdbPassword` | Password for the user specified in `initdbUser` | `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 | `{}` (evaluated as a template) | +| `service.loadBalancerIP` | loadBalancerIP if service type is `LoadBalancer` | `nil` | +| `service.loadBalancerSourceRanges` | Address that are allowed when svc is LoadBalancer | `[]` (evaluated as a template) | +| `schedulerName` | Name of the k8s scheduler (other than default) | `nil` | +| `shmVolume.enabled` | Enable emptyDir volume for /dev/shm for primary and read replica(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 | `{}` | +| `persistence.selector` | Selector to match an existing Persistent Volume (this value is evaluated as a template) | `{}` | +| `commonAnnotations` | Annotations to be added to all deployed resources (rendered as a template) | `{}` | +| `primary.podAffinityPreset` | PostgreSQL primary pod affinity preset. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `primary.podAntiAffinityPreset` | PostgreSQL primary pod anti-affinity preset. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` | `soft` | +| `primary.nodeAffinityPreset.type` | PostgreSQL primary node affinity preset type. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `primary.nodeAffinityPreset.key` | PostgreSQL primary node label key to match Ignored if `primary.affinity` is set. | `""` | +| `primary.nodeAffinityPreset.values` | PostgreSQL primary node label values to match. Ignored if `primary.affinity` is set. | `[]` | +| `primary.affinity` | Affinity for PostgreSQL primary pods assignment | `{}` (evaluated as a template) | +| `primary.nodeSelector` | Node labels for PostgreSQL primary pods assignment | `{}` (evaluated as a template) | +| `primary.tolerations` | Tolerations for PostgreSQL primary pods assignment | `[]` (evaluated as a template) | +| `primary.anotations` | Map of annotations to add to the statefulset (postgresql primary) | `{}` | +| `primary.labels` | Map of labels to add to the statefulset (postgresql primary) | `{}` | +| `primary.podAnnotations` | Map of annotations to add to the pods (postgresql primary) | `{}` | +| `primary.podLabels` | Map of labels to add to the pods (postgresql primary) | `{}` | +| `primary.priorityClassName` | Priority Class to use for each pod (postgresql primary) | `nil` | +| `primary.extraInitContainers` | Additional init containers to add to the pods (postgresql primary) | `[]` | +| `primary.extraVolumeMounts` | Additional volume mounts to add to the pods (postgresql primary) | `[]` | +| `primary.extraVolumes` | Additional volumes to add to the pods (postgresql primary) | `[]` | +| `primary.sidecars` | Add additional containers to the pod | `[]` | +| `primary.service.type` | Allows using a different service type for primary | `nil` | +| `primary.service.nodePort` | Allows using a different nodePort for primary | `nil` | +| `primary.service.clusterIP` | Allows using a different clusterIP for primary | `nil` | +| `primaryAsStandBy.enabled` | Whether to enable current cluster's primary as standby server of another cluster or not. | `false` | +| `primaryAsStandBy.primaryHost` | The Host of replication primary in the other cluster. | `nil` | +| `primaryAsStandBy.primaryPort ` | The Port of replication primary in the other cluster. | `nil` | +| `readReplicas.podAffinityPreset` | PostgreSQL read only pod affinity preset. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `readReplicas.podAntiAffinityPreset` | PostgreSQL read only pod anti-affinity preset. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` | `soft` | +| `readReplicas.nodeAffinityPreset.type` | PostgreSQL read only node affinity preset type. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` | `""` | +| `readReplicas.nodeAffinityPreset.key` | PostgreSQL read only node label key to match Ignored if `primary.affinity` is set. | `""` | +| `readReplicas.nodeAffinityPreset.values` | PostgreSQL read only node label values to match. Ignored if `primary.affinity` is set. | `[]` | +| `readReplicas.affinity` | Affinity for PostgreSQL read only pods assignment | `{}` (evaluated as a template) | +| `readReplicas.nodeSelector` | Node labels for PostgreSQL read only pods assignment | `{}` (evaluated as a template) | +| `readReplicas.anotations` | Map of annotations to add to the statefulsets (postgresql readReplicas) | `{}` | +| `readReplicas.resources` | CPU/Memory resource requests/limits override for readReplicass. Will fallback to `values.resources` if not defined. | `{}` | +| `readReplicas.labels` | Map of labels to add to the statefulsets (postgresql readReplicas) | `{}` | +| `readReplicas.podAnnotations` | Map of annotations to add to the pods (postgresql readReplicas) | `{}` | +| `readReplicas.podLabels` | Map of labels to add to the pods (postgresql readReplicas) | `{}` | +| `readReplicas.priorityClassName` | Priority Class to use for each pod (postgresql readReplicas) | `nil` | +| `readReplicas.extraInitContainers` | Additional init containers to add to the pods (postgresql readReplicas) | `[]` | +| `readReplicas.extraVolumeMounts` | Additional volume mounts to add to the pods (postgresql readReplicas) | `[]` | +| `readReplicas.extraVolumes` | Additional volumes to add to the pods (postgresql readReplicas) | `[]` | +| `readReplicas.sidecars` | Add additional containers to the pod | `[]` | +| `readReplicas.service.type` | Allows using a different service type for readReplicas | `nil` | +| `readReplicas.service.nodePort` | Allows using a different nodePort for readReplicas | `nil` | +| `readReplicas.service.clusterIP` | Allows using a different clusterIP for readReplicas | `nil` | +| `readReplicas.persistence.enabled` | Whether to enable readReplicas replicas persistence | `true` | +| `terminationGracePeriodSeconds` | Seconds the pod needs to terminate gracefully | `nil` | +| `resources` | CPU/Memory resource requests/limits | Memory: `256Mi`, CPU: `250m` | +| `securityContext.*` | Other pod security context to be included as-is in the pod spec | `{}` | +| `securityContext.enabled` | Enable security context | `true` | +| `securityContext.fsGroup` | Group ID for the pod | `1001` | +| `containerSecurityContext.*` | Other container security context to be included as-is in the container spec | `{}` | +| `containerSecurityContext.enabled` | Enable container security context | `true` | +| `containerSecurityContext.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` | +| `serviceAccount.name` | Name of existing service account | `nil` | +| `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 | `{}` | +| `startupProbe.enabled` | Enable startupProbe | `false` | +| `startupProbe.initialDelaySeconds` | Delay before liveness probe is initiated | 30 | +| `startupProbe.periodSeconds` | How often to perform the probe | 15 | +| `startupProbe.timeoutSeconds` | When the probe times | 5 | +| `startupProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded. | 10 | +| `startupProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed. | 1 | +| `livenessProbe.enabled` | Enable livenessProbe | `true` | +| `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` | Enable readinessProbe | `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 | +| `tls.enabled` | Enable TLS traffic support | `false` | +| `tls.preferServerCiphers` | Whether to use the server's TLS cipher preferences rather than the client's | `true` | +| `tls.certificatesSecret` | Name of an existing secret that contains the certificates | `nil` | +| `tls.certFilename` | Certificate filename | `""` | +| `tls.certKeyFilename` | Certificate key filename | `""` | +| `tls.certCAFilename` | CA Certificate filename. If provided, PostgreSQL will authenticate TLS/SSL clients by requesting them a certificate. | `nil` | +| `tls.crlFilename` | File containing a Certificate Revocation List | `nil` | +| `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 Exporter Image registry | `docker.io` | +| `metrics.image.repository` | PostgreSQL Exporter Image name | `bitnami/postgres-exporter` | +| `metrics.image.tag` | PostgreSQL Exporter Image tag | `{TAG_NAME}` | +| `metrics.image.pullPolicy` | PostgreSQL Exporter 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.extraEnvVars` | Extra environment variables to add to exporter | `{}` (evaluated as a template) | +| `metrics.securityContext.*` | Other container security context to be included as-is in the container spec | `{}` | +| `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"}` | +| `psp.create` | Create Pod Security Policy | `false` | +| `rbac.create` | Create Role and RoleBinding (required for PSP to work) | `false` | +| `extraDeploy` | Array of extra objects to deploy with the release (evaluated as a template). | `nil` | + +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 \ + bitnami/postgresql +``` + +The above command sets the PostgreSQL `postgres` account password to `secretpassword`. Additionally it creates a database named `my-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 bitnami/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. + +### Customizing primary and read replica services in a replicated configuration + +At the top level, there is a service object which defines the services for both primary and readReplicas. For deeper customization, there are service objects for both the primary and read types individually. This allows you to override the values in the top level service object so that the primary and read can be of different service types and with different clusterIPs / nodePorts. Also in the case you want the primary and read to be of type nodePort, you will need to set the nodePorts to different values to prevent a collision. The values that are deeper in the primary.service or readReplicas.service objects will take precedence over the top level service object. + +### 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=X.Y.Z`. This approach is also applicable to other images like exporters. + +### 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 add additional PostgreSQL configuration parameters using the `postgresqlExtendedConf` parameter as a dict, using camelCase, e.g. {"sharedBuffers": "500MB"}. Alternatively, to replace the entire default configuration use `postgresqlConfiguration`. + +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`. + +### Securing traffic using TLS + +TLS support can be enabled in the chart by specifying the `tls.` parameters while creating a release. The following parameters should be configured to properly enable the TLS support in the chart: + +- `tls.enabled`: Enable TLS support. Defaults to `false` +- `tls.certificatesSecret`: Name of an existing secret that contains the certificates. No defaults. +- `tls.certFilename`: Certificate filename. No defaults. +- `tls.certKeyFilename`: Certificate key filename. No defaults. + +For example: + +* First, create the secret with the cetificates files: + + ```console + kubectl create secret generic certificates-tls-secret --from-file=./cert.crt --from-file=./cert.key --from-file=./ca.crt + ``` + +* Then, use the following parameters: + + ```console + volumePermissions.enabled=true + tls.enabled=true + tls.certificatesSecret="certificates-tls-secret" + tls.certFilename="cert.crt" + tls.certKeyFilename="cert.key" + ``` + + > Note TLS and VolumePermissions: PostgreSQL requires certain permissions on sensitive files (such as certificate keys) to start up. Due to an on-going [issue](https://github.com/kubernetes/kubernetes/issues/57923) regarding kubernetes permissions and the use of `containerSecurityContext.runAsUser`, you must enable `volumePermissions` to ensure everything works as expected. + +### 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 primary +primary: + sidecars: + - name: your-image-name + image: your-image + imagePullPolicy: Always + ports: + - name: portname + containerPort: 1234 +# For the PostgreSQL replicas +readReplicas: + 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,containerSecurityContext.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. + +``` +image.repository=postgres +image.tag=10.6 +postgresqlDataDir=/data/pgdata +persistence.mountPath=/data/ +``` + +### Setting Pod's affinity + +This chart allows you to set your custom affinity using the `XXX.affinity` paremeter(s). Find more infomation about Pod's affinity in the [kubernetes documentation](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity). + +As an alternative, you can use of the preset configurations for pod affinity, pod anti-affinity, and node affinity available at the [bitnami/common](https://github.com/bitnami/charts/tree/master/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 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 bitnami/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. + +### To 10.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. +- Move dependency information from the *requirements.yaml* to the *Chart.yaml* +- After running `helm dependency update`, a *Chart.lock* file is generated containing the same structure used in the previous *requirements.lock* +- The different fields present in the *Chart.yaml* file has been ordered alphabetically in a homogeneous way for all the Bitnami Helm Chart. + +**Considerations when upgrading to this version** + +- 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** + +- https://docs.bitnami.com/tutorials/resolve-helm2-helm3-post-migration-issues/ +- https://helm.sh/docs/topics/v2_v3_migration/ +- https://helm.sh/blog/migrate-from-helm-v2-to-helm-v3/ + +#### Breaking changes + +- The term `master` has been replaced with `primary` and `slave` with `readReplicas` throughout the chart. Role names have changed from `master` and `slave` to `primary` and `read`. + +To upgrade to `10.0.0`, it should be done reusing the PVCs used to hold the PostgreSQL data on your previous release. To do so, follow the instructions below (the following example assumes that the release name is `postgresql`): + +> NOTE: Please, create a backup of your database before running any of those actions. + +Obtain the credentials and the names of the PVCs used to hold the PostgreSQL data on your current release: + +```console +$ export POSTGRESQL_PASSWORD=$(kubectl get secret --namespace default postgresql -o jsonpath="{.data.postgresql-password}" | base64 --decode) +$ export POSTGRESQL_PVC=$(kubectl get pvc -l app.kubernetes.io/instance=postgresql,role=master -o jsonpath="{.items[0].metadata.name}") +``` + +Delete the PostgreSQL statefulset. Notice the option `--cascade=false`: + +```console +$ kubectl delete statefulsets.apps postgresql-postgresql --cascade=false +``` + +Now the upgrade works: + +```console +$ helm upgrade postgresql bitnami/postgresql --set postgresqlPassword=$POSTGRESQL_PASSWORD --set persistence.existingClaim=$POSTGRESQL_PVC +``` + +You will have to delete the existing PostgreSQL pod and the new statefulset is going to create a new one + +```console +$ kubectl delete pod postgresql-postgresql-0 +``` + +Finally, you should see the lines below in PostgreSQL container logs: + +```console +$ kubectl logs $(kubectl get pods -l app.kubernetes.io/instance=postgresql,app.kubernetes.io/name=postgresql,role=primary -o jsonpath="{.items[0].metadata.name}") +... +postgresql 08:05:12.59 INFO ==> Deploying PostgreSQL with persisted data... +... +``` + +### To 9.0.0 + +In this version the chart was adapted to follow the Helm label best practices, see [PR 3021](https://github.com/bitnami/charts/pull/3021). That means the backward compatibility is not guarantee when upgrading the chart to this major version. + +As a workaround, you can delete the existing statefulset (using the `--cascade=false` flag pods are not deleted) before upgrade the chart. For example, this can be a valid workflow: + +- Deploy an old version (8.X.X) + +```console +$ helm install postgresql bitnami/postgresql --version 8.10.14 +``` + +- Old version is up and running + +```console +$ helm ls +NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION +postgresql default 1 2020-08-04 13:39:54.783480286 +0000 UTC deployed postgresql-8.10.14 11.8.0 + +$ kubectl get pods +NAME READY STATUS RESTARTS AGE +postgresql-postgresql-0 1/1 Running 0 76s +``` + +- The upgrade to the latest one (9.X.X) is going to fail + +```console +$ helm upgrade postgresql bitnami/postgresql +Error: UPGRADE FAILED: cannot patch "postgresql-postgresql" with kind StatefulSet: StatefulSet.apps "postgresql-postgresql" is invalid: spec: Forbidden: updates to statefulset spec for fields other than 'replicas', 'template', and 'updateStrategy' are forbidden +``` + +- Delete the statefulset + +```console +$ kubectl delete statefulsets.apps --cascade=false postgresql-postgresql +statefulset.apps "postgresql-postgresql" deleted +``` + +- Now the upgrade works + +```console +$ helm upgrade postgresql bitnami/postgresql +$ helm ls +NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION +postgresql default 3 2020-08-04 13:42:08.020385884 +0000 UTC deployed postgresql-9.1.2 11.8.0 +``` + +- We can kill the existing pod and the new statefulset is going to create a new one: + +```console +$ kubectl delete pod postgresql-postgresql-0 +pod "postgresql-postgresql-0" deleted + +$ kubectl get pods +NAME READY STATUS RESTARTS AGE +postgresql-postgresql-0 1/1 Running 0 19s +``` + +Please, note that without the `--cascade=false` both objects (statefulset and pod) are going to be removed and both objects will be deployed again with the `helm upgrade` command + +### To 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. + +### To 7.1.0 + +Adds support for LDAP configuration. + +### To 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. + +### To 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 + +### To 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: + +```console +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. + +### To 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 +``` + +### To 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`. + +### To 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 bitnami/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/chart/deps/postgresql/ci/commonAnnotations.yaml b/chart/deps/postgresql/ci/commonAnnotations.yaml new file mode 100644 index 000000000..97e18a4cc --- /dev/null +++ b/chart/deps/postgresql/ci/commonAnnotations.yaml @@ -0,0 +1,3 @@ +commonAnnotations: + helm.sh/hook: "\"pre-install, pre-upgrade\"" + helm.sh/hook-weight: "-1" diff --git a/chart/deps/postgresql/ci/default-values.yaml b/chart/deps/postgresql/ci/default-values.yaml new file mode 100644 index 000000000..fc2ba605a --- /dev/null +++ b/chart/deps/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/chart/deps/postgresql/ci/shmvolume-disabled-values.yaml b/chart/deps/postgresql/ci/shmvolume-disabled-values.yaml new file mode 100644 index 000000000..347d3b40a --- /dev/null +++ b/chart/deps/postgresql/ci/shmvolume-disabled-values.yaml @@ -0,0 +1,2 @@ +shmVolume: + enabled: false diff --git a/chart/deps/postgresql/files/README.md b/chart/deps/postgresql/files/README.md new file mode 100644 index 000000000..1813a2fea --- /dev/null +++ b/chart/deps/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/chart/deps/postgresql/files/conf.d/README.md b/chart/deps/postgresql/files/conf.d/README.md new file mode 100644 index 000000000..184c1875d --- /dev/null +++ b/chart/deps/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/chart/deps/postgresql/files/docker-entrypoint-initdb.d/README.md b/chart/deps/postgresql/files/docker-entrypoint-initdb.d/README.md new file mode 100644 index 000000000..cba38091e --- /dev/null +++ b/chart/deps/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/chart/deps/postgresql/templates/NOTES.txt b/chart/deps/postgresql/templates/NOTES.txt new file mode 100644 index 000000000..4e98958c1 --- /dev/null +++ b/chart/deps/postgresql/templates/NOTES.txt @@ -0,0 +1,59 @@ +** 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 "common.names.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local - Read/Write connection +{{- if .Values.replication.enabled }} + {{ template "common.names.fullname" . }}-read.{{ .Release.Namespace }}.svc.cluster.local - Read only connection +{{- end }} + +{{- if not (eq (include "postgresql.username" .) "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 "common.names.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 "common.names.fullname" . }}-client=true" {{- end }} --command -- psql --host {{ template "common.names.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 "common.names.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 "common.names.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 "common.names.fullname" . }}' + + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "common.names.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 "common.names.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" . -}} + +{{- include "common.warnings.rollingTag" .Values.image -}} + +{{- $passwordValidationErrors := include "common.validations.values.postgresql.passwords" (dict "secret" (include "common.names.fullname" .) "context" $) -}} + +{{- include "common.errors.upgrade.passwords.empty" (dict "validationErrors" (list $passwordValidationErrors) "context" $) -}} diff --git a/chart/deps/postgresql/templates/_helpers.tpl b/chart/deps/postgresql/templates/_helpers.tpl new file mode 100644 index 000000000..25d2072d4 --- /dev/null +++ b/chart/deps/postgresql/templates/_helpers.tpl @@ -0,0 +1,919 @@ +{{/* 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 -}} + +{{/* +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 -}} + +{{/* +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 -}} + +{{/* +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 -}} + +{{/* +Return the proper image name +{{ include "common.images.image" ( dict "imageRoot" .Values.path.to.the.image "global" $) }} +*/}} +{{- define "common.images.image" -}} +{{- $registryName := .imageRoot.registry -}} +{{- $repositoryName := .imageRoot.repository -}} +{{- $tag := .imageRoot.tag | toString -}} +{{- if .global }} + {{- if .global.imageRegistry }} + {{- $registryName = .global.imageRegistry -}} + {{- end -}} +{{- end -}} +{{- if $registryName }} +{{- printf "%s/%s:%s" $registryName $repositoryName $tag -}} +{{- else -}} +{{- printf "%s:%s" $repositoryName $tag -}} +{{- end -}} +{{- end -}} + +{{/* +Return the proper Docker Image Registry Secret Names +{{ 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 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 }} + - {{ . }} + {{- 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 }} + - {{ . }} + {{- 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 soft podAffinity/podAntiAffinity definition +{{ include "common.affinities.pods.soft" (dict "component" "FOO" "context" $) -}} +*/}} +{{- define "common.affinities.pods.soft" -}} +{{- $component := default "" .component -}} +preferredDuringSchedulingIgnoredDuringExecution: + - podAffinityTerm: + labelSelector: + matchLabels: {{- (include "common.labels.matchLabels" .context) | nindent 10 }} + {{- if not (empty $component) }} + {{ printf "app.kubernetes.io/component: %s" $component }} + {{- end }} + namespaces: + - {{ .context.Release.Namespace | quote }} + topologyKey: kubernetes.io/hostname + weight: 1 +{{- end -}} + +{{/* +Return a hard podAffinity/podAntiAffinity definition +{{ include "common.affinities.pods.hard" (dict "component" "FOO" "context" $) -}} +*/}} +{{- define "common.affinities.pods.hard" -}} +{{- $component := default "" .component -}} +requiredDuringSchedulingIgnoredDuringExecution: + - labelSelector: + matchLabels: {{- (include "common.labels.matchLabels" .context) | nindent 8 }} + {{- if not (empty $component) }} + {{ printf "app.kubernetes.io/component: %s" $component }} + {{- end }} + namespaces: + - {{ .context.Release.Namespace | quote }} + topologyKey: kubernetes.io/hostname +{{- end -}} + +{{/* +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 -}} + +{{/* +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 -}} + +{{/* +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 -}} + +{{/* +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 (not $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 -}} + +{{/* +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 -}} + +{{/* +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 {{ .context.Release.Namespace | quote }} {{ .secret }} -o jsonpath="{.data.{{ .field }}}" | base64 --decode) +{{- 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 -}} + +{{/* +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 -}} + +{{/* +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 upgrade the release%s" -}} + {{- printf $errorString $validationErrors | fail -}} + {{- end -}} +{{- end -}} + +{{/* +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 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 -}} + +{{/* +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 -}} + +{{/* +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.primary.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 "primary" | trunc 63 | trimSuffix "-" -}} +{{- else -}} +{{- printf "%s" $fullname | trunc 63 | trimSuffix "-" -}} +{{- end -}} +{{- end -}} + +{{/* +Return the proper PostgreSQL image name +*/}} +{{- define "postgresql.image" -}} +{{ include "common.images.image" (dict "imageRoot" .Values.image "global" .Values.global) }} +{{- end -}} + +{{/* +Return the proper PostgreSQL metrics image name +*/}} +{{- define "postgresql.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 "postgresql.volumePermissions.image" -}} +{{ include "common.images.image" (dict "imageRoot" .Values.volumePermissions.image "global" .Values.global) }} +{{- end -}} + +{{/* +Return the proper Docker Image Registry Secret Names +*/}} +{{- define "postgresql.imagePullSecrets" -}} +{{ include "common.images.pullSecrets" (dict "images" (list .Values.image .Values.metrics.image .Values.volumePermissions.image) "global" .Values.global) }} +{{- 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 -}} + +{{/* +Get the password secret. +*/}} +{{- define "postgresql.secretName" -}} +{{- if .Values.global.postgresql.existingSecret }} + {{- printf "%s" (tpl .Values.global.postgresql.existingSecret $) -}} +{{- else if .Values.existingSecret -}} + {{- printf "%s" (tpl .Values.existingSecret $) -}} +{{- else -}} + {{- printf "%s" (include "common.names.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Return true if we should use an existingSecret. +*/}} +{{- define "postgresql.useExistingSecret" -}} +{{- if or .Values.global.postgresql.existingSecret .Values.existingSecret -}} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Return true if a secret object should be created +*/}} +{{- define "postgresql.createSecret" -}} +{{- if not (include "postgresql.useExistingSecret" .) -}} + {{- true -}} +{{- end -}} +{{- end -}} + +{{/* +Get the configuration ConfigMap name. +*/}} +{{- define "postgresql.configurationCM" -}} +{{- if .Values.configurationConfigMap -}} +{{- printf "%s" (tpl .Values.configurationConfigMap $) -}} +{{- else -}} +{{- printf "%s-configuration" (include "common.names.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 "common.names.fullname" .) -}} +{{- end -}} +{{- end -}} + +{{/* +Return true if a configmap should be mounted with PostgreSQL configuration +*/}} +{{- define "postgresql.mountConfigurationCM" -}} +{{- if or (.Files.Glob "files/postgresql.conf") (.Files.Glob "files/pg_hba.conf") .Values.postgresqlConfiguration .Values.pgHbaConfiguration .Values.configurationConfigMap }} + {{- true -}} +{{- 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 "common.names.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 "common.names.fullname" .) -}} +{{- end -}} + +{{/* +Get the readiness probe command +*/}} +{{- define "postgresql.readinessProbeCommand" -}} +- | +{{- if (include "postgresql.database" .) }} + exec pg_isready -U {{ include "postgresql.username" . | quote }} -d "dbname={{ include "postgresql.database" . }} {{- if and .Values.tls.enabled .Values.tls.certCAFilename }} sslcert={{ include "postgresql.tlsCert" . }} sslkey={{ include "postgresql.tlsCertKey" . }}{{- end }}" -h 127.0.0.1 -p {{ template "postgresql.port" . }} +{{- else }} + exec pg_isready -U {{ include "postgresql.username" . | quote }} {{- if and .Values.tls.enabled .Values.tls.certCAFilename }} -d "sslcert={{ include "postgresql.tlsCert" . }} sslkey={{ include "postgresql.tlsCertKey" . }}"{{- end }} -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 -}} + +{{/* +Compile all warnings into a single message, and call fail. +*/}} +{{- define "postgresql.validateValues" -}} +{{- $messages := list -}} +{{- $messages := append $messages (include "postgresql.validateValues.ldapConfigurationMethod" .) -}} +{{- $messages := append $messages (include "postgresql.validateValues.psp" .) -}} +{{- $messages := append $messages (include "postgresql.validateValues.tls" .) -}} +{{- $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 -}} + +{{/* +Validate values of Postgresql - If PSP is enabled RBAC should be enabled too +*/}} +{{- define "postgresql.validateValues.psp" -}} +{{- if and .Values.psp.create (not .Values.rbac.create) }} +postgresql: psp.create, rbac.create + RBAC should be enabled if PSP is enabled in order for PSP to work. + More info at https://kubernetes.io/docs/concepts/policy/pod-security-policy/#authorizing-policies +{{- end -}} +{{- end -}} + +{{/* +Return the appropriate apiVersion for podsecuritypolicy. +*/}} +{{- define "podsecuritypolicy.apiVersion" -}} +{{- if semverCompare "<1.10-0" .Capabilities.KubeVersion.GitVersion -}} +{{- print "extensions/v1beta1" -}} +{{- else -}} +{{- print "policy/v1beta1" -}} +{{- 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 -}} + +{{/* +Validate values of Postgresql TLS - When TLS is enabled, so must be VolumePermissions +*/}} +{{- define "postgresql.validateValues.tls" -}} +{{- if and .Values.tls.enabled (not .Values.volumePermissions.enabled) }} +postgresql: tls.enabled, volumePermissions.enabled + When TLS is enabled you must enable volumePermissions as well to ensure certificates files have + the right permissions. +{{- end -}} +{{- end -}} + +{{/* +Return the path to the cert file. +*/}} +{{- define "postgresql.tlsCert" -}} +{{- required "Certificate filename is required when TLS in enabled" .Values.tls.certFilename | printf "/opt/bitnami/postgresql/certs/%s" -}} +{{- end -}} + +{{/* +Return the path to the cert key file. +*/}} +{{- define "postgresql.tlsCertKey" -}} +{{- required "Certificate Key filename is required when TLS in enabled" .Values.tls.certKeyFilename | printf "/opt/bitnami/postgresql/certs/%s" -}} +{{- end -}} + +{{/* +Return the path to the CA cert file. +*/}} +{{- define "postgresql.tlsCACert" -}} +{{- printf "/opt/bitnami/postgresql/certs/%s" .Values.tls.certCAFilename -}} +{{- end -}} + +{{/* +Return the path to the CRL file. +*/}} +{{- define "postgresql.tlsCRL" -}} +{{- if .Values.tls.crlFilename -}} +{{- printf "/opt/bitnami/postgresql/certs/%s" .Values.tls.crlFilename -}} +{{- end -}} +{{- end -}} diff --git a/chart/deps/postgresql/templates/configmap.yaml b/chart/deps/postgresql/templates/configmap.yaml new file mode 100644 index 000000000..627d88027 --- /dev/null +++ b/chart/deps/postgresql/templates/configmap.yaml @@ -0,0 +1,27 @@ +{{ 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 "common.names.fullname" . }}-configuration + labels: + {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +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/chart/deps/postgresql/templates/extended-config-configmap.yaml b/chart/deps/postgresql/templates/extended-config-configmap.yaml new file mode 100644 index 000000000..bd477e44c --- /dev/null +++ b/chart/deps/postgresql/templates/extended-config-configmap.yaml @@ -0,0 +1,22 @@ +{{- if and (or (.Files.Glob "files/conf.d/*.conf") .Values.postgresqlExtendedConf) (not .Values.extendedConfConfigMap)}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "common.names.fullname" . }}-extended-configuration + labels: + {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +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/chart/deps/postgresql/templates/extra-list.yaml b/chart/deps/postgresql/templates/extra-list.yaml new file mode 100644 index 000000000..9ac65f9e1 --- /dev/null +++ b/chart/deps/postgresql/templates/extra-list.yaml @@ -0,0 +1,4 @@ +{{- range .Values.extraDeploy }} +--- +{{ include "common.tplvalues.render" (dict "value" . "context" $) }} +{{- end }} diff --git a/chart/deps/postgresql/templates/initialization-configmap.yaml b/chart/deps/postgresql/templates/initialization-configmap.yaml new file mode 100644 index 000000000..7796c67a9 --- /dev/null +++ b/chart/deps/postgresql/templates/initialization-configmap.yaml @@ -0,0 +1,25 @@ +{{- 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 "common.names.fullname" . }}-init-scripts + labels: + {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +{{- 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/chart/deps/postgresql/templates/metrics-configmap.yaml b/chart/deps/postgresql/templates/metrics-configmap.yaml new file mode 100644 index 000000000..fa539582b --- /dev/null +++ b/chart/deps/postgresql/templates/metrics-configmap.yaml @@ -0,0 +1,14 @@ +{{- if and .Values.metrics.enabled .Values.metrics.customMetrics }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ template "postgresql.metricsCM" . }} + labels: + {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +data: + custom-metrics.yaml: {{ toYaml .Values.metrics.customMetrics | quote }} +{{- end }} diff --git a/chart/deps/postgresql/templates/metrics-svc.yaml b/chart/deps/postgresql/templates/metrics-svc.yaml new file mode 100644 index 000000000..af8b67e2f --- /dev/null +++ b/chart/deps/postgresql/templates/metrics-svc.yaml @@ -0,0 +1,26 @@ +{{- if .Values.metrics.enabled }} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "common.names.fullname" . }}-metrics + labels: + {{- include "common.labels.standard" . | nindent 4 }} + annotations: + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- toYaml .Values.metrics.service.annotations | nindent 4 }} + namespace: {{ .Release.Namespace }} +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: + {{- include "common.labels.matchLabels" . | nindent 4 }} + role: primary +{{- end }} diff --git a/chart/deps/postgresql/templates/networkpolicy.yaml b/chart/deps/postgresql/templates/networkpolicy.yaml new file mode 100644 index 000000000..4f2740ea0 --- /dev/null +++ b/chart/deps/postgresql/templates/networkpolicy.yaml @@ -0,0 +1,39 @@ +{{- if .Values.networkPolicy.enabled }} +kind: NetworkPolicy +apiVersion: {{ template "postgresql.networkPolicy.apiVersion" . }} +metadata: + name: {{ template "common.names.fullname" . }} + labels: + {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +spec: + podSelector: + matchLabels: + {{- include "common.labels.matchLabels" . | nindent 6 }} + ingress: + # Allow inbound connections + - ports: + - port: {{ template "postgresql.port" . }} + {{- 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 }} + role: read + {{- end }} + {{- if .Values.metrics.enabled }} + # Allow prometheus scrapes + - ports: + - port: 9187 + {{- end }} +{{- end }} diff --git a/chart/deps/postgresql/templates/podsecuritypolicy.yaml b/chart/deps/postgresql/templates/podsecuritypolicy.yaml new file mode 100644 index 000000000..0c49694fa --- /dev/null +++ b/chart/deps/postgresql/templates/podsecuritypolicy.yaml @@ -0,0 +1,38 @@ +{{- if .Values.psp.create }} +apiVersion: {{ include "podsecuritypolicy.apiVersion" . }} +kind: PodSecurityPolicy +metadata: + name: {{ template "common.names.fullname" . }} + labels: + {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +spec: + privileged: false + volumes: + - 'configMap' + - 'secret' + - 'persistentVolumeClaim' + - 'emptyDir' + - 'projected' + hostNetwork: false + hostIPC: false + hostPID: false + runAsUser: + rule: 'RunAsAny' + seLinux: + rule: 'RunAsAny' + supplementalGroups: + rule: 'MustRunAs' + ranges: + - min: 1 + max: 65535 + fsGroup: + rule: 'MustRunAs' + ranges: + - min: 1 + max: 65535 + readOnlyRootFilesystem: false +{{- end }} diff --git a/chart/deps/postgresql/templates/prometheusrule.yaml b/chart/deps/postgresql/templates/prometheusrule.yaml new file mode 100644 index 000000000..d0f408c78 --- /dev/null +++ b/chart/deps/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 "common.names.fullname" . }} +{{- with .Values.metrics.prometheusRule.namespace }} + namespace: {{ . }} +{{- end }} + labels: + {{- include "common.labels.standard" . | nindent 4 }} + {{- with .Values.metrics.prometheusRule.additionalLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} +spec: +{{- with .Values.metrics.prometheusRule.rules }} + groups: + - name: {{ template "postgresql.name" $ }} + rules: {{ tpl (toYaml .) $ | nindent 8 }} +{{- end }} +{{- end }} diff --git a/chart/deps/postgresql/templates/role.yaml b/chart/deps/postgresql/templates/role.yaml new file mode 100644 index 000000000..017a5716b --- /dev/null +++ b/chart/deps/postgresql/templates/role.yaml @@ -0,0 +1,20 @@ +{{- if .Values.rbac.create }} +kind: Role +apiVersion: {{ include "common.capabilities.rbac.apiVersion" . }} +metadata: + name: {{ template "common.names.fullname" . }} + labels: + {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +rules: + {{- if .Values.psp.create }} + - apiGroups: ["extensions"] + resources: ["podsecuritypolicies"] + verbs: ["use"] + resourceNames: + - {{ template "common.names.fullname" . }} + {{- end }} +{{- end }} diff --git a/chart/deps/postgresql/templates/rolebinding.yaml b/chart/deps/postgresql/templates/rolebinding.yaml new file mode 100644 index 000000000..189775a15 --- /dev/null +++ b/chart/deps/postgresql/templates/rolebinding.yaml @@ -0,0 +1,20 @@ +{{- if .Values.rbac.create }} +kind: RoleBinding +apiVersion: {{ include "common.capabilities.rbac.apiVersion" . }} +metadata: + name: {{ template "common.names.fullname" . }} + labels: + {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +roleRef: + kind: Role + name: {{ template "common.names.fullname" . }} + apiGroup: rbac.authorization.k8s.io +subjects: + - kind: ServiceAccount + name: {{ default (include "common.names.fullname" . ) .Values.serviceAccount.name }} + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/chart/deps/postgresql/templates/secrets.yaml b/chart/deps/postgresql/templates/secrets.yaml new file mode 100644 index 000000000..dc301daba --- /dev/null +++ b/chart/deps/postgresql/templates/secrets.yaml @@ -0,0 +1,22 @@ +{{- if (include "postgresql.createSecret" .) }} +apiVersion: v1 +kind: Secret +metadata: + name: {{ template "common.names.fullname" . }} + labels: + {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +type: Opaque +data: + postgresql-postgres-password: {{ include "postgresql.postgres.password" . | b64enc | quote }} + 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/chart/deps/postgresql/templates/serviceaccount.yaml b/chart/deps/postgresql/templates/serviceaccount.yaml new file mode 100644 index 000000000..03f0f50e7 --- /dev/null +++ b/chart/deps/postgresql/templates/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if and (.Values.serviceAccount.enabled) (not .Values.serviceAccount.name) }} +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + {{- include "common.labels.standard" . | nindent 4 }} + name: {{ template "common.names.fullname" . }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +{{- end }} diff --git a/chart/deps/postgresql/templates/servicemonitor.yaml b/chart/deps/postgresql/templates/servicemonitor.yaml new file mode 100644 index 000000000..587ce85b8 --- /dev/null +++ b/chart/deps/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 "common.names.fullname" . }} + {{- if .Values.metrics.serviceMonitor.namespace }} + namespace: {{ .Values.metrics.serviceMonitor.namespace }} + {{- end }} + labels: + {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.metrics.serviceMonitor.additionalLabels }} + {{- toYaml .Values.metrics.serviceMonitor.additionalLabels | nindent 4 }} + {{- end }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 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: + {{- include "common.labels.matchLabels" . | nindent 6 }} +{{- end }} diff --git a/chart/deps/postgresql/templates/statefulset-readreplicas.yaml b/chart/deps/postgresql/templates/statefulset-readreplicas.yaml new file mode 100644 index 000000000..7e93d6568 --- /dev/null +++ b/chart/deps/postgresql/templates/statefulset-readreplicas.yaml @@ -0,0 +1,411 @@ +{{- if .Values.replication.enabled }} +{{- $readReplicasResources := coalesce .Values.readReplicas.resources .Values.resources -}} +apiVersion: {{ include "common.capabilities.statefulset.apiVersion" . }} +kind: StatefulSet +metadata: + name: "{{ template "common.names.fullname" . }}-read" + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: read +{{- with .Values.readReplicas.labels }} +{{ toYaml . | indent 4 }} +{{- end }} + annotations: + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- with .Values.readReplicas.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +spec: + serviceName: {{ template "common.names.fullname" . }}-headless + replicas: {{ .Values.replication.readReplicas }} + selector: + matchLabels: + {{- include "common.labels.matchLabels" . | nindent 6 }} + role: read + template: + metadata: + name: {{ template "common.names.fullname" . }} + labels: + {{- include "common.labels.standard" . | nindent 8 }} + app.kubernetes.io/component: read + role: read +{{- with .Values.readReplicas.podLabels }} +{{ toYaml . | indent 8 }} +{{- end }} +{{- with .Values.readReplicas.podAnnotations }} + annotations: +{{ toYaml . | indent 8 }} +{{- end }} + spec: + {{- if .Values.schedulerName }} + schedulerName: "{{ .Values.schedulerName }}" + {{- end }} +{{- include "postgresql.imagePullSecrets" . | indent 6 }} + {{- if .Values.readReplicas.affinity }} + affinity: {{- include "common.tplvalues.render" (dict "value" .Values.readReplicas.affinity "context" $) | nindent 8 }} + {{- else }} + affinity: + podAffinity: {{- include "common.affinities.pods" (dict "type" .Values.readReplicas.podAffinityPreset "component" "read" "context" $) | nindent 10 }} + podAntiAffinity: {{- include "common.affinities.pods" (dict "type" .Values.readReplicas.podAntiAffinityPreset "component" "read" "context" $) | nindent 10 }} + nodeAffinity: {{- include "common.affinities.nodes" (dict "type" .Values.readReplicas.nodeAffinityPreset.type "key" .Values.readReplicas.nodeAffinityPreset.key "values" .Values.readReplicas.nodeAffinityPreset.values) | nindent 10 }} + {{- end }} + {{- if .Values.readReplicas.nodeSelector }} + nodeSelector: {{- include "common.tplvalues.render" (dict "value" .Values.readReplicas.nodeSelector "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.readReplicas.tolerations }} + tolerations: {{- include "common.tplvalues.render" (dict "value" .Values.readReplicas.tolerations "context" $) | nindent 8 }} + {{- end }} + {{- if .Values.terminationGracePeriodSeconds }} + terminationGracePeriodSeconds: {{ .Values.terminationGracePeriodSeconds }} + {{- end }} + {{- if .Values.securityContext.enabled }} + securityContext: {{- omit .Values.securityContext "enabled" | toYaml | nindent 8 }} + {{- end }} + {{- if .Values.serviceAccount.enabled }} + serviceAccountName: {{ default (include "common.names.fullname" . ) .Values.serviceAccount.name}} + {{- end }} + {{- if or .Values.readReplicas.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) .Values.tls.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 }} + {{- if eq ( toString ( .Values.volumePermissions.securityContext.runAsUser )) "auto" }} + chown `id -u`:`id -G | cut -d " " -f2` {{ .Values.persistence.mountPath }} + {{- else }} + chown {{ .Values.containerSecurityContext.runAsUser }}:{{ .Values.securityContext.fsGroup }} {{ .Values.persistence.mountPath }} + {{- end }} + mkdir -p {{ .Values.persistence.mountPath }}/data {{- if (include "postgresql.mountConfigurationCM" .) }} {{ .Values.persistence.mountPath }}/conf {{- end }} + chmod 700 {{ .Values.persistence.mountPath }}/data {{- if (include "postgresql.mountConfigurationCM" .) }} {{ .Values.persistence.mountPath }}/conf {{- end }} + find {{ .Values.persistence.mountPath }} -mindepth 1 -maxdepth 1 {{- if not (include "postgresql.mountConfigurationCM" .) }} -not -name "conf" {{- end }} -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.containerSecurityContext.runAsUser }}:{{ .Values.securityContext.fsGroup }} + {{- end }} + {{- end }} + {{- if and .Values.shmVolume.enabled .Values.shmVolume.chmod.enabled }} + chmod -R 777 /dev/shm + {{- end }} + {{- if .Values.tls.enabled }} + cp /tmp/certs/* /opt/bitnami/postgresql/certs/ + {{- if eq ( toString ( .Values.volumePermissions.securityContext.runAsUser )) "auto" }} + chown -R `id -u`:`id -G | cut -d " " -f2` /opt/bitnami/postgresql/certs/ + {{- else }} + chown -R {{ .Values.containerSecurityContext.runAsUser }}:{{ .Values.securityContext.fsGroup }} /opt/bitnami/postgresql/certs/ + {{- end }} + chmod 600 {{ template "postgresql.tlsCertKey" . }} + {{- end }} + {{- if eq ( toString ( .Values.volumePermissions.securityContext.runAsUser )) "auto" }} + securityContext: {{- omit .Values.volumePermissions.securityContext "runAsUser" | toYaml | nindent 12 }} + {{- else }} + securityContext: {{- .Values.volumePermissions.securityContext | toYaml | nindent 12 }} + {{- 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 }} + {{- if .Values.tls.enabled }} + - name: raw-certificates + mountPath: /tmp/certs + - name: postgresql-certificates + mountPath: /opt/bitnami/postgresql/certs + {{- end }} + {{- end }} + {{- if .Values.readReplicas.extraInitContainers }} + {{- include "common.tplvalues.render" ( dict "value" .Values.readReplicas.extraInitContainers "context" $ ) | nindent 8 }} + {{- end }} + {{- end }} + {{- if .Values.readReplicas.priorityClassName }} + priorityClassName: {{ .Values.readReplicas.priorityClassName }} + {{- end }} + containers: + - name: {{ template "common.names.fullname" . }} + image: {{ template "postgresql.image" . }} + imagePullPolicy: "{{ .Values.image.pullPolicy }}" + {{- if $readReplicasResources }} + resources: {{- toYaml $readReplicasResources | nindent 12 }} + {{- end }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 12 }} + {{- 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 "common.names.fullname" . }} + - name: POSTGRES_MASTER_PORT_NUMBER + value: {{ include "postgresql.port" . | quote }} + {{- if and (not (eq .Values.postgresqlUsername "postgres")) (or .Values.postgresqlPostgresPassword (include "postgresql.useExistingSecret" .)) }} + {{- 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 }} + - name: POSTGRESQL_ENABLE_TLS + value: {{ ternary "yes" "no" .Values.tls.enabled | quote }} + {{- if .Values.tls.enabled }} + - name: POSTGRESQL_TLS_PREFER_SERVER_CIPHERS + value: {{ ternary "yes" "no" .Values.tls.preferServerCiphers | quote }} + - name: POSTGRESQL_TLS_CERT_FILE + value: {{ template "postgresql.tlsCert" . }} + - name: POSTGRESQL_TLS_KEY_FILE + value: {{ template "postgresql.tlsCertKey" . }} + {{- if .Values.tls.certCAFilename }} + - name: POSTGRESQL_TLS_CA_FILE + value: {{ template "postgresql.tlsCACert" . }} + {{- end }} + {{- if .Values.tls.crlFilename }} + - name: POSTGRESQL_TLS_CRL_FILE + value: {{ template "postgresql.tlsCRL" . }} + {{- end }} + {{- end }} + - name: POSTGRESQL_LOG_HOSTNAME + value: {{ .Values.audit.logHostname | quote }} + - name: POSTGRESQL_LOG_CONNECTIONS + value: {{ .Values.audit.logConnections | quote }} + - name: POSTGRESQL_LOG_DISCONNECTIONS + value: {{ .Values.audit.logDisconnections | quote }} + {{- if .Values.audit.logLinePrefix }} + - name: POSTGRESQL_LOG_LINE_PREFIX + value: {{ .Values.audit.logLinePrefix | quote }} + {{- end }} + {{- if .Values.audit.logTimezone }} + - name: POSTGRESQL_LOG_TIMEZONE + value: {{ .Values.audit.logTimezone | quote }} + {{- end }} + {{- if .Values.audit.pgAuditLog }} + - name: POSTGRESQL_PGAUDIT_LOG + value: {{ .Values.audit.pgAuditLog | quote }} + {{- end }} + - name: POSTGRESQL_PGAUDIT_LOG_CATALOG + value: {{ .Values.audit.pgAuditLogCatalog | quote }} + - name: POSTGRESQL_CLIENT_MIN_MESSAGES + value: {{ .Values.audit.clientMinMessages | quote }} + - name: POSTGRESQL_SHARED_PRELOAD_LIBRARIES + value: {{ .Values.postgresqlSharedPreloadLibraries | quote }} + {{- if .Values.postgresqlMaxConnections }} + - name: POSTGRESQL_MAX_CONNECTIONS + value: {{ .Values.postgresqlMaxConnections | quote }} + {{- end }} + {{- if .Values.postgresqlPostgresConnectionLimit }} + - name: POSTGRESQL_POSTGRES_CONNECTION_LIMIT + value: {{ .Values.postgresqlPostgresConnectionLimit | quote }} + {{- end }} + {{- if .Values.postgresqlDbUserConnectionLimit }} + - name: POSTGRESQL_USERNAME_CONNECTION_LIMIT + value: {{ .Values.postgresqlDbUserConnectionLimit | quote }} + {{- end }} + {{- if .Values.postgresqlTcpKeepalivesInterval }} + - name: POSTGRESQL_TCP_KEEPALIVES_INTERVAL + value: {{ .Values.postgresqlTcpKeepalivesInterval | quote }} + {{- end }} + {{- if .Values.postgresqlTcpKeepalivesIdle }} + - name: POSTGRESQL_TCP_KEEPALIVES_IDLE + value: {{ .Values.postgresqlTcpKeepalivesIdle | quote }} + {{- end }} + {{- if .Values.postgresqlStatementTimeout }} + - name: POSTGRESQL_STATEMENT_TIMEOUT + value: {{ .Values.postgresqlStatementTimeout | quote }} + {{- end }} + {{- if .Values.postgresqlTcpKeepalivesCount }} + - name: POSTGRESQL_TCP_KEEPALIVES_COUNT + value: {{ .Values.postgresqlTcpKeepalivesCount | quote }} + {{- end }} + {{- if .Values.postgresqlPghbaRemoveFilters }} + - name: POSTGRESQL_PGHBA_REMOVE_FILTERS + value: {{ .Values.postgresqlPghbaRemoveFilters | quote }} + {{- 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 "dbname={{ include "postgresql.database" . }} {{- if and .Values.tls.enabled .Values.tls.certCAFilename }} sslcert={{ include "postgresql.tlsCert" . }} sslkey={{ include "postgresql.tlsCertKey" . }}{{- end }}" -h 127.0.0.1 -p {{ template "postgresql.port" . }} + {{- else }} + - exec pg_isready -U {{ include "postgresql.username" . | quote }} {{- if and .Values.tls.enabled .Values.tls.certCAFilename }} -d "sslcert={{ include "postgresql.tlsCert" . }} sslkey={{ include "postgresql.tlsCertKey" . }}"{{- end }} -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 }} + {{- else if .Values.customLivenessProbe }} + livenessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.customLivenessProbe "context" $) | nindent 12 }} + {{- 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 }} + {{- else if .Values.customReadinessProbe }} + readinessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.customReadinessProbe "context" $) | nindent 12 }} + {{- 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.tls.enabled }} + - name: postgresql-certificates + mountPath: /opt/bitnami/postgresql/certs + readOnly: true + {{- end }} + {{- if .Values.readReplicas.extraVolumeMounts }} + {{- toYaml .Values.readReplicas.extraVolumeMounts | nindent 12 }} + {{- end }} +{{- if .Values.readReplicas.sidecars }} +{{- include "common.tplvalues.render" ( dict "value" .Values.readReplicas.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.tls.enabled }} + - name: raw-certificates + secret: + secretName: {{ required "A secret containing TLS certificates is required when TLS is enabled" .Values.tls.certificatesSecret }} + - name: postgresql-certificates + emptyDir: {} + {{- end }} + {{- if .Values.shmVolume.enabled }} + - name: dshm + emptyDir: + medium: Memory + sizeLimit: 1Gi + {{- end }} + {{- if or (not .Values.persistence.enabled) (not .Values.readReplicas.persistence.enabled) }} + - name: data + emptyDir: {} + {{- end }} + {{- if .Values.readReplicas.extraVolumes }} + {{- toYaml .Values.readReplicas.extraVolumes | nindent 8 }} + {{- end }} + updateStrategy: + type: {{ .Values.updateStrategy.type }} + {{- if (eq "Recreate" .Values.updateStrategy.type) }} + rollingUpdate: null + {{- end }} +{{- if and .Values.persistence.enabled .Values.readReplicas.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 "common.storage.class" (dict "persistence" .Values.persistence "global" .Values.global) }} + + {{- if .Values.persistence.selector }} + selector: {{- include "common.tplvalues.render" (dict "value" .Values.persistence.selector "context" $) | nindent 10 }} + {{- end -}} +{{- end }} +{{- end }} diff --git a/chart/deps/postgresql/templates/statefulset.yaml b/chart/deps/postgresql/templates/statefulset.yaml new file mode 100644 index 000000000..4d7c5468f --- /dev/null +++ b/chart/deps/postgresql/templates/statefulset.yaml @@ -0,0 +1,618 @@ +apiVersion: {{ include "common.capabilities.statefulset.apiVersion" . }} +kind: StatefulSet +metadata: + name: {{ template "postgresql.primary.fullname" . }} + labels: {{- include "common.labels.standard" . | nindent 4 }} + app.kubernetes.io/component: primary + {{- with .Values.primary.labels }} + {{- toYaml . | nindent 4 }} + {{- end }} + annotations: + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- with .Values.primary.annotations }} + {{- toYaml . | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +spec: + serviceName: {{ template "common.names.fullname" . }}-headless + replicas: 1 + updateStrategy: + type: {{ .Values.updateStrategy.type }} + {{- if (eq "Recreate" .Values.updateStrategy.type) }} + rollingUpdate: null + {{- end }} + selector: + matchLabels: + {{- include "common.labels.matchLabels" . | nindent 6 }} + role: primary + template: + metadata: + name: {{ template "common.names.fullname" . }} + labels: + {{- include "common.labels.standard" . | nindent 8 }} + role: primary + app.kubernetes.io/component: primary + {{- with .Values.primary.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.primary.podAnnotations }} + annotations: {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- if .Values.schedulerName }} + schedulerName: "{{ .Values.schedulerName }}" + {{- end }} +{{- include "postgresql.imagePullSecrets" . | indent 6 }} + {{- 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 "component" "primary" "context" $) | nindent 10 }} + podAntiAffinity: {{- include "common.affinities.pods" (dict "type" .Values.primary.podAntiAffinityPreset "component" "primary" "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.terminationGracePeriodSeconds }} + terminationGracePeriodSeconds: {{ .Values.terminationGracePeriodSeconds }} + {{- end }} + {{- if .Values.securityContext.enabled }} + securityContext: {{- omit .Values.securityContext "enabled" | toYaml | nindent 8 }} + {{- end }} + {{- if .Values.serviceAccount.enabled }} + serviceAccountName: {{ default (include "common.names.fullname" . ) .Values.serviceAccount.name }} + {{- end }} + {{- if or .Values.primary.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) .Values.tls.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 }} + {{- if eq ( toString ( .Values.volumePermissions.securityContext.runAsUser )) "auto" }} + chown `id -u`:`id -G | cut -d " " -f2` {{ .Values.persistence.mountPath }} + {{- else }} + chown {{ .Values.containerSecurityContext.runAsUser }}:{{ .Values.securityContext.fsGroup }} {{ .Values.persistence.mountPath }} + {{- end }} + mkdir -p {{ .Values.persistence.mountPath }}/data {{- if (include "postgresql.mountConfigurationCM" .) }} {{ .Values.persistence.mountPath }}/conf {{- end }} + chmod 700 {{ .Values.persistence.mountPath }}/data {{- if (include "postgresql.mountConfigurationCM" .) }} {{ .Values.persistence.mountPath }}/conf {{- end }} + find {{ .Values.persistence.mountPath }} -mindepth 1 -maxdepth 1 {{- if not (include "postgresql.mountConfigurationCM" .) }} -not -name "conf" {{- end }} -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.containerSecurityContext.runAsUser }}:{{ .Values.securityContext.fsGroup }} + {{- end }} + {{- end }} + {{- if and .Values.shmVolume.enabled .Values.shmVolume.chmod.enabled }} + chmod -R 777 /dev/shm + {{- end }} + {{- if .Values.tls.enabled }} + cp /tmp/certs/* /opt/bitnami/postgresql/certs/ + {{- if eq ( toString ( .Values.volumePermissions.securityContext.runAsUser )) "auto" }} + chown -R `id -u`:`id -G | cut -d " " -f2` /opt/bitnami/postgresql/certs/ + {{- else }} + chown -R {{ .Values.containerSecurityContext.runAsUser }}:{{ .Values.securityContext.fsGroup }} /opt/bitnami/postgresql/certs/ + {{- end }} + chmod 600 {{ template "postgresql.tlsCertKey" . }} + {{- end }} + {{- if eq ( toString ( .Values.volumePermissions.securityContext.runAsUser )) "auto" }} + securityContext: {{- omit .Values.volumePermissions.securityContext "runAsUser" | toYaml | nindent 12 }} + {{- else }} + securityContext: {{- .Values.volumePermissions.securityContext | toYaml | nindent 12 }} + {{- 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 }} + {{- if .Values.tls.enabled }} + - name: raw-certificates + mountPath: /tmp/certs + - name: postgresql-certificates + mountPath: /opt/bitnami/postgresql/certs + {{- end }} + {{- end }} + {{- if .Values.primary.extraInitContainers }} + {{- include "common.tplvalues.render" ( dict "value" .Values.primary.extraInitContainers "context" $ ) | nindent 8 }} + {{- end }} + {{- end }} + {{- if .Values.primary.priorityClassName }} + priorityClassName: {{ .Values.primary.priorityClassName }} + {{- end }} + containers: + - name: {{ template "common.names.fullname" . }} + image: {{ template "postgresql.image" . }} + imagePullPolicy: "{{ .Values.image.pullPolicy }}" + {{- if .Values.resources }} + resources: {{- toYaml .Values.resources | nindent 12 }} + {{- end }} + {{- if .Values.containerSecurityContext.enabled }} + securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 12 }} + {{- end }} + args: + {{- range $key, $value := default dict .Values.postgresqlConfiguration }} + - -c + - '{{ $key | snakecase }}={{ $value }}' + {{- end }} + {{- if .Values.pgHbaConfiguration }} + - -c + - hba_file=/bitnami/postgresql/conf/pg_hba.conf + {{- 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.primaryAsStandBy.enabled }} + - name: POSTGRES_MASTER_HOST + value: {{ .Values.primaryAsStandBy.primaryHost }} + - name: POSTGRES_MASTER_PORT_NUMBER + value: {{ .Values.primaryAsStandBy.primaryPort | quote }} + {{- end }} + {{- if or .Values.replication.enabled .Values.primaryAsStandBy.enabled }} + - name: POSTGRES_REPLICATION_MODE + {{- if .Values.primaryAsStandBy.enabled }} + value: "slave" + {{- else }} + value: "master" + {{- end }} + - 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 not (eq (include "postgresql.username" .) "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 "common.tplvalues.render" (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}} + - name: POSTGRESQL_ENABLE_TLS + value: {{ ternary "yes" "no" .Values.tls.enabled | quote }} + {{- if .Values.tls.enabled }} + - name: POSTGRESQL_TLS_PREFER_SERVER_CIPHERS + value: {{ ternary "yes" "no" .Values.tls.preferServerCiphers | quote }} + - name: POSTGRESQL_TLS_CERT_FILE + value: {{ template "postgresql.tlsCert" . }} + - name: POSTGRESQL_TLS_KEY_FILE + value: {{ template "postgresql.tlsCertKey" . }} + {{- if .Values.tls.certCAFilename }} + - name: POSTGRESQL_TLS_CA_FILE + value: {{ template "postgresql.tlsCACert" . }} + {{- end }} + {{- if .Values.tls.crlFilename }} + - name: POSTGRESQL_TLS_CRL_FILE + value: {{ template "postgresql.tlsCRL" . }} + {{- end }} + {{- end }} + - name: POSTGRESQL_LOG_HOSTNAME + value: {{ .Values.audit.logHostname | quote }} + - name: POSTGRESQL_LOG_CONNECTIONS + value: {{ .Values.audit.logConnections | quote }} + - name: POSTGRESQL_LOG_DISCONNECTIONS + value: {{ .Values.audit.logDisconnections | quote }} + {{- if .Values.audit.logLinePrefix }} + - name: POSTGRESQL_LOG_LINE_PREFIX + value: {{ .Values.audit.logLinePrefix | quote }} + {{- end }} + {{- if .Values.audit.logTimezone }} + - name: POSTGRESQL_LOG_TIMEZONE + value: {{ .Values.audit.logTimezone | quote }} + {{- end }} + {{- if .Values.audit.pgAuditLog }} + - name: POSTGRESQL_PGAUDIT_LOG + value: {{ .Values.audit.pgAuditLog | quote }} + {{- end }} + - name: POSTGRESQL_PGAUDIT_LOG_CATALOG + value: {{ .Values.audit.pgAuditLogCatalog | quote }} + - name: POSTGRESQL_CLIENT_MIN_MESSAGES + value: {{ .Values.audit.clientMinMessages | quote }} + - name: POSTGRESQL_SHARED_PRELOAD_LIBRARIES + value: {{ .Values.postgresqlSharedPreloadLibraries | quote }} + {{- if .Values.postgresqlMaxConnections }} + - name: POSTGRESQL_MAX_CONNECTIONS + value: {{ .Values.postgresqlMaxConnections | quote }} + {{- end }} + {{- if .Values.postgresqlPostgresConnectionLimit }} + - name: POSTGRESQL_POSTGRES_CONNECTION_LIMIT + value: {{ .Values.postgresqlPostgresConnectionLimit | quote }} + {{- end }} + {{- if .Values.postgresqlDbUserConnectionLimit }} + - name: POSTGRESQL_USERNAME_CONNECTION_LIMIT + value: {{ .Values.postgresqlDbUserConnectionLimit | quote }} + {{- end }} + {{- if .Values.postgresqlTcpKeepalivesInterval }} + - name: POSTGRESQL_TCP_KEEPALIVES_INTERVAL + value: {{ .Values.postgresqlTcpKeepalivesInterval | quote }} + {{- end }} + {{- if .Values.postgresqlTcpKeepalivesIdle }} + - name: POSTGRESQL_TCP_KEEPALIVES_IDLE + value: {{ .Values.postgresqlTcpKeepalivesIdle | quote }} + {{- end }} + {{- if .Values.postgresqlStatementTimeout }} + - name: POSTGRESQL_STATEMENT_TIMEOUT + value: {{ .Values.postgresqlStatementTimeout | quote }} + {{- end }} + {{- if .Values.postgresqlTcpKeepalivesCount }} + - name: POSTGRESQL_TCP_KEEPALIVES_COUNT + value: {{ .Values.postgresqlTcpKeepalivesCount | quote }} + {{- end }} + {{- if .Values.postgresqlPghbaRemoveFilters }} + - name: POSTGRESQL_PGHBA_REMOVE_FILTERS + value: {{ .Values.postgresqlPghbaRemoveFilters | quote }} + {{- end }} + {{- if .Values.extraEnvVarsCM }} + envFrom: + - configMapRef: + name: {{ tpl .Values.extraEnvVarsCM . }} + {{- end }} + ports: + - name: tcp-postgresql + containerPort: {{ template "postgresql.port" . }} + {{- if .Values.startupProbe.enabled }} + startupProbe: + exec: + command: + - /bin/sh + - -c + {{- if (include "postgresql.database" .) }} + - exec pg_isready -U {{ include "postgresql.username" . | quote }} -d "dbname={{ include "postgresql.database" . }} {{- if and .Values.tls.enabled .Values.tls.certCAFilename }} sslcert={{ include "postgresql.tlsCert" . }} sslkey={{ include "postgresql.tlsCertKey" . }}{{- end }}" -h 127.0.0.1 -p {{ template "postgresql.port" . }} + {{- else }} + - exec pg_isready -U {{ include "postgresql.username" . | quote }} {{- if and .Values.tls.enabled .Values.tls.certCAFilename }} -d "sslcert={{ include "postgresql.tlsCert" . }} sslkey={{ include "postgresql.tlsCertKey" . }}"{{- end }} -h 127.0.0.1 -p {{ template "postgresql.port" . }} + {{- end }} + initialDelaySeconds: {{ .Values.startupProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.startupProbe.periodSeconds }} + timeoutSeconds: {{ .Values.startupProbe.timeoutSeconds }} + successThreshold: {{ .Values.startupProbe.successThreshold }} + failureThreshold: {{ .Values.startupProbe.failureThreshold }} + {{- else if .Values.customStartupProbe }} + startupProbe: {{- include "common.tplvalues.render" (dict "value" .Values.customStartupProbe "context" $) | nindent 12 }} + {{- end }} + {{- if .Values.livenessProbe.enabled }} + livenessProbe: + exec: + command: + - /bin/sh + - -c + {{- if (include "postgresql.database" .) }} + - exec pg_isready -U {{ include "postgresql.username" . | quote }} -d "dbname={{ include "postgresql.database" . }} {{- if and .Values.tls.enabled .Values.tls.certCAFilename }} sslcert={{ include "postgresql.tlsCert" . }} sslkey={{ include "postgresql.tlsCertKey" . }}{{- end }}" -h 127.0.0.1 -p {{ template "postgresql.port" . }} + {{- else }} + - exec pg_isready -U {{ include "postgresql.username" . | quote }} {{- if and .Values.tls.enabled .Values.tls.certCAFilename }} -d "sslcert={{ include "postgresql.tlsCert" . }} sslkey={{ include "postgresql.tlsCertKey" . }}"{{- end }} -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 }} + {{- else if .Values.customLivenessProbe }} + livenessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.customLivenessProbe "context" $) | nindent 12 }} + {{- 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 }} + {{- else if .Values.customReadinessProbe }} + readinessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.customReadinessProbe "context" $) | nindent 12 }} + {{- 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.tls.enabled }} + - name: postgresql-certificates + mountPath: /opt/bitnami/postgresql/certs + readOnly: true + {{- 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.primary.extraVolumeMounts }} + {{- toYaml .Values.primary.extraVolumeMounts | nindent 12 }} + {{- end }} +{{- if .Values.primary.sidecars }} +{{- include "common.tplvalues.render" ( dict "value" .Values.primary.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: {{- omit .Values.metrics.securityContext "enabled" | toYaml | nindent 12 }} + {{- 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" .) }} + {{- $sslmode := ternary "require" "disable" .Values.tls.enabled }} + {{- if and .Values.tls.enabled .Values.tls.certCAFilename }} + - name: DATA_SOURCE_NAME + value: {{ printf "host=127.0.0.1 port=%d user=%s sslmode=%s sslcert=%s sslkey=%s" (int (include "postgresql.port" .)) (include "postgresql.username" .) $sslmode (include "postgresql.tlsCert" .) (include "postgresql.tlsCertKey" .) }} + {{- else }} + - name: DATA_SOURCE_URI + value: {{ printf "127.0.0.1:%d/%s?sslmode=%s" (int (include "postgresql.port" .)) $database $sslmode }} + {{- end }} + {{- 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.metrics.extraEnvVars }} + {{- include "common.tplvalues.render" (dict "value" .Values.metrics.extraEnvVars "context" $) | nindent 12 }} + {{- end }} + {{- 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.tls.enabled }} + - name: postgresql-certificates + mountPath: /opt/bitnami/postgresql/certs + readOnly: true + {{- 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.tls.enabled }} + - name: raw-certificates + secret: + secretName: {{ required "A secret containing TLS certificates is required when TLS is enabled" .Values.tls.certificatesSecret }} + - name: postgresql-certificates + emptyDir: {} + {{- end }} + {{- if .Values.primary.extraVolumes }} + {{- toYaml .Values.primary.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 "common.storage.class" (dict "persistence" .Values.persistence "global" .Values.global) }} + {{- if .Values.persistence.selector }} + selector: {{- include "common.tplvalues.render" (dict "value" .Values.persistence.selector "context" $) | nindent 10 }} + {{- end -}} +{{- end }} diff --git a/chart/deps/postgresql/templates/svc-headless.yaml b/chart/deps/postgresql/templates/svc-headless.yaml new file mode 100644 index 000000000..6f5f3b9ee --- /dev/null +++ b/chart/deps/postgresql/templates/svc-headless.yaml @@ -0,0 +1,28 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ template "common.names.fullname" . }}-headless + labels: + {{- include "common.labels.standard" . | nindent 4 }} + {{- if .Values.commonAnnotations }} + annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + # Use this annotation in addition to the actual publishNotReadyAddresses + # field below because the annotation will stop being respected soon but the + # field is broken in some versions of Kubernetes: + # https://github.com/kubernetes/kubernetes/issues/58662 + service.alpha.kubernetes.io/tolerate-unready-endpoints: "true" + namespace: {{ .Release.Namespace }} +spec: + type: ClusterIP + clusterIP: None + # We want all pods in the StatefulSet to have their addresses published for + # the sake of the other Postgresql pods even before they're ready, since they + # have to be able to talk to each other in order to become ready. + publishNotReadyAddresses: true + ports: + - name: tcp-postgresql + port: {{ template "postgresql.port" . }} + targetPort: tcp-postgresql + selector: + {{- include "common.labels.matchLabels" . | nindent 4 }} diff --git a/chart/deps/postgresql/templates/svc-read.yaml b/chart/deps/postgresql/templates/svc-read.yaml new file mode 100644 index 000000000..56195ea1e --- /dev/null +++ b/chart/deps/postgresql/templates/svc-read.yaml @@ -0,0 +1,43 @@ +{{- if .Values.replication.enabled }} +{{- $serviceAnnotations := coalesce .Values.readReplicas.service.annotations .Values.service.annotations -}} +{{- $serviceType := coalesce .Values.readReplicas.service.type .Values.service.type -}} +{{- $serviceLoadBalancerIP := coalesce .Values.readReplicas.service.loadBalancerIP .Values.service.loadBalancerIP -}} +{{- $serviceLoadBalancerSourceRanges := coalesce .Values.readReplicas.service.loadBalancerSourceRanges .Values.service.loadBalancerSourceRanges -}} +{{- $serviceClusterIP := coalesce .Values.readReplicas.service.clusterIP .Values.service.clusterIP -}} +{{- $serviceNodePort := coalesce .Values.readReplicas.service.nodePort .Values.service.nodePort -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "common.names.fullname" . }}-read + labels: + {{- include "common.labels.standard" . | nindent 4 }} + annotations: + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- if $serviceAnnotations }} + {{- include "common.tplvalues.render" (dict "value" $serviceAnnotations "context" $) | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +spec: + type: {{ $serviceType }} + {{- if and $serviceLoadBalancerIP (eq $serviceType "LoadBalancer") }} + loadBalancerIP: {{ $serviceLoadBalancerIP }} + {{- end }} + {{- if and (eq $serviceType "LoadBalancer") $serviceLoadBalancerSourceRanges }} + loadBalancerSourceRanges: {{- include "common.tplvalues.render" (dict "value" $serviceLoadBalancerSourceRanges "context" $) | nindent 4 }} + {{- end }} + {{- if and (eq $serviceType "ClusterIP") $serviceClusterIP }} + clusterIP: {{ $serviceClusterIP }} + {{- end }} + ports: + - name: tcp-postgresql + port: {{ template "postgresql.port" . }} + targetPort: tcp-postgresql + {{- if $serviceNodePort }} + nodePort: {{ $serviceNodePort }} + {{- end }} + selector: + {{- include "common.labels.matchLabels" . | nindent 4 }} + role: read +{{- end }} diff --git a/chart/deps/postgresql/templates/svc.yaml b/chart/deps/postgresql/templates/svc.yaml new file mode 100644 index 000000000..a29431b6a --- /dev/null +++ b/chart/deps/postgresql/templates/svc.yaml @@ -0,0 +1,41 @@ +{{- $serviceAnnotations := coalesce .Values.primary.service.annotations .Values.service.annotations -}} +{{- $serviceType := coalesce .Values.primary.service.type .Values.service.type -}} +{{- $serviceLoadBalancerIP := coalesce .Values.primary.service.loadBalancerIP .Values.service.loadBalancerIP -}} +{{- $serviceLoadBalancerSourceRanges := coalesce .Values.primary.service.loadBalancerSourceRanges .Values.service.loadBalancerSourceRanges -}} +{{- $serviceClusterIP := coalesce .Values.primary.service.clusterIP .Values.service.clusterIP -}} +{{- $serviceNodePort := coalesce .Values.primary.service.nodePort .Values.service.nodePort -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ template "common.names.fullname" . }} + labels: + {{- include "common.labels.standard" . | nindent 4 }} + annotations: + {{- if .Values.commonAnnotations }} + {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} + {{- end }} + {{- if $serviceAnnotations }} + {{- include "common.tplvalues.render" (dict "value" $serviceAnnotations "context" $) | nindent 4 }} + {{- end }} + namespace: {{ .Release.Namespace }} +spec: + type: {{ $serviceType }} + {{- if and $serviceLoadBalancerIP (eq $serviceType "LoadBalancer") }} + loadBalancerIP: {{ $serviceLoadBalancerIP }} + {{- end }} + {{- if and (eq $serviceType "LoadBalancer") $serviceLoadBalancerSourceRanges }} + loadBalancerSourceRanges: {{- include "common.tplvalues.render" (dict "value" $serviceLoadBalancerSourceRanges "context" $) | nindent 4 }} + {{- end }} + {{- if and (eq $serviceType "ClusterIP") $serviceClusterIP }} + clusterIP: {{ $serviceClusterIP }} + {{- end }} + ports: + - name: tcp-postgresql + port: {{ template "postgresql.port" . }} + targetPort: tcp-postgresql + {{- if $serviceNodePort }} + nodePort: {{ $serviceNodePort }} + {{- end }} + selector: + {{- include "common.labels.matchLabels" . | nindent 4 }} + role: primary diff --git a/chart/deps/postgresql/values.schema.json b/chart/deps/postgresql/values.schema.json new file mode 100644 index 000000000..66a2a9dd0 --- /dev/null +++ b/chart/deps/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 + }, + "readReplicas": { + "type": "integer", + "title": "read Replicas", + "form": true, + "hidden": { + "value": false, + "path": "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/chart/deps/postgresql/values.yaml b/chart/deps/postgresql/values.yaml new file mode 100644 index 000000000..abe0c858c --- /dev/null +++ b/chart/deps/postgresql/values.yaml @@ -0,0 +1,840 @@ +## 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.11.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 common.names.fullname template (will maintain the release name) +## +# nameOverride: + +## String to fully override common.names.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 + +## Container Security Context +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ +## +containerSecurityContext: + enabled: true + runAsUser: 1001 + capabilities: + drop: + - ALL + +## 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: + +## Pod Security Policy +## ref: https://kubernetes.io/docs/concepts/policy/pod-security-policy/ +## +psp: + create: false + +## Creates role for ServiceAccount +## Required for PSP +## +rbac: + create: false + +replication: + enabled: false + user: repl_user + password: repl_password + readReplicas: 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 `readReplicas` defined above, set the number of those that will have synchronous replication + ## NOTE: It cannot be > readReplicas + 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: + +## Configure current cluster's primary server to be the standby server in other cluster. +## This will allow cross cluster replication and provide cross cluster high availability. +## You will need to configure pgHbaConfiguration if you want to enable this feature with local cluster replication enabled. +## +primaryAsStandBy: + enabled: false + # primaryHost: + # primaryPort: + +## 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: + +## Audit settings +## https://github.com/bitnami/bitnami-docker-postgresql#auditing +## +audit: + ## Log client hostnames + ## + logHostname: false + ## Log connections to the server + ## + logConnections: false + ## Log disconnections + ## + logDisconnections: false + ## Operation to audit using pgAudit (default if not set) + ## + pgAuditLog: "" + ## Log catalog using pgAudit + ## + pgAuditLogCatalog: "off" + ## Log level for clients + ## + clientMinMessages: error + ## Template for log line prefix (default if not set) + ## + logLinePrefix: "" + ## Log timezone + ## + logTimezone: "" + +## Shared preload libraries +## +postgresqlSharedPreloadLibraries: "pgaudit" + +## Maximum total connections +## +postgresqlMaxConnections: + +## Maximum connections for the postgres user +## +postgresqlPostgresConnectionLimit: + +## Maximum connections for the created user +## +postgresqlDbUserConnectionLimit: + +## TCP keepalives interval +## +postgresqlTcpKeepalivesInterval: + +## TCP keepalives idle +## +postgresqlTcpKeepalivesIdle: + +## TCP keepalives count +## +postgresqlTcpKeepalivesCount: + +## Statement timeout +## +postgresqlStatementTimeout: + +## Remove pg_hba.conf lines with the following comma-separated patterns +## (cannot be used with custom pg_hba.conf) +## +postgresqlPghbaRemoveFilters: + +## 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: {} + +## 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. Evaluated as a template. + ## + 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. Evaluated as a template. + ## https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/#restrict-access-for-loadbalancer-service + ## + # loadBalancerSourceRanges: + # - 10.10.10.0/24 + +## Start primary and read(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 ignored if `volumePermissions.enabled` is `false` + ## + chmod: + enabled: true + +## PostgreSQL data Persistent Volume Storage Class +## If defined, storageClassName: <storageClass> +## 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: {} + ## selector can be used to match an existing PersistentVolume + ## selector: + ## matchLabels: + ## app: my-app + selector: {} + +## updateStrategy for PostgreSQL StatefulSet and its reads StatefulSets +## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies +## +updateStrategy: + type: RollingUpdate + +## +## PostgreSQL Primary parameters +## +primary: + ## PostgreSQL Primary pod affinity preset + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity + ## Allowed values: soft, hard + ## + podAffinityPreset: "" + + ## PostgreSQL Primary pod anti-affinity preset + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity + ## Allowed values: soft, hard + ## + podAntiAffinityPreset: soft + + ## PostgreSQL Primary node affinity preset + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity + ## Allowed values: soft, hard + ## + nodeAffinityPreset: + ## Node affinity type + ## Allowed values: soft, hard + type: "" + ## Node label key to match + ## E.g. + ## key: "kubernetes.io/e2e-az-name" + ## + key: "" + ## Node label values to match + ## E.g. + ## values: + ## - e2e-az1 + ## - e2e-az2 + ## + values: [] + + ## Affinity for PostgreSQL primary pods assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity + ## Note: primary.podAffinityPreset, primary.podAntiAffinityPreset, and primary.nodeAffinityPreset will be ignored when it's set + ## + affinity: {} + + ## Node labels for PostgreSQL primary pods assignment + ## ref: https://kubernetes.io/docs/user-guide/node-selection/ + ## + nodeSelector: {} + + ## Tolerations for PostgreSQL primary pods assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ + ## + tolerations: [] + + labels: {} + annotations: {} + podLabels: {} + podAnnotations: {} + priorityClassName: '' + ## Extra init containers + ## Example + ## + ## extraInitContainers: + ## - name: do-something + ## image: busybox + ## command: ['do', 'something'] + ## + extraInitContainers: [] + + ## Additional PostgreSQL primary Volume mounts + ## + extraVolumeMounts: [] + ## Additional PostgreSQL primary 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: [] + + ## Override the service configuration for primary + ## + service: {} + # type: + # nodePort: + # clusterIP: + +## +## PostgreSQL read only replica parameters +## +readReplicas: + ## PostgreSQL read only pod affinity preset + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity + ## Allowed values: soft, hard + ## + podAffinityPreset: "" + + ## PostgreSQL read only pod anti-affinity preset + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity + ## Allowed values: soft, hard + ## + podAntiAffinityPreset: soft + + ## PostgreSQL read only node affinity preset + ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity + ## Allowed values: soft, hard + ## + nodeAffinityPreset: + ## Node affinity type + ## Allowed values: soft, hard + type: "" + ## Node label key to match + ## E.g. + ## key: "kubernetes.io/e2e-az-name" + ## + key: "" + ## Node label values to match + ## E.g. + ## values: + ## - e2e-az1 + ## - e2e-az2 + ## + values: [] + + ## Affinity for PostgreSQL read only pods assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity + ## Note: readReplicas.podAffinityPreset, readReplicas.podAntiAffinityPreset, and readReplicas.nodeAffinityPreset will be ignored when it's set + ## + affinity: {} + + ## Node labels for PostgreSQL read only pods assignment + ## ref: https://kubernetes.io/docs/user-guide/node-selection/ + ## + nodeSelector: {} + + ## Tolerations for PostgreSQL read only pods assignment + ## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ + ## + tolerations: [] + labels: {} + annotations: {} + podLabels: {} + podAnnotations: {} + priorityClassName: '' + + ## Extra init containers + ## Example + ## + ## extraInitContainers: + ## - name: do-something + ## image: busybox + ## command: ['do', 'something'] + ## + extraInitContainers: [] + + ## Additional PostgreSQL read replicas Volume mounts + ## + extraVolumeMounts: [] + + ## Additional PostgreSQL read replicas 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: [] + + ## Override the service configuration for read + ## + service: {} + # type: + # nodePort: + # clusterIP: + + ## Whether to enable PostgreSQL read replicas data Persistent + ## + persistence: + enabled: true + + # Override the resource configuration for read replicas + resources: + requests: + memory: 256Mi + cpu: 250m + limits: + memory: 256Mi + cpu: 250m + +## Configure resource requests and limits +## ref: http://kubernetes.io/docs/user-guide/compute-resources/ +## +resources: + requests: + memory: 256Mi + cpu: 250m + limits: + memory: 256Mi + cpu: 250m + +## Add annotations to all the deployed resources +## +commonAnnotations: {} + +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. + ## + ## Example: + ## explicitNamespacesSelector: + ## matchLabels: + ## role: frontend + ## matchExpressions: + ## - {key: role, operator: In, values: [frontend]} + ## + explicitNamespacesSelector: {} + +## Configure extra options for startup, liveness and readiness probes +## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#configure-probes +## +startupProbe: + enabled: false + initialDelaySeconds: 30 + periodSeconds: 15 + timeoutSeconds: 5 + failureThreshold: 10 + successThreshold: 1 + +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 + +## Custom Startup probe +## +customStartupProbe: {} + +## Custom Liveness probe +## +customLivenessProbe: {} + +## Custom Rediness probe +## +customReadinessProbe: {} + +## +## TLS configuration +## +tls: + # Enable TLS traffic + enabled: false + # + # Whether to use the server's TLS cipher preferences rather than the client's. + preferServerCiphers: true + # + # Name of the Secret that contains the certificates + certificatesSecret: '' + # + # Certificate filename + certFilename: '' + # + # Certificate Key filename + certKeyFilename: '' + # + # CA Certificate filename + # If provided, PostgreSQL will authenticate TLS/SSL clients by requesting them a certificate + # ref: https://www.postgresql.org/docs/9.6/auth-methods.html + certCAFilename: + # + # File containing a Certificate Revocation List + crlFilename: + +## Configure metrics exporter +## +metrics: + enabled: false + resources: + requests: + memory: 256Mi + cpu: 100m + limits: + memory: 256Mi + cpu: 100m + 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: '' + ## These are just examples rules, please adapt them to your needs. + ## Make sure to constraint the rules to the current postgresql service. + ## rules: + ## - alert: HugeReplicationLag + ## expr: pg_replication_lag{service="{{ template "common.names.fullname" . }}-metrics"} / 3600 > 1 + ## for: 1m + ## labels: + ## severity: critical + ## annotations: + ## description: replication for {{ template "common.names.fullname" . }} PostgreSQL is lagging by {{ "{{ $value }}" }} hour(s). + ## summary: PostgreSQL replication is lagging by {{ "{{ $value }}" }} hour(s). + ## + rules: [] + + image: + registry: docker.io + repository: bitnami/postgres-exporter + tag: 0.8.0-debian-10-r362 + 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_bytes 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" + # + ## An array to add extra env vars to configure postgres-exporter + ## see: https://github.com/wrouesnel/postgres_exporter#environment-variables + ## For example: + # extraEnvVars: + # - name: PG_EXPORTER_DISABLE_DEFAULT_METRICS + # value: "true" + extraEnvVars: {} + + ## 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 + +## Array with extra yaml to deploy with the chart. Evaluated as a template +## +extraDeploy: [] diff --git a/bb_prod/bigbang/ci-hr/templates/_helpers.tpl b/chart/templates/_helpers.tpl similarity index 69% rename from bb_prod/bigbang/ci-hr/templates/_helpers.tpl rename to chart/templates/_helpers.tpl index 52d6b505e..5a1108e9a 100644 --- a/bb_prod/bigbang/ci-hr/templates/_helpers.tpl +++ b/chart/templates/_helpers.tpl @@ -1,7 +1,7 @@ {{/* Expand the name of the chart. */}} -{{- define "bbci.name" -}} +{{- define "mattermost.name" -}} {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} {{- end }} @@ -10,7 +10,7 @@ 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 "bbci.fullname" -}} +{{- define "mattermost.fullname" -}} {{- if .Values.fullnameOverride }} {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} {{- else }} @@ -26,36 +26,35 @@ If release name contains chart name it will be used as a full name. {{/* Create chart name and version as used by the chart label. */}} -{{- define "bbci.chart" -}} +{{- define "mattermost.chart" -}} {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} {{- end }} {{/* Common labels */}} -{{- define "bbci.labels" -}} -helm.sh/chart: {{ include "bbci.chart" . }} -{{ include "bbci.selectorLabels" . }} -{{- if .Chart.AppVersion }} -app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} -{{- end }} +{{- define "mattermost.labels" -}} +helm.sh/chart: {{ include "mattermost.chart" . }} +{{ include "mattermost.selectorLabels" . }} +app.kubernetes.io/version: {{ .Chart.Version | quote }} app.kubernetes.io/managed-by: {{ .Release.Service }} {{- end }} {{/* Selector labels */}} -{{- define "bbci.selectorLabels" -}} -app.kubernetes.io/name: {{ include "bbci.name" . }} +{{- define "mattermost.selectorLabels" -}} +app.kubernetes.io/name: {{ include "mattermost.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} +app.kubernetes.io/part-of: {{ .Release.Name }} {{- end }} {{/* Create the name of the service account to use */}} -{{- define "bbci.serviceAccountName" -}} +{{- define "mattermost.serviceAccountName" -}} {{- if .Values.serviceAccount.create }} -{{- default (include "bbci.fullname" .) .Values.serviceAccount.name }} +{{- default (include "mattermost.fullname" .) .Values.serviceAccount.name }} {{- else }} {{- default "default" .Values.serviceAccount.name }} {{- end }} diff --git a/chart/templates/bigbang/mattermost-dashboards.yaml b/chart/templates/bigbang/mattermost-dashboards.yaml new file mode 100644 index 000000000..784fe63d0 --- /dev/null +++ b/chart/templates/bigbang/mattermost-dashboards.yaml @@ -0,0 +1,12 @@ +{{- if and .Values.monitoring.enabled .Values.enterprise.enabled }} +apiVersion: v1 +kind: ConfigMap +metadata: + name: mattermost-grafana-dashboards + namespace: monitoring + labels: + grafana_dashboard: "1" +data: + mattermost-performance-monitoring-v2_rev2.json: | + {{ .Files.Get "dashboards/mattermost-performance-monitoring-v2_rev2.json" | nindent 4 }} +{{- end }} \ No newline at end of file diff --git a/chart/templates/bigbang/mattermost-gossip-svc.yaml b/chart/templates/bigbang/mattermost-gossip-svc.yaml new file mode 100644 index 000000000..f00f70161 --- /dev/null +++ b/chart/templates/bigbang/mattermost-gossip-svc.yaml @@ -0,0 +1,20 @@ +{{- /* Mattermost uses a gossip protocol for HA clustering. In order for Istio to properly route this traffic it needs to be explicitly defined in a service with a `tcp-` prefix. */ -}} +{{- if (eq .Values.istio.injection "enabled") }} +apiVersion: v1 +kind: Service +metadata: + name: mattermost-gossip + namespace: {{ .Release.Namespace }} + labels: + app: mattermost +spec: + type: ClusterIP + clusterIP: None + selector: + app: mattermost + ports: + - name: tcp-gossip + port: 8074 + protocol: TCP + targetPort: 8074 +{{- end }} diff --git a/chart/templates/bigbang/networkpolicies/allow-dns-egress.yaml b/chart/templates/bigbang/networkpolicies/allow-dns-egress.yaml new file mode 100644 index 000000000..1669d1371 --- /dev/null +++ b/chart/templates/bigbang/networkpolicies/allow-dns-egress.yaml @@ -0,0 +1,22 @@ +{{- if .Values.networkPolicies.enabled }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-dns-egress + namespace: {{ .Release.Namespace }} +spec: + podSelector: {} + policyTypes: + - Egress + # Allow access to DNS + egress: + - to: + - namespaceSelector: {} + ports: + - port: 53 + protocol: UDP + {{- if .Values.openshift }} + - port: 5353 + protocol: UDP + {{- end }} +{{- end }} \ No newline at end of file diff --git a/chart/templates/bigbang/networkpolicies/allow-egress-tempo.yaml b/chart/templates/bigbang/networkpolicies/allow-egress-tempo.yaml new file mode 100644 index 000000000..7d7e1d37c --- /dev/null +++ b/chart/templates/bigbang/networkpolicies/allow-egress-tempo.yaml @@ -0,0 +1,21 @@ +{{- if and .Values.networkPolicies.enabled (eq .Values.istio.injection "enabled") }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-tempo-egress + namespace: {{ .Release.Namespace }} +spec: + podSelector: {} + policyTypes: + - Egress + egress: + - to: + - namespaceSelector: + matchLabels: + app.kubernetes.io/name: tempo + podSelector: + matchLabels: + app.kubernetes.io/name: tempo + ports: + - port: 9411 +{{- end }} diff --git a/chart/templates/bigbang/networkpolicies/allow-elastic-egress.yaml b/chart/templates/bigbang/networkpolicies/allow-elastic-egress.yaml new file mode 100644 index 000000000..6946600c3 --- /dev/null +++ b/chart/templates/bigbang/networkpolicies/allow-elastic-egress.yaml @@ -0,0 +1,24 @@ +{{- if and .Values.networkPolicies.enabled .Values.elasticsearch.enabled }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-elastic-egress + namespace: {{ .Release.Namespace }} +spec: + podSelector: + matchLabels: + app: mattermost + policyTypes: + - Egress + egress: + - to: + - namespaceSelector: + matchLabels: + app.kubernetes.io/name: logging + podSelector: + matchLabels: + common.k8s.elastic.co/type: elasticsearch + ports: + - port: 9200 + protocol: TCP +{{- end }} \ No newline at end of file diff --git a/chart/templates/bigbang/networkpolicies/allow-external-postgres.yaml b/chart/templates/bigbang/networkpolicies/allow-external-postgres.yaml new file mode 100644 index 000000000..c400a930b --- /dev/null +++ b/chart/templates/bigbang/networkpolicies/allow-external-postgres.yaml @@ -0,0 +1,20 @@ +{{- if and .Values.networkPolicies.enabled (not .Values.postgresql.install) }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-external-postgres-egress-upgrade + namespace: {{ .Release.Namespace }} +spec: + podSelector: + matchLabels: + app: mattermost-update-check + policyTypes: + - Egress + egress: + - to: + - ipBlock: + cidr: 0.0.0.0/0 + # ONLY Block requests to AWS metadata IP + except: + - 169.254.169.254/32 +{{- end }} diff --git a/chart/templates/bigbang/networkpolicies/allow-in-ns.yaml b/chart/templates/bigbang/networkpolicies/allow-in-ns.yaml new file mode 100644 index 000000000..495131c6c --- /dev/null +++ b/chart/templates/bigbang/networkpolicies/allow-in-ns.yaml @@ -0,0 +1,18 @@ +{{- if .Values.networkPolicies.enabled }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-in-ns + namespace: {{ .Release.Namespace }} +spec: + podSelector: {} + policyTypes: + - Ingress + - Egress + ingress: + - from: + - podSelector: {} + egress: + - to: + - podSelector: {} +{{- end }} \ No newline at end of file diff --git a/chart/templates/bigbang/networkpolicies/allow-istio-egress.yaml b/chart/templates/bigbang/networkpolicies/allow-istio-egress.yaml new file mode 100644 index 000000000..9e4284712 --- /dev/null +++ b/chart/templates/bigbang/networkpolicies/allow-istio-egress.yaml @@ -0,0 +1,21 @@ +{{- if and .Values.networkPolicies.enabled (eq .Values.istio.injection "enabled") }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-istio-egress + namespace: {{ .Release.Namespace }} +spec: + podSelector: {} + policyTypes: + - Egress + egress: + - to: + - namespaceSelector: + matchLabels: + app.kubernetes.io/name: istio-controlplane + podSelector: + matchLabels: + istio: pilot + ports: + - port: 15012 +{{- end }} diff --git a/chart/templates/bigbang/networkpolicies/allow-istio-ingress.yaml b/chart/templates/bigbang/networkpolicies/allow-istio-ingress.yaml new file mode 100644 index 000000000..c7d198755 --- /dev/null +++ b/chart/templates/bigbang/networkpolicies/allow-istio-ingress.yaml @@ -0,0 +1,24 @@ +{{- if and .Values.networkPolicies.enabled .Values.istio.enabled }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-istio-ingress + namespace: {{ .Release.Namespace }} +spec: + podSelector: + matchLabels: + app: mattermost + policyTypes: + - Ingress + ingress: + - from: + - namespaceSelector: + matchLabels: + app.kubernetes.io/name: istio-controlplane + podSelector: + matchLabels: + {{- toYaml .Values.networkPolicies.ingressLabels | nindent 12 }} + ports: + - port: 8065 + protocol: TCP +{{- end }} \ No newline at end of file diff --git a/chart/templates/bigbang/networkpolicies/allow-mattermost-egress.yaml b/chart/templates/bigbang/networkpolicies/allow-mattermost-egress.yaml new file mode 100644 index 000000000..ab17aa5bf --- /dev/null +++ b/chart/templates/bigbang/networkpolicies/allow-mattermost-egress.yaml @@ -0,0 +1,20 @@ +{{- if .Values.networkPolicies.enabled }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-mattermost-egress + namespace: {{ .Release.Namespace }} +spec: + podSelector: + matchLabels: + app: mattermost + policyTypes: + - Egress + egress: + - to: + - ipBlock: + cidr: 0.0.0.0/0 + # ONLY Block requests to AWS metadata IP + except: + - 169.254.169.254/32 +{{- end }} diff --git a/chart/templates/bigbang/networkpolicies/allow-minio-monitoring.yaml b/chart/templates/bigbang/networkpolicies/allow-minio-monitoring.yaml new file mode 100644 index 000000000..539036065 --- /dev/null +++ b/chart/templates/bigbang/networkpolicies/allow-minio-monitoring.yaml @@ -0,0 +1,22 @@ +{{- if and .Values.networkPolicies.enabled .Values.monitoring.enabled .Values.minio.install .Values.minio.tenant.metrics.enabled }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-minio-metrics-scraping + namespace: {{ .Release.Namespace }} +spec: + podSelector: + matchLabels: + app: minio + v1.min.io/tenant: mattermost-minio + policyTypes: + - Ingress + ingress: + - from: + - namespaceSelector: + matchLabels: + app.kubernetes.io/name: monitoring + ports: + - port: {{ .Values.minio.tenant.metrics.port }} + protocol: TCP +{{- end }} diff --git a/chart/templates/bigbang/networkpolicies/allow-minio-operator-egress.yaml b/chart/templates/bigbang/networkpolicies/allow-minio-operator-egress.yaml new file mode 100644 index 000000000..5457c88ee --- /dev/null +++ b/chart/templates/bigbang/networkpolicies/allow-minio-operator-egress.yaml @@ -0,0 +1,21 @@ +{{- if and .Values.networkPolicies.enabled .Values.minio.install }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-minio-operator-egress + namespace: {{ .Release.Namespace }} +spec: + podSelector: + matchLabels: + app: minio + policyTypes: + - Egress + egress: + - to: + - namespaceSelector: + matchLabels: + app.kubernetes.io/name: minioOperator + ports: + - port: 4222 + protocol: TCP +{{- end }} diff --git a/chart/templates/bigbang/networkpolicies/allow-minio-operator-ingress-upgrade.yaml b/chart/templates/bigbang/networkpolicies/allow-minio-operator-ingress-upgrade.yaml new file mode 100644 index 000000000..f8a507008 --- /dev/null +++ b/chart/templates/bigbang/networkpolicies/allow-minio-operator-ingress-upgrade.yaml @@ -0,0 +1,24 @@ +{{- if and .Values.networkPolicies.enabled .Values.minio.install }} +# this can be removed after the upgrade to 4.4.3 +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-minio-operator-ingress-upgrade + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": pre-upgrade +spec: + ingress: + - from: + - namespaceSelector: + matchLabels: + app.kubernetes.io/name: minioOperator + ports: + - port: 9000 + protocol: TCP + podSelector: + matchLabels: + app: mattermost-minio + policyTypes: + - Ingress +{{- end }} diff --git a/chart/templates/bigbang/networkpolicies/allow-minio-operator-ingress.yaml b/chart/templates/bigbang/networkpolicies/allow-minio-operator-ingress.yaml new file mode 100644 index 000000000..b742d0355 --- /dev/null +++ b/chart/templates/bigbang/networkpolicies/allow-minio-operator-ingress.yaml @@ -0,0 +1,21 @@ +{{- if and .Values.networkPolicies.enabled .Values.minio.install }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-minio-operator-ingress + namespace: {{ .Release.Namespace }} +spec: + ingress: + - from: + - namespaceSelector: + matchLabels: + app.kubernetes.io/name: minioOperator + ports: + - port: 9000 + protocol: TCP + podSelector: + matchLabels: + app: minio + policyTypes: + - Ingress +{{- end }} diff --git a/chart/templates/bigbang/networkpolicies/allow-monitoring-ingress.yaml b/chart/templates/bigbang/networkpolicies/allow-monitoring-ingress.yaml new file mode 100644 index 000000000..63ed9dc21 --- /dev/null +++ b/chart/templates/bigbang/networkpolicies/allow-monitoring-ingress.yaml @@ -0,0 +1,24 @@ +{{- if and .Values.networkPolicies.enabled .Values.monitoring.enabled .Values.enterprise.enabled }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-monitoring-ingress + namespace: {{ .Release.Namespace }} +spec: + podSelector: + matchLabels: + app: mattermost + policyTypes: + - Ingress + ingress: + - from: + - namespaceSelector: + matchLabels: + app.kubernetes.io/name: monitoring + podSelector: + matchLabels: + app: prometheus + ports: + - port: 8067 + protocol: TCP +{{- end }} \ No newline at end of file diff --git a/chart/templates/bigbang/networkpolicies/allow-sidecar-monitoring-ingress.yaml b/chart/templates/bigbang/networkpolicies/allow-sidecar-monitoring-ingress.yaml new file mode 100644 index 000000000..872d5b818 --- /dev/null +++ b/chart/templates/bigbang/networkpolicies/allow-sidecar-monitoring-ingress.yaml @@ -0,0 +1,22 @@ +{{- if and .Values.networkPolicies.enabled .Values.monitoring.enabled (eq .Values.istio.injection "enabled") }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-prometheus-ingress + namespace: "{{ .Release.Namespace }}" +spec: + podSelector: {} + policyTypes: + - Ingress + ingress: + - from: + - namespaceSelector: + matchLabels: + app.kubernetes.io/name: monitoring + podSelector: + matchLabels: + app: prometheus + ports: + - port: 15020 + protocol: TCP +{{- end }} diff --git a/chart/templates/bigbang/networkpolicies/allow-test-egress.yaml b/chart/templates/bigbang/networkpolicies/allow-test-egress.yaml new file mode 100644 index 000000000..a9ccfb392 --- /dev/null +++ b/chart/templates/bigbang/networkpolicies/allow-test-egress.yaml @@ -0,0 +1,26 @@ +{{- $bbtests := .Values.bbtests | default dict -}} +{{- $enabled := (hasKey $bbtests "enabled") -}} +{{- if $enabled }} +{{- if and .Values.networkPolicies.enabled .Values.bbtests.enabled }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-test-egress + namespace: {{ .Release.Namespace }} +spec: + podSelector: + matchLabels: + helm-test: enabled + egress: + - to: + - ipBlock: + cidr: {{ .Values.networkPolicies.controlPlaneCidr }} + {{- if eq .Values.networkPolicies.controlPlaneCidr "0.0.0.0/0" }} + # ONLY Block requests to cloud metadata IP + except: + - 169.254.169.254/32 + {{- end }} + policyTypes: + - Egress +{{- end }} +{{- end }} diff --git a/chart/templates/bigbang/networkpolicies/deny-default.yaml b/chart/templates/bigbang/networkpolicies/deny-default.yaml new file mode 100644 index 000000000..c3f0c6b17 --- /dev/null +++ b/chart/templates/bigbang/networkpolicies/deny-default.yaml @@ -0,0 +1,14 @@ +{{- if .Values.networkPolicies.enabled }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: default-deny + namespace: {{ .Release.Namespace }} +spec: + podSelector: {} + policyTypes: + - Ingress + - Egress + ingress: [] + egress: [] +{{- end }} \ No newline at end of file diff --git a/chart/templates/db-credentials.yaml b/chart/templates/db-credentials.yaml new file mode 100644 index 000000000..6c0b5ebd6 --- /dev/null +++ b/chart/templates/db-credentials.yaml @@ -0,0 +1,16 @@ +{{- if and (not .Values.database.secret) .Values.postgresql.install }} +apiVersion: v1 +kind: Secret +type: Opaque +metadata: + name: {{ include "mattermost.fullname" . }}-dbcreds + namespace: {{ .Release.Namespace }} + labels: + {{ include "mattermost.labels" . | nindent 4 }} + app.kubernetes.io/component: "database" +stringData: + {{- with .Values.postgresql }} + DB_CONNECTION_CHECK_URL: "postgres://{{ .postgresqlUsername }}:{{ .postgresqlPassword }}@{{ .fullnameOverride }}:5432/{{ .postgresqlDatabase }}?connect_timeout=10&sslmode=disable" + DB_CONNECTION_STRING: "postgres://{{ .postgresqlUsername }}:{{ .postgresqlPassword }}@{{ .fullnameOverride }}:5432/{{ .postgresqlDatabase }}?connect_timeout=10&sslmode=disable" + {{- end }} +{{- end }} diff --git a/chart/templates/default-bucket.yaml b/chart/templates/default-bucket.yaml new file mode 100644 index 000000000..23aa569e0 --- /dev/null +++ b/chart/templates/default-bucket.yaml @@ -0,0 +1,71 @@ +{{- if .Values.minio.install }} +apiVersion: batch/v1 +kind: Job +metadata: + name: default-minio-bucket-creation + namespace: {{ .Release.Namespace }} + annotations: + "helm.sh/hook": post-install,post-upgrade + "helm.sh/hook-weight": "-5" + "helm.sh/hook-delete-policy": before-hook-creation +spec: + template: + metadata: + name: default-minio-bucket-creation + spec: + imagePullSecrets: + {{- with .Values.global.imagePullSecrets }} + {{ . | toYaml | nindent 8 }} + {{- end }} + restartPolicy: Never + containers: + - name: minio-bucket-creation + image: {{ .Values.minio.bucketCreationImage }} + command: + - /bin/sh + - -c + - | + set -ex + attempt_counter=0 + max_attempts=25 + until [ $(mc config host add bigbang http://{{ .Values.minio.service.nameOverride }} {{ .Values.minio.secrets.accessKey }} {{ .Values.minio.secrets.secretKey }} >/dev/null; echo $?) -eq 0 ]; do + if [ ${attempt_counter} -eq ${max_attempts} ];then + echo "Max attempts reached" + {{- if eq .Values.istio.injection "enabled" }} + until curl -fsI http://localhost:15021/healthz/ready; do + echo "Waiting for Istio sidecar proxy..." + sleep 3 + done + sleep 5 + echo "Stopping the istio proxy..." + curl -X POST http://localhost:15020/quitquitquit + {{- end }} + exit 1 + fi + attempt_counter=$(($attempt_counter+1)) + sleep 10 + done + if [ $(mc ls bigbang/mattermost >/dev/null; echo $?) -eq 0 ]; then + echo "Default Bucket Exists" + else + mc mb bigbang/mattermost + fi + {{- if eq .Values.istio.injection "enabled" }} + until curl -fsI http://localhost:15021/healthz/ready; do + echo "Waiting for Istio sidecar proxy..." + sleep 3 + done + sleep 5 + echo "Stopping the istio proxy..." + curl -X POST http://localhost:15020/quitquitquit + {{- end }} + exit 0 + securityContext: {{- toYaml .Values.minio.containerSecurityContext | nindent 12 }} + resources: + requests: + memory: 256Mi + cpu: 100m + limits: + memory: 256Mi + cpu: 100m +{{- end }} diff --git a/chart/templates/env-secret.yaml b/chart/templates/env-secret.yaml new file mode 100644 index 000000000..323d13fac --- /dev/null +++ b/chart/templates/env-secret.yaml @@ -0,0 +1,52 @@ +apiVersion: v1 +kind: Secret +metadata: + name: mattermost-envs + namespace: {{ .Release.Namespace }} + labels: + {{ include "mattermost.labels" . | nindent 4 }} + app.kubernetes.io/component: "envs" + annotations: + "helm.sh/hook": "pre-install,pre-upgrade" +type: Opaque +stringData: + {{- if .Values.ingress.host }} + MM_SERVICESETTINGS_SITEURL: "https://{{ .Values.ingress.host }}" + {{- else if .Values.istio.chat.hosts }} + MM_SERVICESETTINGS_SITEURL: "https://{{ tpl (index .Values.istio.chat.hosts 0) $ }}" + {{- else }} + MM_SERVICESETTINGS_SITEURL: "https://chat.bigbang.dev" + {{- end }} + {{- if .Values.sso.enabled }} + MM_GITLABSETTINGS_ENABLE: "{{ .Values.sso.enabled }}" + MM_GITLABSETTINGS_ID: "{{ .Values.sso.client_id }}" + MM_GITLABSETTINGS_SECRET: "{{ .Values.sso.client_secret }}" + MM_GITLABSETTINGS_AUTHENDPOINT: "{{ .Values.sso.auth_endpoint }}" + MM_GITLABSETTINGS_TOKENENDPOINT: "{{ .Values.sso.token_endpoint }}" + MM_GITLABSETTINGS_USERAPIENDPOINT: "{{ .Values.sso.user_api_endpoint }}" + {{- end }} + {{- if .Values.minio.install }} + MM_FILESETTINGS_AMAZONS3SSL: "false" + {{- end }} + {{- if .Values.elasticsearch.enabled }} + MM_ELASTICSEARCHSETTINGS_CONNECTIONURL: "{{ .Values.elasticsearch.connectionurl }}" + MM_ELASTICSEARCHSETTINGS_ENABLEAUTOCOMPLETE: "{{ .Values.elasticsearch.enableautocomplete }}" + MM_ELASTICSEARCHSETTINGS_ENABLEINDEXING: "{{ .Values.elasticsearch.enableindexing }}" + MM_ELASTICSEARCHSETTINGS_INDEXPREFIX: "{{ .Values.elasticsearch.indexprefix }}" + MM_ELASTICSEARCHSETTINGS_BULKINDEXINGTIMEWINDOWSECONDS: "{{ .Values.elasticsearch.bulkindexingtimewindowseconds }}" + MM_ELASTICSEARCHSETTINGS_ENABLESEARCHING: "{{ .Values.elasticsearch.enablesearching }}" + MM_ELASTICSEARCHSETTINGS_SKIPTLSVERIFICATION: "{{ .Values.elasticsearch.skiptlsverification }}" + MM_ELASTICSEARCHSETTINGS_SNIFF: "{{ .Values.elasticsearch.sniff }}" + MM_ELASTICSEARCHSETTINGS_USERNAME: {{ .Values.elasticsearch.username | default "elastic" }} + {{- if .Values.elasticsearch.password }} + MM_ELASTICSEARCHSETTINGS_PASSWORD: {{ .Values.elasticsearch.password }} + {{- else }} + {{ $secretname := printf "%s-es-elastic-user" ( .Values.elasticsearch.name | default "logging-ek" )}} + {{- with lookup "v1" "Secret" (.Values.elasticsearch.namespace | default "logging" ) $secretname }} + MM_ELASTICSEARCHSETTINGS_PASSWORD: {{ .data.elastic | b64dec }} + {{- end }} + {{- end }} + {{- end }} + {{- if .Values.mattermostEnvs }} + {{ tpl (toYaml .Values.mattermostEnvs) . | nindent 2}} + {{- end }} diff --git a/chart/templates/license.yaml b/chart/templates/license.yaml new file mode 100644 index 000000000..7e559ac6c --- /dev/null +++ b/chart/templates/license.yaml @@ -0,0 +1,15 @@ +{{- if and .Values.enterprise.enabled .Values.enterprise.license }} +apiVersion: v1 +kind: Secret +metadata: + name: mattermost-license + namespace: {{ .Release.Namespace }} + labels: + {{ include "mattermost.labels" . | nindent 4 }} + app.kubernetes.io/component: "license" + annotations: + "helm.sh/hook": "pre-install,pre-upgrade" +type: Opaque +data: + license: {{ .Values.enterprise.license | b64enc }} +{{- end }} diff --git a/chart/templates/mattermost.yaml b/chart/templates/mattermost.yaml new file mode 100644 index 000000000..af1cfd1a5 --- /dev/null +++ b/chart/templates/mattermost.yaml @@ -0,0 +1,246 @@ +apiVersion: installation.mattermost.com/v1beta1 +kind: Mattermost +metadata: + name: {{ .Release.Name }} + namespace: {{ .Release.Namespace }} + labels: + {{ include "mattermost.labels" . | nindent 4 }} + app.kubernetes.io/component: "instance" +spec: + image: {{ .Values.image.name }} + imagePullPolicy: {{ .Values.image.imagePullPolicy }} + {{- with .Values.global.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 4 }} + {{- end }} + + size: {{ .Values.users }}users + version: {{ .Values.image.tag }} + {{- if .Values.enterprise.enabled }} + replicas: {{ .Values.replicaCount }} + {{- else }} + replicas: 1 + {{- end }} + + ingress: + enabled: {{ .Values.ingress.enabled }} + host: {{ .Values.ingress.host }} + annotations: + {{- toYaml .Values.ingress.annotations | nindent 6 }} + tlsSecret: {{ .Values.ingress.tlsSecret}} + + {{- if and .Values.enterprise.enabled .Values.enterprise.license }} + licenseSecret: "mattermost-license" + {{- end }} + + mattermostEnv: + - name: MM_SERVICESETTINGS_SITEURL + valueFrom: + secretKeyRef: + key: MM_SERVICESETTINGS_SITEURL + name: "mattermost-envs" + {{- if .Values.sso.enabled }} + - name: MM_GITLABSETTINGS_ENABLE + valueFrom: + secretKeyRef: + key: MM_GITLABSETTINGS_ENABLE + name: "mattermost-envs" + - name: MM_GITLABSETTINGS_ID + valueFrom: + secretKeyRef: + key: MM_GITLABSETTINGS_ID + name: "mattermost-envs" + - name: MM_GITLABSETTINGS_SECRET + valueFrom: + secretKeyRef: + key: MM_GITLABSETTINGS_SECRET + name: "mattermost-envs" + - name: MM_GITLABSETTINGS_AUTHENDPOINT + valueFrom: + secretKeyRef: + key: MM_GITLABSETTINGS_AUTHENDPOINT + name: "mattermost-envs" + - name: MM_GITLABSETTINGS_TOKENENDPOINT + valueFrom: + secretKeyRef: + key: MM_GITLABSETTINGS_TOKENENDPOINT + name: "mattermost-envs" + - name: MM_GITLABSETTINGS_USERAPIENDPOINT + valueFrom: + secretKeyRef: + key: MM_GITLABSETTINGS_USERAPIENDPOINT + name: "mattermost-envs" + {{- end }} + {{- if .Values.minio.install }} + - name: MM_FILESETTINGS_AMAZONS3SSL + valueFrom: + secretKeyRef: + key: MM_FILESETTINGS_AMAZONS3SSL + name: "mattermost-envs" + {{- end }} + {{- if .Values.elasticsearch.enabled }} + - name: MM_ELASTICSEARCHSETTINGS_CONNECTIONURL + valueFrom: + secretKeyRef: + key: MM_ELASTICSEARCHSETTINGS_CONNECTIONURL + name: "mattermost-envs" + - name: MM_ELASTICSEARCHSETTINGS_ENABLEAUTOCOMPLETE + valueFrom: + secretKeyRef: + key: MM_ELASTICSEARCHSETTINGS_ENABLEAUTOCOMPLETE + name: "mattermost-envs" + - name: MM_ELASTICSEARCHSETTINGS_ENABLEINDEXING + valueFrom: + secretKeyRef: + key: MM_ELASTICSEARCHSETTINGS_ENABLEINDEXING + name: "mattermost-envs" + - name: MM_ELASTICSEARCHSETTINGS_INDEXPREFIX + valueFrom: + secretKeyRef: + key: MM_ELASTICSEARCHSETTINGS_INDEXPREFIX + name: "mattermost-envs" + - name: MM_ELASTICSEARCHSETTINGS_BULKINDEXINGTIMEWINDOWSECONDS + valueFrom: + secretKeyRef: + key: MM_ELASTICSEARCHSETTINGS_BULKINDEXINGTIMEWINDOWSECONDS + name: "mattermost-envs" + - name: MM_ELASTICSEARCHSETTINGS_ENABLESEARCHING + valueFrom: + secretKeyRef: + key: MM_ELASTICSEARCHSETTINGS_ENABLESEARCHING + name: "mattermost-envs" + - name: MM_ELASTICSEARCHSETTINGS_SKIPTLSVERIFICATION + valueFrom: + secretKeyRef: + key: MM_ELASTICSEARCHSETTINGS_SKIPTLSVERIFICATION + name: "mattermost-envs" + - name: MM_ELASTICSEARCHSETTINGS_SNIFF + valueFrom: + secretKeyRef: + key: MM_ELASTICSEARCHSETTINGS_SNIFF + name: "mattermost-envs" + - name: MM_ELASTICSEARCHSETTINGS_USERNAME + valueFrom: + secretKeyRef: + key: MM_ELASTICSEARCHSETTINGS_USERNAME + name: "mattermost-envs" + - name: MM_ELASTICSEARCHSETTINGS_PASSWORD + valueFrom: + secretKeyRef: + key: MM_ELASTICSEARCHSETTINGS_PASSWORD + name: "mattermost-envs" + {{- end }} + {{- range $k, $v := .Values.mattermostEnvs }} + - name: {{ $k }} + valueFrom: + secretKeyRef: + key: {{ $k }} + name: "mattermost-envs" + {{- end }} + {{- range .Values.existingSecretEnvs }} + - {{ tpl (toYaml .) $ | nindent 4 }} + {{- end }} + + # Give MM enough time to keel over if needed + probes: + livenessProbe: + initialDelaySeconds: 120 + periodSeconds: 120 + + scheduling: + {{- with .Values.resources }} + resources: + {{- toYaml . | nindent 6 }} + {{- end }} + {{- if .Values.affinity }} + affinity: + {{ toYaml .Values.affinity | nindent 6 }} + {{- end }} + + {{- if .Values.nodeSelector }} + nodeSelector: + {{ toYaml .Values.nodeSelector | nindent 6 }} + {{- end }} + + {{- if .Values.tolerations}} + tolerations: + {{- toYaml .Values.tolerations | nindent 6 }} + {{- end }} + + {{- with .Values.volumes }} + volumes: + {{- toYaml . | nindent 4}} + {{- end }} + + {{- with .Values.volumeMounts }} + volumeMounts: + {{- toYaml . | nindent 4}} + {{- end }} + + {{- if or .Values.podLabels .Values.podAnnotations .Values.securityContext .Values.containerSecurityContext }} + podTemplate: + {{- with .Values.podLabels }} + extraLabels: + {{- toYaml . | nindent 6 }} + {{- end }} + {{- with .Values.podAnnotations }} + extraAnnotations: + {{- toYaml . | nindent 6 }} + {{- end }} + {{- with .Values.securityContext }} + securityContext: + {{- toYaml . | nindent 6 }} + {{- end }} + {{- with .Values.containerSecurityContext }} + containerSecurityContext: + {{- toYaml . | nindent 6 }} + {{- end }} + {{- end }} + + database: + external: + secret: {{ .Values.database.secret | default (printf "%s-dbcreds" (include "mattermost.fullname" .)) }} + disableReadinessCheck: {{ .Values.database.readinessCheck.disableDefault }} + + fileStore: + external: + url: {{ .Values.fileStore.url | default .Values.minio.service.nameOverride }} + bucket: {{ .Values.fileStore.bucket | default "mattermost" }} + secret: {{ .Values.fileStore.secret | default .Values.minio.secrets.name }} + + podExtensions: + initContainers: + - command: + {{- toYaml .Values.database.readinessCheck.command | nindent 10 }} + image: {{ .Values.database.readinessCheck.image }} + imagePullPolicy: {{ .Values.image.imagePullPolicy }} + securityContext: + capabilities: + drop: + - ALL + name: db-readiness-check + resources: + requests: + memory: 128Mi + cpu: 50m + limits: + memory: 128Mi + cpu: 50m + env: + {{- (tpl (toYaml .Values.database.readinessCheck.env) .) | nindent 10 }} + + {{- if .Values.resourcePatch }} + resourcePatch: + {{- toYaml .Values.resourcePatch | nindent 4 }} + {{- end }} + + updateJob: + disabled: {{ .Values.updateJob.disabled }} + {{- with .Values.updateJob.annotations }} + extraAnnotations: + {{- toYaml . | nindent 6 }} + {{- end }} + {{- with .Values.updateJob.labels }} + extraLabels: + {{- toYaml . | nindent 6 }} + {{- end }} diff --git a/chart/templates/role-binding.yaml b/chart/templates/role-binding.yaml new file mode 100644 index 000000000..a095e860e --- /dev/null +++ b/chart/templates/role-binding.yaml @@ -0,0 +1,21 @@ +{{- if .Values.monitoring.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBindingList +items: +- apiVersion: rbac.authorization.k8s.io/v1 + kind: RoleBinding + metadata: + name: prometheus-k8s + namespace: {{ .Release.Namespace }} + labels: + {{ include "mattermost.labels" . | nindent 6 }} + app.kubernetes.io/component: "monitoring" + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: prometheus-k8s + subjects: + - kind: ServiceAccount + name: prometheus-k8s + namespace: {{ .Values.monitoring.namespace }} +{{- end }} diff --git a/chart/templates/role.yaml b/chart/templates/role.yaml new file mode 100644 index 000000000..ea79ecc74 --- /dev/null +++ b/chart/templates/role.yaml @@ -0,0 +1,24 @@ +{{- if .Values.monitoring.enabled }} +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleList +items: +- apiVersion: rbac.authorization.k8s.io/v1 + kind: Role + metadata: + name: prometheus-k8s + namespace: {{ .Release.Namespace }} + labels: + {{ include "mattermost.labels" . | nindent 6 }} + app.kubernetes.io/component: "monitoring" + rules: + - apiGroups: + - "" + resources: + - services + - endpoints + - pods + verbs: + - get + - list + - watch +{{- end }} diff --git a/chart/templates/service-monitor.yaml b/chart/templates/service-monitor.yaml new file mode 100644 index 000000000..3db3a6f44 --- /dev/null +++ b/chart/templates/service-monitor.yaml @@ -0,0 +1,24 @@ +{{- if and .Values.monitoring.enabled .Values.enterprise.enabled }} +apiVersion: monitoring.coreos.com/v1 +kind: ServiceMonitor +metadata: + labels: + prometheus: k8s + release: monitoring + {{ include "mattermost.labels" . | nindent 4 }} + app.kubernetes.io/component: "monitoring" + name: mattermost-enterprise-metrics + namespace: {{ .Release.Namespace }} +spec: + selector: + matchLabels: + app: {{ .Release.Name }} + namespaceSelector: + matchNames: + - {{ .Release.Namespace }} + endpoints: + - interval: 30s + port: metrics + scheme: http + jobLabel: mattermost +{{- end }} diff --git a/chart/templates/tests/test-ui.yaml b/chart/templates/tests/test-ui.yaml new file mode 100644 index 000000000..163bd16f9 --- /dev/null +++ b/chart/templates/tests/test-ui.yaml @@ -0,0 +1,13 @@ +{{- include "gluon.tests.cypress-configmap.overrides" (list . "mattermost-test.cypress-configmap") }} +{{- define "mattermost-test.cypress-configmap" }} +metadata: + labels: + {{ include "mattermost.labels" . | nindent 4 }} +{{- end }} +--- +{{- include "gluon.tests.cypress-runner.overrides" (list . "mattermost-test.cypress-runner") -}} +{{- define "mattermost-test.cypress-runner" -}} +metadata: + labels: + {{ include "mattermost.labels" . | nindent 4 }} +{{- end }} diff --git a/chart/templates/virtualservice.yaml b/chart/templates/virtualservice.yaml new file mode 100644 index 000000000..f47b867ab --- /dev/null +++ b/chart/templates/virtualservice.yaml @@ -0,0 +1,32 @@ +{{- if and .Values.istio.enabled .Values.istio.chat.enabled -}} +apiVersion: networking.istio.io/v1beta1 +kind: VirtualService +metadata: + name: {{ template "mattermost.fullname" . }} + namespace: {{ .Release.Namespace }} + labels: + {{ include "mattermost.labels" . | nindent 4 }} + app.kubernetes.io/component: "chat" + {{- if .Values.istio.chat.labels }} + {{ toYaml .Values.istio.chat.labels | indent 4 }} + {{- end }} + {{- if .Values.istio.chat.annotations }} + annotations: + {{ toYaml .Values.istio.chat.annotations | indent 4 }} + {{- end }} +spec: + gateways: + {{- range .Values.istio.chat.gateways }} + - {{ . }} + {{- end }} + hosts: + {{- range .Values.istio.chat.hosts }} + - {{ tpl . $ }} + {{- end }} + http: + - route: + - destination: + host: {{ .Release.Name }}.{{ .Release.Namespace }}.svc.cluster.local + port: + number: 8065 # hardcode this because the operator does +{{- end }} diff --git a/chart/tests/cypress/cypress.json b/chart/tests/cypress/cypress.json new file mode 100644 index 000000000..a8a81e405 --- /dev/null +++ b/chart/tests/cypress/cypress.json @@ -0,0 +1,6 @@ +{ + "pluginsFile": false, + "supportFile": false, + "fixturesFolder": false, + "videoCompression": 20 +} diff --git a/chart/tests/cypress/mattermost-health.spec.js b/chart/tests/cypress/mattermost-health.spec.js new file mode 100644 index 000000000..210d23458 --- /dev/null +++ b/chart/tests/cypress/mattermost-health.spec.js @@ -0,0 +1,126 @@ +Cypress.on('uncaught:exception', (err, runnable) => { + // returning false here prevents Cypress from failing the test + return false +}) + +describe('Mattermost Healthcheck', function() { + + // Conditional check for inconsistent "welcome to mattermost" banner behavior + function bannercheck() { + cy.wait(3000) + cy.get('body').then($body => { + if ($body.find('.link > span').length > 0) { + //evaluates as true if banner exists at all + cy.get('.link > span').then($header => { + if ($header.is(':visible')){ + // evaluates to true if the banner is visible + console.log("Banner is Present") + $header.click() + } else { + console.log("Banner is not present") + } + }); + } + }) + } + + // This provides us with a login account on fresh installs + before(() => { + cy.visit(Cypress.env('url')) + cy.wait(8000) + // cy.wait(15000) + cy.get('div[id="root"]').should('be.visible') + + cy.url().then(($url) => { + if ($url.includes('signup')) { + // note: Mattermost behaves differently on first login depending on the URL + // https://chat.bigbang.dev versus http://mattermost.mattermost.svc.cluster.local:8065 + // explicitly visit the signup_email page + // so that the test works the same locally and in the pipeline + cy.visit(Cypress.env('url')+'/signup_email') + cy.wait(5000) + // cy.wait(10000) + cy.get('input[id="input_email"]').type(Cypress.env('mm_email')) + // #input + cy.get('input[id="input_name"]').type(Cypress.env('mm_user')) + cy.get('input[id="input_password-input"]').type(Cypress.env('mm_password')) + cy.get('button[id="saveSetting"]').click() + } + }) + }) + + beforeEach(() => { + cy.visit(Cypress.env('url')) + cy.wait(5000) + // cy.wait(10000) + cy.get('div[id="root"]').should('be.visible') + + cy.url().then(($url) => { + if ($url.includes('landing')) { + cy.get('a[class="btn btn-default btn-lg get-app__continue"]').click() + } + }) + cy.wait(5000) + + // Check if login is needed + cy.url().then(($url) => { + if ($url.includes('login')) { + cy.get('input[id="input_loginId"]').type(Cypress.env('mm_user')) + cy.get('input[id="input_password-input"]').type(Cypress.env('mm_password')) + cy.get('button[id="saveSetting"]').click() + } + }) + cy.wait(500) + }) + + it('should create / persist teams', function() { + cy.wait(5000) + + cy.url().then(($url) => { + cy.wait(1000) + if ($url.includes('select_team')) { + // create a team + cy.get('a[id="createNewTeamLink"]').click() + cy.wait(3000) + // Input Big Bang + cy.get('input[id="teamNameInput"]').type('Big Bang') + // Click Next + cy.get('button[id="teamNameNextButton"]').click() + //cy.get('input[id="teamURLInput"]').should('include', 'big-bang') + // Click finish + cy.get('button[id="teamURLFinishButton"]').click() + // Give some time for dialog load + } + bannercheck() + }) + + // click on Town Square + cy.wait(1000) + cy.visit(Cypress.env('url')+'/big-bang/channels/town-square') + cy.wait(10000) + cy.title().should('include', 'Town Square - Big Bang Mattermost') + }) + + it('should allow chatting', function() { + bannercheck() + let randomChat = "Hello " + Math.random().toString(36).substring(8); + cy.wait(5000) + cy.get('body').then($body => { + if ($body.find('.close > [aria-hidden="true"]').length > 0) { + cy.get('.close > [aria-hidden="true"]').click() + } + }) + // cy.wait(10000) + cy.get('textarea[id="post_textbox"]').type(randomChat).type('{enter}') + cy.get('p').contains(randomChat).should('be.visible') + }) + + it('should have file storage connection', function() { + bannercheck() + cy.visit(Cypress.env('url')+'/admin_console/environment/file_storage') + cy.wait(10000) + + cy.get('span:contains("Test Connection")', {timeout: 10000}).click() + cy.get('div[class="alert alert-success"]').should('be.visible') + }) +}) diff --git a/chart/values.yaml b/chart/values.yaml new file mode 100644 index 000000000..0fff3acb2 --- /dev/null +++ b/chart/values.yaml @@ -0,0 +1,279 @@ +hostname: bigbang.dev + +istio: + # -- Toggle istio integration + enabled: false + chat: + # Toggle vs creation + enabled: true + annotations: {} + labels: {} + gateways: + - istio-system/main + hosts: + - chat.{{ .Values.hostname }} + injection: disabled + +# -- Specification to configure an Ingress with Mattermost +ingress: + enabled: false + host: "" + annotations: {} + tlsSecret: "" + +# NOTE: Requires enterprise.enabled to have any effect +monitoring: + enabled: false + namespace: monitoring + +networkPolicies: + enabled: false + ingressLabels: + app: istio-ingressgateway + istio: ingressgateway + controlPlaneCidr: 0.0.0.0/0 + +sso: + enabled: false + client_id: platform1_a8604cc9-f5e9-4656-802d-d05624370245_bb8-mattermost + # Change to your client secret + client_secret: nothing + # Change to your respective IDP endpoints + auth_endpoint: https://login.dso.mil/auth/realms/baby-yoda/protocol/openid-connect/auth + token_endpoint: https://login.dso.mil/auth/realms/baby-yoda/protocol/openid-connect/token + user_api_endpoint: https://login.dso.mil/auth/realms/baby-yoda/protocol/openid-connect/userinfo + +# Repo and image tag +image: + name: registry1.dso.mil/ironbank/opensource/mattermost/mattermost + tag: 7.5.0 + imagePullPolicy: IfNotPresent + +global: + imagePullSecrets: + - name: private-registry + +# Mattermost instance desired replicas +replicaCount: 1 + +users: null # Allowable: 100, 1000, 5000, 10000, 25000 + +enterprise: + enabled: false + license: "" + # Example: + # license: | + # LICENSE HERE + +nameOverride: "" + +updateJob: + # -- Must be disabled when Istio injected + disabled: true + labels: {} + annotations: {} + +resources: + limits: + cpu: 2 + memory: 4Gi + requests: + cpu: 2 + memory: 4Gi + +affinity: {} + # podAntiAffinity: + # requiredDuringSchedulingIgnoredDuringExecution: + # - topologyKey: "kubernetes.io/hostname" + # labelSelector: + # matchLabels: + # dont-schedule-with: mattermost + # nodeAffinity: + # requiredDuringSchedulingIgnoredDuringExecution: + # nodeSelectorTerms: + # - matchExpressions: + # - key: node-type + # operator: In + # values: + # - "mattermost" + +nodeSelector: {} + # node-type: mattermost + +tolerations: {} +# - key: "key1" +# operator: "Equal" +# value: "value1" +# effect: "NoSchedule" + +# Any ENVs provided here get put into a `mattermost-envs` secret and pulled into the env +mattermostEnvs: {} + # MM_ENV_NAME: "{{ .Values.users }}" + # ANOTHER_ENV_NAME: "anothervalue" + +# Use this to point to pull in ENV values from existing secrets +existingSecretEnvs: {} + # - name: MM_SQLSETTINGS_DATASOURCEREPLICAS + # valueFrom: + # secretKeyRef: + # key: READER_DB_CONNECTION_STRING + # name: '{{ .Values.database.secret | default (printf "%s-dbcreds" (include "mattermost.fullname" .)) }}' + # - name: MM_ANOTHER_VAR + # valueFrom: + # secretKeyRef: + # key: DB_CONNECTION_CHECK_URL + # name: "mysecretname" + +volumes: {} + # - name: ca-cert + # secret: + # secretName: ca-secret + # defaultMode: 0644 + +volumeMounts: {} + # - name: ca-cert + # mountPath: /etc/ssl/certs + # readOnly: true + +# -- Pod labels for Mattermost server pods +podLabels: {} + +# -- Pod annotations for Mattermost server pods +podAnnotations: {} + +# -- securityContext for Mattermost server pods +securityContext: {} + +# -- containerSecurityContext for Mattermost server containers +containerSecurityContext: + capabilities: + drop: + - ALL + +minio: + install: false + bucketCreationImage: "registry1.dso.mil/ironbank/opensource/minio/mc:RELEASE.2022-08-23T05-45-20Z" + # Override the minio service name for easier connection setup + service: + nameOverride: "minio.mattermost.svc.cluster.local" + secrets: + name: "mattermost-objstore-creds" + accessKey: "minio" + secretKey: "minio123" # default key, change this! + tenant: + metrics: + enabled: false + port: 9000 + containerSecurityContext: + capabilities: + drop: + - ALL + +postgresql: + install: false + + image: + registry: "registry1.dso.mil/ironbank" + repository: "opensource/postgres/postgresql11" + tag: "11.18-1" + + pullSecrets: + - private-registry + + postgresqlUsername: "mattermost" + postgresqlPassword: "bigbang" # default password, change this! + postgresqlDatabase: "mattermost" + + fullnameOverride: "mattermost-postgresql" # Overrides the name used for resource creation + + securityContext: + fsGroup: 26 + containerSecurityContext: + runAsUser: 26 + capabilities: + drop: + - ALL + #permissions for initContainers + volumePermissions: + securityContext: + capabilities: + drop: + - ALL + + # Set the configs to allow listening and connecting from other pods + postgresqlConfiguration: {"listen_addresses": "*"} + pgHbaConfiguration: |- + local all all md5 + host all all all md5 + +database: + # Name of an existing secret to pull credentials from, leave empty for chart created database + # Must at minimum contain DB_CONNECTION_STRING + secret: "" + # Init container for DB readiness check + readinessCheck: + # Disable the default readiness check which uses a non-IB image + disableDefault: true + # Defaults to Ironbank postgres, can be changed for different DB types (MySQL) + image: registry1.dso.mil/ironbank/opensource/postgres/postgresql12:12.13 + # Defaults to a readiness check for postgres, can be changed for different DB types + command: + - /bin/sh + - -c + - until pg_isready --dbname="$DB_CONNECTION_CHECK_URL"; do echo waiting for database; sleep 5; done; + # Pass in the credentials needed for the DB check + env: + - name: DB_CONNECTION_CHECK_URL + valueFrom: + secretKeyRef: + key: DB_CONNECTION_CHECK_URL + name: '{{ .Values.database.secret | default (printf "%s-dbcreds" (include "mattermost.fullname" .)) }}' + +fileStore: + # Name of an existing secret to pull credentials from, leave empty for chart created minio + secret: "" + # URL for existing file store, leave empty for chart created minio + url: "" + # Bucket for existing file store, leave empty for chart created minio + bucket: "" + +elasticsearch: + # NOTE: Elasticsearch settings can be defined, but will not work unless enterprise mode is enabled. + enabled: false + # The address of the Elasticsearch server, default is internal elasticsearch + connectionurl: "https://logging-ek-es-http.logging.svc.cluster.local:9200" + # if using BB elasticsearch leave user/pass blank + username: "" + password: "" + # When true, indexing of new posts occurs automatically. Search queries will use database search until "Enable Elasticsearch for search queries" is enabled. + enableindexing: true + # Elasticsearch index prefix + indexprefix: "mm-" + # When true, Mattermost will not require the Elasticsearch certificate to be signed by a trusted Certificate Authority + skiptlsverification: true + # Frequency to index to elasticsearch + bulkindexingtimewindowseconds: 3600 + # When true, sniffing finds and connects to all data nodes in your cluster automatically. + sniff: false + # When true, Elasticsearch will be used for all search queries using the latest index. Search results may be incomplete until a bulk index of the existing post database is finished. When false, database search is used. + enablesearching: true + # When true, Elasticsearch will be used for all autocompletion queries on users and channels using the latest index. Autocompletion results may be incomplete until a bulk index of the existing users and channels database is finished. When false, database autocomplete is used. + enableautocomplete: true + +openshift: false + +# Custom patch on the Mattermost resources before applying +resourcePatch: {} +# Example: Patch a label onto the deployment: + # deployment: + # patch: '[{"op":"add","path":"/spec/template/spec/metadata/labels","value":{"istio-version": "1.2.3"}}]' + +bbtests: + enabled: false + cypress: + artifacts: true + envs: + cypress_url: http://mattermost.mattermost.svc.cluster.local:8065 + cypress_mm_email: "test@bigbang.dev" + cypress_mm_user: "bigbang" + cypress_mm_password: "Bigbang#123" diff --git a/docs/AFFINITY.md b/docs/AFFINITY.md new file mode 100644 index 000000000..06e9c4299 --- /dev/null +++ b/docs/AFFINITY.md @@ -0,0 +1,44 @@ +# Node Affinity & Anti-Affinity with Mattermost + +Affinity is exposed through values options for this package. If you want to schedule your pods to deploy on specific nodes you can do that through the `nodeSelector` value and as needed the `affinity` value. Additional info is provided below as well to help in configuring this. + +It is good to have a basic knowledge of node affinity and available options to you before customizing in this way - the upstream kubernetes documentation [has a good walkthrough of this](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity). + +## Values for NodeSelector + +The `nodeSelector` value at the top level can be set to do basic node selection for deployments. See the below example for an example to schedule pods to only nodes with the label `node-type` equal to `mattermost`: + +```yaml +nodeSelector: + node-type: mattermost +``` + +## Values for Affinity + +The `affinity` value at the top level should be used to specify affinity. The format to include follows what you'd specify at a pod/deployment level. See the example below for scheduling the operator pods only to nodes with the label `node-type` equal to `mattermost`: + +```yaml +affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: node-type + operator: In + values: + - mattermost +``` + +## Values for Anti-Affinity + +The `affinity` value at the top level can be set in the same way to schedule pods based on anti-affinity. See the below example to schedule pods to not be present on the nodes that already have pods with the `dont-schedule-with: mattermost` label: + +```yaml +affinity: + podAntiAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + - topologyKey: "kubernetes.io/hostname" + labelSelector: + matchLabels: + dont-schedule-with: mattermost +``` diff --git a/docs/DEVELOPMENT_MAINTENANCE.md b/docs/DEVELOPMENT_MAINTENANCE.md new file mode 100644 index 000000000..26c3e1c1e --- /dev/null +++ b/docs/DEVELOPMENT_MAINTENANCE.md @@ -0,0 +1,45 @@ +# Upgrading to a new version + +Mattermost is a Big Bang built chart. As a result there is no `Kptfile` to handle any automatic updates from upstream. The below details the steps required to update to a new version of the Mattermost package. + +1. Review the [upstream changelog](https://docs.mattermost.com/install/self-managed-changelog.html) for the update you are going to, as well as any versions skipped over between the last BB release and this one. Note any breaking changes and new features. + +2. Modify the `image.tag` value in `chart/values.yaml` to point to the newest version of Mattermost. + +3. You should check the latest version of `minio-instance` and run `kpt pkg update chart/deps/minio@4.5.4-bb.1 --strategy alpha-git-patch` then `helm dependency update chart` if applicable. + +4. Based on the upstream changelog review from earlier, make any changes required to resolve breaking changes. + +5. Modify the `version` in `Chart.yaml` - this is a BigBang built and owned chart so we sync the chart version with the appVersion (ex: appVersion `6.6.0` -> chart version `6.6.0-bb.0`). Also modify the `appVersion` and the `bigbang.dev/applicationVersions` to the new upstream version of Mattermost (same version you put in for the image tag value). + +6. Update `CHANGELOG.md` adding an entry for the new version and noting all changes (at minimum should include `Updated Mattermost to x.x.x`). + +7. Generate the `README.md` updates by following the [guide in gluon](https://repo1.dso.mil/platform-one/big-bang/apps/library-charts/gluon/-/blob/master/docs/bb-package-readme.md). + +8. Validate that `tests/dependencies.yaml` points to the latest tag for `mattermost-operator`. If it doesn't, update it. + +9. Open an MR in "Draft" status and validate that CI passes. This will perform a number of smoke tests against the package, but it is good to manually deploy to test some things that CI doesn't. Follow the steps below for manual testing. + +10. Once all manual testing is complete take your MR out of "Draft" status and add the review label. + +# Testing for updates + +NOTE: For these testing steps it is good to do them on both a clean install and an upgrade. For clean install, point mattermost to your branch. For an upgrade do an install with mattermost pointing to the latest tag, then perform a helm upgrade with mattermost pointing to your branch. + +You will want to install with: +- Mattermost, Mattermost Operator, and Minio Operator enabled +- Istio enabled +- [Dev SSO values](https://repo1.dso.mil/platform-one/big-bang/bigbang/-/blob/master/chart/dev-sso-values.yaml) for Mattermost +- [Enterprise enabled](https://repo1.dso.mil/platform-one/big-bang/bigbang/-/blob/7b14b4739b26b900cf7e6f1c075edc33c271eca6/chart/values.yaml#L962) - if you do not pass a license in, navigate to the System Console after install to start a trial +- Elasticsearch enabled + [integration enabled](https://repo1.dso.mil/platform-one/big-bang/bigbang/-/blob/7b14b4739b26b900cf7e6f1c075edc33c271eca6/chart/values.yaml#L1038) +- Monitoring enabled + +Testing Steps: +- Log in with SSO via your `login.dso.mil` account. +- Follow the initial setup to create a team and send an initial chat. +- Under account settings, upload a profile picture. Validate the upload is successful and your profile picture is visible. +- Navigate to prometheus and validate that the Mattermost target shows as up (make sure you are on enterprise and have started a trial). +- Under system console -> elastic -> index now and validate success (make sure you are on enterprise and have started a trial). +- Check Grafana for data in the `Mattermost Performance Monitoring v2` dashboard (Ensure you change the server on the dashboard to point to the mattermost pod ip) + +When in doubt with any testing or upgrade steps ask one of the CODEOWNERS for assistance. diff --git a/docs/keycloak.md b/docs/keycloak.md new file mode 100644 index 000000000..12d56cdcc --- /dev/null +++ b/docs/keycloak.md @@ -0,0 +1,104 @@ +# Keycloak SSO Mattermost Config + +## Keycloak Client Setup + +The Keycloak client can be set up by following [this tutorial](https://medium.com/@mrtcve/mattermost-teams-edition-replacing-gitlab-sso-with-keycloak-dabf13ebb99e). A summary is provided below, but if there are any issues refer to the source linked. + +Create client: +- client id - you pick, "mattermost" +- enabled - on +- client protocol - openid-connect +- access type - confidential +- standard flow enabled - on +- valid redirect URIs - "{mattermosturl}/signup/gitlab/complete" + +Under the mappers tab, create a new mapper: +- name - mattermostId +- mapper type - user attribute +- user attribute - mattermostId +- token claim name - id +- claim JSON type - long +- add to userinfo - on + +Create username mapper: +- name - username +- mapper type - user property +- property - username +- token claim name - username +- claim JSON type - string +- add to userinfo - on +- all other sliders off + +Create email mapper: +- name - email +- mapper type - user property +- property - email +- token claim name - email +- claim JSON type - string +- add to userinfo - on +- all other sliders off + +Add mattermostid to existing user: +- Login to keycloak Admin Console with the master realm user +- Go to your realm +- Go to the users section and edit the user +- Go to the Attributes tab +- In the bottom row type `mattermostId` in the key and a random number in the `value` field. +- Click Add. + +This mattermostid needs to be unique per user, so it's a bad idea to generate these by hand. This process is just a way to edit test/existing users. + +## Helm Values + +First get the values you need for your Keycloak: +- client_id: This is the client id you created and picked earlier +- client_secret: This is under the credential tab for your client, you can click regenerate and then copy it +- endpoints: Go to your realm settings and then open the "OpenID Endpoint Configuration". There should be values for authorization_endpoint, token_endpoint, and userinfo_endpoint which correspond to the auth, token, and user_api endpoints in the values. + +Modify your values.yaml according to these example values to enable Gitlab Auth provider for SSO. If you have a licensed version of Mattermost that supports OIDC the Mattermost OIDC client backend will obtain the endpoints automatically from the [well-known OIDC endpoint](https://login.dso.mil/auth/realms/baby-yoda/.well-known/openid-configuration). +``` +# SSO Additions +sso: + enabled: true + client_id: platform1_a8604cc9-f5e9-4656-802d-d05624370245_bb8-mattermost + client_secret: nothing # Change to your Keycloak client secret + auth_endpoint: https://login.dso.mil/auth/realms/baby-yoda/protocol/openid-connect/auth + token_endpoint: https://login.dso.mil/auth/realms/baby-yoda/protocol/openid-connect/token + user_api_endpoint: https://login.dso.mil/auth/realms/baby-yoda/protocol/openid-connect/userinfo + +``` + +Example install: +``` +helm upgrade -i mattermost chart -n mattermost --create-namespace -f my-values.yml +``` + +## Role Based Authentication + +Role based authentication can be configured as long as you are on an enterprise version. + +Follow the steps in [this tutorial](https://docs.mattermost.com/deployment/advanced-permissions.html) to customize the permissions given to users. In general permissions can be edited under the "System Console -> User Management -> Permissions". Users should be created by default under the "Member" group, except for the first user to sign up or login. + +## OIDC Custom CA + +Mattermost can be configured to point to specific files to trust with an OIDC auth connection, here is an example when using Big Bang to deploy mattermost, assuming you are populating a secret named "ca-cert" in the same namespace, with a key of cert.pem and value of a single PEM encoded certificate (an easy way to make this secret is included below as well): + +```yaml +addons: + mattermost: + values: + volumes: + - name: ca-cert + secret: + secretName: ca-secret + defaultMode: 0644 + volumeMounts: + - name: ca-cert + mountPath: /etc/ssl/certs + readOnly: true +``` + +For secret creation with this example and a pem file at `/path/to/cert.pem`: +```bash +kubectl create secret generic ca-secret --from-file=cert.pem=/path/to/cert.pem -n mattermost +``` diff --git a/docs/overview.md b/docs/overview.md new file mode 100644 index 000000000..228cef8fe --- /dev/null +++ b/docs/overview.md @@ -0,0 +1,16 @@ +# Mattermost + +## Overview + +This package contains an installation of Mattermost using a helm chart built by Big Bang that leverages the operator. + +## Mattermost + +[Mattermost](https://mattermost.com/) is an open-source, self-hostable online chat service with file sharing, search, and integrations. +This repo provides an implementation of Mattermost for Big Bang. Installation requires that the [Mattermost Operator](https://repo1.dso.mil/platform-one/big-bang/apps/collaboration-tools/mattermost-operator) be installed in your cluster as a prerequisite. + +## How it works + +Mattermost is a single pane for collaboration, installed and configured via a `mattermost` CustomResource and reconciled by the operator. You can visit your installation via browser or connect through one of their Desktop apps available for many operating systems. + +Please review the BigBang [Architecture Document](https://repo1.dso.mil/platform-one/big-bang/bigbang/-/blob/master/charter/packages/mattermost/Architecture.md) for more information about it's role within BigBang. diff --git a/renovate.json b/renovate.json index a5cdf193a..d7f57a1bc 100644 --- a/renovate.json +++ b/renovate.json @@ -1,33 +1,54 @@ { - "baseBranches": ["main"], - "configWarningReuseIssue": false, - "dependencyDashboard": true, - "dependencyDashboardHeader": "- [ ] Review Big Bang release notes for breaking changes or manual steps (i.e. Flux updates)", - "dependencyDashboardTitle": "Renovate: Upgrade Big Bang Version for BB-CI (Production)", - "draftPR": true, - "enabledManagers": ["regex"], - "labels": ["kind::ci","renovate"], - "packageRules": [ - { - "matchDatasources": ["docker", "git-tags"], - "groupName": "ironbank", - "postUpgradeTasks": { - "commands": [ - "upgrade-gitlabrunner '{{{newValue}}}'" - ], - "fileFilters": ["**/**-runner-hr.yaml","bb_prod/bigbang/prod/values.yaml"] - } - } - ], - "regexManagers": [ - { - "fileMatch": ["^bb_prod/bigbang/(base|prod)/kustomization\\.yaml$"], - "matchStrings": ["ref=(?<currentValue>.+)", - "tag: \"(?<currentValue>.+)\""], - "depNameTemplate": "https://repo1.dso.mil/platform-one/big-bang/bigbang.git", - "datasourceTemplate": "git-tags" - - } - ], - "separateMajorMinor": false + "baseBranches": ["main"], + "configWarningReuseIssue": false, + "dependencyDashboard": "true", + "dependencyDashboardHeader": "- [ ] Follow steps in [package update docs](./docs/PACKAGE_UPDATES.md) to upgrade the package.", + "dependencyDashboardTitle": "Renovate: Upgrade Mattermost Package Dependencies", + "draftPR": true, + "enabledManagers": ["regex"], + "ignorePaths": ["chart/charts/**"], + "labels": ["mattermost","Package Sustainment","kind::maintenance"], + "packageRules": [ + { + "matchDatasources": ["docker"], + "groupName": "Ironbank", + "registryUrls": ["https://registry1.dso.mil"], + "postUpgradeTasks": { + "commands": [ + "match-chart-yaml-appversion", + "regenerate-helm-docs", + "bump-changelog '- {{{replace 'registry1.dso.mil/' '' depName}}} updated from {{{currentVersion}}} to {{{newVersion}}}'" + ], + "fileFilters": ["chart/Chart.yaml", "README.md", "CHANGELOG.md"] + } + } + ], + "separateMajorMinor": false, + "regexManagers": [ + { + "fileMatch": ["^chart/values\\.yaml$"], + "matchStrings": [ + "image:\\s+name:\\s+[^/]+/(?<depName>.+)\\s+tag:\\s+(?<currentValue>.+)" + ], + "depNameTemplate": "registry1.dso.mil/ironbank/opensource/mattermost/mattermost", + "datasourceTemplate": "docker" + }, + { + "fileMatch": ["^chart/Chart\\.yaml$"], + "matchStrings": [ + "- Mattermost:\\s+(?<currentValue>.+)", + "appVersion:\\s+(?<currentValue>.+)" + ], + "depNameTemplate": "registry1.dso.mil/ironbank/opensource/mattermost/mattermost", + "datasourceTemplate": "docker" + }, + { + "fileMatch": ["^chart/Chart\\.yaml$"], + "matchStrings": [ + "image:[^\\S\\r\\n]+(?<depName>.+):(?<currentValue>.+)" + ], + "datasourceTemplate": "docker" + } + ] } + diff --git a/storageclass/ebs-gp3-storage-class.yaml b/storageclass/ebs-gp3-storage-class.yaml deleted file mode 100644 index e15b1d5d2..000000000 --- a/storageclass/ebs-gp3-storage-class.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: storage.k8s.io/v1 -kind: StorageClass -metadata: - name: ebs - annotations: - storageclass.kubernetes.io/is-default-class: "true" -provisioner: kubernetes.io/aws-ebs -parameters: - type: gp3 -reclaimPolicy: Delete -allowVolumeExpansion: true -mountOptions: - - debug -volumeBindingMode: WaitForFirstConsumer \ No newline at end of file diff --git a/tests/dependencies.yaml b/tests/dependencies.yaml new file mode 100644 index 000000000..de33714de --- /dev/null +++ b/tests/dependencies.yaml @@ -0,0 +1,7 @@ +mattermostoperator: + git: "https://repo1.dso.mil/platform-one/big-bang/apps/collaboration-tools/mattermost-operator.git" + namespace: "mattermost-operator" + +miniooperator: + git: "https://repo1.dso.mil/platform-one/big-bang/apps/application-utilities/minio-operator.git" + namespace: "minio-operator" diff --git a/tests/policy/mattermost-minio-ib.rego b/tests/policy/mattermost-minio-ib.rego new file mode 100644 index 000000000..63d7c4435 --- /dev/null +++ b/tests/policy/mattermost-minio-ib.rego @@ -0,0 +1,35 @@ +package main + +name = input.metadata.name + +registries_allow := ["registry1.dsop.io/ironbank", "registry1.dso.mil/ironbank", "registry.dsop.io", "registry.dso.mil"] +registries_warn := ["registry.dsop.io", "registry.dso.mil"] + +in_allowed_registries(image) { + startswith(image, registries_allow[i]) +} + +in_warning_registries(image) { + startswith(image, registries_warn[i]) +} + +# Deny non-approved registries +deny[msg] { + image := input.spec.image + not in_allowed_registries(image) + msg := sprintf("Image '%v' in '%v' is not from approved registries", [image, name]) +} + +# Warn about non-Ironbank Images +warn[msg] { + image := input.spec.image + in_warning_registries(image) + msg := sprintf("Image '%v' in '%v' is not from Ironbank", [image, name]) +} + +# Warn about DSOP Images +warn[msg] { + image := input.spec.image + contains(image, ".dsop.io") + msg := sprintf("Update 'dsop.io' to 'dso.mil' for image '%v' in '%v'", [image, name]) +} diff --git a/tests/policy/postgres-ib.rego b/tests/policy/postgres-ib.rego new file mode 100644 index 000000000..df2e9a1b5 --- /dev/null +++ b/tests/policy/postgres-ib.rego @@ -0,0 +1,35 @@ +package main + +name = input.metadata.name + +registries_allow_pg := ["registry1.dsop.io/ironbank", "registry1.dso.mil/ironbank", "registry.dsop.io", "registry.dso.mil"] +registries_warn_pg := ["registry.dsop.io", "registry.dso.mil"] + +in_allowed_registries_pg(image) { + startswith(image, registries_allow[i]) +} + +in_warning_registries_pg(image) { + startswith(image, registries_warn[i]) +} + +# Deny non-approved registries +deny[msg] { + image := input.spec.dockerImage + not in_allowed_registries_pg(image) + msg := sprintf("Image '%v' in '%v' is not from approved registries", [image, name]) +} + +# Warn about non-Ironbank Images +warn[msg] { + image := input.spec.dockerImage + in_warning_registries_pg(image) + msg := sprintf("Image '%v' in '%v' is not from Ironbank", [image, name]) +} + +# Warn about DSOP Images +warn[msg] { + image := input.spec.dockerImage + contains(image, ".dsop.io") + msg := sprintf("Update 'dsop.io' to 'dso.mil' for image '%v' in '%v'", [image, name]) +} diff --git a/tests/test-values.yml b/tests/test-values.yml new file mode 100644 index 000000000..d2eb3a2d5 --- /dev/null +++ b/tests/test-values.yml @@ -0,0 +1,22 @@ +minio: + install: true + tenant: + pools: + - servers: 1 + volumesPerServer: 4 + size: 256Mi + securityContext: + runAsUser: 1001 + runAsGroup: 1001 + fsGroup: 1001 + +postgresql: + install: true + +networkPolicies: + enabled: true + +bbtests: + enabled: true + cypress: + image: registry1.dso.mil/bigbang-ci/cypress-kubectl:8.3.1 diff --git a/tests/wait.sh b/tests/wait.sh new file mode 100755 index 000000000..bafe4a44b --- /dev/null +++ b/tests/wait.sh @@ -0,0 +1,27 @@ +#!/bin/bash +wait_project() { + # interval and timeout are in seconds + interval=5 + timeout=600 + # + resourcename=mattermost + counter=0 + # need to remove the default "set -e" to allow commands to return nonzero exit codes without the script failing + set +e + while true; do + if kubectl get $resourcename --namespace=$resourcename -o jsonpath='{.items[0].status.state}' | \ + grep "^stable" 1>/dev/null + then + echo "$resourcename custom resource creation finished" + break + fi + sleep $interval + let counter++ + if [[ $(($counter * $interval)) -ge $timeout ]]; then + echo "$resourcename timeout waiting $timeout seconds for resource creation, running describe..." 1>&2 + kubectl describe $resourcename --namespace=$resourcename 1>&2 + exit 1 + fi + done + set -e +} -- GitLab From 9eceaaf136c29b411d757775aafda9b97ffa3d8c Mon Sep 17 00:00:00 2001 From: Micah Nagel <micah.nagel@defenseunicorns.com> Date: Tue, 7 Feb 2023 14:03:00 -0700 Subject: [PATCH 2/5] update --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index dd868621c..640be579e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ 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). --- -## [7.5.1-bb.4] - 2022-01-17 +## [7.5.0-bb.4] - 2022-01-17 ### Changed - Update gluon to new registry1 location + latest version (0.3.2) -- GitLab From 1b846dba0a9303a3c15dc103ac0318a7b7dac7f1 Mon Sep 17 00:00:00 2001 From: Micah Nagel <micah.nagel@defenseunicorns.com> Date: Tue, 7 Feb 2023 14:13:52 -0700 Subject: [PATCH 3/5] update --- CHANGELOG.md | 272 +- CODEOWNERS | 2 +- CONTRIBUTING.md | 2 +- README.md | 133 +- chart/Chart.lock | 16 +- chart/Chart.yaml | 51 +- chart/charts/gluon-0.3.2.tgz | Bin 3152 -> 0 bytes .../mattermost-operator-crds-1.19.0.tgz | Bin 0 -> 45324 bytes chart/charts/minio-instance-4.5.4-bb.2.tgz | Bin 20002 -> 0 bytes chart/charts/postgresql-10.3.5.tgz | Bin 40072 -> 0 bytes ...ermost-performance-monitoring-v2_rev2.json | 2272 ------ chart/deps/postgresql/.helmignore | 21 - chart/deps/postgresql/Chart.lock | 6 - chart/deps/postgresql/Chart.yaml | 29 - chart/deps/postgresql/Kptfile | 11 - chart/deps/postgresql/README.md | 770 -- .../deps/postgresql/ci/commonAnnotations.yaml | 3 - chart/deps/postgresql/ci/default-values.yaml | 1 - .../ci/shmvolume-disabled-values.yaml | 2 - chart/deps/postgresql/files/README.md | 1 - chart/deps/postgresql/files/conf.d/README.md | 4 - .../docker-entrypoint-initdb.d/README.md | 3 - chart/deps/postgresql/templates/NOTES.txt | 59 - chart/deps/postgresql/templates/_helpers.tpl | 919 --- .../deps/postgresql/templates/configmap.yaml | 27 - .../templates/extended-config-configmap.yaml | 22 - .../deps/postgresql/templates/extra-list.yaml | 4 - .../templates/initialization-configmap.yaml | 25 - .../templates/metrics-configmap.yaml | 14 - .../postgresql/templates/metrics-svc.yaml | 26 - .../postgresql/templates/networkpolicy.yaml | 39 - .../templates/podsecuritypolicy.yaml | 38 - .../postgresql/templates/prometheusrule.yaml | 23 - chart/deps/postgresql/templates/role.yaml | 20 - .../postgresql/templates/rolebinding.yaml | 20 - chart/deps/postgresql/templates/secrets.yaml | 22 - .../postgresql/templates/serviceaccount.yaml | 12 - .../postgresql/templates/servicemonitor.yaml | 33 - .../templates/statefulset-readreplicas.yaml | 411 - .../postgresql/templates/statefulset.yaml | 618 -- .../postgresql/templates/svc-headless.yaml | 28 - chart/deps/postgresql/templates/svc-read.yaml | 43 - chart/deps/postgresql/templates/svc.yaml | 41 - chart/deps/postgresql/values.schema.json | 103 - chart/deps/postgresql/values.yaml | 840 -- chart/mattermost-operator-crds/Chart.yaml | 15 + .../templates/clusterinstallations.yaml | 1677 ++++ .../templates/mattermostrestoredbs.yaml | 98 + .../templates/mattermosts.yaml | 4974 ++++++++++++ chart/templates/_helpers.tpl | 61 - .../bigbang/mattermost-dashboards.yaml | 12 - .../bigbang/mattermost-gossip-svc.yaml | 20 - .../networkpolicies/allow-egress-tempo.yaml | 21 - .../networkpolicies/allow-elastic-egress.yaml | 24 - .../allow-external-postgres.yaml | 20 - .../bigbang/networkpolicies/allow-in-ns.yaml | 18 - .../networkpolicies/allow-istio-egress.yaml | 21 - .../networkpolicies/allow-istio-ingress.yaml | 24 - .../allow-mattermost-egress.yaml | 20 - .../allow-minio-monitoring.yaml | 22 - .../allow-minio-operator-egress.yaml | 21 - .../allow-minio-operator-ingress-upgrade.yaml | 24 - .../allow-minio-operator-ingress.yaml | 21 - .../allow-monitoring-ingress.yaml | 24 - .../allow-sidecar-monitoring-ingress.yaml | 22 - .../networkpolicies/allow-test-egress.yaml | 26 - .../{deny-default.yaml => default-deny.yaml} | 4 +- ...{allow-dns-egress.yaml => egress-dns.yaml} | 22 +- .../networkpolicies/egress-istiod.yaml | 21 + .../networkpolicies/egress-kube-api.yaml | 20 + .../networkpolicies/ingress-prometheus.yaml | 22 + .../bigbang/peer-authentication.yaml | 10 + chart/templates/clusterrole.yaml | 123 + chart/templates/clusterrolebinding.yaml | 17 + chart/templates/db-credentials.yaml | 16 - chart/templates/default-bucket.yaml | 71 - chart/templates/deployment.yaml | 72 + chart/templates/env-secret.yaml | 52 - chart/templates/license.yaml | 15 - chart/templates/mattermost.yaml | 246 - chart/templates/role-binding.yaml | 21 - chart/templates/role.yaml | 24 - chart/templates/service-monitor.yaml | 24 - chart/templates/service.yaml | 21 + chart/templates/serviceaccount.yaml | 10 + chart/templates/tests/test-ui.yaml | 13 - chart/templates/virtualservice.yaml | 32 - chart/tests/cypress/cypress.json | 6 - chart/tests/cypress/mattermost-health.spec.js | 126 - chart/values.yaml | 305 +- docs/AFFINITY.md | 14 +- docs/DEVELOPMENT_MAINTENANCE.md | 58 +- docs/developer-guide.md | 32 + docs/keycloak.md | 104 - docs/overview.md | 13 +- docs/upstream/Kptfile | 11 + docs/upstream/mattermost-operator.yaml | 6935 +++++++++++++++++ renovate.json | 20 +- tests/dependencies.yaml | 7 - tests/policy/mattermost-minio-ib.rego | 35 - tests/policy/postgres-ib.rego | 35 - tests/post-install-packages.yaml | 6 + tests/test-values.yml | 21 +- tests/wait.sh | 27 - 104 files changed, 14281 insertions(+), 8481 deletions(-) delete mode 100644 chart/charts/gluon-0.3.2.tgz create mode 100644 chart/charts/mattermost-operator-crds-1.19.0.tgz delete mode 100644 chart/charts/minio-instance-4.5.4-bb.2.tgz delete mode 100644 chart/charts/postgresql-10.3.5.tgz delete mode 100644 chart/dashboards/mattermost-performance-monitoring-v2_rev2.json delete mode 100644 chart/deps/postgresql/.helmignore delete mode 100644 chart/deps/postgresql/Chart.lock delete mode 100644 chart/deps/postgresql/Chart.yaml delete mode 100644 chart/deps/postgresql/Kptfile delete mode 100644 chart/deps/postgresql/README.md delete mode 100644 chart/deps/postgresql/ci/commonAnnotations.yaml delete mode 100644 chart/deps/postgresql/ci/default-values.yaml delete mode 100644 chart/deps/postgresql/ci/shmvolume-disabled-values.yaml delete mode 100644 chart/deps/postgresql/files/README.md delete mode 100644 chart/deps/postgresql/files/conf.d/README.md delete mode 100644 chart/deps/postgresql/files/docker-entrypoint-initdb.d/README.md delete mode 100644 chart/deps/postgresql/templates/NOTES.txt delete mode 100644 chart/deps/postgresql/templates/_helpers.tpl delete mode 100644 chart/deps/postgresql/templates/configmap.yaml delete mode 100644 chart/deps/postgresql/templates/extended-config-configmap.yaml delete mode 100644 chart/deps/postgresql/templates/extra-list.yaml delete mode 100644 chart/deps/postgresql/templates/initialization-configmap.yaml delete mode 100644 chart/deps/postgresql/templates/metrics-configmap.yaml delete mode 100644 chart/deps/postgresql/templates/metrics-svc.yaml delete mode 100644 chart/deps/postgresql/templates/networkpolicy.yaml delete mode 100644 chart/deps/postgresql/templates/podsecuritypolicy.yaml delete mode 100644 chart/deps/postgresql/templates/prometheusrule.yaml delete mode 100644 chart/deps/postgresql/templates/role.yaml delete mode 100644 chart/deps/postgresql/templates/rolebinding.yaml delete mode 100644 chart/deps/postgresql/templates/secrets.yaml delete mode 100644 chart/deps/postgresql/templates/serviceaccount.yaml delete mode 100644 chart/deps/postgresql/templates/servicemonitor.yaml delete mode 100644 chart/deps/postgresql/templates/statefulset-readreplicas.yaml delete mode 100644 chart/deps/postgresql/templates/statefulset.yaml delete mode 100644 chart/deps/postgresql/templates/svc-headless.yaml delete mode 100644 chart/deps/postgresql/templates/svc-read.yaml delete mode 100644 chart/deps/postgresql/templates/svc.yaml delete mode 100644 chart/deps/postgresql/values.schema.json delete mode 100644 chart/deps/postgresql/values.yaml create mode 100644 chart/mattermost-operator-crds/Chart.yaml create mode 100644 chart/mattermost-operator-crds/templates/clusterinstallations.yaml create mode 100644 chart/mattermost-operator-crds/templates/mattermostrestoredbs.yaml create mode 100644 chart/mattermost-operator-crds/templates/mattermosts.yaml delete mode 100644 chart/templates/_helpers.tpl delete mode 100644 chart/templates/bigbang/mattermost-dashboards.yaml delete mode 100644 chart/templates/bigbang/mattermost-gossip-svc.yaml delete mode 100644 chart/templates/bigbang/networkpolicies/allow-egress-tempo.yaml delete mode 100644 chart/templates/bigbang/networkpolicies/allow-elastic-egress.yaml delete mode 100644 chart/templates/bigbang/networkpolicies/allow-external-postgres.yaml delete mode 100644 chart/templates/bigbang/networkpolicies/allow-in-ns.yaml delete mode 100644 chart/templates/bigbang/networkpolicies/allow-istio-egress.yaml delete mode 100644 chart/templates/bigbang/networkpolicies/allow-istio-ingress.yaml delete mode 100644 chart/templates/bigbang/networkpolicies/allow-mattermost-egress.yaml delete mode 100644 chart/templates/bigbang/networkpolicies/allow-minio-monitoring.yaml delete mode 100644 chart/templates/bigbang/networkpolicies/allow-minio-operator-egress.yaml delete mode 100644 chart/templates/bigbang/networkpolicies/allow-minio-operator-ingress-upgrade.yaml delete mode 100644 chart/templates/bigbang/networkpolicies/allow-minio-operator-ingress.yaml delete mode 100644 chart/templates/bigbang/networkpolicies/allow-monitoring-ingress.yaml delete mode 100644 chart/templates/bigbang/networkpolicies/allow-sidecar-monitoring-ingress.yaml delete mode 100644 chart/templates/bigbang/networkpolicies/allow-test-egress.yaml rename chart/templates/bigbang/networkpolicies/{deny-default.yaml => default-deny.yaml} (89%) rename chart/templates/bigbang/networkpolicies/{allow-dns-egress.yaml => egress-dns.yaml} (53%) create mode 100644 chart/templates/bigbang/networkpolicies/egress-istiod.yaml create mode 100644 chart/templates/bigbang/networkpolicies/egress-kube-api.yaml create mode 100644 chart/templates/bigbang/networkpolicies/ingress-prometheus.yaml create mode 100644 chart/templates/bigbang/peer-authentication.yaml create mode 100644 chart/templates/clusterrole.yaml create mode 100644 chart/templates/clusterrolebinding.yaml delete mode 100644 chart/templates/db-credentials.yaml delete mode 100644 chart/templates/default-bucket.yaml create mode 100644 chart/templates/deployment.yaml delete mode 100644 chart/templates/env-secret.yaml delete mode 100644 chart/templates/license.yaml delete mode 100644 chart/templates/mattermost.yaml delete mode 100644 chart/templates/role-binding.yaml delete mode 100644 chart/templates/role.yaml delete mode 100644 chart/templates/service-monitor.yaml create mode 100644 chart/templates/service.yaml create mode 100644 chart/templates/serviceaccount.yaml delete mode 100644 chart/templates/tests/test-ui.yaml delete mode 100644 chart/templates/virtualservice.yaml delete mode 100644 chart/tests/cypress/cypress.json delete mode 100644 chart/tests/cypress/mattermost-health.spec.js create mode 100644 docs/developer-guide.md delete mode 100644 docs/keycloak.md create mode 100644 docs/upstream/Kptfile create mode 100644 docs/upstream/mattermost-operator.yaml delete mode 100644 tests/dependencies.yaml delete mode 100644 tests/policy/mattermost-minio-ib.rego delete mode 100644 tests/policy/postgres-ib.rego create mode 100644 tests/post-install-packages.yaml delete mode 100755 tests/wait.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 640be579e..39f50dba9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,274 +3,92 @@ 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). --- -## [7.5.0-bb.4] - 2022-01-17 +## [1.19.0-bb.0] - 2022-12-06 ### Changed -- Update gluon to new registry1 location + latest version (0.3.2) +- ironbank/opensource/mattermost/mattermost-operator updated from 1.18.1 to 1.19.0 -## [7.5.1-bb.3] - 2022-01-11 -### Changed -- Add support for istio injection via network policies / pod annotation value support -- Disable update job for MM to prevent upgrade issues - -## [7.5.1-bb.2] - 2022-01-11 -### Changed -- Changed minio subchart to utilize OCI -- Updated minio subchart to latest 4.5.4-bb.2 - -## [7.5.1-bb.1] - 2022-12-15 -### Changed -- Set capabilities to drop all - -## [7.5.1-bb.0] - 2022-11-18 -### Changed -- ironbank/opensource/mattermost/mattermost updated from 7.4.0 to 7.5.1 - -## [7.4.0-bb.0] - 2022-10-18 -### Changed -- ironbank/opensource/mattermost/mattermost updated from 7.3.0 to 7.4.0 - -## [7.3.0-bb.1] - 2022-10-05 -### Updated -- updated minio and gluon dependencies - -## [7.3.0-bb.0] - 2022-09-27 -### Changed -- ironbank/opensource/mattermost/mattermost updated from 7.2.0 to 7.3.0 - -## [7.2.0-bb.1] - 2022-09-28 -### Changed -- Change default SSO auth endpoints to use direct Keycloak endpoints. - -## [7.2.0-bb.0] - 2022-08-23 -### Changed -- Upgraded MM to 7.2.0 - -## [7.1.2-bb.1] - 2022-08-09 +## [1.18.1-bb.1] - 2022-09-08 ### Added -- Added grafana dashboard configmap and dashboard json when `monitoring.enabled` and `enterprise.enabled` - -## [7.1.2-bb.0] - 2022-07-29 -### Changed -- Upgraded MM to 7.1.2 - -## [7.0.1-bb.1] - 2022-07-11 -### Changed -- Upgraded cypress test to reduce inconsistent/flaky behavior with a conditional check +- Added default securitycontext to container (drop capabilities, non-privileged, read only fs) +- Added post install package to validate MM successful install -## [7.0.1-bb.0] - 2022-07-06 +## [1.18.1-bb.0] - 2022-06-23 ### Changed -- Upgraded MM to 7.0.1 -- Updated tests to resolve cypress test failures from updated application html/css +- Updated to latest 1.18.1 image/manifests -## [6.7.0-bb.0] - 2022-05-19 +## [1.18.0-bb.1] - 2022-06-14 ### Changed -- Upgraded MM to 6.7.0 +- Adding securityContext section to deployment template and chart values. -## [6.6.0-bb.0] - 2022-04-22 +## [1.18.0-bb.0] - 2022-04-20 ### Changed -- Upgrade Mattermost to application version 6.6.0 +- Updated to latest IB image 1.18.0 -## [0.7.0-bb.1] - 2022-03-28 +## [1.17.0-bb.3] - 2022-04-12 ### Added -- NetworkPolicy Template to allow metrics scraping of minio tenants - -## [0.7.0-bb.0] - 2022-03-03 -### Changed -- Updated MM to 6.4.1 +- Default Istio `PeerAuthentication` for mTLS -## [0.6.0-bb.1] - 2022-02-28 -### Changed -- Updated Minio to 4.4.10 - -## [0.6.0-bb.0] - 2022-02-22 -### Changed -- Upgrade Mattermost to application version 6.3.4 - -## [0.5.0-bb.0] - 2022-02-07 -### Changed -- Upgrade Mattermost to application version 6.3.3 - 1. v5 -> v6 has some major migrations and upstream notes long migration times, they have provided analysis on the time these take - [see here](https://docs.mattermost.com/upgrade/important-upgrade-notes.html#:~:text=Longer%20migration%20times,70%2B%20million%20posts.) - 2. v5 servers cannot run with v6 servers, i.e. upgrade WILL have downtime - [see here](https://docs.mattermost.com/upgrade/important-upgrade-notes.html#:~:text=The%20field%20type,a%20large%20extent.) - 3. v6.1 has additional schema changes, MM has provided analysis on these changes as well - [see here](https://docs.mattermost.com/upgrade/important-upgrade-notes.html#:~:text=Please%20refer%20to%20the%20schema%20migration%20analysis%20when%20upgrading%20to%20v6.1.) - 4. v6.2 has modified autocomplete to include private channels, which requires re-indexing if using Elastic - [see here](https://docs.mattermost.com/upgrade/important-upgrade-notes.html#:~:text=Channel%20results%20in,in%20autocomplete%20results.) - -## [0.4.0-bb.4] - 2022-02-15 -### Changed -- Update mino dependency chart to 4.4.3-bb.3 - -## [0.4.0-bb.3] - 2022-02-14 -### Changed -- Fixed bug with ENV for SITE_URL not being set in certain cases - -## [0.4.0-bb.2] - 2022-02-02 -### Updated -- Update minio dependency chart to 4.4.3-bb.2 - -## [0.4.0-bb.1] - 2022-01-31 +## [1.17.0-bb.2] - 2022-01-31 ### Updated - Update Chart.yaml to follow new standardization for release automation - Added renovate check to update new standardization -## [0.4.0-bb.0] - 2022-01-25 -### Changed -- Relocated `bbtests` to `values.yaml` - -## [0.3.0-bb.0] - 2021-12-06 -### Changed -- Added a conditional addition of tolerations to mattermost.yaml -- Added a spot for tolerations in values.yaml - -## [0.2.4-bb.0] - 2021-11-02 -### Changed -- Disabled ingress by default -- Disabled readiness container by default, added default initcontainer with IB postgres image - -## [0.2.3-bb.0] - 2021-10-27 -### Changed -- Fixed minio operator ingress networkpolicy labels - -## [0.2.2-bb.0] - 2021-09-30 -### Changed -- Updated Mattermost to 5.39.0 +## [1.17.0-bb.1] - 2022-01-12 +### Added +- Ability to specify pod annotations in value file. -## [0.2.1-bb.0] - 2021-09-29 +## [1.17.0-bb.0] - 2022-01-03 ### Changed -- Updated Minio and Minio-operator to 4.2.3-bb.2 +- Updated to latest IB image 1.17 -## [0.2.0-bb.2] - 2021-09-14 +## [1.16.0-bb.0] - 2021-11-01 ### Changed -- Updated Minio, and Minio-operator - -## [0.2.0-bb.1] - 2021-09-01 -### Fixed -- Missing appVersion update in Chart.yaml - -## [0.2.0-bb.0] - 2021-08-27 -### Changed -- Updated to latest IB 5.38.2 -- Modified tests to remove buggy "site name" test and accomodate the new "tutorial" setup - -## [0.1.8-bb.0] - 2021-08-19 -### Fix -- Fixes issue with default bucket creation already existing - -## [0.1.8-bb.0] - 2021-08-18 +- Updated to latest IB image 1.16 ### Added -- default user value set to null -- Set replica count to 1 -- Resource requests and limits for all containers -- Updated to latest Minio and Minio Operator dependency -- Updated Gluon test library +- Documentation on how to perform a package update with kpt/manual changes -## [0.1.7-bb.1] - 2021-07-23 +## [1.15.0-bb.0] - 2021-09-21 ### Changed -- Updated to latest IronBank image 5.37.0 -- Updated to latest Minio 4.1.2 package as dependency -- Moved to Gluon test library -- Pulled in changes from main-minio2 branch - +- Updated to latest IB image 1.15 ### Added -- Added BigBang networkPolicies +- Added support for sidecars via networkPolicies -## [0.1.7-bb.0] - 2021-05-17 -### Changed -- Updated to latest Minio package as dependency - -## [0.1.6-bb.8] - 2021-07-21 -### Changed -- Add openshift toggle, conditionally add port 5353 egress. Changing "openshift:" to true in values.yaml will enable. - -## [0.1.6-bb.7] - 2021-07-08 -### Changed -- Update Mattermost to version 5.36.1 - -## [0.1.6-bb.6] - 2021-06-22 -### Changed -- Update Mattermost to version 5.36.0 - -## [0.1.6-bb.5] - 2021-06-21 -### Fixed -- NetworkPolicy blocking an init container, added policy to allow postgres egress for the init container -- Redo of test egress -- Move around DNS policy - -## [0.1.6-bb.4] - 2021-06-07 +## [1.14.0-bb.4] - 2021-08-30 ### Added -- Ability to pass volumes / volumeMounts to MM pods +- Added support for tolerations -## [0.1.6-bb.3] - 2021-06-04 +## [1.14.0-bb.3] - 2021-08-19 ### Added -- Add IPS with new operator -- Switch to the IB image being used directly +- Added resource requests and limits -## [0.1.6-bb.2] - 2021-06-02 -### Changed -- Restricted test policy to just cluster - -## [0.1.6-bb.1] - 2021-06-01 -### Changed -- Moved tests to gluon library +## [1.14.0-bb.2] - 2021-06-21 ### Added -- Default NetworkPolicies added +- Added network policies with a default deny and egress allowed to the API -## [0.1.6-bb.0] - 2021-05-11 +## [1.14.0-bb.1] - 2021-06-03 ### Changed -- Migrated Cypress tests to Helm tests -- Added additional testing of file storage and settings modification +- Remove network policies -## [0.1.5-bb.0] - 2021-05-06 +## [1.14.0-bb.0] - 2021-06-01 ### Changed -- Updated to 5.34.2 -- Cleaned up values and test-values +- Updated to latest v1.14.0 operator from IB -## [0.1.4-bb.0] - 2021-04-23 +## [1.13.0-bb.3] - 2021-05-25 ### Added -- Added Elastic Search declarative configuration. - -## [0.1.3-bb.2] - 2021-04-19 -### Changed -- Pulled in official BB Minio via kpt -- Refactored the Minio connection secret +- Basic network policies to deny all ingress, allow egress only within cluster -## [0.1.3-bb.1] - 2021-04-15 +## [1.13.0-bb.2] - 2021-04-05 ### Added -- Added Minio security context +- Modified pod affinity spec and values, documentation -## [0.1.3-bb.0] - 2021-04-08 +## [1.13.0-bb.1] - 2021-03-30 ### Added -- Values passthroughs for secret env values -- Moved all envs to a secret that chart creates +- Added pod affinity and anti-affinity, documentation -## [0.1.2-bb.0] - 2021-04-05 +## [1.13.0-bb.0] - 2021-03-15 ### Changed -- Modified the way affinity is passed to simplify and standardize - -## [0.1.1-bb.3] - 2021-03-25 -### Fixed -- Minio virtualservice disabled by default - -## [0.1.1-bb.2] - 2021-03-25 -### Changed -- Updated Cypress test to handle upgrades -- Added a test to make sure chats can send - -## [0.1.1-bb.1] - 2021-03-24 -### Changed -- Refactored the Minio dependency to use the BB upstream with kpt - -## [0.1.1-bb.0] - 2021-03-15 -### Changed -- Bumped Mattermost image to 5.32.1 -- Added a ENV to set S3 connection to insecure when using the built-in minio (due to an operator change) - -## [0.1.0-bb.2] - 2021-02-26 -### Fixed -- Bumped Minio image version to the newest IB image to fix an issue - -## [0.1.0-bb.1] - 2021-02-25 -### Fixed -- Fixed issue with the dependency listing in the chart, Flux did not properly install +- Bumped the operator image to 1.13.0 from Ironbank -## [0.1.0-bb.0] - 2021-02-24 +## [1.12.0-bb.0] - 2021-02-12 ### Added -- Initial chart built from operator v1.12.0 using Ironbank images +- Initial operator from upstream v1.12.0 using Ironbank images diff --git a/CODEOWNERS b/CODEOWNERS index 1448d092e..8fb24e6fe 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1 +1 @@ -* @micah.nagel @trkdashin @blloyd +* @trkdashin @micah.nagel @blloyd diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e9f2890f0..090789142 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -13,7 +13,7 @@ Development requires the Kubernetes CLI tool as well as a local Kubernetes clust To contribute a change: 1. Create a branch on the cloned repository with a descriptive name, prefixed with your name. For example, `gd/add-ingress` is an appropriate branch name. -2. Make the changes in code (review the [development/maintenance doc](./docs/DEVELOPMENT_MAINTENANCE.md), especially for new MM versions). +2. Make the changes in code. 3. Write tests using [KUTTL](https://kuttl.dev) and [Conftest](https://conftest.dev) 4. Make commits using the [Conventional Commits](https://www.conventionalcommits.org/) format. This helps with automation for changelog. Update `CHANGELOG.md` in the same commit using the [Keep a Changelog](https://keepachangelog.com). Depending on tooling maturity, this step may be automated. 5. Open a merge request using one of the provided templates. If this merge request is solving a preexisting issue, add the issue reference into the description of the MR. diff --git a/README.md b/README.md index de9c7497b..4f93a83c4 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,11 @@ -# mattermost +# mattermost-operator -   +   -Deployment of mattermost +Deployment of mattermost operator using Helm + +## Upstream References +* <https://github.com/mattermost/mattermost-operator> ## Learn More * [Application Overview](docs/overview.md) @@ -25,116 +28,32 @@ https://helm.sh/docs/intro/install/ * Clone down the repository * cd into directory ```bash -helm install mattermost chart/ +helm install mattermost-operator chart/ ``` ## Values | Key | Type | Default | Description | |-----|------|---------|-------------| -| hostname | string | `"bigbang.dev"` | | -| istio.enabled | bool | `false` | Toggle istio integration | -| istio.chat.enabled | bool | `true` | | -| istio.chat.annotations | object | `{}` | | -| istio.chat.labels | object | `{}` | | -| istio.chat.gateways[0] | string | `"istio-system/main"` | | -| istio.chat.hosts[0] | string | `"chat.{{ .Values.hostname }}"` | | -| istio.injection | string | `"disabled"` | | -| ingress | object | `{"annotations":{},"enabled":false,"host":"","tlsSecret":""}` | Specification to configure an Ingress with Mattermost | -| monitoring.enabled | bool | `false` | | -| monitoring.namespace | string | `"monitoring"` | | -| networkPolicies.enabled | bool | `false` | | -| networkPolicies.ingressLabels.app | string | `"istio-ingressgateway"` | | -| networkPolicies.ingressLabels.istio | string | `"ingressgateway"` | | -| networkPolicies.controlPlaneCidr | string | `"0.0.0.0/0"` | | -| sso.enabled | bool | `false` | | -| sso.client_id | string | `"platform1_a8604cc9-f5e9-4656-802d-d05624370245_bb8-mattermost"` | | -| sso.client_secret | string | `"nothing"` | | -| sso.auth_endpoint | string | `"https://login.dso.mil/auth/realms/baby-yoda/protocol/openid-connect/auth"` | | -| sso.token_endpoint | string | `"https://login.dso.mil/auth/realms/baby-yoda/protocol/openid-connect/token"` | | -| sso.user_api_endpoint | string | `"https://login.dso.mil/auth/realms/baby-yoda/protocol/openid-connect/userinfo"` | | -| image.name | string | `"registry1.dso.mil/ironbank/opensource/mattermost/mattermost"` | | -| image.tag | string | `"7.5.1"` | | -| image.imagePullPolicy | string | `"IfNotPresent"` | | -| global.imagePullSecrets[0].name | string | `"private-registry"` | | -| replicaCount | int | `1` | | -| users | string | `nil` | | -| enterprise.enabled | bool | `false` | | -| enterprise.license | string | `""` | | -| nameOverride | string | `""` | | -| updateJob.disabled | bool | `true` | Must be disabled when Istio injected | -| updateJob.labels | object | `{}` | | -| updateJob.annotations | object | `{}` | | -| resources.limits.cpu | int | `2` | | -| resources.limits.memory | string | `"4Gi"` | | -| resources.requests.cpu | int | `2` | | -| resources.requests.memory | string | `"4Gi"` | | -| affinity | object | `{}` | | -| nodeSelector | object | `{}` | | -| tolerations | object | `{}` | | -| mattermostEnvs | object | `{}` | | -| existingSecretEnvs | object | `{}` | | -| volumes | object | `{}` | | -| volumeMounts | object | `{}` | | -| podLabels | object | `{}` | Pod labels for Mattermost server pods | -| podAnnotations | object | `{}` | Pod annotations for Mattermost server pods | -| securityContext | object | `{}` | securityContext for Mattermost server pods | -| containerSecurityContext | object | `{"capabilities":{"drop":["ALL"]}}` | containerSecurityContext for Mattermost server containers | -| minio.install | bool | `false` | | -| minio.bucketCreationImage | string | `"registry1.dso.mil/ironbank/opensource/minio/mc:RELEASE.2022-08-23T05-45-20Z"` | | -| minio.service.nameOverride | string | `"minio.mattermost.svc.cluster.local"` | | -| minio.secrets.name | string | `"mattermost-objstore-creds"` | | -| minio.secrets.accessKey | string | `"minio"` | | -| minio.secrets.secretKey | string | `"minio123"` | | -| minio.tenant.metrics.enabled | bool | `false` | | -| minio.tenant.metrics.port | int | `9000` | | -| minio.containerSecurityContext.capabilities.drop[0] | string | `"ALL"` | | -| postgresql.install | bool | `false` | | -| postgresql.image.registry | string | `"registry1.dso.mil/ironbank"` | | -| postgresql.image.repository | string | `"opensource/postgres/postgresql11"` | | -| postgresql.image.tag | string | `"11.18-1"` | | -| postgresql.image.pullSecrets[0] | string | `"private-registry"` | | -| postgresql.postgresqlUsername | string | `"mattermost"` | | -| postgresql.postgresqlPassword | string | `"bigbang"` | | -| postgresql.postgresqlDatabase | string | `"mattermost"` | | -| postgresql.fullnameOverride | string | `"mattermost-postgresql"` | | -| postgresql.securityContext.fsGroup | int | `26` | | -| postgresql.containerSecurityContext.runAsUser | int | `26` | | -| postgresql.containerSecurityContext.capabilities.drop[0] | string | `"ALL"` | | -| postgresql.volumePermissions.securityContext.capabilities.drop[0] | string | `"ALL"` | | -| postgresql.postgresqlConfiguration.listen_addresses | string | `"*"` | | -| postgresql.pgHbaConfiguration | string | `"local all all md5\nhost all all all md5"` | | -| database.secret | string | `""` | | -| database.readinessCheck.disableDefault | bool | `true` | | -| database.readinessCheck.image | string | `"registry1.dso.mil/ironbank/opensource/postgres/postgresql12:12.13"` | | -| database.readinessCheck.command[0] | string | `"/bin/sh"` | | -| database.readinessCheck.command[1] | string | `"-c"` | | -| database.readinessCheck.command[2] | string | `"until pg_isready --dbname=\"$DB_CONNECTION_CHECK_URL\"; do echo waiting for database; sleep 5; done;"` | | -| database.readinessCheck.env[0].name | string | `"DB_CONNECTION_CHECK_URL"` | | -| database.readinessCheck.env[0].valueFrom.secretKeyRef.key | string | `"DB_CONNECTION_CHECK_URL"` | | -| database.readinessCheck.env[0].valueFrom.secretKeyRef.name | string | `"{{ .Values.database.secret \| default (printf \"%s-dbcreds\" (include \"mattermost.fullname\" .)) }}"` | | -| fileStore.secret | string | `""` | | -| fileStore.url | string | `""` | | -| fileStore.bucket | string | `""` | | -| elasticsearch.enabled | bool | `false` | | -| elasticsearch.connectionurl | string | `"https://logging-ek-es-http.logging.svc.cluster.local:9200"` | | -| elasticsearch.username | string | `""` | | -| elasticsearch.password | string | `""` | | -| elasticsearch.enableindexing | bool | `true` | | -| elasticsearch.indexprefix | string | `"mm-"` | | -| elasticsearch.skiptlsverification | bool | `true` | | -| elasticsearch.bulkindexingtimewindowseconds | int | `3600` | | -| elasticsearch.sniff | bool | `false` | | -| elasticsearch.enablesearching | bool | `true` | | -| elasticsearch.enableautocomplete | bool | `true` | | -| openshift | bool | `false` | | -| resourcePatch | object | `{}` | | -| bbtests.enabled | bool | `false` | | -| bbtests.cypress.artifacts | bool | `true` | | -| bbtests.cypress.envs.cypress_url | string | `"http://mattermost.mattermost.svc.cluster.local:8065"` | | -| bbtests.cypress.envs.cypress_mm_email | string | `"test@bigbang.dev"` | | -| bbtests.cypress.envs.cypress_mm_user | string | `"bigbang"` | | -| bbtests.cypress.envs.cypress_mm_password | string | `"Bigbang#123"` | | +| image.imagePullPolicy | string | `"IfNotPresent"` | Default image pull policy | +| image.repository | string | `"registry1.dso.mil/ironbank/opensource/mattermost/mattermost-operator"` | Full image name | +| image.tag | string | `"v1.19.0"` | Image tag | +| replicas.count | int | `1` | Mattermost operator desired replicas | +| imagePullSecrets | list | `[{"name":"private-registry"}]` | Image pull secrets | +| resources | object | `{"limits":{"cpu":"100m","memory":"512Mi"},"requests":{"cpu":"100m","memory":"512Mi"}}` | Resources for operator pod(s) | +| securityContext | object | `{"runAsGroup":65532,"runAsNonRoot":true,"runAsUser":65532}` | securityContext for Kubernetes pod(s) | +| containerSecurityContext | object | `{"capabilities":{"drop":["ALL"]},"privileged":false,"readOnlyRootFilesystem":true}` | containerSecurityContext for operator container | +| affinity | object | `{}` | Affinity for operator pod(s) | +| nodeSelector | object | `{}` | Node selector for operator pod(s) | +| tolerations | object | `{}` | Tolerations for operator pod(s) | +| podAnnotations | object | `{}` | Annotations for operator pod(s) | +| networkPolicies.enabled | bool | `false` | Toggle on/off Big Bang provided network policies | +| networkPolicies.controlPlaneCidr | string | `"0.0.0.0/0"` | See `kubectl cluster-info` and then resolve to IP | +| istio.enabled | bool | `false` | Toggle on/off istio interaction, used for network policies and mTLS | +| istio.mtls | object | `{"mode":"STRICT"}` | Default peer authentication | +| istio.mtls.mode | string | `"STRICT"` | STRICT = Allow only mutual TLS traffic, PERMISSIVE = Allow both plain text and mutual TLS traffic | +| monitoring.enabled | bool | `false` | Toggle on/off monitoring interaction, used for network policies | +| openshift | bool | `false` | Openshift feature toggle, used for DNS network policy | ## Contributing diff --git a/chart/Chart.lock b/chart/Chart.lock index 9105a8a6c..3dcc8d926 100644 --- a/chart/Chart.lock +++ b/chart/Chart.lock @@ -1,12 +1,6 @@ dependencies: -- name: postgresql - repository: file://./deps/postgresql - version: 10.3.5 -- name: minio-instance - repository: oci://registry1.dso.mil/bigbang - version: 4.5.4-bb.2 -- name: gluon - repository: oci://registry1.dso.mil/bigbang - version: 0.3.2 -digest: sha256:4826e20ab1e1cdbe95e2ccbddd969329c7e8646f276a95d2dbf1850882652de1 -generated: "2023-01-17T12:00:42.140338-07:00" +- name: mattermost-operator-crds + repository: file://mattermost-operator-crds + version: 1.19.0 +digest: sha256:43e807661683571d8094f86b8d2352fd3278a7867a549383d5c5e0d6ebe442fe +generated: "2022-12-06T09:20:26.42029-07:00" diff --git a/chart/Chart.yaml b/chart/Chart.yaml index 98aa72cbf..563b86485 100644 --- a/chart/Chart.yaml +++ b/chart/Chart.yaml @@ -1,42 +1,25 @@ ---- apiVersion: v2 -name: mattermost +name: mattermost-operator +description: "Deployment of mattermost operator using Helm" +home: https://github.com/mattermost/mattermost-operator type: application -version: 7.5.0-bb.4 -appVersion: 7.5.0 -description: "Deployment of mattermost" +version: 1.19.0-bb.0 +appVersion: 1.19.0 +kubeVersion: ">=1.12.0-0" keywords: - Mattermost - - Instance -kubeVersion: ">=1.12.0-0" + - Operator +maintainers: + - name: Mattermost + email: info@mattermost.com dependencies: - - name: postgresql - version: 10.3.5 - alias: postgresql - condition: postgresql.install - repository: file://./deps/postgresql - - name: minio-instance - version: 4.5.4-bb.2 - alias: minio - condition: minio.install - repository: oci://registry1.dso.mil/bigbang - - name: gluon - version: 0.3.2 - repository: oci://registry1.dso.mil/bigbang + - name: mattermost-operator-crds + version: 1.19.0 + condition: installCRDs + repository: "file://mattermost-operator-crds" annotations: bigbang.dev/applicationVersions: | - - Mattermost: 7.5.0 + - Mattermost Operator: 1.19.0 helm.sh/images: | - - name: mattermost - image: registry1.dso.mil/ironbank/opensource/mattermost/mattermost:7.5.0 - - name: minio - condition: minio.install - image: registry1.dso.mil/ironbank/opensource/minio/minio:RELEASE.2022-11-17T23-20-09Z - - name: mc - condition: minio.install - image: registry1.dso.mil/ironbank/opensource/minio/mc:RELEASE.2022-08-23T05-45-20Z - - name: postgresql11 - condition: postgresql.install - image: registry1.dso.mil/ironbank/opensource/postgres/postgresql11:11.18-1 - - name: postgresql12 - image: registry1.dso.mil/ironbank/opensource/postgres/postgresql12:12.13 + - name: mattermost-operator + image: registry1.dso.mil/ironbank/opensource/mattermost/mattermost-operator:v1.19.0 diff --git a/chart/charts/gluon-0.3.2.tgz b/chart/charts/gluon-0.3.2.tgz deleted file mode 100644 index dbc09aef2326a0bb33e9df8340a09e3db4c4b486..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3152 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 diff --git a/chart/charts/mattermost-operator-crds-1.19.0.tgz b/chart/charts/mattermost-operator-crds-1.19.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..0fe609db737cca24a7ff8ee7b41649e4378f9e87 GIT binary patch literal 45324 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≥t?~WF zrZ4#B@W?(eIIHpIq1J@2;AQfL%gXfFgCDrm9Y(x+pk$sarLOVfziFD5FOe6nv<y;$ z#%Kz7(%g7vy)aLzZJ~=G-@Lj4D3bi<SI<EP`hWlRSI@oM`*|}9M%!RMt6%M4kz;&R zDhNIpj|vn}-)KB*Y6C5;_n)g+(P@gd{7RxNO*RqIlS60`lA)^(86tsL-hIfBLu&j2 z+ITYbD4Hz)acIqR#i0iO=I+G4ny~M}v@Z%$A*Hc8UQEdBIDdEj-*3*oxp)IO!$3xl zxvg6BeD9dM_?tF#CxTm+$9kpSy}s@k2GlRED{9B(G;AK|x}px%NAlGm2A;^6=M?g5 z<?bk?KppyW@sx%q&w|Mp^0layBQ`>Ak?~%ZFz-KDrH#U9Shgs~B*PtF!}G>>r|u61 zag~bnE27V&jA>ueJ#}C7WF2J9oa=S$OX7>Z-W72auD9()t69EF|2)^q$yFNa`(q+P zs8+*eK@-pUe!SWz%}@9X6s|akcmjlR0gJE9E8RTM0d$3QhFw&eMt+4bd9QB5=u=SU zgK<rsjBbBFGV?FSLh(4-SeR7U)3rB2Sc^*Jdn9vQ^}N`2MB@{$Y%9-U`N9Ihm|E)| zQ6i*ag(5eOlD3=VfUEhBo!I<{2JyI{oODF`)8)Aq5lxDd)W?N1=fyb}-gO5uD;|d> zr<dn@UolZu5274X@ZhTTN+-TU|E-%tIIK>5UAO2O9Mhf7BVCV@xm$jbw&&VKT`e!v zo@<9!D}1H=Jo9m{+h{fyNR@A!DZMKhn<{^>-hd~41bO*Rzn~2eYQGvnL+$knhrK-t z@i5chn}C!;{cZRYZPHq?jl|mgS*G?v<ZktUo$x**SfGAG3r`VAMLJUPkU!8e_4M<K zikQda7b^ePS_%~WTk=Xa-O^d*YQc)0MLFgaeNePO&%$BZIT1-kGt<3Cw>W(M{Q2{M z7bGRmfBoxUL+5gmvV?DTfxuJ$_Tt5Y{NJ;;Z=e@{5oomlWqoi)fP5FQoRR;$e*doN zi`V|E9|NL<2LFK01vz)3zFaFDt=14xmy3$f)e26Xf4*B(9rWEdMIZR>+Vq1{=PZK| zHm;Ty^e>t6A{gBVbFeG|mM|k3zhh)2p%dIJsy$}Oq>{>Y?|n22TXd+&e?He;MBA>` zB{0neOu#Y{1BIf)Tw-m3!$L*8>#7}=@(`K{Xq)Q^WW*ud5SgZ_E%fj6z8YULA1T*c z8qs92?Rap?n$V@FE90z3XplP}gb2Yg8ZW_z$F%_%QR})*@mrF`j4+z4$sOCyksKA2 z$Ci%PWZ;}uwB2T`X}qTdbi`LAOcOxSoFzyIbK;Vy)FNCgL?~sPI|dTn&;dexl^Usk z#ttu$P%quoCLJm&)pYOJR_?AM8l$EpkBwnbL>7Cmk~(+3F$SMd0ge$ZLqkLjH4Pw~ zXqh!nLig296x)Gnh~^~N&31FjA|Xmv)dfx$TgKLT#-t=1_Gd%OJ7$VW(JXeCt`zcY zMLg`#aF4N)ie|Z)bg?ay31reC?-S)thFuhlQDy4eG7c>y7S>myGqmgwkl0zRHD`{5 zN(hap_~wp%nv{%eQPPf2(l@mspksOg>{Zl%6IpG#cfL-!txZ=F9RZW{Nv*ogan?9b zM?v(6t*&S~4rZJfI}Rp_yaBOx6iA;4Y{lGB5K$z)Ty^>(x3Xv(?PU~b!#32Z@yNy_ zVH!EwD=UwJ>5;1)*?AmR`{nG|Zf9hK;N2bjbt(+Gxw~VBwpngeIQcB2x1tiz2%@sY zazWl|&W8FbT}qMF)i|g+lVz7h;$(G;iBTXi|J;Cd*e3R>1%4LKLG|Lt6Zx_ISH^T- z@%m!BV|3S*aXnY!`)`j6YKvUmXAXVCF*X1$5^PME9FCFa(MYp<?9;U_E@YBSTGDqs zD_3L|6c!^Q>u#4LRvWU$wx5nV@5Y9Bc6IqEI=ch!r>nJtown6K^1NAAM8V-^MDv*a z)s&s7z2=$g#fhfzo!YW!y=@G9O+DIZh$fH$sKOJfJ^()u7~LJa&3%DYk!$F$h<1<o zS>q5U#x;G3{gr5*3+!zxXW062q(}{>6eaFC0V1X2MH?lw?brH}IK+HxySKLM2Vod2 z$z;tobc8LS8L!pOnBox+C9f)mmFrl!kwmP<cIqQ;b7XRK|6)W(EF63HwOg5`9E94d zmRQR&?UBjiyUaDige6&9l_KT$xOblkE#n*aDS^<Ua<o)CsvqA^!E`Y*LdImeh#QmX zVlrI>#uqSMIAV$6b{ctT&KPZHXqDOosSbl=w5J{|p5p`*ZT!%d4{WK^m@tly7mMRD zyJPJkT1l#_)%-Vv<%vk~u&a5OBPXeZ>qgObO+gz|<PVG_<8KVEZ_EPsVu>b5<n_UV zZX@2>nivsb`rTm*_Gme`31Z3khU?NcikvbzLIF|Z1OY6PqMm7Biw)a|ayzpC)gMPP zwq`aO+9}A<NnMsKud?lDmZ&ZMVjD!dqZ5*D3x0wbYFwjIcdTr6WjV9v68M$n_vD_I z-T?5vvA0m1@_R1FwwhPlynR3FY7k7&+l#Qb-`=Z3dXJTf>)NWKu8O+SylV7TYEoya z)7aZNvwF^GkV-Fl;SZzS9R;l_R^~6sf1UjC7k`_d{{H0Thv)PE{ik1?{BZ&Q^Yhc+ zpZ?AK;}@r=CnrDr;qA9KR~LWcr+@n)uQzx2ufLuAz%Kq23FP$j_y00-qMzoC@pR7f zYA(t-Zs|)>m7YAwzG2XP*u8F8e^b0@oPt0=Nl@heGhN*M)V5$tiJ|$8(%Q!RBEA;? z!U*6PLHa0mT;0kE+Pp-ayRXcI`&k38bcqF}mqkL2i@L&isz{L0QCH-06$x^Ry6Q$< zUF!yA&Tc_4)tI?Pgj``TRV2{ZkSlQc##-U0kgJF>g<MS`S7TsKcFW0b`OjdtoI<Xq zkgF-=Y6`h})Q~H!WQmgSX#16-8DCYnm2U2OmL#Iis~d60B4R3{)b$;>5ewDY{S)c# zv`)FwkN4RBGySSjTow1qQ<hqok^8T2dZqBOAZMgx2`{+P=b&Z_{Ei32^pA_x4lBvB zmF}^XJN;lCZBw(91E!2(hcHc^OvBug{YA?*vBVhHf8)%i74ZTnj%#VVGf`S_UBKxn zXbAgM@DdLDHA_UE?qdtO=)y3kO1p%jZ5L^cU?As{W<2H9)<!ewuFVv0;JA%Blg*wJ zS`?+A$(l)L^|KMXt;<N*v{dYt4#GD;R#I8EC8elnwA|FQME6vgPmZvFp@>wKTc~j) z^mH6NaOWb#+iXOZn!1v|d9L)#MBjELazn+wN{}qxcdppY%AHl~6aV^4vKDnI7w6;r zDwQBF;7zpEn2h&|WQ@v+ym(GFJg=2l5e3ju4Sw~@5!WzYDTr4GcH`3YZ7Z-Jj@Am0 z>cm>O;JjhKtWNNPW?kctN?wU4pjh$pcce5OEn7rB7#6dGfPSpSa$Cif%lSX}Y`ad5 z`EjQP=it0|+rc#@I0s&ekyUP$ZXccQ50(I<ks4Edk}wH8^t@?vocj%8@YUcJJ$8{9 zZwXAZBVVxDSP&s0X}n~&TvnsHMB)qW!zp=wi=DYPbyo2rv)J)2!UEG=(sesf$EHoO zx)at!4!WUE>>OBLVI!zgG2()j)S^ETn+=tx=-nVR32-4@U{LYJl4cfdZv4lghqGci z&Aooewz&u%C*mPD9Wt<eX!}%>h{W-4XmZE$6goyDa?)*1H@bkm?Vroi4(x!{6-~fc zOU2Z!Lb2*lOx1Ek)zyzu$sK<BBQf&3<o5}Skww)64qe2(w|v^pGIZ%Q?%WEX@7AFT zMVe`*$ckSd(_xF<gR<N{M(5d>2CH~=1|RT}(;%NMmvLpKjz4^J-lkz6XeuQ6CKJgW zIcFfWe~H$s<uZMoCRS6krJrQ8rE=*<!%wNz(uUrS>F~E^q=ZWdFPd8XdtRNNt;NH< z67yQJIrj#0MXlwIZ6UHPdNqH*7g~y3CBV1}zbnb%!#C%OJ;jya<&r#ORq`xh#rm0^ z&R2t;jmUW=$`K8o&qyM&Oc&`AEAomJYYXPlS{U!S=Fi)Lrw}5(-H3Bd)O-4tIESQM zrlU(<e~BaW$PEb|yYtXzyIGIl!RYwfyaI}~<GpJ)CDNe%dZt6B$mI&vft000+_SRe zDU-y~qUI2KLFPRB9N3-uDt$G5l}0IS4uMne?GiYeZg~C^AaN1hk(8V>_*cUW_f>^t zLNj*xK1v<B#%J3ak@?XnXdke34VAk8gS`kO7wNEtekybCoO7(weJGo~r4@Ui@m%)C zEUDd)#vX>Vt4s1N!aTgjekm%H0MEsS@)4~;oh~Bo<gP40f)Ew|^6#lFnOZBYPitdS zIdf;Jk1wD!@`+=<G9m6w=S97%K~r4g@F$Yxl7p|z3Hi|N_PK20(@}`=-^@4SmgnYX z3+)VEQSL47IY}K~jnpA!c9GL%=65W%nMFpyug>7TXDOHJzvTM*4LA(y+(^#B%F&uc z+T=mJ)Q8_Sq)(&D&R^!Knl_WI$S@5FE9=Mjuyh>Fl98J=PwuW9zkn#!Z@Kf6Wn_K{ zP42vRDB;!1x*DTW-#8rPTBAh)AXG|`@WyEr@}xBUjHJMR4qpyP6MOSTv&mWjqSE1Y zAq81u<lrUX(Hhk<?55Qu%y@0kw_*Jk5uPoxsYd?OT^x6})iw*$<?Nr_3!<g6!M|Ic zI{buBUJdI8-lWGqYeL82i@#~N2M~rr6m{kcj@s$Tc7T-}zki5(WS<5ezI2{$ijMbS zHhn%u_ww+<?Pl*hN=8mx22OugjTdky<Hq#za3ta9zxw4b`?`AsMf%>(AHMD$e%I4b z=a2o<XX4UjdQxEgJUWmb^SG@)f>!de*~Kw5JvOH}KF>CgN$krJ4oNqy{NcyT3?7jW z>}L7~WS4Lj@NRx@@FEW7{syvo{nWFa@%uRK6ZUPIFt#;KI9oR|3pYNmAS&bb;5^$A zA)j$MwNJx(arEZT?%h2L$7Azd4RsIh8v%A8>vbrTH3DLPuIpg_Y6Qg5Y&QaCsO?rC z^Nj#IHbZq>Uh0^@^SGe%_{>usI~REX{Q0Ks9MTAoLs_PSnWK@@9F<!dP1xe%*u&YQ z5mD}s7PualaX2o|u&LM{lB&b$g=0+6<JywD_EfFTnv;g%d$xMCe<y?@ZBMMwlDK0S zR{Qa*Vo+pgZ!|vr{<`B9v_J3JvUl<Z4vsI}^m=kz`4-!_tz@h2pfQF4B>+)MEMuzv z1L3rn>Fnx~Bqf84QkuyH>8U4@Mi|w#L}%nKT2*XQRQ|&;d9A)0uhkyr^6%^`#^$iD zH#DECn+X0Z$0gvmmv<=pf|$GmKrR^6AY}>rP{$bKXv0Py(USo1_P`G|gXrAeL=;ij z(cDfe=@wV!qB`9sT+2y_0-(ro1=)EGgtpd%Y7@;e@gRfKbh)eAceOs9F1%!9!^={X z+VkNq!wXZ<v`T4htVA`W)Q<IQR=LNo5`^ZMND@k5Gx3Ai07pAXmuyY%xu{EoTnTcd z{{*aVEBqyljls8uP<D$=*|TT%H26sdD43}*R<Akx#AUSu&Tf|}3I4=l+`TLmKmE>| zLe#zj?i+1!?*1D7w68Fyv)D01&!i4a0-YIOSfI1+@Qf$h%ky|q$GXAQi{u+BSxVm0 zoZh1TeR6&E&D&G8p+Qf_<$0eU-FyK*@b2);v(8#aG0;a`?IYyS8BP;waI;%903F<> zCW-7`?fXr7_tcK<X6pM9H{TcLfZd)ycwr9NKn7lt6|L$Hed-wStBREyA**Y6R2wmL z3GyY>bx8pXNEOZFEs(>ckmV^9ZP>-Yj?vCM3Vc^5wXT`-Ny2kk(Y`u5p3MfQOvKkS zswksbFuHQI1tn!gCbk=9N>6Y(ga;h^OmiT+Y-gI7Ojg!8BuWcw;<34M2cpYh;9HjK zNWsy#YhE>~k~1eysc*RN4^$GGAUEEnJpL*tKEra!6sw{=th&7#QPGU#^=8RRHTzuT zb2W9I1HA`CSG2rkjsw1DDb9kM!{b{`kJ;S68pPrit&$$9+F=K9uZ82WZR*Jv3P7LD z$}ikBiyD~0&Me?LJrze_lXh|iEi0bX87*;A{cI;JbJ3#6xX)@GV^0|%8f_3B-V$I7 zx<JH)yWt1C?63Eaz@q|p;X{KxFyUx2YT4T(5YW0v6{+~}<>`&~Czj1Rt+<*psl*cG zCSEc-k}%y5HHqBe8Tv`5|BjjEV=%JbbF%XzGAyURhZefsU+Sgponls{g(~ttfB#>N ze1)EEmVzpdPu}r7eMye6WlE9NCC&6d6OpGJoW3u~4}bb!_+gf6V;%nZ|4d^V)0oCr TF#i7l00960f*y+y0AL0HUGsa& literal 0 HcmV?d00001 diff --git a/chart/charts/minio-instance-4.5.4-bb.2.tgz b/chart/charts/minio-instance-4.5.4-bb.2.tgz deleted file mode 100644 index 26eeec62dc5a2252ac5161f421a8af2e2c1c41f8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20002 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 diff --git a/chart/charts/postgresql-10.3.5.tgz b/chart/charts/postgresql-10.3.5.tgz deleted file mode 100644 index 7f724ea8f422a69026caf14d122613d5dd462688..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 40072 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 diff --git a/chart/dashboards/mattermost-performance-monitoring-v2_rev2.json b/chart/dashboards/mattermost-performance-monitoring-v2_rev2.json deleted file mode 100644 index 069318415..000000000 --- a/chart/dashboards/mattermost-performance-monitoring-v2_rev2.json +++ /dev/null @@ -1,2272 +0,0 @@ -{ - "__requires": [ - { - "type": "grafana", - "id": "grafana", - "name": "Grafana", - "version": "7.2.1" - }, - { - "type": "panel", - "id": "graph", - "name": "Graph", - "version": "" - }, - { - "type": "datasource", - "id": "prometheus", - "name": "Prometheus", - "version": "1.0.0" - } - ], - "annotations": { - "list": [ - { - "builtIn": 1, - "datasource": "-- Grafana --", - "enable": true, - "hide": true, - "iconColor": "rgba(0, 211, 255, 1)", - "name": "Annotations & Alerts", - "type": "dashboard" - } - ] - }, - "editable": true, - "gnetId": 15582, - "graphTooltip": 1, - "id": null, - "iteration": 1642655786188, - "links": [], - "panels": [ - { - "collapsed": true, - "datasource": "${DS_PROMETHEUS}", - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 0 - }, - "id": 6, - "panels": [ - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "${DS_PROMETHEUS}", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 1 - }, - "hiddenSeries": false, - "id": 12, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.2.1", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "rate(mattermost_http_requests_total{instance=~\"$server\"}[1m])", - "interval": "", - "legendFormat": "{{instance}}", - "refId": "A" - }, - { - "expr": "sum(rate(mattermost_http_requests_total{instance=~\"$server\"}[1m]))", - "interval": "", - "legendFormat": "Total", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "HTTP Requests per second", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:1128", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:1129", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "${DS_PROMETHEUS}", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 1 - }, - "hiddenSeries": false, - "id": 20, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.2.1", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(rate(mattermost_db_store_time_count{instance=~\"$server\"}[5m])) by (instance)", - "interval": "", - "legendFormat": "{{instance}}", - "refId": "A" - }, - { - "expr": "sum(rate(mattermost_db_store_time_count{instance=~\"$server\"}[5m]))", - "interval": "", - "legendFormat": "Total", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "DB Calls per second", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:1772", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:1773", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "${DS_PROMETHEUS}", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 9 - }, - "hiddenSeries": false, - "id": 16, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.2.1", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "histogram_quantile(0.99, sum(rate(mattermost_api_time_bucket{instance=~\"$server\"}[1m])) by (instance,le))", - "interval": "", - "legendFormat": "p99-{{instance}}", - "refId": "A" - }, - { - "expr": "histogram_quantile(0.50, sum(rate(mattermost_api_time_bucket{instance=~\"$server\"}[1m])) by (instance,le))", - "interval": "", - "legendFormat": "p50-{{instance}}", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "API Latency", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:1298", - "format": "s", - "label": null, - "logBase": 2, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:1299", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "${DS_PROMETHEUS}", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 9 - }, - "hiddenSeries": false, - "id": 18, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.2.1", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "histogram_quantile(0.99, sum(rate(mattermost_db_store_time_bucket{instance=~\"$server\"}[1m])) by (instance,le))", - "interval": "", - "legendFormat": "p99-{{instance}}", - "refId": "A" - }, - { - "expr": "histogram_quantile(0.50, sum(rate(mattermost_db_store_time_bucket{instance=~\"$server\"}[1m])) by (instance,le))", - "interval": "", - "legendFormat": "p50-{{instance}}", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Store latency", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:1381", - "format": "s", - "label": null, - "logBase": 2, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:1382", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "${DS_PROMETHEUS}", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 17 - }, - "hiddenSeries": false, - "id": 28, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.2.1", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(increase(mattermost_db_store_time_count{instance=~\"$server\",method=~\"$top_db_count\"}[5m])) by (method)", - "interval": "", - "legendFormat": "{{method}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Top 10 DB Calls by Count", - "tooltip": { - "shared": true, - "sort": 2, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:2313", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:2314", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "${DS_PROMETHEUS}", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 17 - }, - "hiddenSeries": false, - "id": 30, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.2.1", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(increase(mattermost_api_time_count{instance=~\"$server\",handler=~\"$top_api_count\"}[5m])) by (handler)", - "interval": "", - "legendFormat": "{{handler}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Top 10 API Requests by Count", - "tooltip": { - "shared": true, - "sort": 2, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:2422", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:2423", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "${DS_PROMETHEUS}", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 25 - }, - "hiddenSeries": false, - "id": 32, - "legend": { - "alignAsTable": true, - "avg": true, - "current": false, - "max": true, - "min": true, - "rightSide": true, - "show": true, - "sort": "avg", - "sortDesc": true, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null as zero", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.2.1", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(increase(mattermost_db_store_time_sum{instance=~\"$server\",method=~\"$top_db_latency\"}[5m])) by (method) / sum(increase(mattermost_db_store_time_count{instance=~\"$server\",method=~\"$top_db_latency\"}[5m])) by (method)", - "interval": "", - "legendFormat": "{{method}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Top 10 DB calls by duration", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:2505", - "format": "s", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:2506", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "${DS_PROMETHEUS}", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 25 - }, - "hiddenSeries": false, - "id": 34, - "legend": { - "alignAsTable": true, - "avg": true, - "current": false, - "max": true, - "min": true, - "rightSide": true, - "show": true, - "sort": "avg", - "sortDesc": true, - "total": false, - "values": true - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null as zero", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.2.1", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(increase(mattermost_api_time_sum{instance=~\"$server\",handler=~\"$top_api_latency\"}[5m])) by (handler) / sum(increase(mattermost_api_time_count{instance=~\"$server\",handler=~\"$top_api_latency\"}[5m])) by (handler)", - "interval": "", - "legendFormat": "{{handler}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Top 10 API requests by duration", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:2667", - "format": "s", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:2668", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "${DS_PROMETHEUS}", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 33 - }, - "hiddenSeries": false, - "id": 24, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null as zero", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.2.1", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "histogram_quantile (\n 0.99,\n sum by (le, instance)(\n rate(mattermost_api_time_bucket{instance=~\"$server\",handler=\"getPostsForChannelAroundLastUnread\"}[5m])\n )\n)", - "interval": "", - "legendFormat": "p99-{{instance}}", - "refId": "A" - }, - { - "expr": "histogram_quantile (\n 0.50,\n sum by (le, instance)(\n rate(mattermost_api_time_bucket{instance=~\"$server\",handler=\"getPostsForChannelAroundLastUnread\"}[5m])\n )\n)", - "interval": "", - "legendFormat": "p50-{{instance}}", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Channel Load Duration", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:2118", - "format": "s", - "label": null, - "logBase": 2, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:2119", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "${DS_PROMETHEUS}", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 33 - }, - "hiddenSeries": false, - "id": 26, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null as zero", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.2.1", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "histogram_quantile (\n 0.99,\n sum by (le, instance)(\n rate(mattermost_api_time_bucket{instance=~\"$server\",handler=\"createPost\"}[5m])\n )\n)", - "interval": "", - "legendFormat": "p99-{{instance}}", - "refId": "A" - }, - { - "expr": "histogram_quantile (\n 0.50,\n sum by (le, instance)(\n rate(mattermost_api_time_bucket{instance=~\"$server\",handler=\"createPost\"}[5m])\n )\n)", - "interval": "", - "legendFormat": "p50-{{instance}}", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "CreatePost duration", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:2216", - "format": "s", - "label": null, - "logBase": 2, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:2217", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "${DS_PROMETHEUS}", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 41 - }, - "hiddenSeries": false, - "id": 14, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.2.1", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "mattermost_http_websockets_total{instance=~\"$server\"}", - "interval": "", - "legendFormat": "{{instance}}", - "refId": "A" - }, - { - "expr": "sum(mattermost_http_websockets_total{instance=~\"$server\"})", - "interval": "", - "legendFormat": "Total", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Number of connected devices (WebSocket Connections)", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:1215", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:1216", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "${DS_PROMETHEUS}", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 41 - }, - "hiddenSeries": false, - "id": 22, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.2.1", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(mattermost_db_master_connections_total)", - "interval": "", - "legendFormat": "master", - "refId": "A" - }, - { - "expr": "sum(mattermost_db_read_replica_connections_total)", - "interval": "", - "legendFormat": "replica", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "DB Connections", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:1932", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:1933", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "${DS_PROMETHEUS}", - "description": "This needs to be configured in config.json for it to work", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 49 - }, - "hiddenSeries": false, - "id": 44, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.2.1", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "mattermost_db_replica_lag_time{instance=~\"$server\"}", - "interval": "", - "legendFormat": "{{instance}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Replica Lag", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:3692", - "format": "none", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:3693", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - } - ], - "title": "Application Metrics", - "type": "row" - }, - { - "collapsed": true, - "datasource": "${DS_PROMETHEUS}", - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 1 - }, - "id": 36, - "panels": [ - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "${DS_PROMETHEUS}", - "description": "Lower numbers are better, and zero means \"totally healthy\".", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 2 - }, - "hiddenSeries": false, - "id": 38, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.2.1", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "mattermost_cluster_cluster_health_score{instance=~\"$server\"}", - "interval": "", - "legendFormat": "{{instance}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Cluster Health", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:2865", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:2866", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "${DS_PROMETHEUS}", - "description": "", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 12, - "y": 2 - }, - "hiddenSeries": false, - "id": 40, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.2.1", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "histogram_quantile(0.99, sum(rate(mattermost_cluster_cluster_request_duration_seconds_bucket{instance=~\"$server\"}[5m])) by (le,instance))", - "interval": "", - "legendFormat": "p99-{{instance}}", - "refId": "A" - }, - { - "expr": "histogram_quantile(0.50, sum(rate(mattermost_cluster_cluster_request_duration_seconds_bucket{instance=~\"$server\"}[5m])) by (le,instance))", - "interval": "", - "legendFormat": "p50-{{instance}}", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Cluster Request Duration", - "tooltip": { - "shared": true, - "sort": 2, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:2948", - "format": "s", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:2949", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "${DS_PROMETHEUS}", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 10 - }, - "hiddenSeries": false, - "id": 42, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.2.1", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "sum(rate(mattermost_cluster_cluster_request_duration_seconds_count{instance=~\"$server\"}[5m])) by (instance)", - "interval": "", - "legendFormat": "{{instance}}", - "refId": "A" - }, - { - "expr": "sum(rate(mattermost_cluster_cluster_request_duration_seconds_count{instance=~\"$server\"}[5m]))", - "interval": "", - "legendFormat": "Total", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Cluster Requests Per Second", - "tooltip": { - "shared": true, - "sort": 2, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:3241", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:3242", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - } - ], - "title": "Cluster Metrics", - "type": "row" - }, - { - "collapsed": true, - "datasource": "${DS_PROMETHEUS}", - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 2 - }, - "id": 48, - "panels": [ - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "${DS_PROMETHEUS}", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 19 - }, - "hiddenSeries": false, - "id": 46, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.2.1", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "mattermost_jobs_active{instance=~\"$server\"}", - "interval": "", - "legendFormat": "{{instance}}", - "refId": "A" - }, - { - "expr": "sum(mattermost_jobs_active{instance=~\"$server\"})", - "interval": "", - "legendFormat": "Total", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Active Jobs", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:4113", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:4114", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - } - ], - "title": "Job Server", - "type": "row" - }, - { - "collapsed": true, - "datasource": "${DS_PROMETHEUS}", - "gridPos": { - "h": 1, - "w": 24, - "x": 0, - "y": 3 - }, - "id": 4, - "panels": [ - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "${DS_PROMETHEUS}", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 9, - "w": 12, - "x": 0, - "y": 20 - }, - "hiddenSeries": false, - "id": 2, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.2.1", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "irate(mattermost_process_cpu_seconds_total{instance=~\"$server\"}[5m])* 100", - "interval": "", - "legendFormat": "{{instance}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "CPU Utilization Percentage", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:99", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:100", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "${DS_PROMETHEUS}", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 9, - "w": 12, - "x": 12, - "y": 20 - }, - "hiddenSeries": false, - "id": 8, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.2.1", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "go_memstats_heap_inuse_bytes{instance=~\"$server\"}", - "interval": "", - "legendFormat": "{{instance}}", - "refId": "A" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Heap Utilization", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:288", - "format": "decbytes", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:289", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - }, - { - "aliasColors": {}, - "bars": false, - "dashLength": 10, - "dashes": false, - "datasource": "${DS_PROMETHEUS}", - "fieldConfig": { - "defaults": { - "custom": {} - }, - "overrides": [] - }, - "fill": 1, - "fillGradient": 0, - "gridPos": { - "h": 8, - "w": 12, - "x": 0, - "y": 29 - }, - "hiddenSeries": false, - "id": 10, - "legend": { - "avg": false, - "current": false, - "max": false, - "min": false, - "show": true, - "total": false, - "values": false - }, - "lines": true, - "linewidth": 1, - "nullPointMode": "null", - "options": { - "alertThreshold": true - }, - "percentage": false, - "pluginVersion": "7.2.1", - "pointradius": 2, - "points": false, - "renderer": "flot", - "seriesOverrides": [], - "spaceLength": 10, - "stack": false, - "steppedLine": false, - "targets": [ - { - "expr": "go_goroutines{instance=~\"$server\"}", - "interval": "", - "legendFormat": "{{instance}}", - "refId": "A" - }, - { - "expr": "sum(go_goroutines{instance=~\"$server\"})", - "interval": "", - "legendFormat": "Total", - "refId": "B" - } - ], - "thresholds": [], - "timeFrom": null, - "timeRegions": [], - "timeShift": null, - "title": "Goroutines", - "tooltip": { - "shared": true, - "sort": 0, - "value_type": "individual" - }, - "type": "graph", - "xaxis": { - "buckets": null, - "mode": "time", - "name": null, - "show": true, - "values": [] - }, - "yaxes": [ - { - "$$hashKey": "object:1021", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - }, - { - "$$hashKey": "object:1022", - "format": "short", - "label": null, - "logBase": 1, - "max": null, - "min": null, - "show": true - } - ], - "yaxis": { - "align": false, - "alignLevel": null - } - } - ], - "title": "System Metrics", - "type": "row" - } - ], - "refresh": "1m", - "schemaVersion": 26, - "style": "dark", - "tags": [], - "templating": { - "list": [ - { - "allValue": null, - "current": {}, - "datasource": "${DS_PROMETHEUS}", - "definition": "label_values(instance)", - "hide": 0, - "includeAll": false, - "label": "server", - "multi": true, - "name": "server", - "options": [], - "query": "label_values(instance)", - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "sort": 0, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "allValue": null, - "current": {}, - "datasource": "${DS_PROMETHEUS}", - "definition": "query_result(topk(10, sum(increase(mattermost_db_store_time_count{instance=~\"$server\"}[${__range_s}s])) by (method)))", - "hide": 0, - "includeAll": true, - "label": null, - "multi": true, - "name": "top_db_count", - "options": [], - "query": "query_result(topk(10, sum(increase(mattermost_db_store_time_count{instance=~\"$server\"}[${__range_s}s])) by (method)))", - "refresh": 2, - "regex": ".*method=\"(.*?)\".*", - "skipUrlSync": false, - "sort": 0, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "allValue": null, - "current": {}, - "datasource": "${DS_PROMETHEUS}", - "definition": "query_result(topk(10, sum(increase(mattermost_db_store_time_sum{instance=~\"$server\"}[${__range_s}s])) by (method) / sum(increase(mattermost_db_store_time_count{instance=~\"$server\"}[${__range_s}s])) by (method)))", - "hide": 0, - "includeAll": true, - "label": null, - "multi": true, - "name": "top_db_latency", - "options": [], - "query": "query_result(topk(10, sum(increase(mattermost_db_store_time_sum{instance=~\"$server\"}[${__range_s}s])) by (method) / sum(increase(mattermost_db_store_time_count{instance=~\"$server\"}[${__range_s}s])) by (method)))", - "refresh": 2, - "regex": ".*method=\"(.*?)\".*", - "skipUrlSync": false, - "sort": 0, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "allValue": null, - "current": {}, - "datasource": "${DS_PROMETHEUS}", - "definition": "query_result(topk(10, sum(increase(mattermost_api_time_count{instance=~\"$server\"}[${__range_s}s])) by (handler)))", - "hide": 0, - "includeAll": true, - "label": null, - "multi": true, - "name": "top_api_count", - "options": [], - "query": "query_result(topk(10, sum(increase(mattermost_api_time_count{instance=~\"$server\"}[${__range_s}s])) by (handler)))", - "refresh": 2, - "regex": ".*handler=\"(.*?)\".*", - "skipUrlSync": false, - "sort": 0, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "allValue": null, - "current": {}, - "datasource": "${DS_PROMETHEUS}", - "definition": "query_result(topk(10, sum(increase(mattermost_api_time_sum{instance=~\"$server\"}[${__range_s}s])) by (handler) / sum(increase(mattermost_api_time_count{instance=~\"$server\"}[${__range_s}s])) by (handler)))", - "hide": 0, - "includeAll": true, - "label": null, - "multi": true, - "name": "top_api_latency", - "options": [], - "query": "query_result(topk(10, sum(increase(mattermost_api_time_sum{instance=~\"$server\"}[${__range_s}s])) by (handler) / sum(increase(mattermost_api_time_count{instance=~\"$server\"}[${__range_s}s])) by (handler)))", - "refresh": 2, - "regex": ".*handler=\"(.*?)\".*", - "skipUrlSync": false, - "sort": 0, - "tagValuesQuery": "", - "tags": [], - "tagsQuery": "", - "type": "query", - "useTags": false - }, - { - "current": { - "selected": false, - "text": "Prometheus", - "value": "Prometheus" - }, - "hide": 0, - "includeAll": false, - "label": "Datasource", - "multi": false, - "name": "DS_PROMETHEUS", - "options": [], - "query": "prometheus", - "refresh": 1, - "regex": "", - "skipUrlSync": false, - "type": "datasource" - } - ] - }, - "time": { - "from": "now-6h", - "to": "now" - }, - "timepicker": {}, - "timezone": "", - "title": "Mattermost Performance Monitoring v2", - "uid": "im7xNX17k", - "version": 9, - "description": "A dashboard to monitor complete Mattermost application performance" -} \ No newline at end of file diff --git a/chart/deps/postgresql/.helmignore b/chart/deps/postgresql/.helmignore deleted file mode 100644 index f0c131944..000000000 --- a/chart/deps/postgresql/.helmignore +++ /dev/null @@ -1,21 +0,0 @@ -# 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/chart/deps/postgresql/Chart.lock b/chart/deps/postgresql/Chart.lock deleted file mode 100644 index 2be1d0932..000000000 --- a/chart/deps/postgresql/Chart.lock +++ /dev/null @@ -1,6 +0,0 @@ -dependencies: -- name: common - repository: https://charts.bitnami.com/bitnami - version: 1.4.0 -digest: sha256:af8a0894ca6f099e57ebfe9ce25a50d9e7548aeaa53b69d051ecc1681d6d77bb -generated: "2021-02-22T16:26:47.159006+01:00" diff --git a/chart/deps/postgresql/Chart.yaml b/chart/deps/postgresql/Chart.yaml deleted file mode 100644 index 53e0c06d7..000000000 --- a/chart/deps/postgresql/Chart.yaml +++ /dev/null @@ -1,29 +0,0 @@ -annotations: - category: Database -apiVersion: v2 -appVersion: 11.11.0 -dependencies: - - name: common - repository: https://charts.bitnami.com/bitnami - version: 1.x.x -description: Chart for PostgreSQL, an object-relational database management system (ORDBMS) with an emphasis on extensibility and on standards-compliance. -engine: gotpl -home: https://github.com/bitnami/charts/tree/master/bitnami/postgresql -icon: https://bitnami.com/assets/stacks/postgresql/img/postgresql-stack-220x234.png -keywords: - - postgresql - - postgres - - database - - sql - - replication - - cluster -maintainers: - - email: containers@bitnami.com - name: Bitnami - - email: cedric@desaintmartin.fr - name: desaintmartin -name: postgresql -sources: - - https://github.com/bitnami/bitnami-docker-postgresql - - https://www.postgresql.org/ -version: 10.3.5 diff --git a/chart/deps/postgresql/Kptfile b/chart/deps/postgresql/Kptfile deleted file mode 100644 index 250659e6f..000000000 --- a/chart/deps/postgresql/Kptfile +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: kpt.dev/v1alpha1 -kind: Kptfile -metadata: - name: postgresql -upstream: - type: git - git: - commit: c2ac165a579a8f06dede2b6fede2f4ec2bfea495 - repo: https://github.com/bitnami/charts - directory: /bitnami/postgresql - ref: master diff --git a/chart/deps/postgresql/README.md b/chart/deps/postgresql/README.md deleted file mode 100644 index 1ccc0c1d4..000000000 --- a/chart/deps/postgresql/README.md +++ /dev/null @@ -1,770 +0,0 @@ -# PostgreSQL - -[PostgreSQL](https://www.postgresql.org/) is an object-relational database management system (ORDBMS) with an emphasis on extensibility and on standards-compliance. - -For HA, please see [this repo](https://github.com/bitnami/charts/tree/master/bitnami/postgresql-ha) - -## TL;DR - -```console -$ helm repo add bitnami https://charts.bitnami.com/bitnami -$ helm install my-release bitnami/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 3.1.0 -- 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 bitnami/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 but PVC's associated with the chart and deletes the release. - -To delete the PVC's associated with `my-release`: - -```console -$ kubectl delete pvc -l release=my-release -``` - -> **Note**: Deleting the PVC's will delete postgresql data as well. Please be cautious before doing it. - -## 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 common.names.fullname template with a string (will prepend the release name) | `nil` | -| `fullnameOverride` | String to fully override common.names.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.*` | Other container security context to be included as-is in the container spec | `{}` | -| `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 against 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.readReplicas` | Number of read replicas 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.readReplicas`. | `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-password` which is the password for `postgresqlUsername` when it is different of `postgres`, `postgresql-postgres-password` which will override `postgresqlPassword`, `postgresql-replication-password` which will override `replication.password` and `postgresql-ldap-password` which will be used to authenticate on LDAP. The value is evaluated as a template. | `nil` | -| `postgresqlPostgresPassword` | PostgreSQL admin password (used when `postgresqlUsername` is not `postgres`, in which case`postgres` is the admin username). | _random 10 character alphanumeric string_ | -| `postgresqlUsername` | PostgreSQL user (creates a non-admin user when `postgresqlUsername` is not `postgres`) | `postgres` | -| `postgresqlPassword` | PostgreSQL user 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. The value is evaluated as a template. | `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)` | -| `postgresqlSharedPreloadLibraries` | Shared preload libraries (comma-separated list) | `pgaudit` | -| `postgresqlMaxConnections` | Maximum total connections | `nil` | -| `postgresqlPostgresConnectionLimit` | Maximum total connections for the postgres user | `nil` | -| `postgresqlDbUserConnectionLimit` | Maximum total connections for the non-admin user | `nil` | -| `postgresqlTcpKeepalivesInterval` | TCP keepalives interval | `nil` | -| `postgresqlTcpKeepalivesIdle` | TCP keepalives idle | `nil` | -| `postgresqlTcpKeepalivesCount` | TCP keepalives count | `nil` | -| `postgresqlStatementTimeout` | Statement timeout | `nil` | -| `postgresqlPghbaRemoveFilters` | Comma-separated list of patterns to remove from the pg_hba.conf file | `nil` | -| `customStartupProbe` | Override default startup probe | `nil` | -| `customLivenessProbe` | Override default liveness probe | `nil` | -| `customReadinessProbe` | Override default readiness probe | `nil` | -| `audit.logHostname` | Add client hostnames to the log file | `false` | -| `audit.logConnections` | Add client log-in operations to the log file | `false` | -| `audit.logDisconnections` | Add client log-outs operations to the log file | `false` | -| `audit.pgAuditLog` | Add operations to log using the pgAudit extension | `nil` | -| `audit.clientMinMessages` | Message log level to share with the user | `nil` | -| `audit.logLinePrefix` | Template string for the log line prefix | `nil` | -| `audit.logTimezone` | Timezone for the log timestamps | `nil` | -| `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` | -| `initdbUser` | PostgreSQL user to execute the .sql and sql.gz scripts | `nil` | -| `initdbPassword` | Password for the user specified in `initdbUser` | `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 | `{}` (evaluated as a template) | -| `service.loadBalancerIP` | loadBalancerIP if service type is `LoadBalancer` | `nil` | -| `service.loadBalancerSourceRanges` | Address that are allowed when svc is LoadBalancer | `[]` (evaluated as a template) | -| `schedulerName` | Name of the k8s scheduler (other than default) | `nil` | -| `shmVolume.enabled` | Enable emptyDir volume for /dev/shm for primary and read replica(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 | `{}` | -| `persistence.selector` | Selector to match an existing Persistent Volume (this value is evaluated as a template) | `{}` | -| `commonAnnotations` | Annotations to be added to all deployed resources (rendered as a template) | `{}` | -| `primary.podAffinityPreset` | PostgreSQL primary pod affinity preset. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` | `""` | -| `primary.podAntiAffinityPreset` | PostgreSQL primary pod anti-affinity preset. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` | `soft` | -| `primary.nodeAffinityPreset.type` | PostgreSQL primary node affinity preset type. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` | `""` | -| `primary.nodeAffinityPreset.key` | PostgreSQL primary node label key to match Ignored if `primary.affinity` is set. | `""` | -| `primary.nodeAffinityPreset.values` | PostgreSQL primary node label values to match. Ignored if `primary.affinity` is set. | `[]` | -| `primary.affinity` | Affinity for PostgreSQL primary pods assignment | `{}` (evaluated as a template) | -| `primary.nodeSelector` | Node labels for PostgreSQL primary pods assignment | `{}` (evaluated as a template) | -| `primary.tolerations` | Tolerations for PostgreSQL primary pods assignment | `[]` (evaluated as a template) | -| `primary.anotations` | Map of annotations to add to the statefulset (postgresql primary) | `{}` | -| `primary.labels` | Map of labels to add to the statefulset (postgresql primary) | `{}` | -| `primary.podAnnotations` | Map of annotations to add to the pods (postgresql primary) | `{}` | -| `primary.podLabels` | Map of labels to add to the pods (postgresql primary) | `{}` | -| `primary.priorityClassName` | Priority Class to use for each pod (postgresql primary) | `nil` | -| `primary.extraInitContainers` | Additional init containers to add to the pods (postgresql primary) | `[]` | -| `primary.extraVolumeMounts` | Additional volume mounts to add to the pods (postgresql primary) | `[]` | -| `primary.extraVolumes` | Additional volumes to add to the pods (postgresql primary) | `[]` | -| `primary.sidecars` | Add additional containers to the pod | `[]` | -| `primary.service.type` | Allows using a different service type for primary | `nil` | -| `primary.service.nodePort` | Allows using a different nodePort for primary | `nil` | -| `primary.service.clusterIP` | Allows using a different clusterIP for primary | `nil` | -| `primaryAsStandBy.enabled` | Whether to enable current cluster's primary as standby server of another cluster or not. | `false` | -| `primaryAsStandBy.primaryHost` | The Host of replication primary in the other cluster. | `nil` | -| `primaryAsStandBy.primaryPort ` | The Port of replication primary in the other cluster. | `nil` | -| `readReplicas.podAffinityPreset` | PostgreSQL read only pod affinity preset. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` | `""` | -| `readReplicas.podAntiAffinityPreset` | PostgreSQL read only pod anti-affinity preset. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` | `soft` | -| `readReplicas.nodeAffinityPreset.type` | PostgreSQL read only node affinity preset type. Ignored if `primary.affinity` is set. Allowed values: `soft` or `hard` | `""` | -| `readReplicas.nodeAffinityPreset.key` | PostgreSQL read only node label key to match Ignored if `primary.affinity` is set. | `""` | -| `readReplicas.nodeAffinityPreset.values` | PostgreSQL read only node label values to match. Ignored if `primary.affinity` is set. | `[]` | -| `readReplicas.affinity` | Affinity for PostgreSQL read only pods assignment | `{}` (evaluated as a template) | -| `readReplicas.nodeSelector` | Node labels for PostgreSQL read only pods assignment | `{}` (evaluated as a template) | -| `readReplicas.anotations` | Map of annotations to add to the statefulsets (postgresql readReplicas) | `{}` | -| `readReplicas.resources` | CPU/Memory resource requests/limits override for readReplicass. Will fallback to `values.resources` if not defined. | `{}` | -| `readReplicas.labels` | Map of labels to add to the statefulsets (postgresql readReplicas) | `{}` | -| `readReplicas.podAnnotations` | Map of annotations to add to the pods (postgresql readReplicas) | `{}` | -| `readReplicas.podLabels` | Map of labels to add to the pods (postgresql readReplicas) | `{}` | -| `readReplicas.priorityClassName` | Priority Class to use for each pod (postgresql readReplicas) | `nil` | -| `readReplicas.extraInitContainers` | Additional init containers to add to the pods (postgresql readReplicas) | `[]` | -| `readReplicas.extraVolumeMounts` | Additional volume mounts to add to the pods (postgresql readReplicas) | `[]` | -| `readReplicas.extraVolumes` | Additional volumes to add to the pods (postgresql readReplicas) | `[]` | -| `readReplicas.sidecars` | Add additional containers to the pod | `[]` | -| `readReplicas.service.type` | Allows using a different service type for readReplicas | `nil` | -| `readReplicas.service.nodePort` | Allows using a different nodePort for readReplicas | `nil` | -| `readReplicas.service.clusterIP` | Allows using a different clusterIP for readReplicas | `nil` | -| `readReplicas.persistence.enabled` | Whether to enable readReplicas replicas persistence | `true` | -| `terminationGracePeriodSeconds` | Seconds the pod needs to terminate gracefully | `nil` | -| `resources` | CPU/Memory resource requests/limits | Memory: `256Mi`, CPU: `250m` | -| `securityContext.*` | Other pod security context to be included as-is in the pod spec | `{}` | -| `securityContext.enabled` | Enable security context | `true` | -| `securityContext.fsGroup` | Group ID for the pod | `1001` | -| `containerSecurityContext.*` | Other container security context to be included as-is in the container spec | `{}` | -| `containerSecurityContext.enabled` | Enable container security context | `true` | -| `containerSecurityContext.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` | -| `serviceAccount.name` | Name of existing service account | `nil` | -| `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 | `{}` | -| `startupProbe.enabled` | Enable startupProbe | `false` | -| `startupProbe.initialDelaySeconds` | Delay before liveness probe is initiated | 30 | -| `startupProbe.periodSeconds` | How often to perform the probe | 15 | -| `startupProbe.timeoutSeconds` | When the probe times | 5 | -| `startupProbe.failureThreshold` | Minimum consecutive failures for the probe to be considered failed after having succeeded. | 10 | -| `startupProbe.successThreshold` | Minimum consecutive successes for the probe to be considered successful after having failed. | 1 | -| `livenessProbe.enabled` | Enable livenessProbe | `true` | -| `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` | Enable readinessProbe | `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 | -| `tls.enabled` | Enable TLS traffic support | `false` | -| `tls.preferServerCiphers` | Whether to use the server's TLS cipher preferences rather than the client's | `true` | -| `tls.certificatesSecret` | Name of an existing secret that contains the certificates | `nil` | -| `tls.certFilename` | Certificate filename | `""` | -| `tls.certKeyFilename` | Certificate key filename | `""` | -| `tls.certCAFilename` | CA Certificate filename. If provided, PostgreSQL will authenticate TLS/SSL clients by requesting them a certificate. | `nil` | -| `tls.crlFilename` | File containing a Certificate Revocation List | `nil` | -| `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 Exporter Image registry | `docker.io` | -| `metrics.image.repository` | PostgreSQL Exporter Image name | `bitnami/postgres-exporter` | -| `metrics.image.tag` | PostgreSQL Exporter Image tag | `{TAG_NAME}` | -| `metrics.image.pullPolicy` | PostgreSQL Exporter 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.extraEnvVars` | Extra environment variables to add to exporter | `{}` (evaluated as a template) | -| `metrics.securityContext.*` | Other container security context to be included as-is in the container spec | `{}` | -| `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"}` | -| `psp.create` | Create Pod Security Policy | `false` | -| `rbac.create` | Create Role and RoleBinding (required for PSP to work) | `false` | -| `extraDeploy` | Array of extra objects to deploy with the release (evaluated as a template). | `nil` | - -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 \ - bitnami/postgresql -``` - -The above command sets the PostgreSQL `postgres` account password to `secretpassword`. Additionally it creates a database named `my-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 bitnami/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. - -### Customizing primary and read replica services in a replicated configuration - -At the top level, there is a service object which defines the services for both primary and readReplicas. For deeper customization, there are service objects for both the primary and read types individually. This allows you to override the values in the top level service object so that the primary and read can be of different service types and with different clusterIPs / nodePorts. Also in the case you want the primary and read to be of type nodePort, you will need to set the nodePorts to different values to prevent a collision. The values that are deeper in the primary.service or readReplicas.service objects will take precedence over the top level service object. - -### 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=X.Y.Z`. This approach is also applicable to other images like exporters. - -### 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 add additional PostgreSQL configuration parameters using the `postgresqlExtendedConf` parameter as a dict, using camelCase, e.g. {"sharedBuffers": "500MB"}. Alternatively, to replace the entire default configuration use `postgresqlConfiguration`. - -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`. - -### Securing traffic using TLS - -TLS support can be enabled in the chart by specifying the `tls.` parameters while creating a release. The following parameters should be configured to properly enable the TLS support in the chart: - -- `tls.enabled`: Enable TLS support. Defaults to `false` -- `tls.certificatesSecret`: Name of an existing secret that contains the certificates. No defaults. -- `tls.certFilename`: Certificate filename. No defaults. -- `tls.certKeyFilename`: Certificate key filename. No defaults. - -For example: - -* First, create the secret with the cetificates files: - - ```console - kubectl create secret generic certificates-tls-secret --from-file=./cert.crt --from-file=./cert.key --from-file=./ca.crt - ``` - -* Then, use the following parameters: - - ```console - volumePermissions.enabled=true - tls.enabled=true - tls.certificatesSecret="certificates-tls-secret" - tls.certFilename="cert.crt" - tls.certKeyFilename="cert.key" - ``` - - > Note TLS and VolumePermissions: PostgreSQL requires certain permissions on sensitive files (such as certificate keys) to start up. Due to an on-going [issue](https://github.com/kubernetes/kubernetes/issues/57923) regarding kubernetes permissions and the use of `containerSecurityContext.runAsUser`, you must enable `volumePermissions` to ensure everything works as expected. - -### 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 primary -primary: - sidecars: - - name: your-image-name - image: your-image - imagePullPolicy: Always - ports: - - name: portname - containerPort: 1234 -# For the PostgreSQL replicas -readReplicas: - 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,containerSecurityContext.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. - -``` -image.repository=postgres -image.tag=10.6 -postgresqlDataDir=/data/pgdata -persistence.mountPath=/data/ -``` - -### Setting Pod's affinity - -This chart allows you to set your custom affinity using the `XXX.affinity` paremeter(s). Find more infomation about Pod's affinity in the [kubernetes documentation](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity). - -As an alternative, you can use of the preset configurations for pod affinity, pod anti-affinity, and node affinity available at the [bitnami/common](https://github.com/bitnami/charts/tree/master/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 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 bitnami/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. - -### To 10.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. -- Move dependency information from the *requirements.yaml* to the *Chart.yaml* -- After running `helm dependency update`, a *Chart.lock* file is generated containing the same structure used in the previous *requirements.lock* -- The different fields present in the *Chart.yaml* file has been ordered alphabetically in a homogeneous way for all the Bitnami Helm Chart. - -**Considerations when upgrading to this version** - -- 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** - -- https://docs.bitnami.com/tutorials/resolve-helm2-helm3-post-migration-issues/ -- https://helm.sh/docs/topics/v2_v3_migration/ -- https://helm.sh/blog/migrate-from-helm-v2-to-helm-v3/ - -#### Breaking changes - -- The term `master` has been replaced with `primary` and `slave` with `readReplicas` throughout the chart. Role names have changed from `master` and `slave` to `primary` and `read`. - -To upgrade to `10.0.0`, it should be done reusing the PVCs used to hold the PostgreSQL data on your previous release. To do so, follow the instructions below (the following example assumes that the release name is `postgresql`): - -> NOTE: Please, create a backup of your database before running any of those actions. - -Obtain the credentials and the names of the PVCs used to hold the PostgreSQL data on your current release: - -```console -$ export POSTGRESQL_PASSWORD=$(kubectl get secret --namespace default postgresql -o jsonpath="{.data.postgresql-password}" | base64 --decode) -$ export POSTGRESQL_PVC=$(kubectl get pvc -l app.kubernetes.io/instance=postgresql,role=master -o jsonpath="{.items[0].metadata.name}") -``` - -Delete the PostgreSQL statefulset. Notice the option `--cascade=false`: - -```console -$ kubectl delete statefulsets.apps postgresql-postgresql --cascade=false -``` - -Now the upgrade works: - -```console -$ helm upgrade postgresql bitnami/postgresql --set postgresqlPassword=$POSTGRESQL_PASSWORD --set persistence.existingClaim=$POSTGRESQL_PVC -``` - -You will have to delete the existing PostgreSQL pod and the new statefulset is going to create a new one - -```console -$ kubectl delete pod postgresql-postgresql-0 -``` - -Finally, you should see the lines below in PostgreSQL container logs: - -```console -$ kubectl logs $(kubectl get pods -l app.kubernetes.io/instance=postgresql,app.kubernetes.io/name=postgresql,role=primary -o jsonpath="{.items[0].metadata.name}") -... -postgresql 08:05:12.59 INFO ==> Deploying PostgreSQL with persisted data... -... -``` - -### To 9.0.0 - -In this version the chart was adapted to follow the Helm label best practices, see [PR 3021](https://github.com/bitnami/charts/pull/3021). That means the backward compatibility is not guarantee when upgrading the chart to this major version. - -As a workaround, you can delete the existing statefulset (using the `--cascade=false` flag pods are not deleted) before upgrade the chart. For example, this can be a valid workflow: - -- Deploy an old version (8.X.X) - -```console -$ helm install postgresql bitnami/postgresql --version 8.10.14 -``` - -- Old version is up and running - -```console -$ helm ls -NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION -postgresql default 1 2020-08-04 13:39:54.783480286 +0000 UTC deployed postgresql-8.10.14 11.8.0 - -$ kubectl get pods -NAME READY STATUS RESTARTS AGE -postgresql-postgresql-0 1/1 Running 0 76s -``` - -- The upgrade to the latest one (9.X.X) is going to fail - -```console -$ helm upgrade postgresql bitnami/postgresql -Error: UPGRADE FAILED: cannot patch "postgresql-postgresql" with kind StatefulSet: StatefulSet.apps "postgresql-postgresql" is invalid: spec: Forbidden: updates to statefulset spec for fields other than 'replicas', 'template', and 'updateStrategy' are forbidden -``` - -- Delete the statefulset - -```console -$ kubectl delete statefulsets.apps --cascade=false postgresql-postgresql -statefulset.apps "postgresql-postgresql" deleted -``` - -- Now the upgrade works - -```console -$ helm upgrade postgresql bitnami/postgresql -$ helm ls -NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION -postgresql default 3 2020-08-04 13:42:08.020385884 +0000 UTC deployed postgresql-9.1.2 11.8.0 -``` - -- We can kill the existing pod and the new statefulset is going to create a new one: - -```console -$ kubectl delete pod postgresql-postgresql-0 -pod "postgresql-postgresql-0" deleted - -$ kubectl get pods -NAME READY STATUS RESTARTS AGE -postgresql-postgresql-0 1/1 Running 0 19s -``` - -Please, note that without the `--cascade=false` both objects (statefulset and pod) are going to be removed and both objects will be deployed again with the `helm upgrade` command - -### To 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. - -### To 7.1.0 - -Adds support for LDAP configuration. - -### To 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. - -### To 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 - -### To 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: - -```console -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. - -### To 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 -``` - -### To 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`. - -### To 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 bitnami/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/chart/deps/postgresql/ci/commonAnnotations.yaml b/chart/deps/postgresql/ci/commonAnnotations.yaml deleted file mode 100644 index 97e18a4cc..000000000 --- a/chart/deps/postgresql/ci/commonAnnotations.yaml +++ /dev/null @@ -1,3 +0,0 @@ -commonAnnotations: - helm.sh/hook: "\"pre-install, pre-upgrade\"" - helm.sh/hook-weight: "-1" diff --git a/chart/deps/postgresql/ci/default-values.yaml b/chart/deps/postgresql/ci/default-values.yaml deleted file mode 100644 index fc2ba605a..000000000 --- a/chart/deps/postgresql/ci/default-values.yaml +++ /dev/null @@ -1 +0,0 @@ -# Leave this file empty to ensure that CI runs builds against the default configuration in values.yaml. diff --git a/chart/deps/postgresql/ci/shmvolume-disabled-values.yaml b/chart/deps/postgresql/ci/shmvolume-disabled-values.yaml deleted file mode 100644 index 347d3b40a..000000000 --- a/chart/deps/postgresql/ci/shmvolume-disabled-values.yaml +++ /dev/null @@ -1,2 +0,0 @@ -shmVolume: - enabled: false diff --git a/chart/deps/postgresql/files/README.md b/chart/deps/postgresql/files/README.md deleted file mode 100644 index 1813a2fea..000000000 --- a/chart/deps/postgresql/files/README.md +++ /dev/null @@ -1 +0,0 @@ -Copy here your postgresql.conf and/or pg_hba.conf files to use it as a config map. diff --git a/chart/deps/postgresql/files/conf.d/README.md b/chart/deps/postgresql/files/conf.d/README.md deleted file mode 100644 index 184c1875d..000000000 --- a/chart/deps/postgresql/files/conf.d/README.md +++ /dev/null @@ -1,4 +0,0 @@ -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/chart/deps/postgresql/files/docker-entrypoint-initdb.d/README.md b/chart/deps/postgresql/files/docker-entrypoint-initdb.d/README.md deleted file mode 100644 index cba38091e..000000000 --- a/chart/deps/postgresql/files/docker-entrypoint-initdb.d/README.md +++ /dev/null @@ -1,3 +0,0 @@ -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/chart/deps/postgresql/templates/NOTES.txt b/chart/deps/postgresql/templates/NOTES.txt deleted file mode 100644 index 4e98958c1..000000000 --- a/chart/deps/postgresql/templates/NOTES.txt +++ /dev/null @@ -1,59 +0,0 @@ -** 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 "common.names.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local - Read/Write connection -{{- if .Values.replication.enabled }} - {{ template "common.names.fullname" . }}-read.{{ .Release.Namespace }}.svc.cluster.local - Read only connection -{{- end }} - -{{- if not (eq (include "postgresql.username" .) "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 "common.names.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 "common.names.fullname" . }}-client=true" {{- end }} --command -- psql --host {{ template "common.names.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 "common.names.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 "common.names.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 "common.names.fullname" . }}' - - export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ template "common.names.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 "common.names.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" . -}} - -{{- include "common.warnings.rollingTag" .Values.image -}} - -{{- $passwordValidationErrors := include "common.validations.values.postgresql.passwords" (dict "secret" (include "common.names.fullname" .) "context" $) -}} - -{{- include "common.errors.upgrade.passwords.empty" (dict "validationErrors" (list $passwordValidationErrors) "context" $) -}} diff --git a/chart/deps/postgresql/templates/_helpers.tpl b/chart/deps/postgresql/templates/_helpers.tpl deleted file mode 100644 index 25d2072d4..000000000 --- a/chart/deps/postgresql/templates/_helpers.tpl +++ /dev/null @@ -1,919 +0,0 @@ -{{/* 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 -}} - -{{/* -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 -}} - -{{/* -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 -}} - -{{/* -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 -}} - -{{/* -Return the proper image name -{{ include "common.images.image" ( dict "imageRoot" .Values.path.to.the.image "global" $) }} -*/}} -{{- define "common.images.image" -}} -{{- $registryName := .imageRoot.registry -}} -{{- $repositoryName := .imageRoot.repository -}} -{{- $tag := .imageRoot.tag | toString -}} -{{- if .global }} - {{- if .global.imageRegistry }} - {{- $registryName = .global.imageRegistry -}} - {{- end -}} -{{- end -}} -{{- if $registryName }} -{{- printf "%s/%s:%s" $registryName $repositoryName $tag -}} -{{- else -}} -{{- printf "%s:%s" $repositoryName $tag -}} -{{- end -}} -{{- end -}} - -{{/* -Return the proper Docker Image Registry Secret Names -{{ 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 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 }} - - {{ . }} - {{- 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 }} - - {{ . }} - {{- 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 soft podAffinity/podAntiAffinity definition -{{ include "common.affinities.pods.soft" (dict "component" "FOO" "context" $) -}} -*/}} -{{- define "common.affinities.pods.soft" -}} -{{- $component := default "" .component -}} -preferredDuringSchedulingIgnoredDuringExecution: - - podAffinityTerm: - labelSelector: - matchLabels: {{- (include "common.labels.matchLabels" .context) | nindent 10 }} - {{- if not (empty $component) }} - {{ printf "app.kubernetes.io/component: %s" $component }} - {{- end }} - namespaces: - - {{ .context.Release.Namespace | quote }} - topologyKey: kubernetes.io/hostname - weight: 1 -{{- end -}} - -{{/* -Return a hard podAffinity/podAntiAffinity definition -{{ include "common.affinities.pods.hard" (dict "component" "FOO" "context" $) -}} -*/}} -{{- define "common.affinities.pods.hard" -}} -{{- $component := default "" .component -}} -requiredDuringSchedulingIgnoredDuringExecution: - - labelSelector: - matchLabels: {{- (include "common.labels.matchLabels" .context) | nindent 8 }} - {{- if not (empty $component) }} - {{ printf "app.kubernetes.io/component: %s" $component }} - {{- end }} - namespaces: - - {{ .context.Release.Namespace | quote }} - topologyKey: kubernetes.io/hostname -{{- end -}} - -{{/* -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 -}} - -{{/* -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 -}} - -{{/* -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 -}} - -{{/* -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 (not $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 -}} - -{{/* -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 -}} - -{{/* -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 {{ .context.Release.Namespace | quote }} {{ .secret }} -o jsonpath="{.data.{{ .field }}}" | base64 --decode) -{{- 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 -}} - -{{/* -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 -}} - -{{/* -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 upgrade the release%s" -}} - {{- printf $errorString $validationErrors | fail -}} - {{- end -}} -{{- end -}} - -{{/* -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 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 -}} - -{{/* -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 -}} - -{{/* -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.primary.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 "primary" | trunc 63 | trimSuffix "-" -}} -{{- else -}} -{{- printf "%s" $fullname | trunc 63 | trimSuffix "-" -}} -{{- end -}} -{{- end -}} - -{{/* -Return the proper PostgreSQL image name -*/}} -{{- define "postgresql.image" -}} -{{ include "common.images.image" (dict "imageRoot" .Values.image "global" .Values.global) }} -{{- end -}} - -{{/* -Return the proper PostgreSQL metrics image name -*/}} -{{- define "postgresql.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 "postgresql.volumePermissions.image" -}} -{{ include "common.images.image" (dict "imageRoot" .Values.volumePermissions.image "global" .Values.global) }} -{{- end -}} - -{{/* -Return the proper Docker Image Registry Secret Names -*/}} -{{- define "postgresql.imagePullSecrets" -}} -{{ include "common.images.pullSecrets" (dict "images" (list .Values.image .Values.metrics.image .Values.volumePermissions.image) "global" .Values.global) }} -{{- 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 -}} - -{{/* -Get the password secret. -*/}} -{{- define "postgresql.secretName" -}} -{{- if .Values.global.postgresql.existingSecret }} - {{- printf "%s" (tpl .Values.global.postgresql.existingSecret $) -}} -{{- else if .Values.existingSecret -}} - {{- printf "%s" (tpl .Values.existingSecret $) -}} -{{- else -}} - {{- printf "%s" (include "common.names.fullname" .) -}} -{{- end -}} -{{- end -}} - -{{/* -Return true if we should use an existingSecret. -*/}} -{{- define "postgresql.useExistingSecret" -}} -{{- if or .Values.global.postgresql.existingSecret .Values.existingSecret -}} - {{- true -}} -{{- end -}} -{{- end -}} - -{{/* -Return true if a secret object should be created -*/}} -{{- define "postgresql.createSecret" -}} -{{- if not (include "postgresql.useExistingSecret" .) -}} - {{- true -}} -{{- end -}} -{{- end -}} - -{{/* -Get the configuration ConfigMap name. -*/}} -{{- define "postgresql.configurationCM" -}} -{{- if .Values.configurationConfigMap -}} -{{- printf "%s" (tpl .Values.configurationConfigMap $) -}} -{{- else -}} -{{- printf "%s-configuration" (include "common.names.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 "common.names.fullname" .) -}} -{{- end -}} -{{- end -}} - -{{/* -Return true if a configmap should be mounted with PostgreSQL configuration -*/}} -{{- define "postgresql.mountConfigurationCM" -}} -{{- if or (.Files.Glob "files/postgresql.conf") (.Files.Glob "files/pg_hba.conf") .Values.postgresqlConfiguration .Values.pgHbaConfiguration .Values.configurationConfigMap }} - {{- true -}} -{{- 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 "common.names.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 "common.names.fullname" .) -}} -{{- end -}} - -{{/* -Get the readiness probe command -*/}} -{{- define "postgresql.readinessProbeCommand" -}} -- | -{{- if (include "postgresql.database" .) }} - exec pg_isready -U {{ include "postgresql.username" . | quote }} -d "dbname={{ include "postgresql.database" . }} {{- if and .Values.tls.enabled .Values.tls.certCAFilename }} sslcert={{ include "postgresql.tlsCert" . }} sslkey={{ include "postgresql.tlsCertKey" . }}{{- end }}" -h 127.0.0.1 -p {{ template "postgresql.port" . }} -{{- else }} - exec pg_isready -U {{ include "postgresql.username" . | quote }} {{- if and .Values.tls.enabled .Values.tls.certCAFilename }} -d "sslcert={{ include "postgresql.tlsCert" . }} sslkey={{ include "postgresql.tlsCertKey" . }}"{{- end }} -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 -}} - -{{/* -Compile all warnings into a single message, and call fail. -*/}} -{{- define "postgresql.validateValues" -}} -{{- $messages := list -}} -{{- $messages := append $messages (include "postgresql.validateValues.ldapConfigurationMethod" .) -}} -{{- $messages := append $messages (include "postgresql.validateValues.psp" .) -}} -{{- $messages := append $messages (include "postgresql.validateValues.tls" .) -}} -{{- $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 -}} - -{{/* -Validate values of Postgresql - If PSP is enabled RBAC should be enabled too -*/}} -{{- define "postgresql.validateValues.psp" -}} -{{- if and .Values.psp.create (not .Values.rbac.create) }} -postgresql: psp.create, rbac.create - RBAC should be enabled if PSP is enabled in order for PSP to work. - More info at https://kubernetes.io/docs/concepts/policy/pod-security-policy/#authorizing-policies -{{- end -}} -{{- end -}} - -{{/* -Return the appropriate apiVersion for podsecuritypolicy. -*/}} -{{- define "podsecuritypolicy.apiVersion" -}} -{{- if semverCompare "<1.10-0" .Capabilities.KubeVersion.GitVersion -}} -{{- print "extensions/v1beta1" -}} -{{- else -}} -{{- print "policy/v1beta1" -}} -{{- 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 -}} - -{{/* -Validate values of Postgresql TLS - When TLS is enabled, so must be VolumePermissions -*/}} -{{- define "postgresql.validateValues.tls" -}} -{{- if and .Values.tls.enabled (not .Values.volumePermissions.enabled) }} -postgresql: tls.enabled, volumePermissions.enabled - When TLS is enabled you must enable volumePermissions as well to ensure certificates files have - the right permissions. -{{- end -}} -{{- end -}} - -{{/* -Return the path to the cert file. -*/}} -{{- define "postgresql.tlsCert" -}} -{{- required "Certificate filename is required when TLS in enabled" .Values.tls.certFilename | printf "/opt/bitnami/postgresql/certs/%s" -}} -{{- end -}} - -{{/* -Return the path to the cert key file. -*/}} -{{- define "postgresql.tlsCertKey" -}} -{{- required "Certificate Key filename is required when TLS in enabled" .Values.tls.certKeyFilename | printf "/opt/bitnami/postgresql/certs/%s" -}} -{{- end -}} - -{{/* -Return the path to the CA cert file. -*/}} -{{- define "postgresql.tlsCACert" -}} -{{- printf "/opt/bitnami/postgresql/certs/%s" .Values.tls.certCAFilename -}} -{{- end -}} - -{{/* -Return the path to the CRL file. -*/}} -{{- define "postgresql.tlsCRL" -}} -{{- if .Values.tls.crlFilename -}} -{{- printf "/opt/bitnami/postgresql/certs/%s" .Values.tls.crlFilename -}} -{{- end -}} -{{- end -}} diff --git a/chart/deps/postgresql/templates/configmap.yaml b/chart/deps/postgresql/templates/configmap.yaml deleted file mode 100644 index 627d88027..000000000 --- a/chart/deps/postgresql/templates/configmap.yaml +++ /dev/null @@ -1,27 +0,0 @@ -{{ 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 "common.names.fullname" . }}-configuration - labels: - {{- include "common.labels.standard" . | nindent 4 }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} - namespace: {{ .Release.Namespace }} -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/chart/deps/postgresql/templates/extended-config-configmap.yaml b/chart/deps/postgresql/templates/extended-config-configmap.yaml deleted file mode 100644 index bd477e44c..000000000 --- a/chart/deps/postgresql/templates/extended-config-configmap.yaml +++ /dev/null @@ -1,22 +0,0 @@ -{{- if and (or (.Files.Glob "files/conf.d/*.conf") .Values.postgresqlExtendedConf) (not .Values.extendedConfConfigMap)}} -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ template "common.names.fullname" . }}-extended-configuration - labels: - {{- include "common.labels.standard" . | nindent 4 }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} - namespace: {{ .Release.Namespace }} -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/chart/deps/postgresql/templates/extra-list.yaml b/chart/deps/postgresql/templates/extra-list.yaml deleted file mode 100644 index 9ac65f9e1..000000000 --- a/chart/deps/postgresql/templates/extra-list.yaml +++ /dev/null @@ -1,4 +0,0 @@ -{{- range .Values.extraDeploy }} ---- -{{ include "common.tplvalues.render" (dict "value" . "context" $) }} -{{- end }} diff --git a/chart/deps/postgresql/templates/initialization-configmap.yaml b/chart/deps/postgresql/templates/initialization-configmap.yaml deleted file mode 100644 index 7796c67a9..000000000 --- a/chart/deps/postgresql/templates/initialization-configmap.yaml +++ /dev/null @@ -1,25 +0,0 @@ -{{- 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 "common.names.fullname" . }}-init-scripts - labels: - {{- include "common.labels.standard" . | nindent 4 }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} - namespace: {{ .Release.Namespace }} -{{- 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/chart/deps/postgresql/templates/metrics-configmap.yaml b/chart/deps/postgresql/templates/metrics-configmap.yaml deleted file mode 100644 index fa539582b..000000000 --- a/chart/deps/postgresql/templates/metrics-configmap.yaml +++ /dev/null @@ -1,14 +0,0 @@ -{{- if and .Values.metrics.enabled .Values.metrics.customMetrics }} -apiVersion: v1 -kind: ConfigMap -metadata: - name: {{ template "postgresql.metricsCM" . }} - labels: - {{- include "common.labels.standard" . | nindent 4 }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} - namespace: {{ .Release.Namespace }} -data: - custom-metrics.yaml: {{ toYaml .Values.metrics.customMetrics | quote }} -{{- end }} diff --git a/chart/deps/postgresql/templates/metrics-svc.yaml b/chart/deps/postgresql/templates/metrics-svc.yaml deleted file mode 100644 index af8b67e2f..000000000 --- a/chart/deps/postgresql/templates/metrics-svc.yaml +++ /dev/null @@ -1,26 +0,0 @@ -{{- if .Values.metrics.enabled }} -apiVersion: v1 -kind: Service -metadata: - name: {{ template "common.names.fullname" . }}-metrics - labels: - {{- include "common.labels.standard" . | nindent 4 }} - annotations: - {{- if .Values.commonAnnotations }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} - {{- toYaml .Values.metrics.service.annotations | nindent 4 }} - namespace: {{ .Release.Namespace }} -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: - {{- include "common.labels.matchLabels" . | nindent 4 }} - role: primary -{{- end }} diff --git a/chart/deps/postgresql/templates/networkpolicy.yaml b/chart/deps/postgresql/templates/networkpolicy.yaml deleted file mode 100644 index 4f2740ea0..000000000 --- a/chart/deps/postgresql/templates/networkpolicy.yaml +++ /dev/null @@ -1,39 +0,0 @@ -{{- if .Values.networkPolicy.enabled }} -kind: NetworkPolicy -apiVersion: {{ template "postgresql.networkPolicy.apiVersion" . }} -metadata: - name: {{ template "common.names.fullname" . }} - labels: - {{- include "common.labels.standard" . | nindent 4 }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} - namespace: {{ .Release.Namespace }} -spec: - podSelector: - matchLabels: - {{- include "common.labels.matchLabels" . | nindent 6 }} - ingress: - # Allow inbound connections - - ports: - - port: {{ template "postgresql.port" . }} - {{- 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 }} - role: read - {{- end }} - {{- if .Values.metrics.enabled }} - # Allow prometheus scrapes - - ports: - - port: 9187 - {{- end }} -{{- end }} diff --git a/chart/deps/postgresql/templates/podsecuritypolicy.yaml b/chart/deps/postgresql/templates/podsecuritypolicy.yaml deleted file mode 100644 index 0c49694fa..000000000 --- a/chart/deps/postgresql/templates/podsecuritypolicy.yaml +++ /dev/null @@ -1,38 +0,0 @@ -{{- if .Values.psp.create }} -apiVersion: {{ include "podsecuritypolicy.apiVersion" . }} -kind: PodSecurityPolicy -metadata: - name: {{ template "common.names.fullname" . }} - labels: - {{- include "common.labels.standard" . | nindent 4 }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} - namespace: {{ .Release.Namespace }} -spec: - privileged: false - volumes: - - 'configMap' - - 'secret' - - 'persistentVolumeClaim' - - 'emptyDir' - - 'projected' - hostNetwork: false - hostIPC: false - hostPID: false - runAsUser: - rule: 'RunAsAny' - seLinux: - rule: 'RunAsAny' - supplementalGroups: - rule: 'MustRunAs' - ranges: - - min: 1 - max: 65535 - fsGroup: - rule: 'MustRunAs' - ranges: - - min: 1 - max: 65535 - readOnlyRootFilesystem: false -{{- end }} diff --git a/chart/deps/postgresql/templates/prometheusrule.yaml b/chart/deps/postgresql/templates/prometheusrule.yaml deleted file mode 100644 index d0f408c78..000000000 --- a/chart/deps/postgresql/templates/prometheusrule.yaml +++ /dev/null @@ -1,23 +0,0 @@ -{{- if and .Values.metrics.enabled .Values.metrics.prometheusRule.enabled }} -apiVersion: monitoring.coreos.com/v1 -kind: PrometheusRule -metadata: - name: {{ template "common.names.fullname" . }} -{{- with .Values.metrics.prometheusRule.namespace }} - namespace: {{ . }} -{{- end }} - labels: - {{- include "common.labels.standard" . | nindent 4 }} - {{- with .Values.metrics.prometheusRule.additionalLabels }} - {{- toYaml . | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} -spec: -{{- with .Values.metrics.prometheusRule.rules }} - groups: - - name: {{ template "postgresql.name" $ }} - rules: {{ tpl (toYaml .) $ | nindent 8 }} -{{- end }} -{{- end }} diff --git a/chart/deps/postgresql/templates/role.yaml b/chart/deps/postgresql/templates/role.yaml deleted file mode 100644 index 017a5716b..000000000 --- a/chart/deps/postgresql/templates/role.yaml +++ /dev/null @@ -1,20 +0,0 @@ -{{- if .Values.rbac.create }} -kind: Role -apiVersion: {{ include "common.capabilities.rbac.apiVersion" . }} -metadata: - name: {{ template "common.names.fullname" . }} - labels: - {{- include "common.labels.standard" . | nindent 4 }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} - namespace: {{ .Release.Namespace }} -rules: - {{- if .Values.psp.create }} - - apiGroups: ["extensions"] - resources: ["podsecuritypolicies"] - verbs: ["use"] - resourceNames: - - {{ template "common.names.fullname" . }} - {{- end }} -{{- end }} diff --git a/chart/deps/postgresql/templates/rolebinding.yaml b/chart/deps/postgresql/templates/rolebinding.yaml deleted file mode 100644 index 189775a15..000000000 --- a/chart/deps/postgresql/templates/rolebinding.yaml +++ /dev/null @@ -1,20 +0,0 @@ -{{- if .Values.rbac.create }} -kind: RoleBinding -apiVersion: {{ include "common.capabilities.rbac.apiVersion" . }} -metadata: - name: {{ template "common.names.fullname" . }} - labels: - {{- include "common.labels.standard" . | nindent 4 }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} - namespace: {{ .Release.Namespace }} -roleRef: - kind: Role - name: {{ template "common.names.fullname" . }} - apiGroup: rbac.authorization.k8s.io -subjects: - - kind: ServiceAccount - name: {{ default (include "common.names.fullname" . ) .Values.serviceAccount.name }} - namespace: {{ .Release.Namespace }} -{{- end }} diff --git a/chart/deps/postgresql/templates/secrets.yaml b/chart/deps/postgresql/templates/secrets.yaml deleted file mode 100644 index dc301daba..000000000 --- a/chart/deps/postgresql/templates/secrets.yaml +++ /dev/null @@ -1,22 +0,0 @@ -{{- if (include "postgresql.createSecret" .) }} -apiVersion: v1 -kind: Secret -metadata: - name: {{ template "common.names.fullname" . }} - labels: - {{- include "common.labels.standard" . | nindent 4 }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} - namespace: {{ .Release.Namespace }} -type: Opaque -data: - postgresql-postgres-password: {{ include "postgresql.postgres.password" . | b64enc | quote }} - 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/chart/deps/postgresql/templates/serviceaccount.yaml b/chart/deps/postgresql/templates/serviceaccount.yaml deleted file mode 100644 index 03f0f50e7..000000000 --- a/chart/deps/postgresql/templates/serviceaccount.yaml +++ /dev/null @@ -1,12 +0,0 @@ -{{- if and (.Values.serviceAccount.enabled) (not .Values.serviceAccount.name) }} -apiVersion: v1 -kind: ServiceAccount -metadata: - labels: - {{- include "common.labels.standard" . | nindent 4 }} - name: {{ template "common.names.fullname" . }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} - namespace: {{ .Release.Namespace }} -{{- end }} diff --git a/chart/deps/postgresql/templates/servicemonitor.yaml b/chart/deps/postgresql/templates/servicemonitor.yaml deleted file mode 100644 index 587ce85b8..000000000 --- a/chart/deps/postgresql/templates/servicemonitor.yaml +++ /dev/null @@ -1,33 +0,0 @@ -{{- if and .Values.metrics.enabled .Values.metrics.serviceMonitor.enabled }} -apiVersion: monitoring.coreos.com/v1 -kind: ServiceMonitor -metadata: - name: {{ include "common.names.fullname" . }} - {{- if .Values.metrics.serviceMonitor.namespace }} - namespace: {{ .Values.metrics.serviceMonitor.namespace }} - {{- end }} - labels: - {{- include "common.labels.standard" . | nindent 4 }} - {{- if .Values.metrics.serviceMonitor.additionalLabels }} - {{- toYaml .Values.metrics.serviceMonitor.additionalLabels | nindent 4 }} - {{- end }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 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: - {{- include "common.labels.matchLabels" . | nindent 6 }} -{{- end }} diff --git a/chart/deps/postgresql/templates/statefulset-readreplicas.yaml b/chart/deps/postgresql/templates/statefulset-readreplicas.yaml deleted file mode 100644 index 7e93d6568..000000000 --- a/chart/deps/postgresql/templates/statefulset-readreplicas.yaml +++ /dev/null @@ -1,411 +0,0 @@ -{{- if .Values.replication.enabled }} -{{- $readReplicasResources := coalesce .Values.readReplicas.resources .Values.resources -}} -apiVersion: {{ include "common.capabilities.statefulset.apiVersion" . }} -kind: StatefulSet -metadata: - name: "{{ template "common.names.fullname" . }}-read" - labels: {{- include "common.labels.standard" . | nindent 4 }} - app.kubernetes.io/component: read -{{- with .Values.readReplicas.labels }} -{{ toYaml . | indent 4 }} -{{- end }} - annotations: - {{- if .Values.commonAnnotations }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} - {{- with .Values.readReplicas.annotations }} - {{- toYaml . | nindent 4 }} - {{- end }} - namespace: {{ .Release.Namespace }} -spec: - serviceName: {{ template "common.names.fullname" . }}-headless - replicas: {{ .Values.replication.readReplicas }} - selector: - matchLabels: - {{- include "common.labels.matchLabels" . | nindent 6 }} - role: read - template: - metadata: - name: {{ template "common.names.fullname" . }} - labels: - {{- include "common.labels.standard" . | nindent 8 }} - app.kubernetes.io/component: read - role: read -{{- with .Values.readReplicas.podLabels }} -{{ toYaml . | indent 8 }} -{{- end }} -{{- with .Values.readReplicas.podAnnotations }} - annotations: -{{ toYaml . | indent 8 }} -{{- end }} - spec: - {{- if .Values.schedulerName }} - schedulerName: "{{ .Values.schedulerName }}" - {{- end }} -{{- include "postgresql.imagePullSecrets" . | indent 6 }} - {{- if .Values.readReplicas.affinity }} - affinity: {{- include "common.tplvalues.render" (dict "value" .Values.readReplicas.affinity "context" $) | nindent 8 }} - {{- else }} - affinity: - podAffinity: {{- include "common.affinities.pods" (dict "type" .Values.readReplicas.podAffinityPreset "component" "read" "context" $) | nindent 10 }} - podAntiAffinity: {{- include "common.affinities.pods" (dict "type" .Values.readReplicas.podAntiAffinityPreset "component" "read" "context" $) | nindent 10 }} - nodeAffinity: {{- include "common.affinities.nodes" (dict "type" .Values.readReplicas.nodeAffinityPreset.type "key" .Values.readReplicas.nodeAffinityPreset.key "values" .Values.readReplicas.nodeAffinityPreset.values) | nindent 10 }} - {{- end }} - {{- if .Values.readReplicas.nodeSelector }} - nodeSelector: {{- include "common.tplvalues.render" (dict "value" .Values.readReplicas.nodeSelector "context" $) | nindent 8 }} - {{- end }} - {{- if .Values.readReplicas.tolerations }} - tolerations: {{- include "common.tplvalues.render" (dict "value" .Values.readReplicas.tolerations "context" $) | nindent 8 }} - {{- end }} - {{- if .Values.terminationGracePeriodSeconds }} - terminationGracePeriodSeconds: {{ .Values.terminationGracePeriodSeconds }} - {{- end }} - {{- if .Values.securityContext.enabled }} - securityContext: {{- omit .Values.securityContext "enabled" | toYaml | nindent 8 }} - {{- end }} - {{- if .Values.serviceAccount.enabled }} - serviceAccountName: {{ default (include "common.names.fullname" . ) .Values.serviceAccount.name}} - {{- end }} - {{- if or .Values.readReplicas.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) .Values.tls.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 }} - {{- if eq ( toString ( .Values.volumePermissions.securityContext.runAsUser )) "auto" }} - chown `id -u`:`id -G | cut -d " " -f2` {{ .Values.persistence.mountPath }} - {{- else }} - chown {{ .Values.containerSecurityContext.runAsUser }}:{{ .Values.securityContext.fsGroup }} {{ .Values.persistence.mountPath }} - {{- end }} - mkdir -p {{ .Values.persistence.mountPath }}/data {{- if (include "postgresql.mountConfigurationCM" .) }} {{ .Values.persistence.mountPath }}/conf {{- end }} - chmod 700 {{ .Values.persistence.mountPath }}/data {{- if (include "postgresql.mountConfigurationCM" .) }} {{ .Values.persistence.mountPath }}/conf {{- end }} - find {{ .Values.persistence.mountPath }} -mindepth 1 -maxdepth 1 {{- if not (include "postgresql.mountConfigurationCM" .) }} -not -name "conf" {{- end }} -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.containerSecurityContext.runAsUser }}:{{ .Values.securityContext.fsGroup }} - {{- end }} - {{- end }} - {{- if and .Values.shmVolume.enabled .Values.shmVolume.chmod.enabled }} - chmod -R 777 /dev/shm - {{- end }} - {{- if .Values.tls.enabled }} - cp /tmp/certs/* /opt/bitnami/postgresql/certs/ - {{- if eq ( toString ( .Values.volumePermissions.securityContext.runAsUser )) "auto" }} - chown -R `id -u`:`id -G | cut -d " " -f2` /opt/bitnami/postgresql/certs/ - {{- else }} - chown -R {{ .Values.containerSecurityContext.runAsUser }}:{{ .Values.securityContext.fsGroup }} /opt/bitnami/postgresql/certs/ - {{- end }} - chmod 600 {{ template "postgresql.tlsCertKey" . }} - {{- end }} - {{- if eq ( toString ( .Values.volumePermissions.securityContext.runAsUser )) "auto" }} - securityContext: {{- omit .Values.volumePermissions.securityContext "runAsUser" | toYaml | nindent 12 }} - {{- else }} - securityContext: {{- .Values.volumePermissions.securityContext | toYaml | nindent 12 }} - {{- 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 }} - {{- if .Values.tls.enabled }} - - name: raw-certificates - mountPath: /tmp/certs - - name: postgresql-certificates - mountPath: /opt/bitnami/postgresql/certs - {{- end }} - {{- end }} - {{- if .Values.readReplicas.extraInitContainers }} - {{- include "common.tplvalues.render" ( dict "value" .Values.readReplicas.extraInitContainers "context" $ ) | nindent 8 }} - {{- end }} - {{- end }} - {{- if .Values.readReplicas.priorityClassName }} - priorityClassName: {{ .Values.readReplicas.priorityClassName }} - {{- end }} - containers: - - name: {{ template "common.names.fullname" . }} - image: {{ template "postgresql.image" . }} - imagePullPolicy: "{{ .Values.image.pullPolicy }}" - {{- if $readReplicasResources }} - resources: {{- toYaml $readReplicasResources | nindent 12 }} - {{- end }} - {{- if .Values.containerSecurityContext.enabled }} - securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 12 }} - {{- 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 "common.names.fullname" . }} - - name: POSTGRES_MASTER_PORT_NUMBER - value: {{ include "postgresql.port" . | quote }} - {{- if and (not (eq .Values.postgresqlUsername "postgres")) (or .Values.postgresqlPostgresPassword (include "postgresql.useExistingSecret" .)) }} - {{- 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 }} - - name: POSTGRESQL_ENABLE_TLS - value: {{ ternary "yes" "no" .Values.tls.enabled | quote }} - {{- if .Values.tls.enabled }} - - name: POSTGRESQL_TLS_PREFER_SERVER_CIPHERS - value: {{ ternary "yes" "no" .Values.tls.preferServerCiphers | quote }} - - name: POSTGRESQL_TLS_CERT_FILE - value: {{ template "postgresql.tlsCert" . }} - - name: POSTGRESQL_TLS_KEY_FILE - value: {{ template "postgresql.tlsCertKey" . }} - {{- if .Values.tls.certCAFilename }} - - name: POSTGRESQL_TLS_CA_FILE - value: {{ template "postgresql.tlsCACert" . }} - {{- end }} - {{- if .Values.tls.crlFilename }} - - name: POSTGRESQL_TLS_CRL_FILE - value: {{ template "postgresql.tlsCRL" . }} - {{- end }} - {{- end }} - - name: POSTGRESQL_LOG_HOSTNAME - value: {{ .Values.audit.logHostname | quote }} - - name: POSTGRESQL_LOG_CONNECTIONS - value: {{ .Values.audit.logConnections | quote }} - - name: POSTGRESQL_LOG_DISCONNECTIONS - value: {{ .Values.audit.logDisconnections | quote }} - {{- if .Values.audit.logLinePrefix }} - - name: POSTGRESQL_LOG_LINE_PREFIX - value: {{ .Values.audit.logLinePrefix | quote }} - {{- end }} - {{- if .Values.audit.logTimezone }} - - name: POSTGRESQL_LOG_TIMEZONE - value: {{ .Values.audit.logTimezone | quote }} - {{- end }} - {{- if .Values.audit.pgAuditLog }} - - name: POSTGRESQL_PGAUDIT_LOG - value: {{ .Values.audit.pgAuditLog | quote }} - {{- end }} - - name: POSTGRESQL_PGAUDIT_LOG_CATALOG - value: {{ .Values.audit.pgAuditLogCatalog | quote }} - - name: POSTGRESQL_CLIENT_MIN_MESSAGES - value: {{ .Values.audit.clientMinMessages | quote }} - - name: POSTGRESQL_SHARED_PRELOAD_LIBRARIES - value: {{ .Values.postgresqlSharedPreloadLibraries | quote }} - {{- if .Values.postgresqlMaxConnections }} - - name: POSTGRESQL_MAX_CONNECTIONS - value: {{ .Values.postgresqlMaxConnections | quote }} - {{- end }} - {{- if .Values.postgresqlPostgresConnectionLimit }} - - name: POSTGRESQL_POSTGRES_CONNECTION_LIMIT - value: {{ .Values.postgresqlPostgresConnectionLimit | quote }} - {{- end }} - {{- if .Values.postgresqlDbUserConnectionLimit }} - - name: POSTGRESQL_USERNAME_CONNECTION_LIMIT - value: {{ .Values.postgresqlDbUserConnectionLimit | quote }} - {{- end }} - {{- if .Values.postgresqlTcpKeepalivesInterval }} - - name: POSTGRESQL_TCP_KEEPALIVES_INTERVAL - value: {{ .Values.postgresqlTcpKeepalivesInterval | quote }} - {{- end }} - {{- if .Values.postgresqlTcpKeepalivesIdle }} - - name: POSTGRESQL_TCP_KEEPALIVES_IDLE - value: {{ .Values.postgresqlTcpKeepalivesIdle | quote }} - {{- end }} - {{- if .Values.postgresqlStatementTimeout }} - - name: POSTGRESQL_STATEMENT_TIMEOUT - value: {{ .Values.postgresqlStatementTimeout | quote }} - {{- end }} - {{- if .Values.postgresqlTcpKeepalivesCount }} - - name: POSTGRESQL_TCP_KEEPALIVES_COUNT - value: {{ .Values.postgresqlTcpKeepalivesCount | quote }} - {{- end }} - {{- if .Values.postgresqlPghbaRemoveFilters }} - - name: POSTGRESQL_PGHBA_REMOVE_FILTERS - value: {{ .Values.postgresqlPghbaRemoveFilters | quote }} - {{- 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 "dbname={{ include "postgresql.database" . }} {{- if and .Values.tls.enabled .Values.tls.certCAFilename }} sslcert={{ include "postgresql.tlsCert" . }} sslkey={{ include "postgresql.tlsCertKey" . }}{{- end }}" -h 127.0.0.1 -p {{ template "postgresql.port" . }} - {{- else }} - - exec pg_isready -U {{ include "postgresql.username" . | quote }} {{- if and .Values.tls.enabled .Values.tls.certCAFilename }} -d "sslcert={{ include "postgresql.tlsCert" . }} sslkey={{ include "postgresql.tlsCertKey" . }}"{{- end }} -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 }} - {{- else if .Values.customLivenessProbe }} - livenessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.customLivenessProbe "context" $) | nindent 12 }} - {{- 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 }} - {{- else if .Values.customReadinessProbe }} - readinessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.customReadinessProbe "context" $) | nindent 12 }} - {{- 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.tls.enabled }} - - name: postgresql-certificates - mountPath: /opt/bitnami/postgresql/certs - readOnly: true - {{- end }} - {{- if .Values.readReplicas.extraVolumeMounts }} - {{- toYaml .Values.readReplicas.extraVolumeMounts | nindent 12 }} - {{- end }} -{{- if .Values.readReplicas.sidecars }} -{{- include "common.tplvalues.render" ( dict "value" .Values.readReplicas.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.tls.enabled }} - - name: raw-certificates - secret: - secretName: {{ required "A secret containing TLS certificates is required when TLS is enabled" .Values.tls.certificatesSecret }} - - name: postgresql-certificates - emptyDir: {} - {{- end }} - {{- if .Values.shmVolume.enabled }} - - name: dshm - emptyDir: - medium: Memory - sizeLimit: 1Gi - {{- end }} - {{- if or (not .Values.persistence.enabled) (not .Values.readReplicas.persistence.enabled) }} - - name: data - emptyDir: {} - {{- end }} - {{- if .Values.readReplicas.extraVolumes }} - {{- toYaml .Values.readReplicas.extraVolumes | nindent 8 }} - {{- end }} - updateStrategy: - type: {{ .Values.updateStrategy.type }} - {{- if (eq "Recreate" .Values.updateStrategy.type) }} - rollingUpdate: null - {{- end }} -{{- if and .Values.persistence.enabled .Values.readReplicas.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 "common.storage.class" (dict "persistence" .Values.persistence "global" .Values.global) }} - - {{- if .Values.persistence.selector }} - selector: {{- include "common.tplvalues.render" (dict "value" .Values.persistence.selector "context" $) | nindent 10 }} - {{- end -}} -{{- end }} -{{- end }} diff --git a/chart/deps/postgresql/templates/statefulset.yaml b/chart/deps/postgresql/templates/statefulset.yaml deleted file mode 100644 index 4d7c5468f..000000000 --- a/chart/deps/postgresql/templates/statefulset.yaml +++ /dev/null @@ -1,618 +0,0 @@ -apiVersion: {{ include "common.capabilities.statefulset.apiVersion" . }} -kind: StatefulSet -metadata: - name: {{ template "postgresql.primary.fullname" . }} - labels: {{- include "common.labels.standard" . | nindent 4 }} - app.kubernetes.io/component: primary - {{- with .Values.primary.labels }} - {{- toYaml . | nindent 4 }} - {{- end }} - annotations: - {{- if .Values.commonAnnotations }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} - {{- with .Values.primary.annotations }} - {{- toYaml . | nindent 4 }} - {{- end }} - namespace: {{ .Release.Namespace }} -spec: - serviceName: {{ template "common.names.fullname" . }}-headless - replicas: 1 - updateStrategy: - type: {{ .Values.updateStrategy.type }} - {{- if (eq "Recreate" .Values.updateStrategy.type) }} - rollingUpdate: null - {{- end }} - selector: - matchLabels: - {{- include "common.labels.matchLabels" . | nindent 6 }} - role: primary - template: - metadata: - name: {{ template "common.names.fullname" . }} - labels: - {{- include "common.labels.standard" . | nindent 8 }} - role: primary - app.kubernetes.io/component: primary - {{- with .Values.primary.podLabels }} - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.primary.podAnnotations }} - annotations: {{- toYaml . | nindent 8 }} - {{- end }} - spec: - {{- if .Values.schedulerName }} - schedulerName: "{{ .Values.schedulerName }}" - {{- end }} -{{- include "postgresql.imagePullSecrets" . | indent 6 }} - {{- 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 "component" "primary" "context" $) | nindent 10 }} - podAntiAffinity: {{- include "common.affinities.pods" (dict "type" .Values.primary.podAntiAffinityPreset "component" "primary" "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.terminationGracePeriodSeconds }} - terminationGracePeriodSeconds: {{ .Values.terminationGracePeriodSeconds }} - {{- end }} - {{- if .Values.securityContext.enabled }} - securityContext: {{- omit .Values.securityContext "enabled" | toYaml | nindent 8 }} - {{- end }} - {{- if .Values.serviceAccount.enabled }} - serviceAccountName: {{ default (include "common.names.fullname" . ) .Values.serviceAccount.name }} - {{- end }} - {{- if or .Values.primary.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) .Values.tls.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 }} - {{- if eq ( toString ( .Values.volumePermissions.securityContext.runAsUser )) "auto" }} - chown `id -u`:`id -G | cut -d " " -f2` {{ .Values.persistence.mountPath }} - {{- else }} - chown {{ .Values.containerSecurityContext.runAsUser }}:{{ .Values.securityContext.fsGroup }} {{ .Values.persistence.mountPath }} - {{- end }} - mkdir -p {{ .Values.persistence.mountPath }}/data {{- if (include "postgresql.mountConfigurationCM" .) }} {{ .Values.persistence.mountPath }}/conf {{- end }} - chmod 700 {{ .Values.persistence.mountPath }}/data {{- if (include "postgresql.mountConfigurationCM" .) }} {{ .Values.persistence.mountPath }}/conf {{- end }} - find {{ .Values.persistence.mountPath }} -mindepth 1 -maxdepth 1 {{- if not (include "postgresql.mountConfigurationCM" .) }} -not -name "conf" {{- end }} -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.containerSecurityContext.runAsUser }}:{{ .Values.securityContext.fsGroup }} - {{- end }} - {{- end }} - {{- if and .Values.shmVolume.enabled .Values.shmVolume.chmod.enabled }} - chmod -R 777 /dev/shm - {{- end }} - {{- if .Values.tls.enabled }} - cp /tmp/certs/* /opt/bitnami/postgresql/certs/ - {{- if eq ( toString ( .Values.volumePermissions.securityContext.runAsUser )) "auto" }} - chown -R `id -u`:`id -G | cut -d " " -f2` /opt/bitnami/postgresql/certs/ - {{- else }} - chown -R {{ .Values.containerSecurityContext.runAsUser }}:{{ .Values.securityContext.fsGroup }} /opt/bitnami/postgresql/certs/ - {{- end }} - chmod 600 {{ template "postgresql.tlsCertKey" . }} - {{- end }} - {{- if eq ( toString ( .Values.volumePermissions.securityContext.runAsUser )) "auto" }} - securityContext: {{- omit .Values.volumePermissions.securityContext "runAsUser" | toYaml | nindent 12 }} - {{- else }} - securityContext: {{- .Values.volumePermissions.securityContext | toYaml | nindent 12 }} - {{- 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 }} - {{- if .Values.tls.enabled }} - - name: raw-certificates - mountPath: /tmp/certs - - name: postgresql-certificates - mountPath: /opt/bitnami/postgresql/certs - {{- end }} - {{- end }} - {{- if .Values.primary.extraInitContainers }} - {{- include "common.tplvalues.render" ( dict "value" .Values.primary.extraInitContainers "context" $ ) | nindent 8 }} - {{- end }} - {{- end }} - {{- if .Values.primary.priorityClassName }} - priorityClassName: {{ .Values.primary.priorityClassName }} - {{- end }} - containers: - - name: {{ template "common.names.fullname" . }} - image: {{ template "postgresql.image" . }} - imagePullPolicy: "{{ .Values.image.pullPolicy }}" - {{- if .Values.resources }} - resources: {{- toYaml .Values.resources | nindent 12 }} - {{- end }} - {{- if .Values.containerSecurityContext.enabled }} - securityContext: {{- omit .Values.containerSecurityContext "enabled" | toYaml | nindent 12 }} - {{- end }} - args: - {{- range $key, $value := default dict .Values.postgresqlConfiguration }} - - -c - - '{{ $key | snakecase }}={{ $value }}' - {{- end }} - {{- if .Values.pgHbaConfiguration }} - - -c - - hba_file=/bitnami/postgresql/conf/pg_hba.conf - {{- 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.primaryAsStandBy.enabled }} - - name: POSTGRES_MASTER_HOST - value: {{ .Values.primaryAsStandBy.primaryHost }} - - name: POSTGRES_MASTER_PORT_NUMBER - value: {{ .Values.primaryAsStandBy.primaryPort | quote }} - {{- end }} - {{- if or .Values.replication.enabled .Values.primaryAsStandBy.enabled }} - - name: POSTGRES_REPLICATION_MODE - {{- if .Values.primaryAsStandBy.enabled }} - value: "slave" - {{- else }} - value: "master" - {{- end }} - - 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 not (eq (include "postgresql.username" .) "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 "common.tplvalues.render" (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}} - - name: POSTGRESQL_ENABLE_TLS - value: {{ ternary "yes" "no" .Values.tls.enabled | quote }} - {{- if .Values.tls.enabled }} - - name: POSTGRESQL_TLS_PREFER_SERVER_CIPHERS - value: {{ ternary "yes" "no" .Values.tls.preferServerCiphers | quote }} - - name: POSTGRESQL_TLS_CERT_FILE - value: {{ template "postgresql.tlsCert" . }} - - name: POSTGRESQL_TLS_KEY_FILE - value: {{ template "postgresql.tlsCertKey" . }} - {{- if .Values.tls.certCAFilename }} - - name: POSTGRESQL_TLS_CA_FILE - value: {{ template "postgresql.tlsCACert" . }} - {{- end }} - {{- if .Values.tls.crlFilename }} - - name: POSTGRESQL_TLS_CRL_FILE - value: {{ template "postgresql.tlsCRL" . }} - {{- end }} - {{- end }} - - name: POSTGRESQL_LOG_HOSTNAME - value: {{ .Values.audit.logHostname | quote }} - - name: POSTGRESQL_LOG_CONNECTIONS - value: {{ .Values.audit.logConnections | quote }} - - name: POSTGRESQL_LOG_DISCONNECTIONS - value: {{ .Values.audit.logDisconnections | quote }} - {{- if .Values.audit.logLinePrefix }} - - name: POSTGRESQL_LOG_LINE_PREFIX - value: {{ .Values.audit.logLinePrefix | quote }} - {{- end }} - {{- if .Values.audit.logTimezone }} - - name: POSTGRESQL_LOG_TIMEZONE - value: {{ .Values.audit.logTimezone | quote }} - {{- end }} - {{- if .Values.audit.pgAuditLog }} - - name: POSTGRESQL_PGAUDIT_LOG - value: {{ .Values.audit.pgAuditLog | quote }} - {{- end }} - - name: POSTGRESQL_PGAUDIT_LOG_CATALOG - value: {{ .Values.audit.pgAuditLogCatalog | quote }} - - name: POSTGRESQL_CLIENT_MIN_MESSAGES - value: {{ .Values.audit.clientMinMessages | quote }} - - name: POSTGRESQL_SHARED_PRELOAD_LIBRARIES - value: {{ .Values.postgresqlSharedPreloadLibraries | quote }} - {{- if .Values.postgresqlMaxConnections }} - - name: POSTGRESQL_MAX_CONNECTIONS - value: {{ .Values.postgresqlMaxConnections | quote }} - {{- end }} - {{- if .Values.postgresqlPostgresConnectionLimit }} - - name: POSTGRESQL_POSTGRES_CONNECTION_LIMIT - value: {{ .Values.postgresqlPostgresConnectionLimit | quote }} - {{- end }} - {{- if .Values.postgresqlDbUserConnectionLimit }} - - name: POSTGRESQL_USERNAME_CONNECTION_LIMIT - value: {{ .Values.postgresqlDbUserConnectionLimit | quote }} - {{- end }} - {{- if .Values.postgresqlTcpKeepalivesInterval }} - - name: POSTGRESQL_TCP_KEEPALIVES_INTERVAL - value: {{ .Values.postgresqlTcpKeepalivesInterval | quote }} - {{- end }} - {{- if .Values.postgresqlTcpKeepalivesIdle }} - - name: POSTGRESQL_TCP_KEEPALIVES_IDLE - value: {{ .Values.postgresqlTcpKeepalivesIdle | quote }} - {{- end }} - {{- if .Values.postgresqlStatementTimeout }} - - name: POSTGRESQL_STATEMENT_TIMEOUT - value: {{ .Values.postgresqlStatementTimeout | quote }} - {{- end }} - {{- if .Values.postgresqlTcpKeepalivesCount }} - - name: POSTGRESQL_TCP_KEEPALIVES_COUNT - value: {{ .Values.postgresqlTcpKeepalivesCount | quote }} - {{- end }} - {{- if .Values.postgresqlPghbaRemoveFilters }} - - name: POSTGRESQL_PGHBA_REMOVE_FILTERS - value: {{ .Values.postgresqlPghbaRemoveFilters | quote }} - {{- end }} - {{- if .Values.extraEnvVarsCM }} - envFrom: - - configMapRef: - name: {{ tpl .Values.extraEnvVarsCM . }} - {{- end }} - ports: - - name: tcp-postgresql - containerPort: {{ template "postgresql.port" . }} - {{- if .Values.startupProbe.enabled }} - startupProbe: - exec: - command: - - /bin/sh - - -c - {{- if (include "postgresql.database" .) }} - - exec pg_isready -U {{ include "postgresql.username" . | quote }} -d "dbname={{ include "postgresql.database" . }} {{- if and .Values.tls.enabled .Values.tls.certCAFilename }} sslcert={{ include "postgresql.tlsCert" . }} sslkey={{ include "postgresql.tlsCertKey" . }}{{- end }}" -h 127.0.0.1 -p {{ template "postgresql.port" . }} - {{- else }} - - exec pg_isready -U {{ include "postgresql.username" . | quote }} {{- if and .Values.tls.enabled .Values.tls.certCAFilename }} -d "sslcert={{ include "postgresql.tlsCert" . }} sslkey={{ include "postgresql.tlsCertKey" . }}"{{- end }} -h 127.0.0.1 -p {{ template "postgresql.port" . }} - {{- end }} - initialDelaySeconds: {{ .Values.startupProbe.initialDelaySeconds }} - periodSeconds: {{ .Values.startupProbe.periodSeconds }} - timeoutSeconds: {{ .Values.startupProbe.timeoutSeconds }} - successThreshold: {{ .Values.startupProbe.successThreshold }} - failureThreshold: {{ .Values.startupProbe.failureThreshold }} - {{- else if .Values.customStartupProbe }} - startupProbe: {{- include "common.tplvalues.render" (dict "value" .Values.customStartupProbe "context" $) | nindent 12 }} - {{- end }} - {{- if .Values.livenessProbe.enabled }} - livenessProbe: - exec: - command: - - /bin/sh - - -c - {{- if (include "postgresql.database" .) }} - - exec pg_isready -U {{ include "postgresql.username" . | quote }} -d "dbname={{ include "postgresql.database" . }} {{- if and .Values.tls.enabled .Values.tls.certCAFilename }} sslcert={{ include "postgresql.tlsCert" . }} sslkey={{ include "postgresql.tlsCertKey" . }}{{- end }}" -h 127.0.0.1 -p {{ template "postgresql.port" . }} - {{- else }} - - exec pg_isready -U {{ include "postgresql.username" . | quote }} {{- if and .Values.tls.enabled .Values.tls.certCAFilename }} -d "sslcert={{ include "postgresql.tlsCert" . }} sslkey={{ include "postgresql.tlsCertKey" . }}"{{- end }} -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 }} - {{- else if .Values.customLivenessProbe }} - livenessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.customLivenessProbe "context" $) | nindent 12 }} - {{- 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 }} - {{- else if .Values.customReadinessProbe }} - readinessProbe: {{- include "common.tplvalues.render" (dict "value" .Values.customReadinessProbe "context" $) | nindent 12 }} - {{- 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.tls.enabled }} - - name: postgresql-certificates - mountPath: /opt/bitnami/postgresql/certs - readOnly: true - {{- 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.primary.extraVolumeMounts }} - {{- toYaml .Values.primary.extraVolumeMounts | nindent 12 }} - {{- end }} -{{- if .Values.primary.sidecars }} -{{- include "common.tplvalues.render" ( dict "value" .Values.primary.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: {{- omit .Values.metrics.securityContext "enabled" | toYaml | nindent 12 }} - {{- 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" .) }} - {{- $sslmode := ternary "require" "disable" .Values.tls.enabled }} - {{- if and .Values.tls.enabled .Values.tls.certCAFilename }} - - name: DATA_SOURCE_NAME - value: {{ printf "host=127.0.0.1 port=%d user=%s sslmode=%s sslcert=%s sslkey=%s" (int (include "postgresql.port" .)) (include "postgresql.username" .) $sslmode (include "postgresql.tlsCert" .) (include "postgresql.tlsCertKey" .) }} - {{- else }} - - name: DATA_SOURCE_URI - value: {{ printf "127.0.0.1:%d/%s?sslmode=%s" (int (include "postgresql.port" .)) $database $sslmode }} - {{- end }} - {{- 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.metrics.extraEnvVars }} - {{- include "common.tplvalues.render" (dict "value" .Values.metrics.extraEnvVars "context" $) | nindent 12 }} - {{- end }} - {{- 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.tls.enabled }} - - name: postgresql-certificates - mountPath: /opt/bitnami/postgresql/certs - readOnly: true - {{- 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.tls.enabled }} - - name: raw-certificates - secret: - secretName: {{ required "A secret containing TLS certificates is required when TLS is enabled" .Values.tls.certificatesSecret }} - - name: postgresql-certificates - emptyDir: {} - {{- end }} - {{- if .Values.primary.extraVolumes }} - {{- toYaml .Values.primary.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 "common.storage.class" (dict "persistence" .Values.persistence "global" .Values.global) }} - {{- if .Values.persistence.selector }} - selector: {{- include "common.tplvalues.render" (dict "value" .Values.persistence.selector "context" $) | nindent 10 }} - {{- end -}} -{{- end }} diff --git a/chart/deps/postgresql/templates/svc-headless.yaml b/chart/deps/postgresql/templates/svc-headless.yaml deleted file mode 100644 index 6f5f3b9ee..000000000 --- a/chart/deps/postgresql/templates/svc-headless.yaml +++ /dev/null @@ -1,28 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ template "common.names.fullname" . }}-headless - labels: - {{- include "common.labels.standard" . | nindent 4 }} - {{- if .Values.commonAnnotations }} - annotations: {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} - # Use this annotation in addition to the actual publishNotReadyAddresses - # field below because the annotation will stop being respected soon but the - # field is broken in some versions of Kubernetes: - # https://github.com/kubernetes/kubernetes/issues/58662 - service.alpha.kubernetes.io/tolerate-unready-endpoints: "true" - namespace: {{ .Release.Namespace }} -spec: - type: ClusterIP - clusterIP: None - # We want all pods in the StatefulSet to have their addresses published for - # the sake of the other Postgresql pods even before they're ready, since they - # have to be able to talk to each other in order to become ready. - publishNotReadyAddresses: true - ports: - - name: tcp-postgresql - port: {{ template "postgresql.port" . }} - targetPort: tcp-postgresql - selector: - {{- include "common.labels.matchLabels" . | nindent 4 }} diff --git a/chart/deps/postgresql/templates/svc-read.yaml b/chart/deps/postgresql/templates/svc-read.yaml deleted file mode 100644 index 56195ea1e..000000000 --- a/chart/deps/postgresql/templates/svc-read.yaml +++ /dev/null @@ -1,43 +0,0 @@ -{{- if .Values.replication.enabled }} -{{- $serviceAnnotations := coalesce .Values.readReplicas.service.annotations .Values.service.annotations -}} -{{- $serviceType := coalesce .Values.readReplicas.service.type .Values.service.type -}} -{{- $serviceLoadBalancerIP := coalesce .Values.readReplicas.service.loadBalancerIP .Values.service.loadBalancerIP -}} -{{- $serviceLoadBalancerSourceRanges := coalesce .Values.readReplicas.service.loadBalancerSourceRanges .Values.service.loadBalancerSourceRanges -}} -{{- $serviceClusterIP := coalesce .Values.readReplicas.service.clusterIP .Values.service.clusterIP -}} -{{- $serviceNodePort := coalesce .Values.readReplicas.service.nodePort .Values.service.nodePort -}} -apiVersion: v1 -kind: Service -metadata: - name: {{ template "common.names.fullname" . }}-read - labels: - {{- include "common.labels.standard" . | nindent 4 }} - annotations: - {{- if .Values.commonAnnotations }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} - {{- if $serviceAnnotations }} - {{- include "common.tplvalues.render" (dict "value" $serviceAnnotations "context" $) | nindent 4 }} - {{- end }} - namespace: {{ .Release.Namespace }} -spec: - type: {{ $serviceType }} - {{- if and $serviceLoadBalancerIP (eq $serviceType "LoadBalancer") }} - loadBalancerIP: {{ $serviceLoadBalancerIP }} - {{- end }} - {{- if and (eq $serviceType "LoadBalancer") $serviceLoadBalancerSourceRanges }} - loadBalancerSourceRanges: {{- include "common.tplvalues.render" (dict "value" $serviceLoadBalancerSourceRanges "context" $) | nindent 4 }} - {{- end }} - {{- if and (eq $serviceType "ClusterIP") $serviceClusterIP }} - clusterIP: {{ $serviceClusterIP }} - {{- end }} - ports: - - name: tcp-postgresql - port: {{ template "postgresql.port" . }} - targetPort: tcp-postgresql - {{- if $serviceNodePort }} - nodePort: {{ $serviceNodePort }} - {{- end }} - selector: - {{- include "common.labels.matchLabels" . | nindent 4 }} - role: read -{{- end }} diff --git a/chart/deps/postgresql/templates/svc.yaml b/chart/deps/postgresql/templates/svc.yaml deleted file mode 100644 index a29431b6a..000000000 --- a/chart/deps/postgresql/templates/svc.yaml +++ /dev/null @@ -1,41 +0,0 @@ -{{- $serviceAnnotations := coalesce .Values.primary.service.annotations .Values.service.annotations -}} -{{- $serviceType := coalesce .Values.primary.service.type .Values.service.type -}} -{{- $serviceLoadBalancerIP := coalesce .Values.primary.service.loadBalancerIP .Values.service.loadBalancerIP -}} -{{- $serviceLoadBalancerSourceRanges := coalesce .Values.primary.service.loadBalancerSourceRanges .Values.service.loadBalancerSourceRanges -}} -{{- $serviceClusterIP := coalesce .Values.primary.service.clusterIP .Values.service.clusterIP -}} -{{- $serviceNodePort := coalesce .Values.primary.service.nodePort .Values.service.nodePort -}} -apiVersion: v1 -kind: Service -metadata: - name: {{ template "common.names.fullname" . }} - labels: - {{- include "common.labels.standard" . | nindent 4 }} - annotations: - {{- if .Values.commonAnnotations }} - {{- include "common.tplvalues.render" ( dict "value" .Values.commonAnnotations "context" $ ) | nindent 4 }} - {{- end }} - {{- if $serviceAnnotations }} - {{- include "common.tplvalues.render" (dict "value" $serviceAnnotations "context" $) | nindent 4 }} - {{- end }} - namespace: {{ .Release.Namespace }} -spec: - type: {{ $serviceType }} - {{- if and $serviceLoadBalancerIP (eq $serviceType "LoadBalancer") }} - loadBalancerIP: {{ $serviceLoadBalancerIP }} - {{- end }} - {{- if and (eq $serviceType "LoadBalancer") $serviceLoadBalancerSourceRanges }} - loadBalancerSourceRanges: {{- include "common.tplvalues.render" (dict "value" $serviceLoadBalancerSourceRanges "context" $) | nindent 4 }} - {{- end }} - {{- if and (eq $serviceType "ClusterIP") $serviceClusterIP }} - clusterIP: {{ $serviceClusterIP }} - {{- end }} - ports: - - name: tcp-postgresql - port: {{ template "postgresql.port" . }} - targetPort: tcp-postgresql - {{- if $serviceNodePort }} - nodePort: {{ $serviceNodePort }} - {{- end }} - selector: - {{- include "common.labels.matchLabels" . | nindent 4 }} - role: primary diff --git a/chart/deps/postgresql/values.schema.json b/chart/deps/postgresql/values.schema.json deleted file mode 100644 index 66a2a9dd0..000000000 --- a/chart/deps/postgresql/values.schema.json +++ /dev/null @@ -1,103 +0,0 @@ -{ - "$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 - }, - "readReplicas": { - "type": "integer", - "title": "read Replicas", - "form": true, - "hidden": { - "value": false, - "path": "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/chart/deps/postgresql/values.yaml b/chart/deps/postgresql/values.yaml deleted file mode 100644 index abe0c858c..000000000 --- a/chart/deps/postgresql/values.yaml +++ /dev/null @@ -1,840 +0,0 @@ -## 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.11.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 common.names.fullname template (will maintain the release name) -## -# nameOverride: - -## String to fully override common.names.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 - -## Container Security Context -## ref: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/ -## -containerSecurityContext: - enabled: true - runAsUser: 1001 - capabilities: - drop: - - ALL - -## 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: - -## Pod Security Policy -## ref: https://kubernetes.io/docs/concepts/policy/pod-security-policy/ -## -psp: - create: false - -## Creates role for ServiceAccount -## Required for PSP -## -rbac: - create: false - -replication: - enabled: false - user: repl_user - password: repl_password - readReplicas: 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 `readReplicas` defined above, set the number of those that will have synchronous replication - ## NOTE: It cannot be > readReplicas - 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: - -## Configure current cluster's primary server to be the standby server in other cluster. -## This will allow cross cluster replication and provide cross cluster high availability. -## You will need to configure pgHbaConfiguration if you want to enable this feature with local cluster replication enabled. -## -primaryAsStandBy: - enabled: false - # primaryHost: - # primaryPort: - -## 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: - -## Audit settings -## https://github.com/bitnami/bitnami-docker-postgresql#auditing -## -audit: - ## Log client hostnames - ## - logHostname: false - ## Log connections to the server - ## - logConnections: false - ## Log disconnections - ## - logDisconnections: false - ## Operation to audit using pgAudit (default if not set) - ## - pgAuditLog: "" - ## Log catalog using pgAudit - ## - pgAuditLogCatalog: "off" - ## Log level for clients - ## - clientMinMessages: error - ## Template for log line prefix (default if not set) - ## - logLinePrefix: "" - ## Log timezone - ## - logTimezone: "" - -## Shared preload libraries -## -postgresqlSharedPreloadLibraries: "pgaudit" - -## Maximum total connections -## -postgresqlMaxConnections: - -## Maximum connections for the postgres user -## -postgresqlPostgresConnectionLimit: - -## Maximum connections for the created user -## -postgresqlDbUserConnectionLimit: - -## TCP keepalives interval -## -postgresqlTcpKeepalivesInterval: - -## TCP keepalives idle -## -postgresqlTcpKeepalivesIdle: - -## TCP keepalives count -## -postgresqlTcpKeepalivesCount: - -## Statement timeout -## -postgresqlStatementTimeout: - -## Remove pg_hba.conf lines with the following comma-separated patterns -## (cannot be used with custom pg_hba.conf) -## -postgresqlPghbaRemoveFilters: - -## 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: {} - -## 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. Evaluated as a template. - ## - 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. Evaluated as a template. - ## https://kubernetes.io/docs/tasks/access-application-cluster/configure-cloud-provider-firewall/#restrict-access-for-loadbalancer-service - ## - # loadBalancerSourceRanges: - # - 10.10.10.0/24 - -## Start primary and read(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 ignored if `volumePermissions.enabled` is `false` - ## - chmod: - enabled: true - -## PostgreSQL data Persistent Volume Storage Class -## If defined, storageClassName: <storageClass> -## 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: {} - ## selector can be used to match an existing PersistentVolume - ## selector: - ## matchLabels: - ## app: my-app - selector: {} - -## updateStrategy for PostgreSQL StatefulSet and its reads StatefulSets -## ref: https://kubernetes.io/docs/concepts/workloads/controllers/statefulset/#update-strategies -## -updateStrategy: - type: RollingUpdate - -## -## PostgreSQL Primary parameters -## -primary: - ## PostgreSQL Primary pod affinity preset - ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity - ## Allowed values: soft, hard - ## - podAffinityPreset: "" - - ## PostgreSQL Primary pod anti-affinity preset - ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity - ## Allowed values: soft, hard - ## - podAntiAffinityPreset: soft - - ## PostgreSQL Primary node affinity preset - ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity - ## Allowed values: soft, hard - ## - nodeAffinityPreset: - ## Node affinity type - ## Allowed values: soft, hard - type: "" - ## Node label key to match - ## E.g. - ## key: "kubernetes.io/e2e-az-name" - ## - key: "" - ## Node label values to match - ## E.g. - ## values: - ## - e2e-az1 - ## - e2e-az2 - ## - values: [] - - ## Affinity for PostgreSQL primary pods assignment - ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity - ## Note: primary.podAffinityPreset, primary.podAntiAffinityPreset, and primary.nodeAffinityPreset will be ignored when it's set - ## - affinity: {} - - ## Node labels for PostgreSQL primary pods assignment - ## ref: https://kubernetes.io/docs/user-guide/node-selection/ - ## - nodeSelector: {} - - ## Tolerations for PostgreSQL primary pods assignment - ## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ - ## - tolerations: [] - - labels: {} - annotations: {} - podLabels: {} - podAnnotations: {} - priorityClassName: '' - ## Extra init containers - ## Example - ## - ## extraInitContainers: - ## - name: do-something - ## image: busybox - ## command: ['do', 'something'] - ## - extraInitContainers: [] - - ## Additional PostgreSQL primary Volume mounts - ## - extraVolumeMounts: [] - ## Additional PostgreSQL primary 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: [] - - ## Override the service configuration for primary - ## - service: {} - # type: - # nodePort: - # clusterIP: - -## -## PostgreSQL read only replica parameters -## -readReplicas: - ## PostgreSQL read only pod affinity preset - ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity - ## Allowed values: soft, hard - ## - podAffinityPreset: "" - - ## PostgreSQL read only pod anti-affinity preset - ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#inter-pod-affinity-and-anti-affinity - ## Allowed values: soft, hard - ## - podAntiAffinityPreset: soft - - ## PostgreSQL read only node affinity preset - ## ref: https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#node-affinity - ## Allowed values: soft, hard - ## - nodeAffinityPreset: - ## Node affinity type - ## Allowed values: soft, hard - type: "" - ## Node label key to match - ## E.g. - ## key: "kubernetes.io/e2e-az-name" - ## - key: "" - ## Node label values to match - ## E.g. - ## values: - ## - e2e-az1 - ## - e2e-az2 - ## - values: [] - - ## Affinity for PostgreSQL read only pods assignment - ## ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity - ## Note: readReplicas.podAffinityPreset, readReplicas.podAntiAffinityPreset, and readReplicas.nodeAffinityPreset will be ignored when it's set - ## - affinity: {} - - ## Node labels for PostgreSQL read only pods assignment - ## ref: https://kubernetes.io/docs/user-guide/node-selection/ - ## - nodeSelector: {} - - ## Tolerations for PostgreSQL read only pods assignment - ## ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ - ## - tolerations: [] - labels: {} - annotations: {} - podLabels: {} - podAnnotations: {} - priorityClassName: '' - - ## Extra init containers - ## Example - ## - ## extraInitContainers: - ## - name: do-something - ## image: busybox - ## command: ['do', 'something'] - ## - extraInitContainers: [] - - ## Additional PostgreSQL read replicas Volume mounts - ## - extraVolumeMounts: [] - - ## Additional PostgreSQL read replicas 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: [] - - ## Override the service configuration for read - ## - service: {} - # type: - # nodePort: - # clusterIP: - - ## Whether to enable PostgreSQL read replicas data Persistent - ## - persistence: - enabled: true - - # Override the resource configuration for read replicas - resources: - requests: - memory: 256Mi - cpu: 250m - limits: - memory: 256Mi - cpu: 250m - -## Configure resource requests and limits -## ref: http://kubernetes.io/docs/user-guide/compute-resources/ -## -resources: - requests: - memory: 256Mi - cpu: 250m - limits: - memory: 256Mi - cpu: 250m - -## Add annotations to all the deployed resources -## -commonAnnotations: {} - -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. - ## - ## Example: - ## explicitNamespacesSelector: - ## matchLabels: - ## role: frontend - ## matchExpressions: - ## - {key: role, operator: In, values: [frontend]} - ## - explicitNamespacesSelector: {} - -## Configure extra options for startup, liveness and readiness probes -## ref: https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/#configure-probes -## -startupProbe: - enabled: false - initialDelaySeconds: 30 - periodSeconds: 15 - timeoutSeconds: 5 - failureThreshold: 10 - successThreshold: 1 - -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 - -## Custom Startup probe -## -customStartupProbe: {} - -## Custom Liveness probe -## -customLivenessProbe: {} - -## Custom Rediness probe -## -customReadinessProbe: {} - -## -## TLS configuration -## -tls: - # Enable TLS traffic - enabled: false - # - # Whether to use the server's TLS cipher preferences rather than the client's. - preferServerCiphers: true - # - # Name of the Secret that contains the certificates - certificatesSecret: '' - # - # Certificate filename - certFilename: '' - # - # Certificate Key filename - certKeyFilename: '' - # - # CA Certificate filename - # If provided, PostgreSQL will authenticate TLS/SSL clients by requesting them a certificate - # ref: https://www.postgresql.org/docs/9.6/auth-methods.html - certCAFilename: - # - # File containing a Certificate Revocation List - crlFilename: - -## Configure metrics exporter -## -metrics: - enabled: false - resources: - requests: - memory: 256Mi - cpu: 100m - limits: - memory: 256Mi - cpu: 100m - 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: '' - ## These are just examples rules, please adapt them to your needs. - ## Make sure to constraint the rules to the current postgresql service. - ## rules: - ## - alert: HugeReplicationLag - ## expr: pg_replication_lag{service="{{ template "common.names.fullname" . }}-metrics"} / 3600 > 1 - ## for: 1m - ## labels: - ## severity: critical - ## annotations: - ## description: replication for {{ template "common.names.fullname" . }} PostgreSQL is lagging by {{ "{{ $value }}" }} hour(s). - ## summary: PostgreSQL replication is lagging by {{ "{{ $value }}" }} hour(s). - ## - rules: [] - - image: - registry: docker.io - repository: bitnami/postgres-exporter - tag: 0.8.0-debian-10-r362 - 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_bytes 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" - # - ## An array to add extra env vars to configure postgres-exporter - ## see: https://github.com/wrouesnel/postgres_exporter#environment-variables - ## For example: - # extraEnvVars: - # - name: PG_EXPORTER_DISABLE_DEFAULT_METRICS - # value: "true" - extraEnvVars: {} - - ## 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 - -## Array with extra yaml to deploy with the chart. Evaluated as a template -## -extraDeploy: [] diff --git a/chart/mattermost-operator-crds/Chart.yaml b/chart/mattermost-operator-crds/Chart.yaml new file mode 100644 index 000000000..634590180 --- /dev/null +++ b/chart/mattermost-operator-crds/Chart.yaml @@ -0,0 +1,15 @@ +apiVersion: v2 +name: mattermost-operator-crds +description: "Deployment of mattermost operator crds using Helm" +home: https://github.com/mattermost/mattermost-operator +type: application +version: "1.19.0" +appVersion: "1.19.0" +kubeVersion: ">=1.12.0-0" +keywords: + - Mattermost + - Operator + - CRDs +maintainers: + - name: Mattermost + email: info@mattermost.com diff --git a/chart/mattermost-operator-crds/templates/clusterinstallations.yaml b/chart/mattermost-operator-crds/templates/clusterinstallations.yaml new file mode 100644 index 000000000..83c781c7a --- /dev/null +++ b/chart/mattermost-operator-crds/templates/clusterinstallations.yaml @@ -0,0 +1,1677 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.7.0 + name: clusterinstallations.mattermost.com + labels: + app.kubernetes.io/managed-by: '{{ .Release.Service }}' + app.kubernetes.io/instance: '{{ .Release.Name }}' + app.kubernetes.io/version: '{{ .Chart.AppVersion }}' + helm.sh/chart: '{{ .Chart.Name }}-{{ .Chart.Version }}' +spec: + group: mattermost.com + names: + kind: ClusterInstallation + listKind: ClusterInstallationList + plural: clusterinstallations + singular: clusterinstallation + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: State of Mattermost + jsonPath: .status.state + name: State + type: string + - description: Image of Mattermost + jsonPath: .status.image + name: Image + type: string + - description: Version of Mattermost + jsonPath: .status.version + name: Version + type: string + - description: Endpoint + jsonPath: .status.endpoint + name: Endpoint + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: ClusterInstallation is the Schema for the clusterinstallations + API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: 'Specification of the desired behavior of the Mattermost + cluster. More info: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#spec-and-status' + properties: + affinity: + description: If specified, affinity will define the pod's scheduling + constraints + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for the + pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to + nodes that satisfy the affinity expressions specified by + this field, but it may choose a node that violates one or + more of the expressions. The node that is most preferred + is the one with the greatest sum of weights, i.e. for each + node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, + etc.), compute a sum by iterating through the elements of + this field and adding "weight" to the sum if the node matches + the corresponding matchExpressions; the node(s) with the + highest sum are the most preferred. + items: + description: An empty preferred scheduling term matches + all objects with implicit weight 0 (i.e. it's a no-op). + A null preferred scheduling term matches no objects (i.e. + is also a no-op). + properties: + preference: + description: A node selector term, associated with the + corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. If the operator is Gt or + Lt, the values array must have a single + element, which will be interpreted as an + integer. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. If the operator is Gt or + Lt, the values array must have a single + element, which will be interpreted as an + integer. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + description: Weight associated with matching the corresponding + nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to an update), the system may or may not try to + eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. + The terms are ORed. + items: + description: A null or empty node selector term matches + no objects. The requirements of them are ANDed. The + TopologySelectorTerm type implements a subset of the + NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. If the operator is Gt or + Lt, the values array must have a single + element, which will be interpreted as an + integer. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. If the operator is Gt or + Lt, the values array must have a single + element, which will be interpreted as an + integer. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate + this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to + nodes that satisfy the affinity expressions specified by + this field, but it may choose a node that violates one or + more of the expressions. The node that is most preferred + is the one with the greatest sum of weights, i.e. for each + node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, + etc.), compute a sum by iterating through the elements of + this field and adding "weight" to the sum if the node has + pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by this + field and the ones listed in the namespaces field. + null selector and null or empty namespaces list + means "this pod's namespace". An empty selector + ({}) matches all namespaces. This field is beta-level + and is only honored when PodAffinityNamespaceSelector + feature is enabled. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. The + term is applied to the union of the namespaces + listed in this field and the ones selected by + namespaceSelector. null or empty namespaces list + and null namespaceSelector means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey + matches that of any node on which any of the selected + pods is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to a pod label update), the system may or may + not try to eventually evict the pod from its node. When + there are multiple elements, the lists of nodes corresponding + to each podAffinityTerm are intersected, i.e. all terms + must be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key <topologyKey> + matches that of any node on which a pod of the set of + pods is running + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". The + requirements are ANDed. + type: object + type: object + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied to the + union of the namespaces selected by this field and + the ones listed in the namespaces field. null selector + and null or empty namespaces list means "this pod's + namespace". An empty selector ({}) matches all namespaces. + This field is beta-level and is only honored when + PodAffinityNamespaceSelector feature is enabled. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". The + requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies a static list of namespace + names that the term applies to. The term is applied + to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. null or + empty namespaces list and null namespaceSelector means + "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of + any node on which any of the selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules (e.g. + avoid putting this pod in the same node, zone, etc. as some + other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to + nodes that satisfy the anti-affinity expressions specified + by this field, but it may choose a node that violates one + or more of the expressions. The node that is most preferred + is the one with the greatest sum of weights, i.e. for each + node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity expressions, + etc.), compute a sum by iterating through the elements of + this field and adding "weight" to the sum if the node has + pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by this + field and the ones listed in the namespaces field. + null selector and null or empty namespaces list + means "this pod's namespace". An empty selector + ({}) matches all namespaces. This field is beta-level + and is only honored when PodAffinityNamespaceSelector + feature is enabled. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. The + term is applied to the union of the namespaces + listed in this field and the ones selected by + namespaceSelector. null or empty namespaces list + and null namespaceSelector means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey + matches that of any node on which any of the selected + pods is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified by + this field are not met at scheduling time, the pod will + not be scheduled onto the node. If the anti-affinity requirements + specified by this field cease to be met at some point during + pod execution (e.g. due to a pod label update), the system + may or may not try to eventually evict the pod from its + node. When there are multiple elements, the lists of nodes + corresponding to each podAffinityTerm are intersected, i.e. + all terms must be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key <topologyKey> + matches that of any node on which a pod of the set of + pods is running + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". The + requirements are ANDed. + type: object + type: object + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied to the + union of the namespaces selected by this field and + the ones listed in the namespaces field. null selector + and null or empty namespaces list means "this pod's + namespace". An empty selector ({}) matches all namespaces. + This field is beta-level and is only honored when + PodAffinityNamespaceSelector feature is enabled. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". The + requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies a static list of namespace + names that the term applies to. The term is applied + to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. null or + empty namespaces list and null namespaceSelector means + "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of + any node on which any of the selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + blueGreen: + description: BlueGreen defines the configuration of BlueGreen deployment + for a ClusterInstallation + properties: + blue: + description: Blue defines the blue deployment. + properties: + image: + description: Image defines the base Docker image that will + be used for the deployment. Required when BlueGreen or Canary + is enabled. + type: string + ingressName: + description: IngressName defines the ingress name that will + be used by the deployment. This option is not used for Canary + builds. + type: string + name: + description: Name defines the name of the deployment + type: string + resourceLabels: + additionalProperties: + type: string + type: object + version: + description: Version defines the Docker image version that + will be used for the deployment. Required when BlueGreen + or Canary is enabled. + type: string + type: object + enable: + description: Enable defines if BlueGreen deployment will be applied. + type: boolean + green: + description: Green defines the green deployment. + properties: + image: + description: Image defines the base Docker image that will + be used for the deployment. Required when BlueGreen or Canary + is enabled. + type: string + ingressName: + description: IngressName defines the ingress name that will + be used by the deployment. This option is not used for Canary + builds. + type: string + name: + description: Name defines the name of the deployment + type: string + resourceLabels: + additionalProperties: + type: string + type: object + version: + description: Version defines the Docker image version that + will be used for the deployment. Required when BlueGreen + or Canary is enabled. + type: string + type: object + productionDeployment: + description: ProductionDeployment defines if the current production + is blue or green. + type: string + type: object + canary: + description: Canary defines the configuration of Canary deployment + for a ClusterInstallation + properties: + deployment: + description: Deployment defines the canary deployment. + properties: + image: + description: Image defines the base Docker image that will + be used for the deployment. Required when BlueGreen or Canary + is enabled. + type: string + ingressName: + description: IngressName defines the ingress name that will + be used by the deployment. This option is not used for Canary + builds. + type: string + name: + description: Name defines the name of the deployment + type: string + resourceLabels: + additionalProperties: + type: string + type: object + version: + description: Version defines the Docker image version that + will be used for the deployment. Required when BlueGreen + or Canary is enabled. + type: string + type: object + enable: + description: Enable defines if a canary build will be deployed. + type: boolean + type: object + database: + description: Database defines the database configuration for a ClusterInstallation. + properties: + backupRemoteDeletePolicy: + description: Defines the backup retention policy. + type: string + backupRestoreSecretName: + description: Defines the secret to be used when performing a database + restore. + type: string + backupSchedule: + description: Defines the interval for backups in cron expression + format. + type: string + backupSecretName: + description: Defines the secret to be used for uploading/restoring + backup. + type: string + backupURL: + description: Defines the object storage url for uploading backups. + type: string + initBucketURL: + description: Defines the AWS S3 bucket where the Database Backup + is stored. The operator will download the file to restore the + data. + type: string + replicas: + description: Defines the number of database replicas. For redundancy + use at least 2 replicas. Setting this will override the number + of replicas set by 'Size'. + format: int32 + type: integer + resources: + description: Defines the resource requests and limits for the + database pods. + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, otherwise + to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + secret: + description: "Optionally enter the name of an already-existing + Secret for connecting to the database. This secret should be + configured as follows: \n User-Managed Database - Key: DB_CONNECTION_STRING + | Value: <FULL_DATABASE_CONNECTION_STRING> Operator-Managed + Database - Key: ROOT_PASSWORD | Value: <ROOT_DATABASE_PASSWORD> + \ - Key: USER | Value: <USER_NAME> - Key: PASSWORD | Value: + <USER_PASSWORD> - Key: DATABASE Value: <DATABASE_NAME> \n + Notes: If you define all secret values for both User-Managed + and Operator-Managed database types, the User-Managed connection + string will take precedence and the Operator-Managed values + will be ignored. If the secret is left blank, the default + behavior is to use an Operator-Managed database with strong + randomly-generated database credentials." + type: string + storageSize: + description: Defines the storage size for the database. ie 50Gi + pattern: ^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$ + type: string + type: + description: Defines the type of database to use for an Operator-Managed + database. This value is ignored when using a User-Managed database. + type: string + type: object + elasticSearch: + description: ElasticSearch defines the ElasticSearch configuration + for a ClusterInstallation. + properties: + host: + type: string + password: + type: string + username: + type: string + type: object + image: + description: Image defines the ClusterInstallation Docker image. + type: string + imagePullPolicy: + description: Specify deployment pull policy. + type: string + ingressAnnotations: + additionalProperties: + type: string + type: object + ingressName: + description: IngressName defines the name to be used when creating + the ingress rules + type: string + livenessProbe: + description: Defines the probe to check if the application is up and + running. + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute inside + the container, the working directory for the command is + root ('/') in the container's filesystem. The command is + simply exec'd, it is not run inside a shell, so traditional + shell instructions ('|', etc) won't work. To use a shell, + you need to explicitly call out to that shell. Exit status + of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe to be + considered failed after having succeeded. Defaults to 3. Minimum + value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. This + is an alpha field and requires enabling GRPCContainerProbe feature + gate. + properties: + port: + description: Port number of the gRPC service. Number must + be in the range 1 to 65535. + format: int32 + type: integer + service: + description: "Service is the name of the service to place + in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + \n If this is not specified, the default behavior is defined + by gRPC." + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the pod + IP. You probably want to set "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows + repeated headers. + items: + description: HTTPHeader describes a custom header to be + used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on the container. + Number must be in the range 1 to 65535. Name must be an + IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. Defaults + to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has started + before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. Default + to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe to be + considered successful after having failed. Defaults to 1. Must + be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on the container. + Number must be in the range 1 to 65535. Name must be an + IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs to terminate + gracefully upon probe failure. The grace period is the duration + in seconds after the processes running in the pod are sent a + termination signal and the time when the processes are forcibly + halted with a kill signal. Set this value longer than the expected + cleanup time for your process. If this value is nil, the pod's + terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. Value must + be non-negative integer. The value zero indicates stop immediately + via the kill signal (no opportunity to shut down). This is a + beta field and requires enabling ProbeTerminationGracePeriod + feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds + is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + mattermostEnv: + description: Optional environment variables to set in the Mattermost + application pods. + items: + description: EnvVar represents an environment variable present in + a Container. + properties: + name: + description: Name of the environment variable. Must be a C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) are expanded using + the previously defined environment variables in the container + and any service environment variables. If a variable cannot + be resolved, the reference in the input string will be unchanged. + Double $$ are reduced to a single $, which allows for escaping + the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the + string literal "$(VAR_NAME)". Escaped references will never + be expanded, regardless of whether the variable exists or + not. Defaults to "".' + type: string + valueFrom: + description: Source for the environment variable's value. Cannot + be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap or its key + must be defined + type: boolean + required: + - key + type: object + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels[''<KEY>'']`, `metadata.annotations[''<KEY>'']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, + status.podIP, status.podIPs.' + properties: + apiVersion: + description: Version of the schema the FieldPath is + written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified + API version. + type: string + required: + - fieldPath + type: object + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.ephemeral-storage) are currently supported.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the exposed + resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + secretKeyRef: + description: Selects a key of a secret in the pod's namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + type: object + required: + - name + type: object + type: array + mattermostLicenseSecret: + description: Secret that contains the mattermost license + type: string + migrate: + description: 'Migrate specifies that the ClusterInstallation CR should + be migrated to the Mattermost CR. CAUTION: Some features like BlueGreen + or Canary are not supported with a new Custom Resource therefore + migration should be performed with extra caution.' + type: boolean + minio: + description: Minio defines the configuration of Minio for a ClusterInstallation. + properties: + externalBucket: + description: Set to the bucket name of your external MinIO or + S3. + type: string + externalURL: + description: Set to use an external MinIO deployment or S3. Must + also set 'Secret' and 'ExternalBucket'. + type: string + replicas: + description: 'Defines the number of Minio replicas. Supply 1 to + run Minio in standalone mode with no redundancy. Supply 4 or + more to run Minio in distributed mode. Note that it is not possible + to upgrade Minio from standalone to distributed mode. Setting + this will override the number of replicas set by ''Size''. More + info: https://docs.min.io/docs/distributed-minio-quickstart-guide.html' + format: int32 + type: integer + resources: + description: Defines the resource requests and limits for the + Minio pods. + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, otherwise + to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + secret: + description: 'Optionally enter the name of already existing secret. + Secret should have two values: "accesskey" and "secretkey". + Required when "ExternalURL" is set.' + type: string + storageSize: + description: Defines the storage size for Minio. ie 50Gi + pattern: ^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$ + type: string + type: object + nodeSelector: + additionalProperties: + type: string + description: 'NodeSelector is a selector which must be true for the + pod to fit on a node. Selector which must match a node''s labels + for the pod to be scheduled on that node. More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/' + type: object + readinessProbe: + description: Defines the probe to check if the application is ready + to accept traffic. + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute inside + the container, the working directory for the command is + root ('/') in the container's filesystem. The command is + simply exec'd, it is not run inside a shell, so traditional + shell instructions ('|', etc) won't work. To use a shell, + you need to explicitly call out to that shell. Exit status + of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe to be + considered failed after having succeeded. Defaults to 3. Minimum + value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. This + is an alpha field and requires enabling GRPCContainerProbe feature + gate. + properties: + port: + description: Port number of the gRPC service. Number must + be in the range 1 to 65535. + format: int32 + type: integer + service: + description: "Service is the name of the service to place + in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + \n If this is not specified, the default behavior is defined + by gRPC." + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the pod + IP. You probably want to set "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows + repeated headers. + items: + description: HTTPHeader describes a custom header to be + used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on the container. + Number must be in the range 1 to 65535. Name must be an + IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. Defaults + to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has started + before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. Default + to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe to be + considered successful after having failed. Defaults to 1. Must + be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on the container. + Number must be in the range 1 to 65535. Name must be an + IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs to terminate + gracefully upon probe failure. The grace period is the duration + in seconds after the processes running in the pod are sent a + termination signal and the time when the processes are forcibly + halted with a kill signal. Set this value longer than the expected + cleanup time for your process. If this value is nil, the pod's + terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. Value must + be non-negative integer. The value zero indicates stop immediately + via the kill signal (no opportunity to shut down). This is a + beta field and requires enabling ProbeTerminationGracePeriod + feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds + is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + replicas: + description: Replicas defines the number of replicas to use for the + Mattermost app servers. Setting this will override the number of + replicas set by 'Size'. + format: int32 + type: integer + resourceLabels: + additionalProperties: + type: string + type: object + resources: + description: Defines the resource requests and limits for the Mattermost + app server pods. + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute resources + allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, otherwise + to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + serviceAnnotations: + additionalProperties: + type: string + type: object + size: + description: 'Size defines the size of the ClusterInstallation. This + is typically specified in number of users. This will override replica + and resource requests/limits appropriately for the provided number + of users. This is a write-only field - its value is erased after + setting appropriate values of resources. Accepted values are: 100users, + 1000users, 5000users, 10000users, 250000users. If replicas and resource + requests/limits are not specified, and Size is not provided the + configuration for 5000users will be applied. Setting ''Replicas'', + ''Resources'', ''Minio.Replicas'', ''Minio.Resource'', ''Database.Replicas'', + or ''Database.Resources'' will override the values set by Size. + Setting new Size will override previous values regardless if set + by Size or manually.' + type: string + useIngressTLS: + type: boolean + useServiceLoadBalancer: + type: boolean + version: + description: Version defines the ClusterInstallation Docker image + version. + type: string + required: + - ingressName + type: object + status: + description: 'Most recent observed status of the Mattermost cluster. Read-only. + Not included when requesting from the apiserver, only from the Mattermost + Operator API itself. More info: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#spec-and-status' + properties: + blueName: + description: The name of the blue deployment in BlueGreen + type: string + endpoint: + description: The endpoint to access the Mattermost instance + type: string + greenName: + description: The name of the green deployment in BlueGreen + type: string + image: + description: The image running on the pods in the Mattermost instance + type: string + migration: + description: The status of migration to Mattermost CR. + properties: + error: + type: string + status: + type: string + type: object + replicas: + description: Total number of non-terminated pods targeted by this + Mattermost deployment + format: int32 + type: integer + state: + description: Represents the running state of the Mattermost instance + type: string + updatedReplicas: + description: Total number of non-terminated pods targeted by this + Mattermost deployment that are running with the desired image. + format: int32 + type: integer + version: + description: The version currently running in the Mattermost instance + type: string + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/chart/mattermost-operator-crds/templates/mattermostrestoredbs.yaml b/chart/mattermost-operator-crds/templates/mattermostrestoredbs.yaml new file mode 100644 index 000000000..5826d8c4e --- /dev/null +++ b/chart/mattermost-operator-crds/templates/mattermostrestoredbs.yaml @@ -0,0 +1,98 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.7.0 + name: mattermostrestoredbs.mattermost.com + labels: + app.kubernetes.io/managed-by: '{{ .Release.Service }}' + app.kubernetes.io/instance: '{{ .Release.Name }}' + app.kubernetes.io/version: '{{ .Chart.AppVersion }}' + helm.sh/chart: '{{ .Chart.Name }}-{{ .Chart.Version }}' +spec: + group: mattermost.com + names: + kind: MattermostRestoreDB + listKind: MattermostRestoreDBList + plural: mattermostrestoredbs + singular: mattermostrestoredb + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: State of Mattermost DB Restore + jsonPath: .status.state + name: State + type: string + - description: Original DB Replicas + jsonPath: .status.originalDBReplicas + name: Original DB Replicas + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: MattermostRestoreDB is the Schema for the mattermostrestoredbs + API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: MattermostRestoreDBSpec defines the desired state of MattermostRestoreDB + properties: + initBucketURL: + description: InitBucketURL defines where the DB backup file is located. + type: string + mattermostClusterName: + description: MattermostClusterName defines the ClusterInstallation + name. + type: string + mattermostDBName: + description: MattermostDBName defines the database name. Need to set + if different from `mattermost`. + type: string + mattermostDBPassword: + description: MattermostDBPassword defines the user password to access + the database. Need to set if the user is different from the one + created by the operator. + type: string + mattermostDBUser: + description: MattermostDBUser defines the user to access the database. + Need to set if the user is different from `mmuser`. + type: string + restoreSecret: + description: RestoreSecret defines the secret that holds the credentials + to MySQL Operator be able to download the DB backup file + type: string + type: object + status: + description: MattermostRestoreDBStatus defines the observed state of MattermostRestoreDB + properties: + originalDBReplicas: + description: The original number of database replicas. will be used + to restore after applying the db restore process. + format: int32 + type: integer + state: + description: Represents the state of the Mattermost restore Database. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/chart/mattermost-operator-crds/templates/mattermosts.yaml b/chart/mattermost-operator-crds/templates/mattermosts.yaml new file mode 100644 index 000000000..133f90f5a --- /dev/null +++ b/chart/mattermost-operator-crds/templates/mattermosts.yaml @@ -0,0 +1,4974 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.7.0 + name: mattermosts.installation.mattermost.com + labels: + app.kubernetes.io/managed-by: '{{ .Release.Service }}' + app.kubernetes.io/instance: '{{ .Release.Name }}' + app.kubernetes.io/version: '{{ .Chart.AppVersion }}' + helm.sh/chart: '{{ .Chart.Name }}-{{ .Chart.Version }}' +spec: + group: installation.mattermost.com + names: + kind: Mattermost + listKind: MattermostList + plural: mattermosts + shortNames: + - mm + singular: mattermost + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: State of Mattermost + jsonPath: .status.state + name: State + type: string + - description: Image of Mattermost + jsonPath: .status.image + name: Image + type: string + - description: Version of Mattermost + jsonPath: .status.version + name: Version + type: string + - description: Endpoint + jsonPath: .status.endpoint + name: Endpoint + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: Mattermost is the Schema for the mattermosts API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: MattermostSpec defines the desired state of Mattermost + properties: + database: + description: External Services + properties: + disableReadinessCheck: + description: DisableReadinessCheck instructs Operator to not add + init container responsible for checking DB access. Can be used + to define custom init containers specified in `spec.PodExtensions.InitContainers`. + type: boolean + external: + description: Defines the configuration of and external database. + properties: + secret: + description: 'Secret contains data necessary to connect to + the external database. The Kubernetes Secret should contain: - + Key: DB_CONNECTION_STRING | Value: Full database connection + string. It can also contain optional fields, such as: - + Key: MM_SQLSETTINGS_DATASOURCEREPLICAS | Value: Connection + string to read replicas of the database. - Key: DB_CONNECTION_CHECK_URL + | Value: The URL used for checking that the database is + accessible. Omitting this value in the secret will cause + Operator to skip adding init container for database check.' + type: string + type: object + operatorManaged: + description: Defines the configuration of database managed by + Kubernetes operator. + properties: + backupRemoteDeletePolicy: + description: Defines the backup retention policy. + type: string + backupRestoreSecretName: + description: Defines the secret to be used when performing + a database restore. + type: string + backupSchedule: + description: Defines the interval for backups in cron expression + format. + type: string + backupSecretName: + description: Defines the secret to be used for uploading/restoring + backup. + type: string + backupURL: + description: Defines the object storage url for uploading + backups. + type: string + initBucketURL: + description: Defines the AWS S3 bucket where the Database + Backup is stored. The operator will download the file to + restore the data. + type: string + replicas: + description: Defines the number of database replicas. For + redundancy use at least 2 replicas. Setting this will override + the number of replicas set by 'Size'. + format: int32 + type: integer + resources: + description: Defines the resource requests and limits for + the database pods. + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of + compute resources required. If Requests is omitted for + a container, it defaults to Limits if that is explicitly + specified, otherwise to an implementation-defined value. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + storageSize: + description: Defines the storage size for the database. ie + 50Gi + pattern: ^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$ + type: string + type: + description: Defines the type of database to use for an Operator-Managed + database. + type: string + type: object + type: object + dnsConfig: + description: Custom DNS configuration to use for the Mattermost Installation + pods. + properties: + nameservers: + description: A list of DNS name server IP addresses. This will + be appended to the base nameservers generated from DNSPolicy. + Duplicated nameservers will be removed. + items: + type: string + type: array + options: + description: A list of DNS resolver options. This will be merged + with the base options generated from DNSPolicy. Duplicated entries + will be removed. Resolution options given in Options will override + those that appear in the base DNSPolicy. + items: + description: PodDNSConfigOption defines DNS resolver options + of a pod. + properties: + name: + description: Required. + type: string + value: + type: string + type: object + type: array + searches: + description: A list of DNS search domains for host-name lookup. + This will be appended to the base search paths generated from + DNSPolicy. Duplicated search paths will be removed. + items: + type: string + type: array + type: object + dnsPolicy: + description: Custom DNS policy to use for the Mattermost Installation + pods. + type: string + elasticSearch: + description: ElasticSearch defines the ElasticSearch configuration + for Mattermost. + properties: + host: + type: string + password: + type: string + username: + type: string + type: object + fileStore: + description: FileStore defines the file store configuration for Mattermost. + properties: + external: + description: Defines the configuration of an external file store. + properties: + bucket: + description: Set to the bucket name of your external MinIO + or S3. + type: string + secret: + description: 'Optionally enter the name of already existing + secret. Secret should have two values: "accesskey" and "secretkey".' + type: string + url: + description: Set to use an external MinIO deployment or S3. + type: string + type: object + operatorManaged: + description: Defines the configuration of file store managed by + Kubernetes operator. + properties: + replicas: + description: 'Defines the number of Minio replicas. Supply + 1 to run Minio in standalone mode with no redundancy. Supply + 4 or more to run Minio in distributed mode. Note that it + is not possible to upgrade Minio from standalone to distributed + mode. Setting this will override the number of replicas + set by ''Size''. More info: https://docs.min.io/docs/distributed-minio-quickstart-guide.html' + format: int32 + type: integer + resources: + description: Defines the resource requests and limits for + the Minio pods. + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of + compute resources required. If Requests is omitted for + a container, it defaults to Limits if that is explicitly + specified, otherwise to an implementation-defined value. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + storageSize: + description: Defines the storage size for Minio. ie 50Gi + pattern: ^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$ + type: string + type: object + type: object + image: + description: Image defines the Mattermost Docker image. + type: string + imagePullPolicy: + description: Specify Mattermost deployment pull policy. + type: string + imagePullSecrets: + description: Specify Mattermost image pull secrets. + items: + description: LocalObjectReference contains enough information to + let you locate the referenced object inside the same namespace. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + type: array + ingress: + description: Ingress defines configuration for Ingress resource created + by the Operator. + properties: + annotations: + additionalProperties: + type: string + description: Annotations defines annotations passed to the Ingress + associated with Mattermost. + type: object + enabled: + description: Enabled determines whether the Operator should create + Ingress resource or not. Disabling ingress on existing installation + will cause Operator to remove it. + type: boolean + host: + description: Host defines the Ingress host to be used when creating + the ingress rules. + type: string + hosts: + description: Hosts allows specifying additional domain names for + Mattermost to use. + items: + description: IngressHost specifies additional hosts configuration. + properties: + hostName: + type: string + type: object + type: array + ingressClass: + description: IngressClass will be set on Ingress resource to associate + it with specified IngressClass resource. + type: string + tlsSecret: + description: TLSSecret specifies secret used for configuring TLS + for Ingress. If empty TLS will not be configured. + type: string + required: + - enabled + type: object + ingressAnnotations: + additionalProperties: + type: string + description: 'IngressAnnotations defines annotations passed to the + Ingress associated with Mattermost. Deprecated: Use Spec.Ingress.Annotations.' + type: object + ingressName: + description: 'IngressName defines the host to be used when creating + the ingress rules. Deprecated: Use Spec.Ingress.Host instead.' + type: string + licenseSecret: + description: LicenseSecret is the name of the secret containing a + Mattermost license. + type: string + mattermostEnv: + description: Optional environment variables to set in the Mattermost + application pods. + items: + description: EnvVar represents an environment variable present in + a Container. + properties: + name: + description: Name of the environment variable. Must be a C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) are expanded using + the previously defined environment variables in the container + and any service environment variables. If a variable cannot + be resolved, the reference in the input string will be unchanged. + Double $$ are reduced to a single $, which allows for escaping + the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the + string literal "$(VAR_NAME)". Escaped references will never + be expanded, regardless of whether the variable exists or + not. Defaults to "".' + type: string + valueFrom: + description: Source for the environment variable's value. Cannot + be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap or its key + must be defined + type: boolean + required: + - key + type: object + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels[''<KEY>'']`, `metadata.annotations[''<KEY>'']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, + status.podIP, status.podIPs.' + properties: + apiVersion: + description: Version of the schema the FieldPath is + written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified + API version. + type: string + required: + - fieldPath + type: object + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.ephemeral-storage) are currently supported.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the exposed + resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + secretKeyRef: + description: Selects a key of a secret in the pod's namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + type: object + required: + - name + type: object + type: array + podExtensions: + description: PodExtensions specify custom extensions for Mattermost + pods. This can be used for custom readiness checks etc. These settings + generally don't need to be changed. + properties: + initContainers: + description: Additional InitContainers injected to pods. The setting + does not override InitContainers defined by the Operator. + items: + description: A single application container that you want to + run within a pod. + properties: + args: + description: 'Arguments to the entrypoint. The docker image''s + CMD is used if this is not provided. Variable references + $(VAR_NAME) are expanded using the container''s environment. + If a variable cannot be resolved, the reference in the + input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) + syntax: i.e. "$$(VAR_NAME)" will produce the string literal + "$(VAR_NAME)". Escaped references will never be expanded, + regardless of whether the variable exists or not. Cannot + be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + items: + type: string + type: array + command: + description: 'Entrypoint array. Not executed within a shell. + The docker image''s ENTRYPOINT is used if this is not + provided. Variable references $(VAR_NAME) are expanded + using the container''s environment. If a variable cannot + be resolved, the reference in the input string will be + unchanged. Double $$ are reduced to a single $, which + allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" + will produce the string literal "$(VAR_NAME)". Escaped + references will never be expanded, regardless of whether + the variable exists or not. Cannot be updated. More info: + https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + items: + type: string + type: array + env: + description: List of environment variables to set in the + container. Cannot be updated. + items: + description: EnvVar represents an environment variable + present in a Container. + properties: + name: + description: Name of the environment variable. Must + be a C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) are + expanded using the previously defined environment + variables in the container and any service environment + variables. If a variable cannot be resolved, the + reference in the input string will be unchanged. + Double $$ are reduced to a single $, which allows + for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" + will produce the string literal "$(VAR_NAME)". Escaped + references will never be expanded, regardless of + whether the variable exists or not. Defaults to + "".' + type: string + valueFrom: + description: Source for the environment variable's + value. Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap + or its key must be defined + type: boolean + required: + - key + type: object + fieldRef: + description: 'Selects a field of the pod: supports + metadata.name, metadata.namespace, `metadata.labels[''<KEY>'']`, + `metadata.annotations[''<KEY>'']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.podIP, + status.podIPs.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in + the specified API version. + type: string + required: + - fieldPath + type: object + resourceFieldRef: + description: 'Selects a resource of the container: + only resources limits and requests (limits.cpu, + limits.memory, limits.ephemeral-storage, requests.cpu, + requests.memory and requests.ephemeral-storage) + are currently supported.' + properties: + containerName: + description: 'Container name: required for + volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of + the exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + secretKeyRef: + description: Selects a key of a secret in the + pod's namespace + properties: + key: + description: The key of the secret to select + from. Must be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the Secret or + its key must be defined + type: boolean + required: + - key + type: object + type: object + required: + - name + type: object + type: array + envFrom: + description: List of sources to populate environment variables + in the container. The keys defined within a source must + be a C_IDENTIFIER. All invalid keys will be reported as + an event when the container is starting. When a key exists + in multiple sources, the value associated with the last + source will take precedence. Values defined by an Env + with a duplicate key will take precedence. Cannot be updated. + items: + description: EnvFromSource represents the source of a + set of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap must + be defined + type: boolean + type: object + prefix: + description: An optional identifier to prepend to + each key in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret must be + defined + type: boolean + type: object + type: object + type: array + image: + description: 'Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config management + to default or override container images in workload controllers + like Deployments and StatefulSets.' + type: string + imagePullPolicy: + description: 'Image pull policy. One of Always, Never, IfNotPresent. + Defaults to Always if :latest tag is specified, or IfNotPresent + otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' + type: string + lifecycle: + description: Actions that the management system should take + in response to container lifecycle events. Cannot be updated. + properties: + postStart: + description: 'PostStart is called immediately after + a container is created. If the handler fails, the + container is terminated and restarted according to + its restart policy. Other management of the container + blocks until the hook completes. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to + execute inside the container, the working + directory for the command is root ('/') in + the container's filesystem. The command is + simply exec'd, it is not run inside a shell, + so traditional shell instructions ('|', etc) + won't work. To use a shell, you need to explicitly + call out to that shell. Exit status of 0 is + treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request + to perform. + properties: + host: + description: Host name to connect to, defaults + to the pod IP. You probably want to set "Host" + in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to + the host. Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: Deprecated. TCPSocket is NOT supported + as a LifecycleHandler and kept for the backward + compatibility. There are no validation of this + field and lifecycle hooks will fail in runtime + when tcp handler is specified. + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + description: 'PreStop is called immediately before a + container is terminated due to an API request or management + event such as liveness/startup probe failure, preemption, + resource contention, etc. The handler is not called + if the container crashes or exits. The Pod''s termination + grace period countdown begins before the PreStop hook + is executed. Regardless of the outcome of the handler, + the container will eventually terminate within the + Pod''s termination grace period (unless delayed by + finalizers). Other management of the container blocks + until the hook completes or until the termination + grace period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to + execute inside the container, the working + directory for the command is root ('/') in + the container's filesystem. The command is + simply exec'd, it is not run inside a shell, + so traditional shell instructions ('|', etc) + won't work. To use a shell, you need to explicitly + call out to that shell. Exit status of 0 is + treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request + to perform. + properties: + host: + description: Host name to connect to, defaults + to the pod IP. You probably want to set "Host" + in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to + the host. Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: Deprecated. TCPSocket is NOT supported + as a LifecycleHandler and kept for the backward + compatibility. There are no validation of this + field and lifecycle hooks will fail in runtime + when tcp handler is specified. + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + description: 'Periodic probe of container liveness. Container + will be restarted if the probe fails. Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for + the command is root ('/') in the container's + filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, you need + to explicitly call out to that shell. Exit status + of 0 is treated as live/healthy and non-zero is + unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC + port. This is an alpha field and requires enabling + GRPCContainerProbe feature gate. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + description: "Service is the name of the service + to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + \n If this is not specified, the default behavior + is defined by gRPC." + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to + the pod IP. You probably want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the + host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container + has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum + value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving + a TCP port. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs + to terminate gracefully upon probe failure. The grace + period is the duration in seconds after the processes + running in the pod are sent a termination signal and + the time when the processes are forcibly halted with + a kill signal. Set this value longer than the expected + cleanup time for your process. If this value is nil, + the pod's terminationGracePeriodSeconds will be used. + Otherwise, this value overrides the value provided + by the pod spec. Value must be non-negative integer. + The value zero indicates stop immediately via the + kill signal (no opportunity to shut down). This is + a beta field and requires enabling ProbeTerminationGracePeriod + feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds + is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe + times out. Defaults to 1 second. Minimum value is + 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + name: + description: Name of the container specified as a DNS_LABEL. + Each container in a pod must have a unique name (DNS_LABEL). + Cannot be updated. + type: string + ports: + description: List of ports to expose from the container. + Exposing a port here gives the system additional information + about the network connections a container uses, but is + primarily informational. Not specifying a port here DOES + NOT prevent that port from being exposed. Any port which + is listening on the default "0.0.0.0" address inside a + container will be accessible from the network. Cannot + be updated. + items: + description: ContainerPort represents a network port in + a single container. + properties: + containerPort: + description: Number of port to expose on the pod's + IP address. This must be a valid port number, 0 + < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external port + to. + type: string + hostPort: + description: Number of port to expose on the host. + If specified, this must be a valid port number, + 0 < x < 65536. If HostNetwork is specified, this + must match ContainerPort. Most containers do not + need this. + format: int32 + type: integer + name: + description: If specified, this must be an IANA_SVC_NAME + and unique within the pod. Each named port in a + pod must have a unique name. Name for the port that + can be referred to by services. + type: string + protocol: + default: TCP + description: Protocol for port. Must be UDP, TCP, + or SCTP. Defaults to "TCP". + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + description: 'Periodic probe of container service readiness. + Container will be removed from service endpoints if the + probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for + the command is root ('/') in the container's + filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, you need + to explicitly call out to that shell. Exit status + of 0 is treated as live/healthy and non-zero is + unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC + port. This is an alpha field and requires enabling + GRPCContainerProbe feature gate. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + description: "Service is the name of the service + to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + \n If this is not specified, the default behavior + is defined by gRPC." + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to + the pod IP. You probably want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the + host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container + has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum + value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving + a TCP port. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs + to terminate gracefully upon probe failure. The grace + period is the duration in seconds after the processes + running in the pod are sent a termination signal and + the time when the processes are forcibly halted with + a kill signal. Set this value longer than the expected + cleanup time for your process. If this value is nil, + the pod's terminationGracePeriodSeconds will be used. + Otherwise, this value overrides the value provided + by the pod spec. Value must be non-negative integer. + The value zero indicates stop immediately via the + kill signal (no opportunity to shut down). This is + a beta field and requires enabling ProbeTerminationGracePeriod + feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds + is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe + times out. Defaults to 1 second. Minimum value is + 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + resources: + description: 'Compute Resources required by this container. + Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of + compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount + of compute resources required. If Requests is omitted + for a container, it defaults to Limits if that is + explicitly specified, otherwise to an implementation-defined + value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + securityContext: + description: 'SecurityContext defines the security options + the container should be run with. If set, the fields of + SecurityContext override the equivalent fields of PodSecurityContext. + More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/' + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation controls whether + a process can gain more privileges than its parent + process. This bool directly controls if the no_new_privs + flag will be set on the container process. AllowPrivilegeEscalation + is true always when the container is: 1) run as Privileged + 2) has CAP_SYS_ADMIN Note that this field cannot be + set when spec.os.name is windows.' + type: boolean + capabilities: + description: The capabilities to add/drop when running + containers. Defaults to the default set of capabilities + granted by the container runtime. Note that this field + cannot be set when spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + type: object + privileged: + description: Run container in privileged mode. Processes + in privileged containers are essentially equivalent + to root on the host. Defaults to false. Note that + this field cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: procMount denotes the type of proc mount + to use for the containers. The default is DefaultProcMount + which uses the container runtime defaults for readonly + paths and masked paths. This requires the ProcMountType + feature flag to be enabled. Note that this field cannot + be set when spec.os.name is windows. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only + root filesystem. Default is false. Note that this + field cannot be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of the container + process. Uses runtime default if unset. May also be + set in PodSecurityContext. If set in both SecurityContext + and PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set + when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as + a non-root user. If true, the Kubelet will validate + the image at runtime to ensure that it does not run + as UID 0 (root) and fail to start the container if + it does. If unset or false, no such validation will + be performed. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container + process. Defaults to user specified in image metadata + if unspecified. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name + is windows. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to the + container. If unspecified, the container runtime will + allocate a random SELinux context for each container. May + also be set in PodSecurityContext. If set in both + SecurityContext and PodSecurityContext, the value + specified in SecurityContext takes precedence. Note + that this field cannot be set when spec.os.name is + windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by this container. + If seccomp options are provided at both the pod & + container level, the container options override the + pod options. Note that this field cannot be set when + spec.os.name is windows. + properties: + localhostProfile: + description: localhostProfile indicates a profile + defined in a file on the node should be used. + The profile must be preconfigured on the node + to work. Must be a descending path, relative to + the kubelet's configured seccomp profile location. + Must only be set if type is "Localhost". + type: string + type: + description: "type indicates which kind of seccomp + profile will be applied. Valid options are: \n + Localhost - a profile defined in a file on the + node should be used. RuntimeDefault - the container + runtime default profile should be used. Unconfined + - no profile should be applied." + type: string + required: + - type + type: object + windowsOptions: + description: The Windows specific settings applied to + all containers. If unspecified, the options from the + PodSecurityContext will be used. If set in both SecurityContext + and PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set + when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA + admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec + named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name + of the GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container + should be run as a 'Host Process' container. This + field is alpha-level and will only be honored + by components that enable the WindowsHostProcessContainers + feature flag. Setting this field without the feature + flag will result in errors when validating the + Pod. All of a Pod's containers must have the same + effective HostProcess value (it is not allowed + to have a mix of HostProcess containers and non-HostProcess + containers). In addition, if HostProcess is true + then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: The UserName in Windows to run the + entrypoint of the container process. Defaults + to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set + in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + startupProbe: + description: 'StartupProbe indicates that the Pod has successfully + initialized. If specified, no other probes are executed + until this completes successfully. If this probe fails, + the Pod will be restarted, just as if the livenessProbe + failed. This can be used to provide different probe parameters + at the beginning of a Pod''s lifecycle, when it might + take a long time to load data or warm a cache, than during + steady-state operation. This cannot be updated. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for + the command is root ('/') in the container's + filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, you need + to explicitly call out to that shell. Exit status + of 0 is treated as live/healthy and non-zero is + unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC + port. This is an alpha field and requires enabling + GRPCContainerProbe feature gate. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + description: "Service is the name of the service + to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + \n If this is not specified, the default behavior + is defined by gRPC." + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to + the pod IP. You probably want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the + host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container + has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum + value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving + a TCP port. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs + to terminate gracefully upon probe failure. The grace + period is the duration in seconds after the processes + running in the pod are sent a termination signal and + the time when the processes are forcibly halted with + a kill signal. Set this value longer than the expected + cleanup time for your process. If this value is nil, + the pod's terminationGracePeriodSeconds will be used. + Otherwise, this value overrides the value provided + by the pod spec. Value must be non-negative integer. + The value zero indicates stop immediately via the + kill signal (no opportunity to shut down). This is + a beta field and requires enabling ProbeTerminationGracePeriod + feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds + is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe + times out. Defaults to 1 second. Minimum value is + 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + stdin: + description: Whether this container should allocate a buffer + for stdin in the container runtime. If this is not set, + reads from stdin in the container will always result in + EOF. Default is false. + type: boolean + stdinOnce: + description: Whether the container runtime should close + the stdin channel after it has been opened by a single + attach. When stdin is true the stdin stream will remain + open across multiple attach sessions. If stdinOnce is + set to true, stdin is opened on container start, is empty + until the first client attaches to stdin, and then remains + open and accepts data until the client disconnects, at + which time stdin is closed and remains closed until the + container is restarted. If this flag is false, a container + processes that reads from stdin will never receive an + EOF. Default is false + type: boolean + terminationMessagePath: + description: 'Optional: Path at which the file to which + the container''s termination message will be written is + mounted into the container''s filesystem. Message written + is intended to be brief final status, such as an assertion + failure message. Will be truncated by the node if greater + than 4096 bytes. The total message length across all containers + will be limited to 12kb. Defaults to /dev/termination-log. + Cannot be updated.' + type: string + terminationMessagePolicy: + description: Indicate how the termination message should + be populated. File will use the contents of terminationMessagePath + to populate the container status message on both success + and failure. FallbackToLogsOnError will use the last chunk + of container log output if the termination message file + is empty and the container exited with an error. The log + output is limited to 2048 bytes or 80 lines, whichever + is smaller. Defaults to File. Cannot be updated. + type: string + tty: + description: Whether this container should allocate a TTY + for itself, also requires 'stdin' to be true. Default + is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of block devices + to be used by the container. + items: + description: volumeDevice describes a mapping of a raw + block device within a container. + properties: + devicePath: + description: devicePath is the path inside of the + container that the device will be mapped to. + type: string + name: + description: name must match the name of a persistentVolumeClaim + in the pod + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + description: Pod volumes to mount into the container's filesystem. + Cannot be updated. + items: + description: VolumeMount describes a mounting of a Volume + within a container. + properties: + mountPath: + description: Path within the container at which the + volume should be mounted. Must not contain ':'. + type: string + mountPropagation: + description: mountPropagation determines how mounts + are propagated from the host to container and the + other way around. When not set, MountPropagationNone + is used. This field is beta in 1.10. + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: Mounted read-only if true, read-write + otherwise (false or unspecified). Defaults to false. + type: boolean + subPath: + description: Path within the volume from which the + container's volume should be mounted. Defaults to + "" (volume's root). + type: string + subPathExpr: + description: Expanded path within the volume from + which the container's volume should be mounted. + Behaves similarly to SubPath but environment variable + references $(VAR_NAME) are expanded using the container's + environment. Defaults to "" (volume's root). SubPathExpr + and SubPath are mutually exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + description: Container's working directory. If not specified, + the container runtime's default will be used, which might + be configured in the container image. Cannot be updated. + type: string + required: + - name + type: object + type: array + type: object + podTemplate: + description: PodTemplate defines configuration for the template for + Mattermost pods. + properties: + containerSecurityContext: + description: Defines the security context for the Mattermost app + server container. + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation controls whether a + process can gain more privileges than its parent process. + This bool directly controls if the no_new_privs flag will + be set on the container process. AllowPrivilegeEscalation + is true always when the container is: 1) run as Privileged + 2) has CAP_SYS_ADMIN Note that this field cannot be set + when spec.os.name is windows.' + type: boolean + capabilities: + description: The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the + container runtime. Note that this field cannot be set when + spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + type: object + privileged: + description: Run container in privileged mode. Processes in + privileged containers are essentially equivalent to root + on the host. Defaults to false. Note that this field cannot + be set when spec.os.name is windows. + type: boolean + procMount: + description: procMount denotes the type of proc mount to use + for the containers. The default is DefaultProcMount which + uses the container runtime defaults for readonly paths and + masked paths. This requires the ProcMountType feature flag + to be enabled. Note that this field cannot be set when spec.os.name + is windows. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only root filesystem. + Default is false. Note that this field cannot be set when + spec.os.name is windows. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of the container + process. Uses runtime default if unset. May also be set + in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set when + spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a non-root + user. If true, the Kubelet will validate the image at runtime + to ensure that it does not run as UID 0 (root) and fail + to start the container if it does. If unset or false, no + such validation will be performed. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container + process. Defaults to user specified in image metadata if + unspecified. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. Note + that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random + SELinux context for each container. May also be set in + PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set when + spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by this container. + If seccomp options are provided at both the pod & container + level, the container options override the pod options. Note + that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile must + be preconfigured on the node to work. Must be a descending + path, relative to the kubelet's configured seccomp profile + location. Must only be set if type is "Localhost". + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - a + profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile + should be used. Unconfined - no profile should be applied." + type: string + required: + - type + type: object + windowsOptions: + description: The Windows specific settings applied to all + containers. If unspecified, the options from the PodSecurityContext + will be used. If set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is + linux. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named + by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. This field is + alpha-level and will only be honored by components that + enable the WindowsHostProcessContainers feature flag. + Setting this field without the feature flag will result + in errors when validating the Pod. All of a Pod's containers + must have the same effective HostProcess value (it is + not allowed to have a mix of HostProcess containers + and non-HostProcess containers). In addition, if HostProcess + is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set in + PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. + type: string + type: object + type: object + extraAnnotations: + additionalProperties: + type: string + description: Defines annotations to add to the Mattermost app + server pods. Overrides of default prometheus annotations are + ignored. + type: object + extraLabels: + additionalProperties: + type: string + description: Defines labels to add to the Mattermost app server + pods. Overrides what is set in ResourceLabels, does not override + default labels (app and cluster labels). + type: object + securityContext: + description: Defines the security context for the Mattermost app + server pods. + properties: + fsGroup: + description: "A special supplemental group that applies to + all containers in a pod. Some volume types allow the Kubelet + to change the ownership of that volume to be owned by the + pod: \n 1. The owning GID will be the FSGroup 2. The setgid + bit is set (new files created in the volume will be owned + by FSGroup) 3. The permission bits are OR'd with rw-rw---- + \n If unset, the Kubelet will not modify the ownership and + permissions of any volume. Note that this field cannot be + set when spec.os.name is windows." + format: int64 + type: integer + fsGroupChangePolicy: + description: 'fsGroupChangePolicy defines behavior of changing + ownership and permission of the volume before being exposed + inside Pod. This field will only apply to volume types which + support fsGroup based ownership(and permissions). It will + have no effect on ephemeral volume types such as: secret, + configmaps and emptydir. Valid values are "OnRootMismatch" + and "Always". If not specified, "Always" is used. Note that + this field cannot be set when spec.os.name is windows.' + type: string + runAsGroup: + description: The GID to run the entrypoint of the container + process. Uses runtime default if unset. May also be set + in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence for that container. Note that this field + cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a non-root + user. If true, the Kubelet will validate the image at runtime + to ensure that it does not run as UID 0 (root) and fail + to start the container if it does. If unset or false, no + such validation will be performed. May also be set in SecurityContext. If + set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container + process. Defaults to user specified in image metadata if + unspecified. May also be set in SecurityContext. If set + in both SecurityContext and PodSecurityContext, the value + specified in SecurityContext takes precedence for that container. + Note that this field cannot be set when spec.os.name is + windows. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to all containers. + If unspecified, the container runtime will allocate a random + SELinux context for each container. May also be set in + SecurityContext. If set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence + for that container. Note that this field cannot be set when + spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by the containers + in this pod. Note that this field cannot be set when spec.os.name + is windows. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile must + be preconfigured on the node to work. Must be a descending + path, relative to the kubelet's configured seccomp profile + location. Must only be set if type is "Localhost". + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - a + profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile + should be used. Unconfined - no profile should be applied." + type: string + required: + - type + type: object + supplementalGroups: + description: A list of groups applied to the first process + run in each container, in addition to the container's primary + GID. If unspecified, no groups will be added to any container. + Note that this field cannot be set when spec.os.name is + windows. + items: + format: int64 + type: integer + type: array + sysctls: + description: Sysctls hold a list of namespaced sysctls used + for the pod. Pods with unsupported sysctls (by the container + runtime) might fail to launch. Note that this field cannot + be set when spec.os.name is windows. + items: + description: Sysctl defines a kernel parameter to be set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + description: The Windows specific settings applied to all + containers. If unspecified, the options within a container's + SecurityContext will be used. If set in both SecurityContext + and PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set when + spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named + by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. This field is + alpha-level and will only be honored by components that + enable the WindowsHostProcessContainers feature flag. + Setting this field without the feature flag will result + in errors when validating the Pod. All of a Pod's containers + must have the same effective HostProcess value (it is + not allowed to have a mix of HostProcess containers + and non-HostProcess containers). In addition, if HostProcess + is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set in + PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. + type: string + type: object + type: object + type: object + probes: + description: Probes defines configuration of liveness and readiness + probe for Mattermost pods. These settings generally don't need to + be changed. + properties: + livenessProbe: + description: Defines the probe to check if the application is + up and running. + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute inside + the container, the working directory for the command is + root ('/') in the container's filesystem. The command + is simply exec'd, it is not run inside a shell, so traditional + shell instructions ('|', etc) won't work. To use a shell, + you need to explicitly call out to that shell. Exit + status of 0 is treated as live/healthy and non-zero + is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe to + be considered failed after having succeeded. Defaults to + 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + This is an alpha field and requires enabling GRPCContainerProbe + feature gate. + properties: + port: + description: Port number of the gRPC service. Number must + be in the range 1 to 65535. + format: int32 + type: integer + service: + description: "Service is the name of the service to place + in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + \n If this is not specified, the default behavior is + defined by gRPC." + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header to + be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on the + container. Number must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has started + before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe to + be considered successful after having failed. Defaults to + 1. Must be 1 for liveness and startup. Minimum value is + 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on the + container. Number must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs to + terminate gracefully upon probe failure. The grace period + is the duration in seconds after the processes running in + the pod are sent a termination signal and the time when + the processes are forcibly halted with a kill signal. Set + this value longer than the expected cleanup time for your + process. If this value is nil, the pod's terminationGracePeriodSeconds + will be used. Otherwise, this value overrides the value + provided by the pod spec. Value must be non-negative integer. + The value zero indicates stop immediately via the kill signal + (no opportunity to shut down). This is a beta field and + requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is + used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times + out. Defaults to 1 second. Minimum value is 1. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + readinessProbe: + description: Defines the probe to check if the application is + ready to accept traffic. + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute inside + the container, the working directory for the command is + root ('/') in the container's filesystem. The command + is simply exec'd, it is not run inside a shell, so traditional + shell instructions ('|', etc) won't work. To use a shell, + you need to explicitly call out to that shell. Exit + status of 0 is treated as live/healthy and non-zero + is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe to + be considered failed after having succeeded. Defaults to + 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + This is an alpha field and requires enabling GRPCContainerProbe + feature gate. + properties: + port: + description: Port number of the gRPC service. Number must + be in the range 1 to 65535. + format: int32 + type: integer + service: + description: "Service is the name of the service to place + in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + \n If this is not specified, the default behavior is + defined by gRPC." + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header to + be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on the + container. Number must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has started + before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe to + be considered successful after having failed. Defaults to + 1. Must be 1 for liveness and startup. Minimum value is + 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on the + container. Number must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs to + terminate gracefully upon probe failure. The grace period + is the duration in seconds after the processes running in + the pod are sent a termination signal and the time when + the processes are forcibly halted with a kill signal. Set + this value longer than the expected cleanup time for your + process. If this value is nil, the pod's terminationGracePeriodSeconds + will be used. Otherwise, this value overrides the value + provided by the pod spec. Value must be non-negative integer. + The value zero indicates stop immediately via the kill signal + (no opportunity to shut down). This is a beta field and + requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is + used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times + out. Defaults to 1 second. Minimum value is 1. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + type: object + replicas: + description: Replicas defines the number of replicas to use for the + Mattermost app servers. + format: int32 + type: integer + resourceLabels: + additionalProperties: + type: string + type: object + resourcePatch: + description: "ResourcePatch specifies JSON patches that can be applied + to resources created by Mattermost Operator. \n WARNING: ResourcePatch + is highly experimental and subject to change. Some patches may be + impossible to perform or may impact the stability of Mattermost + server. \n Use at your own risk when no other options are available." + properties: + deployment: + properties: + disable: + type: boolean + patch: + type: string + type: object + service: + properties: + disable: + type: boolean + patch: + type: string + type: object + type: object + scheduling: + description: Scheduling defines the configuration related to scheduling + of the Mattermost pods as well as resource constraints. These settings + generally don't need to be changed. + properties: + affinity: + description: If specified, affinity will define the pod's scheduling + constraints + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for + the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods + to nodes that satisfy the affinity expressions specified + by this field, but it may choose a node that violates + one or more of the expressions. The node that is most + preferred is the one with the greatest sum of weights, + i.e. for each node that meets all of the scheduling + requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating + through the elements of this field and adding "weight" + to the sum if the node matches the corresponding matchExpressions; + the node(s) with the highest sum are the most preferred. + items: + description: An empty preferred scheduling term matches + all objects with implicit weight 0 (i.e. it's a no-op). + A null preferred scheduling term matches no objects + (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated with + the corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. If the + operator is Gt or Lt, the values array + must have a single element, which will + be interpreted as an integer. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. If the + operator is Gt or Lt, the values array + must have a single element, which will + be interpreted as an integer. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + description: Weight associated with matching the + corresponding nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by + this field are not met at scheduling time, the pod will + not be scheduled onto the node. If the affinity requirements + specified by this field cease to be met at some point + during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from + its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. + The terms are ORed. + items: + description: A null or empty node selector term + matches no objects. The requirements of them are + ANDed. The TopologySelectorTerm type implements + a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. If the + operator is Gt or Lt, the values array + must have a single element, which will + be interpreted as an integer. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. If the + operator is Gt or Lt, the values array + must have a single element, which will + be interpreted as an integer. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. + co-locate this pod in the same node, zone, etc. as some + other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods + to nodes that satisfy the affinity expressions specified + by this field, but it may choose a node that violates + one or more of the expressions. The node that is most + preferred is the one with the greatest sum of weights, + i.e. for each node that meets all of the scheduling + requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating + through the elements of this field and adding "weight" + to the sum if the node has pods which matches the corresponding + podAffinityTerm; the node(s) with the highest sum are + the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by + this field and the ones listed in the namespaces + field. null selector and null or empty namespaces + list means "this pod's namespace". An empty + selector ({}) matches all namespaces. This + field is beta-level and is only honored when + PodAffinityNamespaceSelector feature is enabled. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. + The term is applied to the union of the namespaces + listed in this field and the ones selected + by namespaceSelector. null or empty namespaces + list and null namespaceSelector means "this + pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the + pods matching the labelSelector in the specified + namespaces, where co-located is defined as + running on a node whose value of the label + with key topologyKey matches that of any node + on which any of the selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the + corresponding podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by + this field are not met at scheduling time, the pod will + not be scheduled onto the node. If the affinity requirements + specified by this field cease to be met at some point + during pod execution (e.g. due to a pod label update), + the system may or may not try to eventually evict the + pod from its node. When there are multiple elements, + the lists of nodes corresponding to each podAffinityTerm + are intersected, i.e. all terms must be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not + co-located (anti-affinity) with, where co-located + is defined as running on a node whose value of the + label with key <topologyKey> matches that of any node + on which a pod of the set of pods is running + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by this + field and the ones listed in the namespaces field. + null selector and null or empty namespaces list + means "this pod's namespace". An empty selector + ({}) matches all namespaces. This field is beta-level + and is only honored when PodAffinityNamespaceSelector + feature is enabled. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. The + term is applied to the union of the namespaces + listed in this field and the ones selected by + namespaceSelector. null or empty namespaces list + and null namespaceSelector means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey + matches that of any node on which any of the selected + pods is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules + (e.g. avoid putting this pod in the same node, zone, etc. + as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods + to nodes that satisfy the anti-affinity expressions + specified by this field, but it may choose a node that + violates one or more of the expressions. The node that + is most preferred is the one with the greatest sum of + weights, i.e. for each node that meets all of the scheduling + requirements (resource request, requiredDuringScheduling + anti-affinity expressions, etc.), compute a sum by iterating + through the elements of this field and adding "weight" + to the sum if the node has pods which matches the corresponding + podAffinityTerm; the node(s) with the highest sum are + the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by + this field and the ones listed in the namespaces + field. null selector and null or empty namespaces + list means "this pod's namespace". An empty + selector ({}) matches all namespaces. This + field is beta-level and is only honored when + PodAffinityNamespaceSelector feature is enabled. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. + The term is applied to the union of the namespaces + listed in this field and the ones selected + by namespaceSelector. null or empty namespaces + list and null namespaceSelector means "this + pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the + pods matching the labelSelector in the specified + namespaces, where co-located is defined as + running on a node whose value of the label + with key topologyKey matches that of any node + on which any of the selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the + corresponding podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified + by this field are not met at scheduling time, the pod + will not be scheduled onto the node. If the anti-affinity + requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod + label update), the system may or may not try to eventually + evict the pod from its node. When there are multiple + elements, the lists of nodes corresponding to each podAffinityTerm + are intersected, i.e. all terms must be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not + co-located (anti-affinity) with, where co-located + is defined as running on a node whose value of the + label with key <topologyKey> matches that of any node + on which a pod of the set of pods is running + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by this + field and the ones listed in the namespaces field. + null selector and null or empty namespaces list + means "this pod's namespace". An empty selector + ({}) matches all namespaces. This field is beta-level + and is only honored when PodAffinityNamespaceSelector + feature is enabled. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. The + term is applied to the union of the namespaces + listed in this field and the ones selected by + namespaceSelector. null or empty namespaces list + and null namespaceSelector means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey + matches that of any node on which any of the selected + pods is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + nodeSelector: + additionalProperties: + type: string + description: 'NodeSelector is a selector which must be true for + the pod to fit on a node. Selector which must match a node''s + labels for the pod to be scheduled on that node. More info: + https://kubernetes.io/docs/concepts/configuration/assign-pod-node/' + type: object + resources: + description: Defines the resource requests and limits for the + Mattermost app server pods. + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, otherwise + to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + tolerations: + description: 'Defines tolerations for the Mattermost app server + pods More info: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/' + items: + description: The pod this Toleration is attached to tolerates + any taint that matches the triple <key,value,effect> using + the matching operator <operator>. + properties: + effect: + description: Effect indicates the taint effect to match. + Empty means match all taint effects. When specified, allowed + values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies + to. Empty means match all taint keys. If the key is empty, + operator must be Exists; this combination means to match + all values and all keys. + type: string + operator: + description: Operator represents a key's relationship to + the value. Valid operators are Exists and Equal. Defaults + to Equal. Exists is equivalent to wildcard for value, + so that a pod can tolerate all taints of a particular + category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of + time the toleration (which must be of effect NoExecute, + otherwise this field is ignored) tolerates the taint. + By default, it is not set, which means tolerate the taint + forever (do not evict). Zero and negative values will + be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches + to. If the operator is Exists, the value should be empty, + otherwise just a regular string. + type: string + type: object + type: array + type: object + serviceAnnotations: + additionalProperties: + type: string + type: object + size: + description: 'Size defines the size of the Mattermost. This is typically + specified in number of users. This will override replica and resource + requests/limits appropriately for the provided number of users. + This is a write-only field - its value is erased after setting appropriate + values of resources. Accepted values are: 100users, 1000users, 5000users, + 10000users, and 250000users. If replicas and resource requests/limits + are not specified, and Size is not provided the configuration for + 5000users will be applied. Setting ''Replicas'', ''Scheduling.Resources'', + ''FileStore.Replicas'', ''FileStore.Resource'', ''Database.Replicas'', + or ''Database.Resources'' will override the values set by Size. + Setting new Size will override previous values regardless if set + by Size or manually.' + type: string + updateJob: + description: UpdateJob defines configuration for the template for + the update job. + properties: + disabled: + description: Determines whether to disable the Operator's creation + of the update job. + type: boolean + extraAnnotations: + additionalProperties: + type: string + description: Defines annotations to add to the update job pod. + type: object + extraLabels: + additionalProperties: + type: string + description: Defines labels to add to the update job pod. Overrides + what is set in ResourceLabels, does not override default label + (app label). + type: object + type: object + useIngressTLS: + description: 'UseIngressTLS specifies whether TLS secret should be + configured for Ingress. Deprecated: Use Spec.Ingress.TLSSecret.' + type: boolean + useServiceLoadBalancer: + type: boolean + version: + description: Version defines the Mattermost Docker image version. + type: string + volumeMounts: + description: Defines additional volumeMounts to add to Mattermost + application pods. + items: + description: VolumeMount describes a mounting of a Volume within + a container. + properties: + mountPath: + description: Path within the container at which the volume should + be mounted. Must not contain ':'. + type: string + mountPropagation: + description: mountPropagation determines how mounts are propagated + from the host to container and the other way around. When + not set, MountPropagationNone is used. This field is beta + in 1.10. + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: Mounted read-only if true, read-write otherwise + (false or unspecified). Defaults to false. + type: boolean + subPath: + description: Path within the volume from which the container's + volume should be mounted. Defaults to "" (volume's root). + type: string + subPathExpr: + description: Expanded path within the volume from which the + container's volume should be mounted. Behaves similarly to + SubPath but environment variable references $(VAR_NAME) are + expanded using the container's environment. Defaults to "" + (volume's root). SubPathExpr and SubPath are mutually exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + volumes: + description: Volumes allows for mounting volumes from various sources + into the Mattermost application pods. + items: + description: Volume represents a named volume in a pod that may + be accessed by any container in the pod. + properties: + awsElasticBlockStore: + description: 'AWSElasticBlockStore represents an AWS Disk resource + that is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + properties: + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", + "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + partition: + description: 'The partition in the volume that you want + to mount. If omitted, the default is to mount by volume + name. Examples: For volume /dev/sda1, you specify the + partition as "1". Similarly, the volume partition for + /dev/sda is "0" (or you can leave the property empty).' + format: int32 + type: integer + readOnly: + description: 'Specify "true" to force and set the ReadOnly + property in VolumeMounts to "true". If omitted, the default + is "false". More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: boolean + volumeID: + description: 'Unique ID of the persistent disk resource + in AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: string + required: + - volumeID + type: object + azureDisk: + description: AzureDisk represents an Azure Data Disk mount on + the host and bind mount to the pod. + properties: + cachingMode: + description: 'Host Caching mode: None, Read Only, Read Write.' + type: string + diskName: + description: The Name of the data disk in the blob storage + type: string + diskURI: + description: The URI the data disk in the blob storage + type: string + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + kind: + description: 'Expected values Shared: multiple blob disks + per storage account Dedicated: single blob disk per storage + account Managed: azure managed data disk (only in managed + availability set). defaults to shared' + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + description: AzureFile represents an Azure File Service mount + on the host and bind mount to the pod. + properties: + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + secretName: + description: the name of secret that contains Azure Storage + Account Name and Key + type: string + shareName: + description: Share Name + type: string + required: + - secretName + - shareName + type: object + cephfs: + description: CephFS represents a Ceph FS mount on the host that + shares a pod's lifetime + properties: + monitors: + description: 'Required: Monitors is a collection of Ceph + monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + items: + type: string + type: array + path: + description: 'Optional: Used as the mounted root, rather + than the full Ceph tree, default is /' + type: string + readOnly: + description: 'Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: boolean + secretFile: + description: 'Optional: SecretFile is the path to key ring + for User, default is /etc/ceph/user.secret More info: + https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + secretRef: + description: 'Optional: SecretRef is reference to the authentication + secret for User, default is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + user: + description: 'Optional: User is the rados user name, default + is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + required: + - monitors + type: object + cinder: + description: 'Cinder represents a cinder volume attached and + mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + properties: + fsType: + description: 'Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + readOnly: + description: 'Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: boolean + secretRef: + description: 'Optional: points to a secret object containing + parameters used to connect to OpenStack.' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + volumeID: + description: 'volume id used to identify the volume in cinder. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + required: + - volumeID + type: object + configMap: + description: ConfigMap represents a configMap that should populate + this volume + properties: + defaultMode: + description: 'Optional: mode bits used to set permissions + on created files by default. Must be an octal value between + 0000 and 0777 or a decimal value between 0 and 511. YAML + accepts both octal and decimal values, JSON requires decimal + values for mode bits. Defaults to 0644. Directories within + the path are not affected by this setting. This might + be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits + set.' + format: int32 + type: integer + items: + description: If unspecified, each key-value pair in the + Data field of the referenced ConfigMap will be projected + into the volume as a file whose name is the key and content + is the value. If specified, the listed keys will be projected + into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in + the ConfigMap, the volume setup will error unless it is + marked optional. Paths must be relative and may not contain + the '..' path or start with '..'. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits used to set permissions + on this file. Must be an octal value between 0000 + and 0777 or a decimal value between 0 and 511. YAML + accepts both octal and decimal values, JSON requires + decimal values for mode bits. If not specified, + the volume defaultMode will be used. This might + be in conflict with other options that affect the + file mode, like fsGroup, and the result can be other + mode bits set.' + format: int32 + type: integer + path: + description: The relative path of the file to map + the key to. May not be an absolute path. May not + contain the path element '..'. May not start with + the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap or its keys must + be defined + type: boolean + type: object + csi: + description: CSI (Container Storage Interface) represents ephemeral + storage that is handled by certain external CSI drivers (Beta + feature). + properties: + driver: + description: Driver is the name of the CSI driver that handles + this volume. Consult with your admin for the correct name + as registered in the cluster. + type: string + fsType: + description: Filesystem type to mount. Ex. "ext4", "xfs", + "ntfs". If not provided, the empty value is passed to + the associated CSI driver which will determine the default + filesystem to apply. + type: string + nodePublishSecretRef: + description: NodePublishSecretRef is a reference to the + secret object containing sensitive information to pass + to the CSI driver to complete the CSI NodePublishVolume + and NodeUnpublishVolume calls. This field is optional, + and may be empty if no secret is required. If the secret + object contains more than one secret, all secret references + are passed. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + readOnly: + description: Specifies a read-only configuration for the + volume. Defaults to false (read/write). + type: boolean + volumeAttributes: + additionalProperties: + type: string + description: VolumeAttributes stores driver-specific properties + that are passed to the CSI driver. Consult your driver's + documentation for supported values. + type: object + required: + - driver + type: object + downwardAPI: + description: DownwardAPI represents downward API about the pod + that should populate this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on created files + by default. Must be a Optional: mode bits used to set + permissions on created files by default. Must be an octal + value between 0000 and 0777 or a decimal value between + 0 and 511. YAML accepts both octal and decimal values, + JSON requires decimal values for mode bits. Defaults to + 0644. Directories within the path are not affected by + this setting. This might be in conflict with other options + that affect the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + items: + description: Items is a list of downward API volume file + items: + description: DownwardAPIVolumeFile represents information + to create the file containing the pod field + properties: + fieldRef: + description: 'Required: Selects a field of the pod: + only annotations, labels, name and namespace are + supported.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + mode: + description: 'Optional: mode bits used to set permissions + on this file, must be an octal value between 0000 + and 0777 or a decimal value between 0 and 511. YAML + accepts both octal and decimal values, JSON requires + decimal values for mode bits. If not specified, + the volume defaultMode will be used. This might + be in conflict with other options that affect the + file mode, like fsGroup, and the result can be other + mode bits set.' + format: int32 + type: integer + path: + description: 'Required: Path is the relative path + name of the file to be created. Must not be absolute + or contain the ''..'' path. Must be utf-8 encoded. + The first item of the relative path must not start + with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the container: + only resources limits and requests (limits.cpu, + limits.memory, requests.cpu and requests.memory) + are currently supported.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + emptyDir: + description: 'EmptyDir represents a temporary directory that + shares a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + properties: + medium: + description: 'What type of storage medium should back this + directory. The default is "" which means to use the node''s + default medium. Must be an empty string (default) or Memory. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + description: 'Total amount of local storage required for + this EmptyDir volume. The size limit is also applicable + for memory medium. The maximum usage on memory medium + EmptyDir would be the minimum value between the SizeLimit + specified here and the sum of memory limits of all containers + in a pod. The default is nil which means that the limit + is undefined. More info: http://kubernetes.io/docs/user-guide/volumes#emptydir' + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + description: "Ephemeral represents a volume that is handled + by a cluster storage driver. The volume's lifecycle is tied + to the pod that defines it - it will be created before the + pod starts, and deleted when the pod is removed. \n Use this + if: a) the volume is only needed while the pod runs, b) features + of normal volumes like restoring from snapshot or capacity + \ tracking are needed, c) the storage driver is specified + through a storage class, and d) the storage driver supports + dynamic volume provisioning through a PersistentVolumeClaim + (see EphemeralVolumeSource for more information on the + connection between this volume type and PersistentVolumeClaim). + \n Use PersistentVolumeClaim or one of the vendor-specific + APIs for volumes that persist for longer than the lifecycle + of an individual pod. \n Use CSI for light-weight local ephemeral + volumes if the CSI driver is meant to be used that way - see + the documentation of the driver for more information. \n A + pod can use both types of ephemeral volumes and persistent + volumes at the same time." + properties: + volumeClaimTemplate: + description: "Will be used to create a stand-alone PVC to + provision the volume. The pod in which this EphemeralVolumeSource + is embedded will be the owner of the PVC, i.e. the PVC + will be deleted together with the pod. The name of the + PVC will be `<pod name>-<volume name>` where `<volume + name>` is the name from the `PodSpec.Volumes` array entry. + Pod validation will reject the pod if the concatenated + name is not valid for a PVC (for example, too long). \n + An existing PVC with that name that is not owned by the + pod will *not* be used for the pod to avoid using an unrelated + volume by mistake. Starting the pod is then blocked until + the unrelated PVC is removed. If such a pre-created PVC + is meant to be used by the pod, the PVC has to updated + with an owner reference to the pod once the pod exists. + Normally this should not be necessary, but it may be useful + when manually reconstructing a broken cluster. \n This + field is read-only and no changes will be made by Kubernetes + to the PVC after it has been created. \n Required, must + not be nil." + properties: + metadata: + description: May contain labels and annotations that + will be copied into the PVC when creating it. No other + fields are allowed and will be rejected during validation. + type: object + spec: + description: The specification for the PersistentVolumeClaim. + The entire content is copied unchanged into the PVC + that gets created from this template. The same fields + as in a PersistentVolumeClaim are also valid here. + properties: + accessModes: + description: 'AccessModes contains the desired access + modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' + items: + type: string + type: array + dataSource: + description: 'This field can be used to specify + either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) + * An existing PVC (PersistentVolumeClaim) If the + provisioner or an external controller can support + the specified data source, it will create a new + volume based on the contents of the specified + data source. If the AnyVolumeDataSource feature + gate is enabled, this field will always have the + same contents as the DataSourceRef field.' + properties: + apiGroup: + description: APIGroup is the group for the resource + being referenced. If APIGroup is not specified, + the specified Kind must be in the core API + group. For any other third-party types, APIGroup + is required. + type: string + kind: + description: Kind is the type of resource being + referenced + type: string + name: + description: Name is the name of resource being + referenced + type: string + required: + - kind + - name + type: object + dataSourceRef: + description: 'Specifies the object from which to + populate the volume with data, if a non-empty + volume is desired. This may be any local object + from a non-empty API group (non core object) or + a PersistentVolumeClaim object. When this field + is specified, volume binding will only succeed + if the type of the specified object matches some + installed volume populator or dynamic provisioner. + This field will replace the functionality of the + DataSource field and as such if both fields are + non-empty, they must have the same value. For + backwards compatibility, both fields (DataSource + and DataSourceRef) will be set to the same value + automatically if one of them is empty and the + other is non-empty. There are two important differences + between DataSource and DataSourceRef: * While + DataSource only allows two specific types of objects, + DataSourceRef allows any non-core object, as + well as PersistentVolumeClaim objects. * While + DataSource ignores disallowed values (dropping + them), DataSourceRef preserves all values, and + generates an error if a disallowed value is specified. + (Alpha) Using this field requires the AnyVolumeDataSource + feature gate to be enabled.' + properties: + apiGroup: + description: APIGroup is the group for the resource + being referenced. If APIGroup is not specified, + the specified Kind must be in the core API + group. For any other third-party types, APIGroup + is required. + type: string + kind: + description: Kind is the type of resource being + referenced + type: string + name: + description: Name is the name of resource being + referenced + type: string + required: + - kind + - name + type: object + resources: + description: 'Resources represents the minimum resources + the volume should have. If RecoverVolumeExpansionFailure + feature is enabled users are allowed to specify + resource requirements that are lower than previous + value but must still be higher than capacity recorded + in the status field of the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount + of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum + amount of compute resources required. If Requests + is omitted for a container, it defaults to + Limits if that is explicitly specified, otherwise + to an implementation-defined value. More info: + https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + selector: + description: A label query over volumes to consider + for binding. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + storageClassName: + description: 'Name of the StorageClass required + by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' + type: string + volumeMode: + description: volumeMode defines what type of volume + is required by the claim. Value of Filesystem + is implied when not included in claim spec. + type: string + volumeName: + description: VolumeName is the binding reference + to the PersistentVolume backing this claim. + type: string + type: object + required: + - spec + type: object + type: object + fc: + description: FC represents a Fibre Channel resource that is + attached to a kubelet's host machine and then exposed to the + pod. + properties: + fsType: + description: 'Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + lun: + description: 'Optional: FC target lun number' + format: int32 + type: integer + readOnly: + description: 'Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts.' + type: boolean + targetWWNs: + description: 'Optional: FC target worldwide names (WWNs)' + items: + type: string + type: array + wwids: + description: 'Optional: FC volume world wide identifiers + (wwids) Either wwids or combination of targetWWNs and + lun must be set, but not both simultaneously.' + items: + type: string + type: array + type: object + flexVolume: + description: FlexVolume represents a generic volume resource + that is provisioned/attached using an exec based plugin. + properties: + driver: + description: Driver is the name of the driver to use for + this volume. + type: string + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". The default filesystem depends on FlexVolume + script. + type: string + options: + additionalProperties: + type: string + description: 'Optional: Extra command options if any.' + type: object + readOnly: + description: 'Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts.' + type: boolean + secretRef: + description: 'Optional: SecretRef is reference to the secret + object containing sensitive information to pass to the + plugin scripts. This may be empty if no secret object + is specified. If the secret object contains more than + one secret, all secrets are passed to the plugin scripts.' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + required: + - driver + type: object + flocker: + description: Flocker represents a Flocker volume attached to + a kubelet's host machine. This depends on the Flocker control + service being running + properties: + datasetName: + description: Name of the dataset stored as metadata -> name + on the dataset for Flocker should be considered as deprecated + type: string + datasetUUID: + description: UUID of the dataset. This is unique identifier + of a Flocker dataset + type: string + type: object + gcePersistentDisk: + description: 'GCEPersistentDisk represents a GCE Disk resource + that is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + properties: + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", + "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + partition: + description: 'The partition in the volume that you want + to mount. If omitted, the default is to mount by volume + name. Examples: For volume /dev/sda1, you specify the + partition as "1". Similarly, the volume partition for + /dev/sda is "0" (or you can leave the property empty). + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + format: int32 + type: integer + pdName: + description: 'Unique name of the PD resource in GCE. Used + to identify the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: string + readOnly: + description: 'ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: boolean + required: + - pdName + type: object + gitRepo: + description: 'GitRepo represents a git repository at a particular + revision. DEPRECATED: GitRepo is deprecated. To provision + a container with a git repo, mount an EmptyDir into an InitContainer + that clones the repo using git, then mount the EmptyDir into + the Pod''s container.' + properties: + directory: + description: Target directory name. Must not contain or + start with '..'. If '.' is supplied, the volume directory + will be the git repository. Otherwise, if specified, + the volume will contain the git repository in the subdirectory + with the given name. + type: string + repository: + description: Repository URL + type: string + revision: + description: Commit hash for the specified revision. + type: string + required: + - repository + type: object + glusterfs: + description: 'Glusterfs represents a Glusterfs mount on the + host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md' + properties: + endpoints: + description: 'EndpointsName is the endpoint name that details + Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + path: + description: 'Path is the Glusterfs volume path. More info: + https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + readOnly: + description: 'ReadOnly here will force the Glusterfs volume + to be mounted with read-only permissions. Defaults to + false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: boolean + required: + - endpoints + - path + type: object + hostPath: + description: 'HostPath represents a pre-existing file or directory + on the host machine that is directly exposed to the container. + This is generally used for system agents or other privileged + things that are allowed to see the host machine. Most containers + will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + --- TODO(jonesdl) We need to restrict who can use host directory + mounts and who can/can not mount host directories as read/write.' + properties: + path: + description: 'Path of the directory on the host. If the + path is a symlink, it will follow the link to the real + path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + type: + description: 'Type for HostPath Volume Defaults to "" More + info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + required: + - path + type: object + iscsi: + description: 'ISCSI represents an ISCSI Disk resource that is + attached to a kubelet''s host machine and then exposed to + the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' + properties: + chapAuthDiscovery: + description: whether support iSCSI Discovery CHAP authentication + type: boolean + chapAuthSession: + description: whether support iSCSI Session CHAP authentication + type: boolean + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", + "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + initiatorName: + description: Custom iSCSI Initiator Name. If initiatorName + is specified with iscsiInterface simultaneously, new iSCSI + interface <target portal>:<volume name> will be created + for the connection. + type: string + iqn: + description: Target iSCSI Qualified Name. + type: string + iscsiInterface: + description: iSCSI Interface Name that uses an iSCSI transport. + Defaults to 'default' (tcp). + type: string + lun: + description: iSCSI Target Lun number. + format: int32 + type: integer + portals: + description: iSCSI Target Portal List. The portal is either + an IP or ip_addr:port if the port is other than default + (typically TCP ports 860 and 3260). + items: + type: string + type: array + readOnly: + description: ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. + type: boolean + secretRef: + description: CHAP Secret for iSCSI target and initiator + authentication + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + targetPortal: + description: iSCSI Target Portal. The Portal is either an + IP or ip_addr:port if the port is other than default (typically + TCP ports 860 and 3260). + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + description: 'Volume''s name. Must be a DNS_LABEL and unique + within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + nfs: + description: 'NFS represents an NFS mount on the host that shares + a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + properties: + path: + description: 'Path that is exported by the NFS server. More + info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + readOnly: + description: 'ReadOnly here will force the NFS export to + be mounted with read-only permissions. Defaults to false. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: boolean + server: + description: 'Server is the hostname or IP address of the + NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + description: 'PersistentVolumeClaimVolumeSource represents a + reference to a PersistentVolumeClaim in the same namespace. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + properties: + claimName: + description: 'ClaimName is the name of a PersistentVolumeClaim + in the same namespace as the pod using this volume. More + info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: string + readOnly: + description: Will force the ReadOnly setting in VolumeMounts. + Default false. + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + description: PhotonPersistentDisk represents a PhotonController + persistent disk attached and mounted on kubelets host machine + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + pdID: + description: ID that identifies Photon Controller persistent + disk + type: string + required: + - pdID + type: object + portworxVolume: + description: PortworxVolume represents a portworx volume attached + and mounted on kubelets host machine + properties: + fsType: + description: FSType represents the filesystem type to mount + Must be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + volumeID: + description: VolumeID uniquely identifies a Portworx volume + type: string + required: + - volumeID + type: object + projected: + description: Items for all in one resources secrets, configmaps, + and downward API + properties: + defaultMode: + description: Mode bits used to set permissions on created + files by default. Must be an octal value between 0000 + and 0777 or a decimal value between 0 and 511. YAML accepts + both octal and decimal values, JSON requires decimal values + for mode bits. Directories within the path are not affected + by this setting. This might be in conflict with other + options that affect the file mode, like fsGroup, and the + result can be other mode bits set. + format: int32 + type: integer + sources: + description: list of volume projections + items: + description: Projection that may be projected along with + other supported volume types + properties: + configMap: + description: information about the configMap data + to project + properties: + items: + description: If unspecified, each key-value pair + in the Data field of the referenced ConfigMap + will be projected into the volume as a file + whose name is the key and content is the value. + If specified, the listed keys will be projected + into the specified paths, and unlisted keys + will not be present. If a key is specified which + is not present in the ConfigMap, the volume + setup will error unless it is marked optional. + Paths must be relative and may not contain the + '..' path or start with '..'. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits used to + set permissions on this file. Must be + an octal value between 0000 and 0777 or + a decimal value between 0 and 511. YAML + accepts both octal and decimal values, + JSON requires decimal values for mode + bits. If not specified, the volume defaultMode + will be used. This might be in conflict + with other options that affect the file + mode, like fsGroup, and the result can + be other mode bits set.' + format: int32 + type: integer + path: + description: The relative path of the file + to map the key to. May not be an absolute + path. May not contain the path element + '..'. May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or + its keys must be defined + type: boolean + type: object + downwardAPI: + description: information about the downwardAPI data + to project + properties: + items: + description: Items is a list of DownwardAPIVolume + file + items: + description: DownwardAPIVolumeFile represents + information to create the file containing + the pod field + properties: + fieldRef: + description: 'Required: Selects a field + of the pod: only annotations, labels, + name and namespace are supported.' + properties: + apiVersion: + description: Version of the schema the + FieldPath is written in terms of, + defaults to "v1". + type: string + fieldPath: + description: Path of the field to select + in the specified API version. + type: string + required: + - fieldPath + type: object + mode: + description: 'Optional: mode bits used to + set permissions on this file, must be + an octal value between 0000 and 0777 or + a decimal value between 0 and 511. YAML + accepts both octal and decimal values, + JSON requires decimal values for mode + bits. If not specified, the volume defaultMode + will be used. This might be in conflict + with other options that affect the file + mode, like fsGroup, and the result can + be other mode bits set.' + format: int32 + type: integer + path: + description: 'Required: Path is the relative + path name of the file to be created. Must + not be absolute or contain the ''..'' + path. Must be utf-8 encoded. The first + item of the relative path must not start + with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the + container: only resources limits and requests + (limits.cpu, limits.memory, requests.cpu + and requests.memory) are currently supported.' + properties: + containerName: + description: 'Container name: required + for volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format + of the exposed resources, defaults + to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to + select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + secret: + description: information about the secret data to + project + properties: + items: + description: If unspecified, each key-value pair + in the Data field of the referenced Secret will + be projected into the volume as a file whose + name is the key and content is the value. If + specified, the listed keys will be projected + into the specified paths, and unlisted keys + will not be present. If a key is specified which + is not present in the Secret, the volume setup + will error unless it is marked optional. Paths + must be relative and may not contain the '..' + path or start with '..'. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits used to + set permissions on this file. Must be + an octal value between 0000 and 0777 or + a decimal value between 0 and 511. YAML + accepts both octal and decimal values, + JSON requires decimal values for mode + bits. If not specified, the volume defaultMode + will be used. This might be in conflict + with other options that affect the file + mode, like fsGroup, and the result can + be other mode bits set.' + format: int32 + type: integer + path: + description: The relative path of the file + to map the key to. May not be an absolute + path. May not contain the path element + '..'. May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its + key must be defined + type: boolean + type: object + serviceAccountToken: + description: information about the serviceAccountToken + data to project + properties: + audience: + description: Audience is the intended audience + of the token. A recipient of a token must identify + itself with an identifier specified in the audience + of the token, and otherwise should reject the + token. The audience defaults to the identifier + of the apiserver. + type: string + expirationSeconds: + description: ExpirationSeconds is the requested + duration of validity of the service account + token. As the token approaches expiration, the + kubelet volume plugin will proactively rotate + the service account token. The kubelet will + start trying to rotate the token if the token + is older than 80 percent of its time to live + or if the token is older than 24 hours.Defaults + to 1 hour and must be at least 10 minutes. + format: int64 + type: integer + path: + description: Path is the path relative to the + mount point of the file to project the token + into. + type: string + required: + - path + type: object + type: object + type: array + type: object + quobyte: + description: Quobyte represents a Quobyte mount on the host + that shares a pod's lifetime + properties: + group: + description: Group to map volume access to Default is no + group + type: string + readOnly: + description: ReadOnly here will force the Quobyte volume + to be mounted with read-only permissions. Defaults to + false. + type: boolean + registry: + description: Registry represents a single or multiple Quobyte + Registry services specified as a string as host:port pair + (multiple entries are separated with commas) which acts + as the central registry for volumes + type: string + tenant: + description: Tenant owning the given Quobyte volume in the + Backend Used with dynamically provisioned Quobyte volumes, + value is set by the plugin + type: string + user: + description: User to map volume access to Defaults to serivceaccount + user + type: string + volume: + description: Volume is a string that references an already + created Quobyte volume by name. + type: string + required: + - registry + - volume + type: object + rbd: + description: 'RBD represents a Rados Block Device mount on the + host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md' + properties: + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", + "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + image: + description: 'The rados image name. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + keyring: + description: 'Keyring is the path to key ring for RBDUser. + Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + monitors: + description: 'A collection of Ceph monitors. More info: + https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + items: + type: string + type: array + pool: + description: 'The rados pool name. Default is rbd. More + info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + readOnly: + description: 'ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: boolean + secretRef: + description: 'SecretRef is name of the authentication secret + for RBDUser. If provided overrides keyring. Default is + nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + user: + description: 'The rados user name. Default is admin. More + info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + required: + - image + - monitors + type: object + scaleIO: + description: ScaleIO represents a ScaleIO persistent volume + attached and mounted on Kubernetes nodes. + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Default is "xfs". + type: string + gateway: + description: The host address of the ScaleIO API Gateway. + type: string + protectionDomain: + description: The name of the ScaleIO Protection Domain for + the configured storage. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef references to the secret for ScaleIO + user and other sensitive information. If this is not provided, + Login operation will fail. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + sslEnabled: + description: Flag to enable/disable SSL communication with + Gateway, default false + type: boolean + storageMode: + description: Indicates whether the storage for a volume + should be ThickProvisioned or ThinProvisioned. Default + is ThinProvisioned. + type: string + storagePool: + description: The ScaleIO Storage Pool associated with the + protection domain. + type: string + system: + description: The name of the storage system as configured + in ScaleIO. + type: string + volumeName: + description: The name of a volume already created in the + ScaleIO system that is associated with this volume source. + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + description: 'Secret represents a secret that should populate + this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + properties: + defaultMode: + description: 'Optional: mode bits used to set permissions + on created files by default. Must be an octal value between + 0000 and 0777 or a decimal value between 0 and 511. YAML + accepts both octal and decimal values, JSON requires decimal + values for mode bits. Defaults to 0644. Directories within + the path are not affected by this setting. This might + be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits + set.' + format: int32 + type: integer + items: + description: If unspecified, each key-value pair in the + Data field of the referenced Secret will be projected + into the volume as a file whose name is the key and content + is the value. If specified, the listed keys will be projected + into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in + the Secret, the volume setup will error unless it is marked + optional. Paths must be relative and may not contain the + '..' path or start with '..'. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits used to set permissions + on this file. Must be an octal value between 0000 + and 0777 or a decimal value between 0 and 511. YAML + accepts both octal and decimal values, JSON requires + decimal values for mode bits. If not specified, + the volume defaultMode will be used. This might + be in conflict with other options that affect the + file mode, like fsGroup, and the result can be other + mode bits set.' + format: int32 + type: integer + path: + description: The relative path of the file to map + the key to. May not be an absolute path. May not + contain the path element '..'. May not start with + the string '..'. + type: string + required: + - key + - path + type: object + type: array + optional: + description: Specify whether the Secret or its keys must + be defined + type: boolean + secretName: + description: 'Name of the secret in the pod''s namespace + to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + type: object + storageos: + description: StorageOS represents a StorageOS volume attached + and mounted on Kubernetes nodes. + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef specifies the secret to use for obtaining + the StorageOS API credentials. If not specified, default + values will be attempted. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + volumeName: + description: VolumeName is the human-readable name of the + StorageOS volume. Volume names are only unique within + a namespace. + type: string + volumeNamespace: + description: VolumeNamespace specifies the scope of the + volume within StorageOS. If no namespace is specified + then the Pod's namespace will be used. This allows the + Kubernetes name scoping to be mirrored within StorageOS + for tighter integration. Set VolumeName to any name to + override the default behaviour. Set to "default" if you + are not using namespaces within StorageOS. Namespaces + that do not pre-exist within StorageOS will be created. + type: string + type: object + vsphereVolume: + description: VsphereVolume represents a vSphere volume attached + and mounted on kubelets host machine + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + storagePolicyID: + description: Storage Policy Based Management (SPBM) profile + ID associated with the StoragePolicyName. + type: string + storagePolicyName: + description: Storage Policy Based Management (SPBM) profile + name. + type: string + volumePath: + description: Path that identifies vSphere volume vmdk + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + type: object + status: + description: MattermostStatus defines the observed state of Mattermost + properties: + endpoint: + description: The endpoint to access the Mattermost instance + type: string + error: + description: The last observed error in the deployment of this Mattermost + instance + type: string + image: + description: The image running on the pods in the Mattermost instance + type: string + observedGeneration: + description: The last observed Generation of the Mattermost resource + that was acted on. + format: int64 + type: integer + replicas: + description: Total number of non-terminated pods targeted by this + Mattermost deployment + format: int32 + type: integer + resourcePatch: + description: Status of specified resource patches. + properties: + deploymentPatch: + description: PatchStatus represents status of particular patch. + properties: + applied: + type: boolean + error: + type: string + type: object + servicePatch: + description: PatchStatus represents status of particular patch. + properties: + applied: + type: boolean + error: + type: string + type: object + type: object + state: + description: Represents the running state of the Mattermost instance + type: string + updatedReplicas: + description: Total number of non-terminated pods targeted by this + Mattermost deployment that are running with the desired image. + format: int32 + type: integer + version: + description: The version currently running in the Mattermost instance + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] diff --git a/chart/templates/_helpers.tpl b/chart/templates/_helpers.tpl deleted file mode 100644 index 5a1108e9a..000000000 --- a/chart/templates/_helpers.tpl +++ /dev/null @@ -1,61 +0,0 @@ -{{/* -Expand the name of the chart. -*/}} -{{- define "mattermost.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 "mattermost.fullname" -}} -{{- if .Values.fullnameOverride }} -{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- $name := default .Chart.Name .Values.nameOverride }} -{{- if contains $name .Release.Name }} -{{- .Release.Name | trunc 63 | trimSuffix "-" }} -{{- else }} -{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} -{{- end }} -{{- end }} -{{- end }} - -{{/* -Create chart name and version as used by the chart label. -*/}} -{{- define "mattermost.chart" -}} -{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} -{{- end }} - -{{/* -Common labels -*/}} -{{- define "mattermost.labels" -}} -helm.sh/chart: {{ include "mattermost.chart" . }} -{{ include "mattermost.selectorLabels" . }} -app.kubernetes.io/version: {{ .Chart.Version | quote }} -app.kubernetes.io/managed-by: {{ .Release.Service }} -{{- end }} - -{{/* -Selector labels -*/}} -{{- define "mattermost.selectorLabels" -}} -app.kubernetes.io/name: {{ include "mattermost.name" . }} -app.kubernetes.io/instance: {{ .Release.Name }} -app.kubernetes.io/part-of: {{ .Release.Name }} -{{- end }} - -{{/* -Create the name of the service account to use -*/}} -{{- define "mattermost.serviceAccountName" -}} -{{- if .Values.serviceAccount.create }} -{{- default (include "mattermost.fullname" .) .Values.serviceAccount.name }} -{{- else }} -{{- default "default" .Values.serviceAccount.name }} -{{- end }} -{{- end }} diff --git a/chart/templates/bigbang/mattermost-dashboards.yaml b/chart/templates/bigbang/mattermost-dashboards.yaml deleted file mode 100644 index 784fe63d0..000000000 --- a/chart/templates/bigbang/mattermost-dashboards.yaml +++ /dev/null @@ -1,12 +0,0 @@ -{{- if and .Values.monitoring.enabled .Values.enterprise.enabled }} -apiVersion: v1 -kind: ConfigMap -metadata: - name: mattermost-grafana-dashboards - namespace: monitoring - labels: - grafana_dashboard: "1" -data: - mattermost-performance-monitoring-v2_rev2.json: | - {{ .Files.Get "dashboards/mattermost-performance-monitoring-v2_rev2.json" | nindent 4 }} -{{- end }} \ No newline at end of file diff --git a/chart/templates/bigbang/mattermost-gossip-svc.yaml b/chart/templates/bigbang/mattermost-gossip-svc.yaml deleted file mode 100644 index f00f70161..000000000 --- a/chart/templates/bigbang/mattermost-gossip-svc.yaml +++ /dev/null @@ -1,20 +0,0 @@ -{{- /* Mattermost uses a gossip protocol for HA clustering. In order for Istio to properly route this traffic it needs to be explicitly defined in a service with a `tcp-` prefix. */ -}} -{{- if (eq .Values.istio.injection "enabled") }} -apiVersion: v1 -kind: Service -metadata: - name: mattermost-gossip - namespace: {{ .Release.Namespace }} - labels: - app: mattermost -spec: - type: ClusterIP - clusterIP: None - selector: - app: mattermost - ports: - - name: tcp-gossip - port: 8074 - protocol: TCP - targetPort: 8074 -{{- end }} diff --git a/chart/templates/bigbang/networkpolicies/allow-egress-tempo.yaml b/chart/templates/bigbang/networkpolicies/allow-egress-tempo.yaml deleted file mode 100644 index 7d7e1d37c..000000000 --- a/chart/templates/bigbang/networkpolicies/allow-egress-tempo.yaml +++ /dev/null @@ -1,21 +0,0 @@ -{{- if and .Values.networkPolicies.enabled (eq .Values.istio.injection "enabled") }} -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: allow-tempo-egress - namespace: {{ .Release.Namespace }} -spec: - podSelector: {} - policyTypes: - - Egress - egress: - - to: - - namespaceSelector: - matchLabels: - app.kubernetes.io/name: tempo - podSelector: - matchLabels: - app.kubernetes.io/name: tempo - ports: - - port: 9411 -{{- end }} diff --git a/chart/templates/bigbang/networkpolicies/allow-elastic-egress.yaml b/chart/templates/bigbang/networkpolicies/allow-elastic-egress.yaml deleted file mode 100644 index 6946600c3..000000000 --- a/chart/templates/bigbang/networkpolicies/allow-elastic-egress.yaml +++ /dev/null @@ -1,24 +0,0 @@ -{{- if and .Values.networkPolicies.enabled .Values.elasticsearch.enabled }} -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: allow-elastic-egress - namespace: {{ .Release.Namespace }} -spec: - podSelector: - matchLabels: - app: mattermost - policyTypes: - - Egress - egress: - - to: - - namespaceSelector: - matchLabels: - app.kubernetes.io/name: logging - podSelector: - matchLabels: - common.k8s.elastic.co/type: elasticsearch - ports: - - port: 9200 - protocol: TCP -{{- end }} \ No newline at end of file diff --git a/chart/templates/bigbang/networkpolicies/allow-external-postgres.yaml b/chart/templates/bigbang/networkpolicies/allow-external-postgres.yaml deleted file mode 100644 index c400a930b..000000000 --- a/chart/templates/bigbang/networkpolicies/allow-external-postgres.yaml +++ /dev/null @@ -1,20 +0,0 @@ -{{- if and .Values.networkPolicies.enabled (not .Values.postgresql.install) }} -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: allow-external-postgres-egress-upgrade - namespace: {{ .Release.Namespace }} -spec: - podSelector: - matchLabels: - app: mattermost-update-check - policyTypes: - - Egress - egress: - - to: - - ipBlock: - cidr: 0.0.0.0/0 - # ONLY Block requests to AWS metadata IP - except: - - 169.254.169.254/32 -{{- end }} diff --git a/chart/templates/bigbang/networkpolicies/allow-in-ns.yaml b/chart/templates/bigbang/networkpolicies/allow-in-ns.yaml deleted file mode 100644 index 495131c6c..000000000 --- a/chart/templates/bigbang/networkpolicies/allow-in-ns.yaml +++ /dev/null @@ -1,18 +0,0 @@ -{{- if .Values.networkPolicies.enabled }} -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: allow-in-ns - namespace: {{ .Release.Namespace }} -spec: - podSelector: {} - policyTypes: - - Ingress - - Egress - ingress: - - from: - - podSelector: {} - egress: - - to: - - podSelector: {} -{{- end }} \ No newline at end of file diff --git a/chart/templates/bigbang/networkpolicies/allow-istio-egress.yaml b/chart/templates/bigbang/networkpolicies/allow-istio-egress.yaml deleted file mode 100644 index 9e4284712..000000000 --- a/chart/templates/bigbang/networkpolicies/allow-istio-egress.yaml +++ /dev/null @@ -1,21 +0,0 @@ -{{- if and .Values.networkPolicies.enabled (eq .Values.istio.injection "enabled") }} -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: allow-istio-egress - namespace: {{ .Release.Namespace }} -spec: - podSelector: {} - policyTypes: - - Egress - egress: - - to: - - namespaceSelector: - matchLabels: - app.kubernetes.io/name: istio-controlplane - podSelector: - matchLabels: - istio: pilot - ports: - - port: 15012 -{{- end }} diff --git a/chart/templates/bigbang/networkpolicies/allow-istio-ingress.yaml b/chart/templates/bigbang/networkpolicies/allow-istio-ingress.yaml deleted file mode 100644 index c7d198755..000000000 --- a/chart/templates/bigbang/networkpolicies/allow-istio-ingress.yaml +++ /dev/null @@ -1,24 +0,0 @@ -{{- if and .Values.networkPolicies.enabled .Values.istio.enabled }} -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: allow-istio-ingress - namespace: {{ .Release.Namespace }} -spec: - podSelector: - matchLabels: - app: mattermost - policyTypes: - - Ingress - ingress: - - from: - - namespaceSelector: - matchLabels: - app.kubernetes.io/name: istio-controlplane - podSelector: - matchLabels: - {{- toYaml .Values.networkPolicies.ingressLabels | nindent 12 }} - ports: - - port: 8065 - protocol: TCP -{{- end }} \ No newline at end of file diff --git a/chart/templates/bigbang/networkpolicies/allow-mattermost-egress.yaml b/chart/templates/bigbang/networkpolicies/allow-mattermost-egress.yaml deleted file mode 100644 index ab17aa5bf..000000000 --- a/chart/templates/bigbang/networkpolicies/allow-mattermost-egress.yaml +++ /dev/null @@ -1,20 +0,0 @@ -{{- if .Values.networkPolicies.enabled }} -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: allow-mattermost-egress - namespace: {{ .Release.Namespace }} -spec: - podSelector: - matchLabels: - app: mattermost - policyTypes: - - Egress - egress: - - to: - - ipBlock: - cidr: 0.0.0.0/0 - # ONLY Block requests to AWS metadata IP - except: - - 169.254.169.254/32 -{{- end }} diff --git a/chart/templates/bigbang/networkpolicies/allow-minio-monitoring.yaml b/chart/templates/bigbang/networkpolicies/allow-minio-monitoring.yaml deleted file mode 100644 index 539036065..000000000 --- a/chart/templates/bigbang/networkpolicies/allow-minio-monitoring.yaml +++ /dev/null @@ -1,22 +0,0 @@ -{{- if and .Values.networkPolicies.enabled .Values.monitoring.enabled .Values.minio.install .Values.minio.tenant.metrics.enabled }} -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: allow-minio-metrics-scraping - namespace: {{ .Release.Namespace }} -spec: - podSelector: - matchLabels: - app: minio - v1.min.io/tenant: mattermost-minio - policyTypes: - - Ingress - ingress: - - from: - - namespaceSelector: - matchLabels: - app.kubernetes.io/name: monitoring - ports: - - port: {{ .Values.minio.tenant.metrics.port }} - protocol: TCP -{{- end }} diff --git a/chart/templates/bigbang/networkpolicies/allow-minio-operator-egress.yaml b/chart/templates/bigbang/networkpolicies/allow-minio-operator-egress.yaml deleted file mode 100644 index 5457c88ee..000000000 --- a/chart/templates/bigbang/networkpolicies/allow-minio-operator-egress.yaml +++ /dev/null @@ -1,21 +0,0 @@ -{{- if and .Values.networkPolicies.enabled .Values.minio.install }} -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: allow-minio-operator-egress - namespace: {{ .Release.Namespace }} -spec: - podSelector: - matchLabels: - app: minio - policyTypes: - - Egress - egress: - - to: - - namespaceSelector: - matchLabels: - app.kubernetes.io/name: minioOperator - ports: - - port: 4222 - protocol: TCP -{{- end }} diff --git a/chart/templates/bigbang/networkpolicies/allow-minio-operator-ingress-upgrade.yaml b/chart/templates/bigbang/networkpolicies/allow-minio-operator-ingress-upgrade.yaml deleted file mode 100644 index f8a507008..000000000 --- a/chart/templates/bigbang/networkpolicies/allow-minio-operator-ingress-upgrade.yaml +++ /dev/null @@ -1,24 +0,0 @@ -{{- if and .Values.networkPolicies.enabled .Values.minio.install }} -# this can be removed after the upgrade to 4.4.3 -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: allow-minio-operator-ingress-upgrade - namespace: {{ .Release.Namespace }} - annotations: - "helm.sh/hook": pre-upgrade -spec: - ingress: - - from: - - namespaceSelector: - matchLabels: - app.kubernetes.io/name: minioOperator - ports: - - port: 9000 - protocol: TCP - podSelector: - matchLabels: - app: mattermost-minio - policyTypes: - - Ingress -{{- end }} diff --git a/chart/templates/bigbang/networkpolicies/allow-minio-operator-ingress.yaml b/chart/templates/bigbang/networkpolicies/allow-minio-operator-ingress.yaml deleted file mode 100644 index b742d0355..000000000 --- a/chart/templates/bigbang/networkpolicies/allow-minio-operator-ingress.yaml +++ /dev/null @@ -1,21 +0,0 @@ -{{- if and .Values.networkPolicies.enabled .Values.minio.install }} -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: allow-minio-operator-ingress - namespace: {{ .Release.Namespace }} -spec: - ingress: - - from: - - namespaceSelector: - matchLabels: - app.kubernetes.io/name: minioOperator - ports: - - port: 9000 - protocol: TCP - podSelector: - matchLabels: - app: minio - policyTypes: - - Ingress -{{- end }} diff --git a/chart/templates/bigbang/networkpolicies/allow-monitoring-ingress.yaml b/chart/templates/bigbang/networkpolicies/allow-monitoring-ingress.yaml deleted file mode 100644 index 63ed9dc21..000000000 --- a/chart/templates/bigbang/networkpolicies/allow-monitoring-ingress.yaml +++ /dev/null @@ -1,24 +0,0 @@ -{{- if and .Values.networkPolicies.enabled .Values.monitoring.enabled .Values.enterprise.enabled }} -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: allow-monitoring-ingress - namespace: {{ .Release.Namespace }} -spec: - podSelector: - matchLabels: - app: mattermost - policyTypes: - - Ingress - ingress: - - from: - - namespaceSelector: - matchLabels: - app.kubernetes.io/name: monitoring - podSelector: - matchLabels: - app: prometheus - ports: - - port: 8067 - protocol: TCP -{{- end }} \ No newline at end of file diff --git a/chart/templates/bigbang/networkpolicies/allow-sidecar-monitoring-ingress.yaml b/chart/templates/bigbang/networkpolicies/allow-sidecar-monitoring-ingress.yaml deleted file mode 100644 index 872d5b818..000000000 --- a/chart/templates/bigbang/networkpolicies/allow-sidecar-monitoring-ingress.yaml +++ /dev/null @@ -1,22 +0,0 @@ -{{- if and .Values.networkPolicies.enabled .Values.monitoring.enabled (eq .Values.istio.injection "enabled") }} -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: allow-prometheus-ingress - namespace: "{{ .Release.Namespace }}" -spec: - podSelector: {} - policyTypes: - - Ingress - ingress: - - from: - - namespaceSelector: - matchLabels: - app.kubernetes.io/name: monitoring - podSelector: - matchLabels: - app: prometheus - ports: - - port: 15020 - protocol: TCP -{{- end }} diff --git a/chart/templates/bigbang/networkpolicies/allow-test-egress.yaml b/chart/templates/bigbang/networkpolicies/allow-test-egress.yaml deleted file mode 100644 index a9ccfb392..000000000 --- a/chart/templates/bigbang/networkpolicies/allow-test-egress.yaml +++ /dev/null @@ -1,26 +0,0 @@ -{{- $bbtests := .Values.bbtests | default dict -}} -{{- $enabled := (hasKey $bbtests "enabled") -}} -{{- if $enabled }} -{{- if and .Values.networkPolicies.enabled .Values.bbtests.enabled }} -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: allow-test-egress - namespace: {{ .Release.Namespace }} -spec: - podSelector: - matchLabels: - helm-test: enabled - egress: - - to: - - ipBlock: - cidr: {{ .Values.networkPolicies.controlPlaneCidr }} - {{- if eq .Values.networkPolicies.controlPlaneCidr "0.0.0.0/0" }} - # ONLY Block requests to cloud metadata IP - except: - - 169.254.169.254/32 - {{- end }} - policyTypes: - - Egress -{{- end }} -{{- end }} diff --git a/chart/templates/bigbang/networkpolicies/deny-default.yaml b/chart/templates/bigbang/networkpolicies/default-deny.yaml similarity index 89% rename from chart/templates/bigbang/networkpolicies/deny-default.yaml rename to chart/templates/bigbang/networkpolicies/default-deny.yaml index c3f0c6b17..b8fe2231e 100644 --- a/chart/templates/bigbang/networkpolicies/deny-default.yaml +++ b/chart/templates/bigbang/networkpolicies/default-deny.yaml @@ -7,8 +7,8 @@ metadata: spec: podSelector: {} policyTypes: - - Ingress - - Egress + - Ingress + - Egress ingress: [] egress: [] {{- end }} \ No newline at end of file diff --git a/chart/templates/bigbang/networkpolicies/allow-dns-egress.yaml b/chart/templates/bigbang/networkpolicies/egress-dns.yaml similarity index 53% rename from chart/templates/bigbang/networkpolicies/allow-dns-egress.yaml rename to chart/templates/bigbang/networkpolicies/egress-dns.yaml index 1669d1371..fbf5d4a18 100644 --- a/chart/templates/bigbang/networkpolicies/allow-dns-egress.yaml +++ b/chart/templates/bigbang/networkpolicies/egress-dns.yaml @@ -7,16 +7,16 @@ metadata: spec: podSelector: {} policyTypes: - - Egress + - Egress # Allow access to DNS egress: - - to: - - namespaceSelector: {} - ports: - - port: 53 - protocol: UDP - {{- if .Values.openshift }} - - port: 5353 - protocol: UDP - {{- end }} -{{- end }} \ No newline at end of file + - to: + - namespaceSelector: {} + ports: + - port: 53 + protocol: UDP + {{- if .Values.openshift }} + - port: 5353 + protocol: UDP + {{- end }} +{{- end }} diff --git a/chart/templates/bigbang/networkpolicies/egress-istiod.yaml b/chart/templates/bigbang/networkpolicies/egress-istiod.yaml new file mode 100644 index 000000000..0c950e69d --- /dev/null +++ b/chart/templates/bigbang/networkpolicies/egress-istiod.yaml @@ -0,0 +1,21 @@ +{{- if and .Values.networkPolicies.enabled .Values.istio.enabled }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-istiod-egress + namespace: "{{ .Release.Namespace }}" +spec: + podSelector: {} + policyTypes: + - Egress + egress: + - to: + - namespaceSelector: + matchLabels: + app.kubernetes.io/name: istio-controlplane + podSelector: + matchLabels: + app: istiod + ports: + - port: 15012 +{{- end }} diff --git a/chart/templates/bigbang/networkpolicies/egress-kube-api.yaml b/chart/templates/bigbang/networkpolicies/egress-kube-api.yaml new file mode 100644 index 000000000..653cff6e1 --- /dev/null +++ b/chart/templates/bigbang/networkpolicies/egress-kube-api.yaml @@ -0,0 +1,20 @@ +{{- if .Values.networkPolicies.enabled }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-egress-api + namespace: {{ .Release.Namespace }} +spec: + podSelector: {} + egress: + - to: + - ipBlock: + cidr: {{ .Values.networkPolicies.controlPlaneCidr }} + {{- if eq .Values.networkPolicies.controlPlaneCidr "0.0.0.0/0" }} + # ONLY Block requests to cloud metadata IP + except: + - 169.254.169.254/32 + {{- end }} + policyTypes: + - Egress +{{- end }} diff --git a/chart/templates/bigbang/networkpolicies/ingress-prometheus.yaml b/chart/templates/bigbang/networkpolicies/ingress-prometheus.yaml new file mode 100644 index 000000000..99de46cc9 --- /dev/null +++ b/chart/templates/bigbang/networkpolicies/ingress-prometheus.yaml @@ -0,0 +1,22 @@ +{{- if and .Values.networkPolicies.enabled .Values.istio.enabled .Values.monitoring.enabled }} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-prometheus-ingress + namespace: "{{ .Release.Namespace }}" +spec: + podSelector: {} + policyTypes: + - Ingress + ingress: + - from: + - namespaceSelector: + matchLabels: + app.kubernetes.io/name: monitoring + podSelector: + matchLabels: + app: prometheus + ports: + - port: 15020 + protocol: TCP +{{- end }} \ No newline at end of file diff --git a/chart/templates/bigbang/peer-authentication.yaml b/chart/templates/bigbang/peer-authentication.yaml new file mode 100644 index 000000000..5765cb20c --- /dev/null +++ b/chart/templates/bigbang/peer-authentication.yaml @@ -0,0 +1,10 @@ +{{- if .Values.istio.enabled }} +apiVersion: security.istio.io/v1beta1 +kind: PeerAuthentication +metadata: + name: default-mattermost-operator + namespace: {{ .Release.Namespace }} +spec: + mtls: + mode: {{ .Values.istio.mtls.mode }} +{{- end }} diff --git a/chart/templates/clusterrole.yaml b/chart/templates/clusterrole.yaml new file mode 100644 index 000000000..58519ea11 --- /dev/null +++ b/chart/templates/clusterrole.yaml @@ -0,0 +1,123 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: mattermost-operator + labels: + app.kubernetes.io/managed-by: '{{ .Release.Service }}' + app.kubernetes.io/instance: '{{ .Release.Name }}' + app.kubernetes.io/version: '{{ .Chart.AppVersion }}' + helm.sh/chart: '{{ .Chart.Name }}-{{ .Chart.Version }}' +rules: +- apiGroups: + - "" + resources: + - pods + - services + - configmaps + - secrets + - serviceaccounts + verbs: + - '*' +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - '*' +- apiGroups: + - "" + resources: + - namespaces + verbs: + - get +- apiGroups: + - apps + resources: + - deployments + verbs: + - '*' +- apiGroups: + - apps + resourceNames: + - mattermost-operator + resources: + - deployments/finalizers + verbs: + - update +- apiGroups: + - apps + resources: + - replicasets + verbs: + - get + - list + - watch + - delete +- apiGroups: + - batch + resources: + - jobs + verbs: + - get + - create + - list + - delete + - watch + - update +- apiGroups: + - networking.k8s.io + resources: + - ingresses + verbs: + - '*' +- apiGroups: + - rbac.authorization.k8s.io + resources: + - rolebindings + - roles + verbs: + - get + - create + - list + - delete + - watch + - update +- apiGroups: + - mattermost.com + resources: + - '*' + verbs: + - '*' +- apiGroups: + - installation.mattermost.com + resources: + - '*' + verbs: + - '*' +- apiGroups: + - mysql.presslabs.org + resources: + - mysqlbackups + - mysqlclusters + - mysqlclusters/status + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - miniocontroller.min.io + resources: + - minioinstances + - minioinstances/status + verbs: + - get + - list + - watch + - create + - update + - patch + - delete diff --git a/chart/templates/clusterrolebinding.yaml b/chart/templates/clusterrolebinding.yaml new file mode 100644 index 000000000..914d96847 --- /dev/null +++ b/chart/templates/clusterrolebinding.yaml @@ -0,0 +1,17 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: mattermost-operator + labels: + app.kubernetes.io/managed-by: '{{ .Release.Service }}' + app.kubernetes.io/instance: '{{ .Release.Name }}' + app.kubernetes.io/version: '{{ .Chart.AppVersion }}' + helm.sh/chart: '{{ .Chart.Name }}-{{ .Chart.Version }}' +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: mattermost-operator +subjects: +- kind: ServiceAccount + name: mattermost-operator + namespace: {{ .Release.Namespace }} diff --git a/chart/templates/db-credentials.yaml b/chart/templates/db-credentials.yaml deleted file mode 100644 index 6c0b5ebd6..000000000 --- a/chart/templates/db-credentials.yaml +++ /dev/null @@ -1,16 +0,0 @@ -{{- if and (not .Values.database.secret) .Values.postgresql.install }} -apiVersion: v1 -kind: Secret -type: Opaque -metadata: - name: {{ include "mattermost.fullname" . }}-dbcreds - namespace: {{ .Release.Namespace }} - labels: - {{ include "mattermost.labels" . | nindent 4 }} - app.kubernetes.io/component: "database" -stringData: - {{- with .Values.postgresql }} - DB_CONNECTION_CHECK_URL: "postgres://{{ .postgresqlUsername }}:{{ .postgresqlPassword }}@{{ .fullnameOverride }}:5432/{{ .postgresqlDatabase }}?connect_timeout=10&sslmode=disable" - DB_CONNECTION_STRING: "postgres://{{ .postgresqlUsername }}:{{ .postgresqlPassword }}@{{ .fullnameOverride }}:5432/{{ .postgresqlDatabase }}?connect_timeout=10&sslmode=disable" - {{- end }} -{{- end }} diff --git a/chart/templates/default-bucket.yaml b/chart/templates/default-bucket.yaml deleted file mode 100644 index 23aa569e0..000000000 --- a/chart/templates/default-bucket.yaml +++ /dev/null @@ -1,71 +0,0 @@ -{{- if .Values.minio.install }} -apiVersion: batch/v1 -kind: Job -metadata: - name: default-minio-bucket-creation - namespace: {{ .Release.Namespace }} - annotations: - "helm.sh/hook": post-install,post-upgrade - "helm.sh/hook-weight": "-5" - "helm.sh/hook-delete-policy": before-hook-creation -spec: - template: - metadata: - name: default-minio-bucket-creation - spec: - imagePullSecrets: - {{- with .Values.global.imagePullSecrets }} - {{ . | toYaml | nindent 8 }} - {{- end }} - restartPolicy: Never - containers: - - name: minio-bucket-creation - image: {{ .Values.minio.bucketCreationImage }} - command: - - /bin/sh - - -c - - | - set -ex - attempt_counter=0 - max_attempts=25 - until [ $(mc config host add bigbang http://{{ .Values.minio.service.nameOverride }} {{ .Values.minio.secrets.accessKey }} {{ .Values.minio.secrets.secretKey }} >/dev/null; echo $?) -eq 0 ]; do - if [ ${attempt_counter} -eq ${max_attempts} ];then - echo "Max attempts reached" - {{- if eq .Values.istio.injection "enabled" }} - until curl -fsI http://localhost:15021/healthz/ready; do - echo "Waiting for Istio sidecar proxy..." - sleep 3 - done - sleep 5 - echo "Stopping the istio proxy..." - curl -X POST http://localhost:15020/quitquitquit - {{- end }} - exit 1 - fi - attempt_counter=$(($attempt_counter+1)) - sleep 10 - done - if [ $(mc ls bigbang/mattermost >/dev/null; echo $?) -eq 0 ]; then - echo "Default Bucket Exists" - else - mc mb bigbang/mattermost - fi - {{- if eq .Values.istio.injection "enabled" }} - until curl -fsI http://localhost:15021/healthz/ready; do - echo "Waiting for Istio sidecar proxy..." - sleep 3 - done - sleep 5 - echo "Stopping the istio proxy..." - curl -X POST http://localhost:15020/quitquitquit - {{- end }} - exit 0 - securityContext: {{- toYaml .Values.minio.containerSecurityContext | nindent 12 }} - resources: - requests: - memory: 256Mi - cpu: 100m - limits: - memory: 256Mi - cpu: 100m -{{- end }} diff --git a/chart/templates/deployment.yaml b/chart/templates/deployment.yaml new file mode 100644 index 000000000..a353891c7 --- /dev/null +++ b/chart/templates/deployment.yaml @@ -0,0 +1,72 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mattermost-operator + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/managed-by: "{{ .Release.Service }}" + app.kubernetes.io/instance: "{{ .Release.Name }}" + app.kubernetes.io/version: "{{ .Chart.AppVersion }}" + helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" +spec: + replicas: {{ .Values.replicas.count }} + selector: + matchLabels: + name: mattermost-operator + template: + metadata: + annotations: + {{- with .Values.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + name: mattermost-operator + spec: + serviceAccountName: mattermost-operator + containers: + - name: mattermost-operator + image: {{ .Values.image.repository }}:{{ .Values.image.tag }} + imagePullPolicy: {{ .Values.image.imagePullPolicy }} + args: + - --enable-leader-election + - --metrics-addr=0.0.0.0:8383 + command: + - /mattermost-operator + env: + - name: MAX_RECONCILING_INSTALLATIONS + value: "20" + - name: REQUEUE_ON_LIMIT_DELAY + value: 20s + - name: MAX_RECONCILE_CONCURRENCY + value: "10" + {{- with .Values.resources }} + resources: + {{- toYaml . | nindent 10 }} + {{- end }} + ports: + - containerPort: 8383 + name: metrics + {{- with .Values.containerSecurityContext }} + securityContext: + {{- toYaml . | nindent 10 }} + {{- end }} + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.securityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.nodeSelector }} + nodeSelector: + {{ toYaml .Values.nodeSelector | nindent 8 }} + {{- end }} + {{- if .Values.affinity }} + affinity: + {{ toYaml .Values.affinity | nindent 8 }} + {{- end }} + {{- if .Values.tolerations }} + tolerations: + {{- toYaml .Values.tolerations | nindent 8 }} + {{- end }} diff --git a/chart/templates/env-secret.yaml b/chart/templates/env-secret.yaml deleted file mode 100644 index 323d13fac..000000000 --- a/chart/templates/env-secret.yaml +++ /dev/null @@ -1,52 +0,0 @@ -apiVersion: v1 -kind: Secret -metadata: - name: mattermost-envs - namespace: {{ .Release.Namespace }} - labels: - {{ include "mattermost.labels" . | nindent 4 }} - app.kubernetes.io/component: "envs" - annotations: - "helm.sh/hook": "pre-install,pre-upgrade" -type: Opaque -stringData: - {{- if .Values.ingress.host }} - MM_SERVICESETTINGS_SITEURL: "https://{{ .Values.ingress.host }}" - {{- else if .Values.istio.chat.hosts }} - MM_SERVICESETTINGS_SITEURL: "https://{{ tpl (index .Values.istio.chat.hosts 0) $ }}" - {{- else }} - MM_SERVICESETTINGS_SITEURL: "https://chat.bigbang.dev" - {{- end }} - {{- if .Values.sso.enabled }} - MM_GITLABSETTINGS_ENABLE: "{{ .Values.sso.enabled }}" - MM_GITLABSETTINGS_ID: "{{ .Values.sso.client_id }}" - MM_GITLABSETTINGS_SECRET: "{{ .Values.sso.client_secret }}" - MM_GITLABSETTINGS_AUTHENDPOINT: "{{ .Values.sso.auth_endpoint }}" - MM_GITLABSETTINGS_TOKENENDPOINT: "{{ .Values.sso.token_endpoint }}" - MM_GITLABSETTINGS_USERAPIENDPOINT: "{{ .Values.sso.user_api_endpoint }}" - {{- end }} - {{- if .Values.minio.install }} - MM_FILESETTINGS_AMAZONS3SSL: "false" - {{- end }} - {{- if .Values.elasticsearch.enabled }} - MM_ELASTICSEARCHSETTINGS_CONNECTIONURL: "{{ .Values.elasticsearch.connectionurl }}" - MM_ELASTICSEARCHSETTINGS_ENABLEAUTOCOMPLETE: "{{ .Values.elasticsearch.enableautocomplete }}" - MM_ELASTICSEARCHSETTINGS_ENABLEINDEXING: "{{ .Values.elasticsearch.enableindexing }}" - MM_ELASTICSEARCHSETTINGS_INDEXPREFIX: "{{ .Values.elasticsearch.indexprefix }}" - MM_ELASTICSEARCHSETTINGS_BULKINDEXINGTIMEWINDOWSECONDS: "{{ .Values.elasticsearch.bulkindexingtimewindowseconds }}" - MM_ELASTICSEARCHSETTINGS_ENABLESEARCHING: "{{ .Values.elasticsearch.enablesearching }}" - MM_ELASTICSEARCHSETTINGS_SKIPTLSVERIFICATION: "{{ .Values.elasticsearch.skiptlsverification }}" - MM_ELASTICSEARCHSETTINGS_SNIFF: "{{ .Values.elasticsearch.sniff }}" - MM_ELASTICSEARCHSETTINGS_USERNAME: {{ .Values.elasticsearch.username | default "elastic" }} - {{- if .Values.elasticsearch.password }} - MM_ELASTICSEARCHSETTINGS_PASSWORD: {{ .Values.elasticsearch.password }} - {{- else }} - {{ $secretname := printf "%s-es-elastic-user" ( .Values.elasticsearch.name | default "logging-ek" )}} - {{- with lookup "v1" "Secret" (.Values.elasticsearch.namespace | default "logging" ) $secretname }} - MM_ELASTICSEARCHSETTINGS_PASSWORD: {{ .data.elastic | b64dec }} - {{- end }} - {{- end }} - {{- end }} - {{- if .Values.mattermostEnvs }} - {{ tpl (toYaml .Values.mattermostEnvs) . | nindent 2}} - {{- end }} diff --git a/chart/templates/license.yaml b/chart/templates/license.yaml deleted file mode 100644 index 7e559ac6c..000000000 --- a/chart/templates/license.yaml +++ /dev/null @@ -1,15 +0,0 @@ -{{- if and .Values.enterprise.enabled .Values.enterprise.license }} -apiVersion: v1 -kind: Secret -metadata: - name: mattermost-license - namespace: {{ .Release.Namespace }} - labels: - {{ include "mattermost.labels" . | nindent 4 }} - app.kubernetes.io/component: "license" - annotations: - "helm.sh/hook": "pre-install,pre-upgrade" -type: Opaque -data: - license: {{ .Values.enterprise.license | b64enc }} -{{- end }} diff --git a/chart/templates/mattermost.yaml b/chart/templates/mattermost.yaml deleted file mode 100644 index af1cfd1a5..000000000 --- a/chart/templates/mattermost.yaml +++ /dev/null @@ -1,246 +0,0 @@ -apiVersion: installation.mattermost.com/v1beta1 -kind: Mattermost -metadata: - name: {{ .Release.Name }} - namespace: {{ .Release.Namespace }} - labels: - {{ include "mattermost.labels" . | nindent 4 }} - app.kubernetes.io/component: "instance" -spec: - image: {{ .Values.image.name }} - imagePullPolicy: {{ .Values.image.imagePullPolicy }} - {{- with .Values.global.imagePullSecrets }} - imagePullSecrets: - {{- toYaml . | nindent 4 }} - {{- end }} - - size: {{ .Values.users }}users - version: {{ .Values.image.tag }} - {{- if .Values.enterprise.enabled }} - replicas: {{ .Values.replicaCount }} - {{- else }} - replicas: 1 - {{- end }} - - ingress: - enabled: {{ .Values.ingress.enabled }} - host: {{ .Values.ingress.host }} - annotations: - {{- toYaml .Values.ingress.annotations | nindent 6 }} - tlsSecret: {{ .Values.ingress.tlsSecret}} - - {{- if and .Values.enterprise.enabled .Values.enterprise.license }} - licenseSecret: "mattermost-license" - {{- end }} - - mattermostEnv: - - name: MM_SERVICESETTINGS_SITEURL - valueFrom: - secretKeyRef: - key: MM_SERVICESETTINGS_SITEURL - name: "mattermost-envs" - {{- if .Values.sso.enabled }} - - name: MM_GITLABSETTINGS_ENABLE - valueFrom: - secretKeyRef: - key: MM_GITLABSETTINGS_ENABLE - name: "mattermost-envs" - - name: MM_GITLABSETTINGS_ID - valueFrom: - secretKeyRef: - key: MM_GITLABSETTINGS_ID - name: "mattermost-envs" - - name: MM_GITLABSETTINGS_SECRET - valueFrom: - secretKeyRef: - key: MM_GITLABSETTINGS_SECRET - name: "mattermost-envs" - - name: MM_GITLABSETTINGS_AUTHENDPOINT - valueFrom: - secretKeyRef: - key: MM_GITLABSETTINGS_AUTHENDPOINT - name: "mattermost-envs" - - name: MM_GITLABSETTINGS_TOKENENDPOINT - valueFrom: - secretKeyRef: - key: MM_GITLABSETTINGS_TOKENENDPOINT - name: "mattermost-envs" - - name: MM_GITLABSETTINGS_USERAPIENDPOINT - valueFrom: - secretKeyRef: - key: MM_GITLABSETTINGS_USERAPIENDPOINT - name: "mattermost-envs" - {{- end }} - {{- if .Values.minio.install }} - - name: MM_FILESETTINGS_AMAZONS3SSL - valueFrom: - secretKeyRef: - key: MM_FILESETTINGS_AMAZONS3SSL - name: "mattermost-envs" - {{- end }} - {{- if .Values.elasticsearch.enabled }} - - name: MM_ELASTICSEARCHSETTINGS_CONNECTIONURL - valueFrom: - secretKeyRef: - key: MM_ELASTICSEARCHSETTINGS_CONNECTIONURL - name: "mattermost-envs" - - name: MM_ELASTICSEARCHSETTINGS_ENABLEAUTOCOMPLETE - valueFrom: - secretKeyRef: - key: MM_ELASTICSEARCHSETTINGS_ENABLEAUTOCOMPLETE - name: "mattermost-envs" - - name: MM_ELASTICSEARCHSETTINGS_ENABLEINDEXING - valueFrom: - secretKeyRef: - key: MM_ELASTICSEARCHSETTINGS_ENABLEINDEXING - name: "mattermost-envs" - - name: MM_ELASTICSEARCHSETTINGS_INDEXPREFIX - valueFrom: - secretKeyRef: - key: MM_ELASTICSEARCHSETTINGS_INDEXPREFIX - name: "mattermost-envs" - - name: MM_ELASTICSEARCHSETTINGS_BULKINDEXINGTIMEWINDOWSECONDS - valueFrom: - secretKeyRef: - key: MM_ELASTICSEARCHSETTINGS_BULKINDEXINGTIMEWINDOWSECONDS - name: "mattermost-envs" - - name: MM_ELASTICSEARCHSETTINGS_ENABLESEARCHING - valueFrom: - secretKeyRef: - key: MM_ELASTICSEARCHSETTINGS_ENABLESEARCHING - name: "mattermost-envs" - - name: MM_ELASTICSEARCHSETTINGS_SKIPTLSVERIFICATION - valueFrom: - secretKeyRef: - key: MM_ELASTICSEARCHSETTINGS_SKIPTLSVERIFICATION - name: "mattermost-envs" - - name: MM_ELASTICSEARCHSETTINGS_SNIFF - valueFrom: - secretKeyRef: - key: MM_ELASTICSEARCHSETTINGS_SNIFF - name: "mattermost-envs" - - name: MM_ELASTICSEARCHSETTINGS_USERNAME - valueFrom: - secretKeyRef: - key: MM_ELASTICSEARCHSETTINGS_USERNAME - name: "mattermost-envs" - - name: MM_ELASTICSEARCHSETTINGS_PASSWORD - valueFrom: - secretKeyRef: - key: MM_ELASTICSEARCHSETTINGS_PASSWORD - name: "mattermost-envs" - {{- end }} - {{- range $k, $v := .Values.mattermostEnvs }} - - name: {{ $k }} - valueFrom: - secretKeyRef: - key: {{ $k }} - name: "mattermost-envs" - {{- end }} - {{- range .Values.existingSecretEnvs }} - - {{ tpl (toYaml .) $ | nindent 4 }} - {{- end }} - - # Give MM enough time to keel over if needed - probes: - livenessProbe: - initialDelaySeconds: 120 - periodSeconds: 120 - - scheduling: - {{- with .Values.resources }} - resources: - {{- toYaml . | nindent 6 }} - {{- end }} - {{- if .Values.affinity }} - affinity: - {{ toYaml .Values.affinity | nindent 6 }} - {{- end }} - - {{- if .Values.nodeSelector }} - nodeSelector: - {{ toYaml .Values.nodeSelector | nindent 6 }} - {{- end }} - - {{- if .Values.tolerations}} - tolerations: - {{- toYaml .Values.tolerations | nindent 6 }} - {{- end }} - - {{- with .Values.volumes }} - volumes: - {{- toYaml . | nindent 4}} - {{- end }} - - {{- with .Values.volumeMounts }} - volumeMounts: - {{- toYaml . | nindent 4}} - {{- end }} - - {{- if or .Values.podLabels .Values.podAnnotations .Values.securityContext .Values.containerSecurityContext }} - podTemplate: - {{- with .Values.podLabels }} - extraLabels: - {{- toYaml . | nindent 6 }} - {{- end }} - {{- with .Values.podAnnotations }} - extraAnnotations: - {{- toYaml . | nindent 6 }} - {{- end }} - {{- with .Values.securityContext }} - securityContext: - {{- toYaml . | nindent 6 }} - {{- end }} - {{- with .Values.containerSecurityContext }} - containerSecurityContext: - {{- toYaml . | nindent 6 }} - {{- end }} - {{- end }} - - database: - external: - secret: {{ .Values.database.secret | default (printf "%s-dbcreds" (include "mattermost.fullname" .)) }} - disableReadinessCheck: {{ .Values.database.readinessCheck.disableDefault }} - - fileStore: - external: - url: {{ .Values.fileStore.url | default .Values.minio.service.nameOverride }} - bucket: {{ .Values.fileStore.bucket | default "mattermost" }} - secret: {{ .Values.fileStore.secret | default .Values.minio.secrets.name }} - - podExtensions: - initContainers: - - command: - {{- toYaml .Values.database.readinessCheck.command | nindent 10 }} - image: {{ .Values.database.readinessCheck.image }} - imagePullPolicy: {{ .Values.image.imagePullPolicy }} - securityContext: - capabilities: - drop: - - ALL - name: db-readiness-check - resources: - requests: - memory: 128Mi - cpu: 50m - limits: - memory: 128Mi - cpu: 50m - env: - {{- (tpl (toYaml .Values.database.readinessCheck.env) .) | nindent 10 }} - - {{- if .Values.resourcePatch }} - resourcePatch: - {{- toYaml .Values.resourcePatch | nindent 4 }} - {{- end }} - - updateJob: - disabled: {{ .Values.updateJob.disabled }} - {{- with .Values.updateJob.annotations }} - extraAnnotations: - {{- toYaml . | nindent 6 }} - {{- end }} - {{- with .Values.updateJob.labels }} - extraLabels: - {{- toYaml . | nindent 6 }} - {{- end }} diff --git a/chart/templates/role-binding.yaml b/chart/templates/role-binding.yaml deleted file mode 100644 index a095e860e..000000000 --- a/chart/templates/role-binding.yaml +++ /dev/null @@ -1,21 +0,0 @@ -{{- if .Values.monitoring.enabled }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBindingList -items: -- apiVersion: rbac.authorization.k8s.io/v1 - kind: RoleBinding - metadata: - name: prometheus-k8s - namespace: {{ .Release.Namespace }} - labels: - {{ include "mattermost.labels" . | nindent 6 }} - app.kubernetes.io/component: "monitoring" - roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: prometheus-k8s - subjects: - - kind: ServiceAccount - name: prometheus-k8s - namespace: {{ .Values.monitoring.namespace }} -{{- end }} diff --git a/chart/templates/role.yaml b/chart/templates/role.yaml deleted file mode 100644 index ea79ecc74..000000000 --- a/chart/templates/role.yaml +++ /dev/null @@ -1,24 +0,0 @@ -{{- if .Values.monitoring.enabled }} -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleList -items: -- apiVersion: rbac.authorization.k8s.io/v1 - kind: Role - metadata: - name: prometheus-k8s - namespace: {{ .Release.Namespace }} - labels: - {{ include "mattermost.labels" . | nindent 6 }} - app.kubernetes.io/component: "monitoring" - rules: - - apiGroups: - - "" - resources: - - services - - endpoints - - pods - verbs: - - get - - list - - watch -{{- end }} diff --git a/chart/templates/service-monitor.yaml b/chart/templates/service-monitor.yaml deleted file mode 100644 index 3db3a6f44..000000000 --- a/chart/templates/service-monitor.yaml +++ /dev/null @@ -1,24 +0,0 @@ -{{- if and .Values.monitoring.enabled .Values.enterprise.enabled }} -apiVersion: monitoring.coreos.com/v1 -kind: ServiceMonitor -metadata: - labels: - prometheus: k8s - release: monitoring - {{ include "mattermost.labels" . | nindent 4 }} - app.kubernetes.io/component: "monitoring" - name: mattermost-enterprise-metrics - namespace: {{ .Release.Namespace }} -spec: - selector: - matchLabels: - app: {{ .Release.Name }} - namespaceSelector: - matchNames: - - {{ .Release.Namespace }} - endpoints: - - interval: 30s - port: metrics - scheme: http - jobLabel: mattermost -{{- end }} diff --git a/chart/templates/service.yaml b/chart/templates/service.yaml new file mode 100644 index 000000000..2165cd1a7 --- /dev/null +++ b/chart/templates/service.yaml @@ -0,0 +1,21 @@ +apiVersion: v1 +kind: Service +metadata: + name: mattermost-operator + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/managed-by: '{{ .Release.Service }}' + app.kubernetes.io/instance: '{{ .Release.Name }}' + app.kubernetes.io/version: '{{ .Chart.AppVersion }}' + helm.sh/chart: '{{ .Chart.Name }}-{{ .Chart.Version }}' + app: mattermost-operator + name: mattermost-operator +spec: + ports: + - name: metrics + port: 8383 + protocol: TCP + targetPort: metrics + selector: + name: mattermost-operator + type: ClusterIP diff --git a/chart/templates/serviceaccount.yaml b/chart/templates/serviceaccount.yaml new file mode 100644 index 000000000..5241827ef --- /dev/null +++ b/chart/templates/serviceaccount.yaml @@ -0,0 +1,10 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: mattermost-operator + namespace: {{ .Release.Namespace }} + labels: + app.kubernetes.io/managed-by: "{{ .Release.Service }}" + app.kubernetes.io/instance: "{{ .Release.Name }}" + app.kubernetes.io/version: "{{ .Chart.AppVersion }}" + helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}" diff --git a/chart/templates/tests/test-ui.yaml b/chart/templates/tests/test-ui.yaml deleted file mode 100644 index 163bd16f9..000000000 --- a/chart/templates/tests/test-ui.yaml +++ /dev/null @@ -1,13 +0,0 @@ -{{- include "gluon.tests.cypress-configmap.overrides" (list . "mattermost-test.cypress-configmap") }} -{{- define "mattermost-test.cypress-configmap" }} -metadata: - labels: - {{ include "mattermost.labels" . | nindent 4 }} -{{- end }} ---- -{{- include "gluon.tests.cypress-runner.overrides" (list . "mattermost-test.cypress-runner") -}} -{{- define "mattermost-test.cypress-runner" -}} -metadata: - labels: - {{ include "mattermost.labels" . | nindent 4 }} -{{- end }} diff --git a/chart/templates/virtualservice.yaml b/chart/templates/virtualservice.yaml deleted file mode 100644 index f47b867ab..000000000 --- a/chart/templates/virtualservice.yaml +++ /dev/null @@ -1,32 +0,0 @@ -{{- if and .Values.istio.enabled .Values.istio.chat.enabled -}} -apiVersion: networking.istio.io/v1beta1 -kind: VirtualService -metadata: - name: {{ template "mattermost.fullname" . }} - namespace: {{ .Release.Namespace }} - labels: - {{ include "mattermost.labels" . | nindent 4 }} - app.kubernetes.io/component: "chat" - {{- if .Values.istio.chat.labels }} - {{ toYaml .Values.istio.chat.labels | indent 4 }} - {{- end }} - {{- if .Values.istio.chat.annotations }} - annotations: - {{ toYaml .Values.istio.chat.annotations | indent 4 }} - {{- end }} -spec: - gateways: - {{- range .Values.istio.chat.gateways }} - - {{ . }} - {{- end }} - hosts: - {{- range .Values.istio.chat.hosts }} - - {{ tpl . $ }} - {{- end }} - http: - - route: - - destination: - host: {{ .Release.Name }}.{{ .Release.Namespace }}.svc.cluster.local - port: - number: 8065 # hardcode this because the operator does -{{- end }} diff --git a/chart/tests/cypress/cypress.json b/chart/tests/cypress/cypress.json deleted file mode 100644 index a8a81e405..000000000 --- a/chart/tests/cypress/cypress.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "pluginsFile": false, - "supportFile": false, - "fixturesFolder": false, - "videoCompression": 20 -} diff --git a/chart/tests/cypress/mattermost-health.spec.js b/chart/tests/cypress/mattermost-health.spec.js deleted file mode 100644 index 210d23458..000000000 --- a/chart/tests/cypress/mattermost-health.spec.js +++ /dev/null @@ -1,126 +0,0 @@ -Cypress.on('uncaught:exception', (err, runnable) => { - // returning false here prevents Cypress from failing the test - return false -}) - -describe('Mattermost Healthcheck', function() { - - // Conditional check for inconsistent "welcome to mattermost" banner behavior - function bannercheck() { - cy.wait(3000) - cy.get('body').then($body => { - if ($body.find('.link > span').length > 0) { - //evaluates as true if banner exists at all - cy.get('.link > span').then($header => { - if ($header.is(':visible')){ - // evaluates to true if the banner is visible - console.log("Banner is Present") - $header.click() - } else { - console.log("Banner is not present") - } - }); - } - }) - } - - // This provides us with a login account on fresh installs - before(() => { - cy.visit(Cypress.env('url')) - cy.wait(8000) - // cy.wait(15000) - cy.get('div[id="root"]').should('be.visible') - - cy.url().then(($url) => { - if ($url.includes('signup')) { - // note: Mattermost behaves differently on first login depending on the URL - // https://chat.bigbang.dev versus http://mattermost.mattermost.svc.cluster.local:8065 - // explicitly visit the signup_email page - // so that the test works the same locally and in the pipeline - cy.visit(Cypress.env('url')+'/signup_email') - cy.wait(5000) - // cy.wait(10000) - cy.get('input[id="input_email"]').type(Cypress.env('mm_email')) - // #input - cy.get('input[id="input_name"]').type(Cypress.env('mm_user')) - cy.get('input[id="input_password-input"]').type(Cypress.env('mm_password')) - cy.get('button[id="saveSetting"]').click() - } - }) - }) - - beforeEach(() => { - cy.visit(Cypress.env('url')) - cy.wait(5000) - // cy.wait(10000) - cy.get('div[id="root"]').should('be.visible') - - cy.url().then(($url) => { - if ($url.includes('landing')) { - cy.get('a[class="btn btn-default btn-lg get-app__continue"]').click() - } - }) - cy.wait(5000) - - // Check if login is needed - cy.url().then(($url) => { - if ($url.includes('login')) { - cy.get('input[id="input_loginId"]').type(Cypress.env('mm_user')) - cy.get('input[id="input_password-input"]').type(Cypress.env('mm_password')) - cy.get('button[id="saveSetting"]').click() - } - }) - cy.wait(500) - }) - - it('should create / persist teams', function() { - cy.wait(5000) - - cy.url().then(($url) => { - cy.wait(1000) - if ($url.includes('select_team')) { - // create a team - cy.get('a[id="createNewTeamLink"]').click() - cy.wait(3000) - // Input Big Bang - cy.get('input[id="teamNameInput"]').type('Big Bang') - // Click Next - cy.get('button[id="teamNameNextButton"]').click() - //cy.get('input[id="teamURLInput"]').should('include', 'big-bang') - // Click finish - cy.get('button[id="teamURLFinishButton"]').click() - // Give some time for dialog load - } - bannercheck() - }) - - // click on Town Square - cy.wait(1000) - cy.visit(Cypress.env('url')+'/big-bang/channels/town-square') - cy.wait(10000) - cy.title().should('include', 'Town Square - Big Bang Mattermost') - }) - - it('should allow chatting', function() { - bannercheck() - let randomChat = "Hello " + Math.random().toString(36).substring(8); - cy.wait(5000) - cy.get('body').then($body => { - if ($body.find('.close > [aria-hidden="true"]').length > 0) { - cy.get('.close > [aria-hidden="true"]').click() - } - }) - // cy.wait(10000) - cy.get('textarea[id="post_textbox"]').type(randomChat).type('{enter}') - cy.get('p').contains(randomChat).should('be.visible') - }) - - it('should have file storage connection', function() { - bannercheck() - cy.visit(Cypress.env('url')+'/admin_console/environment/file_storage') - cy.wait(10000) - - cy.get('span:contains("Test Connection")', {timeout: 10000}).click() - cy.get('div[class="alert alert-success"]').should('be.visible') - }) -}) diff --git a/chart/values.yaml b/chart/values.yaml index 0fff3acb2..cbfb318f6 100644 --- a/chart/values.yaml +++ b/chart/values.yaml @@ -1,93 +1,50 @@ -hostname: bigbang.dev - -istio: - # -- Toggle istio integration - enabled: false - chat: - # Toggle vs creation - enabled: true - annotations: {} - labels: {} - gateways: - - istio-system/main - hosts: - - chat.{{ .Values.hostname }} - injection: disabled - -# -- Specification to configure an Ingress with Mattermost -ingress: - enabled: false - host: "" - annotations: {} - tlsSecret: "" - -# NOTE: Requires enterprise.enabled to have any effect -monitoring: - enabled: false - namespace: monitoring - -networkPolicies: - enabled: false - ingressLabels: - app: istio-ingressgateway - istio: ingressgateway - controlPlaneCidr: 0.0.0.0/0 - -sso: - enabled: false - client_id: platform1_a8604cc9-f5e9-4656-802d-d05624370245_bb8-mattermost - # Change to your client secret - client_secret: nothing - # Change to your respective IDP endpoints - auth_endpoint: https://login.dso.mil/auth/realms/baby-yoda/protocol/openid-connect/auth - token_endpoint: https://login.dso.mil/auth/realms/baby-yoda/protocol/openid-connect/token - user_api_endpoint: https://login.dso.mil/auth/realms/baby-yoda/protocol/openid-connect/userinfo - -# Repo and image tag image: - name: registry1.dso.mil/ironbank/opensource/mattermost/mattermost - tag: 7.5.0 + # -- Default image pull policy imagePullPolicy: IfNotPresent + # -- Full image name + repository: "registry1.dso.mil/ironbank/opensource/mattermost/mattermost-operator" + # -- Image tag + tag: v1.19.0 -global: - imagePullSecrets: - - name: private-registry - -# Mattermost instance desired replicas -replicaCount: 1 +replicas: + # -- Mattermost operator desired replicas + count: 1 -users: null # Allowable: 100, 1000, 5000, 10000, 25000 - -enterprise: - enabled: false - license: "" - # Example: - # license: | - # LICENSE HERE - -nameOverride: "" - -updateJob: - # -- Must be disabled when Istio injected - disabled: true - labels: {} - annotations: {} +# -- Image pull secrets +imagePullSecrets: +- name: private-registry +# -- Resources for operator pod(s) resources: - limits: - cpu: 2 - memory: 4Gi requests: - cpu: 2 - memory: 4Gi + memory: 512Mi + cpu: 100m + limits: + memory: 512Mi + cpu: 100m + +# -- securityContext for Kubernetes pod(s) +securityContext: + runAsUser: 65532 + runAsGroup: 65532 + runAsNonRoot: true + +# -- containerSecurityContext for operator container +containerSecurityContext: + capabilities: + drop: + - ALL + privileged: false + readOnlyRootFilesystem: true +# -- Affinity for operator pod(s) affinity: {} # podAntiAffinity: # requiredDuringSchedulingIgnoredDuringExecution: # - topologyKey: "kubernetes.io/hostname" # labelSelector: # matchLabels: - # dont-schedule-with: mattermost + # dont-schedule-with: operator # nodeAffinity: # requiredDuringSchedulingIgnoredDuringExecution: # nodeSelectorTerms: @@ -95,185 +52,41 @@ affinity: {} # - key: node-type # operator: In # values: - # - "mattermost" + # - "operator" +# -- Node selector for operator pod(s) nodeSelector: {} - # node-type: mattermost + # node-type: operator +# -- Tolerations for operator pod(s) tolerations: {} -# - key: "key1" -# operator: "Equal" -# value: "value1" -# effect: "NoSchedule" +#- key: "key1" +# operator: "Equal" +# value: "value1" +# effect: "NoSchedule" -# Any ENVs provided here get put into a `mattermost-envs` secret and pulled into the env -mattermostEnvs: {} - # MM_ENV_NAME: "{{ .Values.users }}" - # ANOTHER_ENV_NAME: "anothervalue" - -# Use this to point to pull in ENV values from existing secrets -existingSecretEnvs: {} - # - name: MM_SQLSETTINGS_DATASOURCEREPLICAS - # valueFrom: - # secretKeyRef: - # key: READER_DB_CONNECTION_STRING - # name: '{{ .Values.database.secret | default (printf "%s-dbcreds" (include "mattermost.fullname" .)) }}' - # - name: MM_ANOTHER_VAR - # valueFrom: - # secretKeyRef: - # key: DB_CONNECTION_CHECK_URL - # name: "mysecretname" - -volumes: {} - # - name: ca-cert - # secret: - # secretName: ca-secret - # defaultMode: 0644 - -volumeMounts: {} - # - name: ca-cert - # mountPath: /etc/ssl/certs - # readOnly: true - -# -- Pod labels for Mattermost server pods -podLabels: {} - -# -- Pod annotations for Mattermost server pods +# -- Annotations for operator pod(s) podAnnotations: {} +#e.g. bigbang.dev/istioVersion: 1.10.3 -# -- securityContext for Mattermost server pods -securityContext: {} - -# -- containerSecurityContext for Mattermost server containers -containerSecurityContext: - capabilities: - drop: - - ALL - -minio: - install: false - bucketCreationImage: "registry1.dso.mil/ironbank/opensource/minio/mc:RELEASE.2022-08-23T05-45-20Z" - # Override the minio service name for easier connection setup - service: - nameOverride: "minio.mattermost.svc.cluster.local" - secrets: - name: "mattermost-objstore-creds" - accessKey: "minio" - secretKey: "minio123" # default key, change this! - tenant: - metrics: - enabled: false - port: 9000 - containerSecurityContext: - capabilities: - drop: - - ALL - -postgresql: - install: false - - image: - registry: "registry1.dso.mil/ironbank" - repository: "opensource/postgres/postgresql11" - tag: "11.18-1" - - pullSecrets: - - private-registry - - postgresqlUsername: "mattermost" - postgresqlPassword: "bigbang" # default password, change this! - postgresqlDatabase: "mattermost" - - fullnameOverride: "mattermost-postgresql" # Overrides the name used for resource creation - - securityContext: - fsGroup: 26 - containerSecurityContext: - runAsUser: 26 - capabilities: - drop: - - ALL - #permissions for initContainers - volumePermissions: - securityContext: - capabilities: - drop: - - ALL - - # Set the configs to allow listening and connecting from other pods - postgresqlConfiguration: {"listen_addresses": "*"} - pgHbaConfiguration: |- - local all all md5 - host all all all md5 - -database: - # Name of an existing secret to pull credentials from, leave empty for chart created database - # Must at minimum contain DB_CONNECTION_STRING - secret: "" - # Init container for DB readiness check - readinessCheck: - # Disable the default readiness check which uses a non-IB image - disableDefault: true - # Defaults to Ironbank postgres, can be changed for different DB types (MySQL) - image: registry1.dso.mil/ironbank/opensource/postgres/postgresql12:12.13 - # Defaults to a readiness check for postgres, can be changed for different DB types - command: - - /bin/sh - - -c - - until pg_isready --dbname="$DB_CONNECTION_CHECK_URL"; do echo waiting for database; sleep 5; done; - # Pass in the credentials needed for the DB check - env: - - name: DB_CONNECTION_CHECK_URL - valueFrom: - secretKeyRef: - key: DB_CONNECTION_CHECK_URL - name: '{{ .Values.database.secret | default (printf "%s-dbcreds" (include "mattermost.fullname" .)) }}' +networkPolicies: + # -- Toggle on/off Big Bang provided network policies + enabled: false + # -- See `kubectl cluster-info` and then resolve to IP + controlPlaneCidr: 0.0.0.0/0 -fileStore: - # Name of an existing secret to pull credentials from, leave empty for chart created minio - secret: "" - # URL for existing file store, leave empty for chart created minio - url: "" - # Bucket for existing file store, leave empty for chart created minio - bucket: "" +istio: + # -- Toggle on/off istio interaction, used for network policies and mTLS + enabled: false + # -- Default peer authentication + mtls: + # -- STRICT = Allow only mutual TLS traffic, + # PERMISSIVE = Allow both plain text and mutual TLS traffic + mode: STRICT -elasticsearch: - # NOTE: Elasticsearch settings can be defined, but will not work unless enterprise mode is enabled. +monitoring: + # -- Toggle on/off monitoring interaction, used for network policies enabled: false - # The address of the Elasticsearch server, default is internal elasticsearch - connectionurl: "https://logging-ek-es-http.logging.svc.cluster.local:9200" - # if using BB elasticsearch leave user/pass blank - username: "" - password: "" - # When true, indexing of new posts occurs automatically. Search queries will use database search until "Enable Elasticsearch for search queries" is enabled. - enableindexing: true - # Elasticsearch index prefix - indexprefix: "mm-" - # When true, Mattermost will not require the Elasticsearch certificate to be signed by a trusted Certificate Authority - skiptlsverification: true - # Frequency to index to elasticsearch - bulkindexingtimewindowseconds: 3600 - # When true, sniffing finds and connects to all data nodes in your cluster automatically. - sniff: false - # When true, Elasticsearch will be used for all search queries using the latest index. Search results may be incomplete until a bulk index of the existing post database is finished. When false, database search is used. - enablesearching: true - # When true, Elasticsearch will be used for all autocompletion queries on users and channels using the latest index. Autocompletion results may be incomplete until a bulk index of the existing users and channels database is finished. When false, database autocomplete is used. - enableautocomplete: true +# -- Openshift feature toggle, used for DNS network policy openshift: false - -# Custom patch on the Mattermost resources before applying -resourcePatch: {} -# Example: Patch a label onto the deployment: - # deployment: - # patch: '[{"op":"add","path":"/spec/template/spec/metadata/labels","value":{"istio-version": "1.2.3"}}]' - -bbtests: - enabled: false - cypress: - artifacts: true - envs: - cypress_url: http://mattermost.mattermost.svc.cluster.local:8065 - cypress_mm_email: "test@bigbang.dev" - cypress_mm_user: "bigbang" - cypress_mm_password: "Bigbang#123" diff --git a/docs/AFFINITY.md b/docs/AFFINITY.md index 06e9c4299..6147e2b5b 100644 --- a/docs/AFFINITY.md +++ b/docs/AFFINITY.md @@ -1,4 +1,4 @@ -# Node Affinity & Anti-Affinity with Mattermost +# Node Affinity & Anti-Affinity with Mattermost Operator Affinity is exposed through values options for this package. If you want to schedule your pods to deploy on specific nodes you can do that through the `nodeSelector` value and as needed the `affinity` value. Additional info is provided below as well to help in configuring this. @@ -6,16 +6,16 @@ It is good to have a basic knowledge of node affinity and available options to y ## Values for NodeSelector -The `nodeSelector` value at the top level can be set to do basic node selection for deployments. See the below example for an example to schedule pods to only nodes with the label `node-type` equal to `mattermost`: +The `nodeSelector` value at the top level can be set to do basic node selection for deployments. See the below example for an example to schedule pods to only nodes with the label `node-type` equal to `operator`: ```yaml nodeSelector: - node-type: mattermost + node-type: operator ``` ## Values for Affinity -The `affinity` value at the top level should be used to specify affinity. The format to include follows what you'd specify at a pod/deployment level. See the example below for scheduling the operator pods only to nodes with the label `node-type` equal to `mattermost`: +The `affinity` value at the top level should be used to specify affinity. The format to include follows what you'd specify at a pod/deployment level. See the example below for scheduling the operator pods only to nodes with the label `node-type` equal to `operator`: ```yaml affinity: @@ -26,12 +26,12 @@ affinity: - key: node-type operator: In values: - - mattermost + - operator ``` ## Values for Anti-Affinity -The `affinity` value at the top level can be set in the same way to schedule pods based on anti-affinity. See the below example to schedule pods to not be present on the nodes that already have pods with the `dont-schedule-with: mattermost` label: +The `affinity` value at the top level can be set in the same way to schedule pods based on anti-affinity. See the below example to schedule pods to not be present on the nodes that already have pods with the `dont-schedule-with: operator` label: ```yaml affinity: @@ -40,5 +40,5 @@ affinity: - topologyKey: "kubernetes.io/hostname" labelSelector: matchLabels: - dont-schedule-with: mattermost + dont-schedule-with: operator ``` diff --git a/docs/DEVELOPMENT_MAINTENANCE.md b/docs/DEVELOPMENT_MAINTENANCE.md index 26c3e1c1e..36a40f516 100644 --- a/docs/DEVELOPMENT_MAINTENANCE.md +++ b/docs/DEVELOPMENT_MAINTENANCE.md @@ -1,45 +1,45 @@ -# Upgrading to a new version +# How to Sync with Upstream -Mattermost is a Big Bang built chart. As a result there is no `Kptfile` to handle any automatic updates from upstream. The below details the steps required to update to a new version of the Mattermost package. +Since the mattermost operator chart is built and maintained by Big Bang syncing with upstream is not as straight forward as a `kpt pkg update`. -1. Review the [upstream changelog](https://docs.mattermost.com/install/self-managed-changelog.html) for the update you are going to, as well as any versions skipped over between the last BB release and this one. Note any breaking changes and new features. +1. Run `kpt pkg update docs/upstream@{NEW OPERATOR TAG}`. Notice that this updates the folder `docs/upstream`. -2. Modify the `image.tag` value in `chart/values.yaml` to point to the newest version of Mattermost. +2. Incrementally copy the CRD sections from `docs/upstream/mattermost-operator.yaml` into their respective files in `chart/mattermost-operator-crds/templates` (it can be helpful to search for `---` to find the sections). At this step the following file names to match the CRD names are: `clusterinstallations.mattermost.com`, `mattermostrestoredbs.mattermost.com`, and `mattermosts.installation.mattermost.com`. -3. You should check the latest version of `minio-instance` and run `kpt pkg update chart/deps/minio@4.5.4-bb.1 --strategy alpha-git-patch` then `helm dependency update chart` if applicable. +3. Modify each CRD file to remove `creationTimestamp: null` and add labels. Labels to add: -4. Based on the upstream changelog review from earlier, make any changes required to resolve breaking changes. +```yaml + labels: + app.kubernetes.io/managed-by: '{{ .Release.Service }}' + app.kubernetes.io/instance: '{{ .Release.Name }}' + app.kubernetes.io/version: '{{ .Chart.AppVersion }}' + helm.sh/chart: '{{ .Chart.Name }}-{{ .Chart.Version }}' +``` -5. Modify the `version` in `Chart.yaml` - this is a BigBang built and owned chart so we sync the chart version with the appVersion (ex: appVersion `6.6.0` -> chart version `6.6.0-bb.0`). Also modify the `appVersion` and the `bigbang.dev/applicationVersions` to the new upstream version of Mattermost (same version you put in for the image tag value). +4. Update `chart/mattermost-operator-crds/Chart.yaml` `version` and `appVersion` to the new operator version. -6. Update `CHANGELOG.md` adding an entry for the new version and noting all changes (at minimum should include `Updated Mattermost to x.x.x`). +5. Update the versions for `chart/Chart.yaml` to the new operator version (`version`, `appVersion`, and dependency `version`). -7. Generate the `README.md` updates by following the [guide in gluon](https://repo1.dso.mil/platform-one/big-bang/apps/library-charts/gluon/-/blob/master/docs/bb-package-readme.md). +6. Run `helm dependency update ./chart` and validate that the new CRD chart tgz is under `chart/charts`. -8. Validate that `tests/dependencies.yaml` points to the latest tag for `mattermost-operator`. If it doesn't, update it. +7. Incrementally copy out the remaining sections from `docs/upstream/mattermost-operator.yaml` into their respective files in `chart/templates`. At this step the following file names to match the object types are: `clusterrole`, `clusterrolebinding`, `service`, `serviceaccount`, and `deployment`. -9. Open an MR in "Draft" status and validate that CI passes. This will perform a number of smoke tests against the package, but it is good to manually deploy to test some things that CI doesn't. Follow the steps below for manual testing. +8. Modify each to add labels and remove `creationTimestamp: null`. Any spot where `namespace:` if referenced should become `{{ .Release.Namespace }}` instead of hardcoded `mattermost-operator`. As before, the labels to add: -10. Once all manual testing is complete take your MR out of "Draft" status and add the review label. +```yaml + labels: + app.kubernetes.io/managed-by: '{{ .Release.Service }}' + app.kubernetes.io/instance: '{{ .Release.Name }}' + app.kubernetes.io/version: '{{ .Chart.AppVersion }}' + helm.sh/chart: '{{ .Chart.Name }}-{{ .Chart.Version }}' +``` -# Testing for updates +9. For the deployment file: make sure that you maintain the existing values mapping for `replicas`, `image`, `resources`, `imagePullSecrets`, `securityContext`, `nodeSelector`, `affinity`, and `tolerations`. These are all BigBang additions that we need to keep. -NOTE: For these testing steps it is good to do them on both a clean install and an upgrade. For clean install, point mattermost to your branch. For an upgrade do an install with mattermost pointing to the latest tag, then perform a helm upgrade with mattermost pointing to your branch. +10. Modify `chart/values.yaml` to use the latest image under `image.tag`. -You will want to install with: -- Mattermost, Mattermost Operator, and Minio Operator enabled -- Istio enabled -- [Dev SSO values](https://repo1.dso.mil/platform-one/big-bang/bigbang/-/blob/master/chart/dev-sso-values.yaml) for Mattermost -- [Enterprise enabled](https://repo1.dso.mil/platform-one/big-bang/bigbang/-/blob/7b14b4739b26b900cf7e6f1c075edc33c271eca6/chart/values.yaml#L962) - if you do not pass a license in, navigate to the System Console after install to start a trial -- Elasticsearch enabled + [integration enabled](https://repo1.dso.mil/platform-one/big-bang/bigbang/-/blob/7b14b4739b26b900cf7e6f1c075edc33c271eca6/chart/values.yaml#L1038) -- Monitoring enabled +11. Add a changelog entry for the Chart version. -Testing Steps: -- Log in with SSO via your `login.dso.mil` account. -- Follow the initial setup to create a team and send an initial chat. -- Under account settings, upload a profile picture. Validate the upload is successful and your profile picture is visible. -- Navigate to prometheus and validate that the Mattermost target shows as up (make sure you are on enterprise and have started a trial). -- Under system console -> elastic -> index now and validate success (make sure you are on enterprise and have started a trial). -- Check Grafana for data in the `Mattermost Performance Monitoring v2` dashboard (Ensure you change the server on the dashboard to point to the mattermost pod ip) +12. Update top-level ./README.md using script from [gluon library](https://repo1.dso.mil/platform-one/big-bang/apps/library-charts/gluon/-/blob/master/docs/bb-package-readme.md). -When in doubt with any testing or upgrade steps ask one of the CODEOWNERS for assistance. +13. Open an MR on Repo1 and validate that all changes look as expected in the diffs and CI passes. Make any necessary changes if something looks off or CI fails. diff --git a/docs/developer-guide.md b/docs/developer-guide.md new file mode 100644 index 000000000..b24da8b11 --- /dev/null +++ b/docs/developer-guide.md @@ -0,0 +1,32 @@ +# Developer guide + +### Dev cluster setup + +Using k3d (https://k3d.io/) start a dev cluster on your local machine. + +```bash +k3d cluster create --k3s-server-arg "--disable=traefik" --k3s-server-arg "--disable=metrics-server" -p 80:80@loadbalancer -p 443:443@loadbalancer -s 1 -a 3 -v ~/.k3d/p1-registry.yaml:/etc/rancher/k3s/registries.yaml +``` + +You will need a file located at: `~/.k3d/p1-registry.yaml` An example of that file is given here. This provides pull access to the repo 1 images that are used from Iron Bank. + +```yaml +configs: + "registry1.dso.mil": + auth: + username: "<repo 1 user>" + password: <repo 1 token> +``` +Install the local chart + +```bash +# Clone the repo +git clone https://repo1.dso.mil/platform-one/big-bang/apps/collaboration-tools/mattermost-operator.git + +# Install the chart (be sure to add a namespace to it if you don't want it in default) +cd mattermost-operator && helm dependency update chart +helm install mattermost-operator chart + +# Check to see if it is up +watch kubectl get all -A +``` diff --git a/docs/keycloak.md b/docs/keycloak.md deleted file mode 100644 index 12d56cdcc..000000000 --- a/docs/keycloak.md +++ /dev/null @@ -1,104 +0,0 @@ -# Keycloak SSO Mattermost Config - -## Keycloak Client Setup - -The Keycloak client can be set up by following [this tutorial](https://medium.com/@mrtcve/mattermost-teams-edition-replacing-gitlab-sso-with-keycloak-dabf13ebb99e). A summary is provided below, but if there are any issues refer to the source linked. - -Create client: -- client id - you pick, "mattermost" -- enabled - on -- client protocol - openid-connect -- access type - confidential -- standard flow enabled - on -- valid redirect URIs - "{mattermosturl}/signup/gitlab/complete" - -Under the mappers tab, create a new mapper: -- name - mattermostId -- mapper type - user attribute -- user attribute - mattermostId -- token claim name - id -- claim JSON type - long -- add to userinfo - on - -Create username mapper: -- name - username -- mapper type - user property -- property - username -- token claim name - username -- claim JSON type - string -- add to userinfo - on -- all other sliders off - -Create email mapper: -- name - email -- mapper type - user property -- property - email -- token claim name - email -- claim JSON type - string -- add to userinfo - on -- all other sliders off - -Add mattermostid to existing user: -- Login to keycloak Admin Console with the master realm user -- Go to your realm -- Go to the users section and edit the user -- Go to the Attributes tab -- In the bottom row type `mattermostId` in the key and a random number in the `value` field. -- Click Add. - -This mattermostid needs to be unique per user, so it's a bad idea to generate these by hand. This process is just a way to edit test/existing users. - -## Helm Values - -First get the values you need for your Keycloak: -- client_id: This is the client id you created and picked earlier -- client_secret: This is under the credential tab for your client, you can click regenerate and then copy it -- endpoints: Go to your realm settings and then open the "OpenID Endpoint Configuration". There should be values for authorization_endpoint, token_endpoint, and userinfo_endpoint which correspond to the auth, token, and user_api endpoints in the values. - -Modify your values.yaml according to these example values to enable Gitlab Auth provider for SSO. If you have a licensed version of Mattermost that supports OIDC the Mattermost OIDC client backend will obtain the endpoints automatically from the [well-known OIDC endpoint](https://login.dso.mil/auth/realms/baby-yoda/.well-known/openid-configuration). -``` -# SSO Additions -sso: - enabled: true - client_id: platform1_a8604cc9-f5e9-4656-802d-d05624370245_bb8-mattermost - client_secret: nothing # Change to your Keycloak client secret - auth_endpoint: https://login.dso.mil/auth/realms/baby-yoda/protocol/openid-connect/auth - token_endpoint: https://login.dso.mil/auth/realms/baby-yoda/protocol/openid-connect/token - user_api_endpoint: https://login.dso.mil/auth/realms/baby-yoda/protocol/openid-connect/userinfo - -``` - -Example install: -``` -helm upgrade -i mattermost chart -n mattermost --create-namespace -f my-values.yml -``` - -## Role Based Authentication - -Role based authentication can be configured as long as you are on an enterprise version. - -Follow the steps in [this tutorial](https://docs.mattermost.com/deployment/advanced-permissions.html) to customize the permissions given to users. In general permissions can be edited under the "System Console -> User Management -> Permissions". Users should be created by default under the "Member" group, except for the first user to sign up or login. - -## OIDC Custom CA - -Mattermost can be configured to point to specific files to trust with an OIDC auth connection, here is an example when using Big Bang to deploy mattermost, assuming you are populating a secret named "ca-cert" in the same namespace, with a key of cert.pem and value of a single PEM encoded certificate (an easy way to make this secret is included below as well): - -```yaml -addons: - mattermost: - values: - volumes: - - name: ca-cert - secret: - secretName: ca-secret - defaultMode: 0644 - volumeMounts: - - name: ca-cert - mountPath: /etc/ssl/certs - readOnly: true -``` - -For secret creation with this example and a pem file at `/path/to/cert.pem`: -```bash -kubectl create secret generic ca-secret --from-file=cert.pem=/path/to/cert.pem -n mattermost -``` diff --git a/docs/overview.md b/docs/overview.md index 228cef8fe..d86c7a3da 100644 --- a/docs/overview.md +++ b/docs/overview.md @@ -1,16 +1,13 @@ -# Mattermost +# Mattermost Operator ## Overview -This package contains an installation of Mattermost using a helm chart built by Big Bang that leverages the operator. +This package contains an installation of Mattermost Operator using a helm chart built by Big Bang. -## Mattermost +## Mattermost Operator -[Mattermost](https://mattermost.com/) is an open-source, self-hostable online chat service with file sharing, search, and integrations. -This repo provides an implementation of Mattermost for Big Bang. Installation requires that the [Mattermost Operator](https://repo1.dso.mil/platform-one/big-bang/apps/collaboration-tools/mattermost-operator) be installed in your cluster as a prerequisite. +The [Mattermost Operator](https://github.com/mattermost/mattermost-operator) is a [Kubernetes Operator](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/) for Mattermost to simplify deploying and managing your Mattermost instance. ## How it works -Mattermost is a single pane for collaboration, installed and configured via a `mattermost` CustomResource and reconciled by the operator. You can visit your installation via browser or connect through one of their Desktop apps available for many operating systems. - -Please review the BigBang [Architecture Document](https://repo1.dso.mil/platform-one/big-bang/bigbang/-/blob/master/charter/packages/mattermost/Architecture.md) for more information about it's role within BigBang. +Mattermost Operator for Kubernetes interacts with Mattermost Custom Resources. For additional information on Mattermost, please reference the [Mattermost overview doc](https://repo1.dso.mil/platform-one/big-bang/apps/collaboration-tools/mattermost/-/blob/main/docs/overview.md). diff --git a/docs/upstream/Kptfile b/docs/upstream/Kptfile new file mode 100644 index 000000000..8fcf3efc9 --- /dev/null +++ b/docs/upstream/Kptfile @@ -0,0 +1,11 @@ +apiVersion: kpt.dev/v1alpha1 +kind: Kptfile +metadata: + name: upstream +upstream: + type: git + git: + commit: 24f43e8b7df514ab8924919068305056ba96cace + repo: https://github.com/mattermost/mattermost-operator + directory: /docs/mattermost-operator + ref: v1.19.0 diff --git a/docs/upstream/mattermost-operator.yaml b/docs/upstream/mattermost-operator.yaml new file mode 100644 index 000000000..b7dd361cc --- /dev/null +++ b/docs/upstream/mattermost-operator.yaml @@ -0,0 +1,6935 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.7.0 + creationTimestamp: null + name: clusterinstallations.mattermost.com +spec: + group: mattermost.com + names: + kind: ClusterInstallation + listKind: ClusterInstallationList + plural: clusterinstallations + singular: clusterinstallation + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: State of Mattermost + jsonPath: .status.state + name: State + type: string + - description: Image of Mattermost + jsonPath: .status.image + name: Image + type: string + - description: Version of Mattermost + jsonPath: .status.version + name: Version + type: string + - description: Endpoint + jsonPath: .status.endpoint + name: Endpoint + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: ClusterInstallation is the Schema for the clusterinstallations + API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: 'Specification of the desired behavior of the Mattermost + cluster. More info: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#spec-and-status' + properties: + affinity: + description: If specified, affinity will define the pod's scheduling + constraints + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for the + pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to + nodes that satisfy the affinity expressions specified by + this field, but it may choose a node that violates one or + more of the expressions. The node that is most preferred + is the one with the greatest sum of weights, i.e. for each + node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, + etc.), compute a sum by iterating through the elements of + this field and adding "weight" to the sum if the node matches + the corresponding matchExpressions; the node(s) with the + highest sum are the most preferred. + items: + description: An empty preferred scheduling term matches + all objects with implicit weight 0 (i.e. it's a no-op). + A null preferred scheduling term matches no objects (i.e. + is also a no-op). + properties: + preference: + description: A node selector term, associated with the + corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. If the operator is Gt or + Lt, the values array must have a single + element, which will be interpreted as an + integer. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. If the operator is Gt or + Lt, the values array must have a single + element, which will be interpreted as an + integer. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + description: Weight associated with matching the corresponding + nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to an update), the system may or may not try to + eventually evict the pod from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. + The terms are ORed. + items: + description: A null or empty node selector term matches + no objects. The requirements of them are ANDed. The + TopologySelectorTerm type implements a subset of the + NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. If the operator is Gt or + Lt, the values array must have a single + element, which will be interpreted as an + integer. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string values. If + the operator is In or NotIn, the values + array must be non-empty. If the operator + is Exists or DoesNotExist, the values array + must be empty. If the operator is Gt or + Lt, the values array must have a single + element, which will be interpreted as an + integer. This array is replaced during a + strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. co-locate + this pod in the same node, zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to + nodes that satisfy the affinity expressions specified by + this field, but it may choose a node that violates one or + more of the expressions. The node that is most preferred + is the one with the greatest sum of weights, i.e. for each + node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling affinity expressions, + etc.), compute a sum by iterating through the elements of + this field and adding "weight" to the sum if the node has + pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by this + field and the ones listed in the namespaces field. + null selector and null or empty namespaces list + means "this pod's namespace". An empty selector + ({}) matches all namespaces. This field is beta-level + and is only honored when PodAffinityNamespaceSelector + feature is enabled. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. The + term is applied to the union of the namespaces + listed in this field and the ones selected by + namespaceSelector. null or empty namespaces list + and null namespaceSelector means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey + matches that of any node on which any of the selected + pods is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by this + field are not met at scheduling time, the pod will not be + scheduled onto the node. If the affinity requirements specified + by this field cease to be met at some point during pod execution + (e.g. due to a pod label update), the system may or may + not try to eventually evict the pod from its node. When + there are multiple elements, the lists of nodes corresponding + to each podAffinityTerm are intersected, i.e. all terms + must be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key <topologyKey> + matches that of any node on which a pod of the set of + pods is running + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". The + requirements are ANDed. + type: object + type: object + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied to the + union of the namespaces selected by this field and + the ones listed in the namespaces field. null selector + and null or empty namespaces list means "this pod's + namespace". An empty selector ({}) matches all namespaces. + This field is beta-level and is only honored when + PodAffinityNamespaceSelector feature is enabled. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". The + requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies a static list of namespace + names that the term applies to. The term is applied + to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. null or + empty namespaces list and null namespaceSelector means + "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of + any node on which any of the selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules (e.g. + avoid putting this pod in the same node, zone, etc. as some + other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods to + nodes that satisfy the anti-affinity expressions specified + by this field, but it may choose a node that violates one + or more of the expressions. The node that is most preferred + is the one with the greatest sum of weights, i.e. for each + node that meets all of the scheduling requirements (resource + request, requiredDuringScheduling anti-affinity expressions, + etc.), compute a sum by iterating through the elements of + this field and adding "weight" to the sum if the node has + pods which matches the corresponding podAffinityTerm; the + node(s) with the highest sum are the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by this + field and the ones listed in the namespaces field. + null selector and null or empty namespaces list + means "this pod's namespace". An empty selector + ({}) matches all namespaces. This field is beta-level + and is only honored when PodAffinityNamespaceSelector + feature is enabled. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. The + term is applied to the union of the namespaces + listed in this field and the ones selected by + namespaceSelector. null or empty namespaces list + and null namespaceSelector means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey + matches that of any node on which any of the selected + pods is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the corresponding + podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified by + this field are not met at scheduling time, the pod will + not be scheduled onto the node. If the anti-affinity requirements + specified by this field cease to be met at some point during + pod execution (e.g. due to a pod label update), the system + may or may not try to eventually evict the pod from its + node. When there are multiple elements, the lists of nodes + corresponding to each podAffinityTerm are intersected, i.e. + all terms must be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not co-located + (anti-affinity) with, where co-located is defined as running + on a node whose value of the label with key <topologyKey> + matches that of any node on which a pod of the set of + pods is running + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". The + requirements are ANDed. + type: object + type: object + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied to the + union of the namespaces selected by this field and + the ones listed in the namespaces field. null selector + and null or empty namespaces list means "this pod's + namespace". An empty selector ({}) matches all namespaces. + This field is beta-level and is only honored when + PodAffinityNamespaceSelector feature is enabled. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a + selector that contains values, a key, and an + operator that relates the key and values. + properties: + key: + description: key is the label key that the + selector applies to. + type: string + operator: + description: operator represents a key's relationship + to a set of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. If the + operator is Exists or DoesNotExist, the + values array must be empty. This array is + replaced during a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is "In", + and the values array contains only "value". The + requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies a static list of namespace + names that the term applies to. The term is applied + to the union of the namespaces listed in this field + and the ones selected by namespaceSelector. null or + empty namespaces list and null namespaceSelector means + "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods matching + the labelSelector in the specified namespaces, where + co-located is defined as running on a node whose value + of the label with key topologyKey matches that of + any node on which any of the selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + blueGreen: + description: BlueGreen defines the configuration of BlueGreen deployment + for a ClusterInstallation + properties: + blue: + description: Blue defines the blue deployment. + properties: + image: + description: Image defines the base Docker image that will + be used for the deployment. Required when BlueGreen or Canary + is enabled. + type: string + ingressName: + description: IngressName defines the ingress name that will + be used by the deployment. This option is not used for Canary + builds. + type: string + name: + description: Name defines the name of the deployment + type: string + resourceLabels: + additionalProperties: + type: string + type: object + version: + description: Version defines the Docker image version that + will be used for the deployment. Required when BlueGreen + or Canary is enabled. + type: string + type: object + enable: + description: Enable defines if BlueGreen deployment will be applied. + type: boolean + green: + description: Green defines the green deployment. + properties: + image: + description: Image defines the base Docker image that will + be used for the deployment. Required when BlueGreen or Canary + is enabled. + type: string + ingressName: + description: IngressName defines the ingress name that will + be used by the deployment. This option is not used for Canary + builds. + type: string + name: + description: Name defines the name of the deployment + type: string + resourceLabels: + additionalProperties: + type: string + type: object + version: + description: Version defines the Docker image version that + will be used for the deployment. Required when BlueGreen + or Canary is enabled. + type: string + type: object + productionDeployment: + description: ProductionDeployment defines if the current production + is blue or green. + type: string + type: object + canary: + description: Canary defines the configuration of Canary deployment + for a ClusterInstallation + properties: + deployment: + description: Deployment defines the canary deployment. + properties: + image: + description: Image defines the base Docker image that will + be used for the deployment. Required when BlueGreen or Canary + is enabled. + type: string + ingressName: + description: IngressName defines the ingress name that will + be used by the deployment. This option is not used for Canary + builds. + type: string + name: + description: Name defines the name of the deployment + type: string + resourceLabels: + additionalProperties: + type: string + type: object + version: + description: Version defines the Docker image version that + will be used for the deployment. Required when BlueGreen + or Canary is enabled. + type: string + type: object + enable: + description: Enable defines if a canary build will be deployed. + type: boolean + type: object + database: + description: Database defines the database configuration for a ClusterInstallation. + properties: + backupRemoteDeletePolicy: + description: Defines the backup retention policy. + type: string + backupRestoreSecretName: + description: Defines the secret to be used when performing a database + restore. + type: string + backupSchedule: + description: Defines the interval for backups in cron expression + format. + type: string + backupSecretName: + description: Defines the secret to be used for uploading/restoring + backup. + type: string + backupURL: + description: Defines the object storage url for uploading backups. + type: string + initBucketURL: + description: Defines the AWS S3 bucket where the Database Backup + is stored. The operator will download the file to restore the + data. + type: string + replicas: + description: Defines the number of database replicas. For redundancy + use at least 2 replicas. Setting this will override the number + of replicas set by 'Size'. + format: int32 + type: integer + resources: + description: Defines the resource requests and limits for the + database pods. + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, otherwise + to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + secret: + description: "Optionally enter the name of an already-existing + Secret for connecting to the database. This secret should be + configured as follows: \n User-Managed Database - Key: DB_CONNECTION_STRING + | Value: <FULL_DATABASE_CONNECTION_STRING> Operator-Managed + Database - Key: ROOT_PASSWORD | Value: <ROOT_DATABASE_PASSWORD> + \ - Key: USER | Value: <USER_NAME> - Key: PASSWORD | Value: + <USER_PASSWORD> - Key: DATABASE Value: <DATABASE_NAME> \n + Notes: If you define all secret values for both User-Managed + and Operator-Managed database types, the User-Managed connection + string will take precedence and the Operator-Managed values + will be ignored. If the secret is left blank, the default + behavior is to use an Operator-Managed database with strong + randomly-generated database credentials." + type: string + storageSize: + description: Defines the storage size for the database. ie 50Gi + pattern: ^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$ + type: string + type: + description: Defines the type of database to use for an Operator-Managed + database. This value is ignored when using a User-Managed database. + type: string + type: object + elasticSearch: + description: ElasticSearch defines the ElasticSearch configuration + for a ClusterInstallation. + properties: + host: + type: string + password: + type: string + username: + type: string + type: object + image: + description: Image defines the ClusterInstallation Docker image. + type: string + imagePullPolicy: + description: Specify deployment pull policy. + type: string + ingressAnnotations: + additionalProperties: + type: string + type: object + ingressName: + description: IngressName defines the name to be used when creating + the ingress rules + type: string + livenessProbe: + description: Defines the probe to check if the application is up and + running. + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute inside + the container, the working directory for the command is + root ('/') in the container's filesystem. The command is + simply exec'd, it is not run inside a shell, so traditional + shell instructions ('|', etc) won't work. To use a shell, + you need to explicitly call out to that shell. Exit status + of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe to be + considered failed after having succeeded. Defaults to 3. Minimum + value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. This + is an alpha field and requires enabling GRPCContainerProbe feature + gate. + properties: + port: + description: Port number of the gRPC service. Number must + be in the range 1 to 65535. + format: int32 + type: integer + service: + description: "Service is the name of the service to place + in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + \n If this is not specified, the default behavior is defined + by gRPC." + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the pod + IP. You probably want to set "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows + repeated headers. + items: + description: HTTPHeader describes a custom header to be + used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on the container. + Number must be in the range 1 to 65535. Name must be an + IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. Defaults + to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has started + before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. Default + to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe to be + considered successful after having failed. Defaults to 1. Must + be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on the container. + Number must be in the range 1 to 65535. Name must be an + IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs to terminate + gracefully upon probe failure. The grace period is the duration + in seconds after the processes running in the pod are sent a + termination signal and the time when the processes are forcibly + halted with a kill signal. Set this value longer than the expected + cleanup time for your process. If this value is nil, the pod's + terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. Value must + be non-negative integer. The value zero indicates stop immediately + via the kill signal (no opportunity to shut down). This is a + beta field and requires enabling ProbeTerminationGracePeriod + feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds + is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + mattermostEnv: + description: Optional environment variables to set in the Mattermost + application pods. + items: + description: EnvVar represents an environment variable present in + a Container. + properties: + name: + description: Name of the environment variable. Must be a C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) are expanded using + the previously defined environment variables in the container + and any service environment variables. If a variable cannot + be resolved, the reference in the input string will be unchanged. + Double $$ are reduced to a single $, which allows for escaping + the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the + string literal "$(VAR_NAME)". Escaped references will never + be expanded, regardless of whether the variable exists or + not. Defaults to "".' + type: string + valueFrom: + description: Source for the environment variable's value. Cannot + be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap or its key + must be defined + type: boolean + required: + - key + type: object + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels[''<KEY>'']`, `metadata.annotations[''<KEY>'']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, + status.podIP, status.podIPs.' + properties: + apiVersion: + description: Version of the schema the FieldPath is + written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified + API version. + type: string + required: + - fieldPath + type: object + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.ephemeral-storage) are currently supported.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the exposed + resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + secretKeyRef: + description: Selects a key of a secret in the pod's namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + type: object + required: + - name + type: object + type: array + mattermostLicenseSecret: + description: Secret that contains the mattermost license + type: string + migrate: + description: 'Migrate specifies that the ClusterInstallation CR should + be migrated to the Mattermost CR. CAUTION: Some features like BlueGreen + or Canary are not supported with a new Custom Resource therefore + migration should be performed with extra caution.' + type: boolean + minio: + description: Minio defines the configuration of Minio for a ClusterInstallation. + properties: + externalBucket: + description: Set to the bucket name of your external MinIO or + S3. + type: string + externalURL: + description: Set to use an external MinIO deployment or S3. Must + also set 'Secret' and 'ExternalBucket'. + type: string + replicas: + description: 'Defines the number of Minio replicas. Supply 1 to + run Minio in standalone mode with no redundancy. Supply 4 or + more to run Minio in distributed mode. Note that it is not possible + to upgrade Minio from standalone to distributed mode. Setting + this will override the number of replicas set by ''Size''. More + info: https://docs.min.io/docs/distributed-minio-quickstart-guide.html' + format: int32 + type: integer + resources: + description: Defines the resource requests and limits for the + Minio pods. + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, otherwise + to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + secret: + description: 'Optionally enter the name of already existing secret. + Secret should have two values: "accesskey" and "secretkey". + Required when "ExternalURL" is set.' + type: string + storageSize: + description: Defines the storage size for Minio. ie 50Gi + pattern: ^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$ + type: string + type: object + nodeSelector: + additionalProperties: + type: string + description: 'NodeSelector is a selector which must be true for the + pod to fit on a node. Selector which must match a node''s labels + for the pod to be scheduled on that node. More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/' + type: object + readinessProbe: + description: Defines the probe to check if the application is ready + to accept traffic. + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute inside + the container, the working directory for the command is + root ('/') in the container's filesystem. The command is + simply exec'd, it is not run inside a shell, so traditional + shell instructions ('|', etc) won't work. To use a shell, + you need to explicitly call out to that shell. Exit status + of 0 is treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe to be + considered failed after having succeeded. Defaults to 3. Minimum + value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. This + is an alpha field and requires enabling GRPCContainerProbe feature + gate. + properties: + port: + description: Port number of the gRPC service. Number must + be in the range 1 to 65535. + format: int32 + type: integer + service: + description: "Service is the name of the service to place + in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + \n If this is not specified, the default behavior is defined + by gRPC." + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the pod + IP. You probably want to set "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP allows + repeated headers. + items: + description: HTTPHeader describes a custom header to be + used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on the container. + Number must be in the range 1 to 65535. Name must be an + IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. Defaults + to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has started + before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. Default + to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe to be + considered successful after having failed. Defaults to 1. Must + be 1 for liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on the container. + Number must be in the range 1 to 65535. Name must be an + IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs to terminate + gracefully upon probe failure. The grace period is the duration + in seconds after the processes running in the pod are sent a + termination signal and the time when the processes are forcibly + halted with a kill signal. Set this value longer than the expected + cleanup time for your process. If this value is nil, the pod's + terminationGracePeriodSeconds will be used. Otherwise, this + value overrides the value provided by the pod spec. Value must + be non-negative integer. The value zero indicates stop immediately + via the kill signal (no opportunity to shut down). This is a + beta field and requires enabling ProbeTerminationGracePeriod + feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds + is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times out. + Defaults to 1 second. Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + replicas: + description: Replicas defines the number of replicas to use for the + Mattermost app servers. Setting this will override the number of + replicas set by 'Size'. + format: int32 + type: integer + resourceLabels: + additionalProperties: + type: string + type: object + resources: + description: Defines the resource requests and limits for the Mattermost + app server pods. + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute resources + allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, otherwise + to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + serviceAnnotations: + additionalProperties: + type: string + type: object + size: + description: 'Size defines the size of the ClusterInstallation. This + is typically specified in number of users. This will override replica + and resource requests/limits appropriately for the provided number + of users. This is a write-only field - its value is erased after + setting appropriate values of resources. Accepted values are: 100users, + 1000users, 5000users, 10000users, 250000users. If replicas and resource + requests/limits are not specified, and Size is not provided the + configuration for 5000users will be applied. Setting ''Replicas'', + ''Resources'', ''Minio.Replicas'', ''Minio.Resource'', ''Database.Replicas'', + or ''Database.Resources'' will override the values set by Size. + Setting new Size will override previous values regardless if set + by Size or manually.' + type: string + useIngressTLS: + type: boolean + useServiceLoadBalancer: + type: boolean + version: + description: Version defines the ClusterInstallation Docker image + version. + type: string + required: + - ingressName + type: object + status: + description: 'Most recent observed status of the Mattermost cluster. Read-only. + Not included when requesting from the apiserver, only from the Mattermost + Operator API itself. More info: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#spec-and-status' + properties: + blueName: + description: The name of the blue deployment in BlueGreen + type: string + endpoint: + description: The endpoint to access the Mattermost instance + type: string + greenName: + description: The name of the green deployment in BlueGreen + type: string + image: + description: The image running on the pods in the Mattermost instance + type: string + migration: + description: The status of migration to Mattermost CR. + properties: + error: + type: string + status: + type: string + type: object + replicas: + description: Total number of non-terminated pods targeted by this + Mattermost deployment + format: int32 + type: integer + state: + description: Represents the running state of the Mattermost instance + type: string + updatedReplicas: + description: Total number of non-terminated pods targeted by this + Mattermost deployment that are running with the desired image. + format: int32 + type: integer + version: + description: The version currently running in the Mattermost instance + type: string + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.7.0 + creationTimestamp: null + name: mattermostrestoredbs.mattermost.com +spec: + group: mattermost.com + names: + kind: MattermostRestoreDB + listKind: MattermostRestoreDBList + plural: mattermostrestoredbs + singular: mattermostrestoredb + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: State of Mattermost DB Restore + jsonPath: .status.state + name: State + type: string + - description: Original DB Replicas + jsonPath: .status.originalDBReplicas + name: Original DB Replicas + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: MattermostRestoreDB is the Schema for the mattermostrestoredbs + API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: MattermostRestoreDBSpec defines the desired state of MattermostRestoreDB + properties: + initBucketURL: + description: InitBucketURL defines where the DB backup file is located. + type: string + mattermostClusterName: + description: MattermostClusterName defines the ClusterInstallation + name. + type: string + mattermostDBName: + description: MattermostDBName defines the database name. Need to set + if different from `mattermost`. + type: string + mattermostDBPassword: + description: MattermostDBPassword defines the user password to access + the database. Need to set if the user is different from the one + created by the operator. + type: string + mattermostDBUser: + description: MattermostDBUser defines the user to access the database. + Need to set if the user is different from `mmuser`. + type: string + restoreSecret: + description: RestoreSecret defines the secret that holds the credentials + to MySQL Operator be able to download the DB backup file + type: string + type: object + status: + description: MattermostRestoreDBStatus defines the observed state of MattermostRestoreDB + properties: + originalDBReplicas: + description: The original number of database replicas. will be used + to restore after applying the db restore process. + format: int32 + type: integer + state: + description: Represents the state of the Mattermost restore Database. + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.7.0 + creationTimestamp: null + name: mattermosts.installation.mattermost.com +spec: + group: installation.mattermost.com + names: + kind: Mattermost + listKind: MattermostList + plural: mattermosts + shortNames: + - mm + singular: mattermost + scope: Namespaced + versions: + - additionalPrinterColumns: + - description: State of Mattermost + jsonPath: .status.state + name: State + type: string + - description: Image of Mattermost + jsonPath: .status.image + name: Image + type: string + - description: Version of Mattermost + jsonPath: .status.version + name: Version + type: string + - description: Endpoint + jsonPath: .status.endpoint + name: Endpoint + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: Mattermost is the Schema for the mattermosts API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: MattermostSpec defines the desired state of Mattermost + properties: + database: + description: External Services + properties: + disableReadinessCheck: + description: DisableReadinessCheck instructs Operator to not add + init container responsible for checking DB access. Can be used + to define custom init containers specified in `spec.PodExtensions.InitContainers`. + type: boolean + external: + description: Defines the configuration of and external database. + properties: + secret: + description: 'Secret contains data necessary to connect to + the external database. The Kubernetes Secret should contain: - + Key: DB_CONNECTION_STRING | Value: Full database connection + string. It can also contain optional fields, such as: - + Key: MM_SQLSETTINGS_DATASOURCEREPLICAS | Value: Connection + string to read replicas of the database. - Key: DB_CONNECTION_CHECK_URL + | Value: The URL used for checking that the database is + accessible. Omitting this value in the secret will cause + Operator to skip adding init container for database check.' + type: string + type: object + operatorManaged: + description: Defines the configuration of database managed by + Kubernetes operator. + properties: + backupRemoteDeletePolicy: + description: Defines the backup retention policy. + type: string + backupRestoreSecretName: + description: Defines the secret to be used when performing + a database restore. + type: string + backupSchedule: + description: Defines the interval for backups in cron expression + format. + type: string + backupSecretName: + description: Defines the secret to be used for uploading/restoring + backup. + type: string + backupURL: + description: Defines the object storage url for uploading + backups. + type: string + initBucketURL: + description: Defines the AWS S3 bucket where the Database + Backup is stored. The operator will download the file to + restore the data. + type: string + replicas: + description: Defines the number of database replicas. For + redundancy use at least 2 replicas. Setting this will override + the number of replicas set by 'Size'. + format: int32 + type: integer + resources: + description: Defines the resource requests and limits for + the database pods. + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of + compute resources required. If Requests is omitted for + a container, it defaults to Limits if that is explicitly + specified, otherwise to an implementation-defined value. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + storageSize: + description: Defines the storage size for the database. ie + 50Gi + pattern: ^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$ + type: string + type: + description: Defines the type of database to use for an Operator-Managed + database. + type: string + type: object + type: object + dnsConfig: + description: Custom DNS configuration to use for the Mattermost Installation + pods. + properties: + nameservers: + description: A list of DNS name server IP addresses. This will + be appended to the base nameservers generated from DNSPolicy. + Duplicated nameservers will be removed. + items: + type: string + type: array + options: + description: A list of DNS resolver options. This will be merged + with the base options generated from DNSPolicy. Duplicated entries + will be removed. Resolution options given in Options will override + those that appear in the base DNSPolicy. + items: + description: PodDNSConfigOption defines DNS resolver options + of a pod. + properties: + name: + description: Required. + type: string + value: + type: string + type: object + type: array + searches: + description: A list of DNS search domains for host-name lookup. + This will be appended to the base search paths generated from + DNSPolicy. Duplicated search paths will be removed. + items: + type: string + type: array + type: object + dnsPolicy: + description: Custom DNS policy to use for the Mattermost Installation + pods. + type: string + elasticSearch: + description: ElasticSearch defines the ElasticSearch configuration + for Mattermost. + properties: + host: + type: string + password: + type: string + username: + type: string + type: object + fileStore: + description: FileStore defines the file store configuration for Mattermost. + properties: + external: + description: Defines the configuration of an external file store. + properties: + bucket: + description: Set to the bucket name of your external MinIO + or S3. + type: string + secret: + description: 'Optionally enter the name of already existing + secret. Secret should have two values: "accesskey" and "secretkey".' + type: string + url: + description: Set to use an external MinIO deployment or S3. + type: string + type: object + operatorManaged: + description: Defines the configuration of file store managed by + Kubernetes operator. + properties: + replicas: + description: 'Defines the number of Minio replicas. Supply + 1 to run Minio in standalone mode with no redundancy. Supply + 4 or more to run Minio in distributed mode. Note that it + is not possible to upgrade Minio from standalone to distributed + mode. Setting this will override the number of replicas + set by ''Size''. More info: https://docs.min.io/docs/distributed-minio-quickstart-guide.html' + format: int32 + type: integer + resources: + description: Defines the resource requests and limits for + the Minio pods. + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of + compute resources required. If Requests is omitted for + a container, it defaults to Limits if that is explicitly + specified, otherwise to an implementation-defined value. + More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + storageSize: + description: Defines the storage size for Minio. ie 50Gi + pattern: ^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$ + type: string + type: object + type: object + image: + description: Image defines the Mattermost Docker image. + type: string + imagePullPolicy: + description: Specify Mattermost deployment pull policy. + type: string + imagePullSecrets: + description: Specify Mattermost image pull secrets. + items: + description: LocalObjectReference contains enough information to + let you locate the referenced object inside the same namespace. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + type: array + ingress: + description: Ingress defines configuration for Ingress resource created + by the Operator. + properties: + annotations: + additionalProperties: + type: string + description: Annotations defines annotations passed to the Ingress + associated with Mattermost. + type: object + enabled: + description: Enabled determines whether the Operator should create + Ingress resource or not. Disabling ingress on existing installation + will cause Operator to remove it. + type: boolean + host: + description: Host defines the Ingress host to be used when creating + the ingress rules. + type: string + hosts: + description: Hosts allows specifying additional domain names for + Mattermost to use. + items: + description: IngressHost specifies additional hosts configuration. + properties: + hostName: + type: string + type: object + type: array + ingressClass: + description: IngressClass will be set on Ingress resource to associate + it with specified IngressClass resource. + type: string + tlsSecret: + description: TLSSecret specifies secret used for configuring TLS + for Ingress. If empty TLS will not be configured. + type: string + required: + - enabled + type: object + ingressAnnotations: + additionalProperties: + type: string + description: 'IngressAnnotations defines annotations passed to the + Ingress associated with Mattermost. Deprecated: Use Spec.Ingress.Annotations.' + type: object + ingressName: + description: 'IngressName defines the host to be used when creating + the ingress rules. Deprecated: Use Spec.Ingress.Host instead.' + type: string + licenseSecret: + description: LicenseSecret is the name of the secret containing a + Mattermost license. + type: string + mattermostEnv: + description: Optional environment variables to set in the Mattermost + application pods. + items: + description: EnvVar represents an environment variable present in + a Container. + properties: + name: + description: Name of the environment variable. Must be a C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) are expanded using + the previously defined environment variables in the container + and any service environment variables. If a variable cannot + be resolved, the reference in the input string will be unchanged. + Double $$ are reduced to a single $, which allows for escaping + the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" will produce the + string literal "$(VAR_NAME)". Escaped references will never + be expanded, regardless of whether the variable exists or + not. Defaults to "".' + type: string + valueFrom: + description: Source for the environment variable's value. Cannot + be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap or its key + must be defined + type: boolean + required: + - key + type: object + fieldRef: + description: 'Selects a field of the pod: supports metadata.name, + metadata.namespace, `metadata.labels[''<KEY>'']`, `metadata.annotations[''<KEY>'']`, + spec.nodeName, spec.serviceAccountName, status.hostIP, + status.podIP, status.podIPs.' + properties: + apiVersion: + description: Version of the schema the FieldPath is + written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the specified + API version. + type: string + required: + - fieldPath + type: object + resourceFieldRef: + description: 'Selects a resource of the container: only + resources limits and requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, requests.memory + and requests.ephemeral-storage) are currently supported.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the exposed + resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + secretKeyRef: + description: Selects a key of a secret in the pod's namespace + properties: + key: + description: The key of the secret to select from. Must + be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the Secret or its key must + be defined + type: boolean + required: + - key + type: object + type: object + required: + - name + type: object + type: array + podExtensions: + description: PodExtensions specify custom extensions for Mattermost + pods. This can be used for custom readiness checks etc. These settings + generally don't need to be changed. + properties: + initContainers: + description: Additional InitContainers injected to pods. The setting + does not override InitContainers defined by the Operator. + items: + description: A single application container that you want to + run within a pod. + properties: + args: + description: 'Arguments to the entrypoint. The docker image''s + CMD is used if this is not provided. Variable references + $(VAR_NAME) are expanded using the container''s environment. + If a variable cannot be resolved, the reference in the + input string will be unchanged. Double $$ are reduced + to a single $, which allows for escaping the $(VAR_NAME) + syntax: i.e. "$$(VAR_NAME)" will produce the string literal + "$(VAR_NAME)". Escaped references will never be expanded, + regardless of whether the variable exists or not. Cannot + be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + items: + type: string + type: array + command: + description: 'Entrypoint array. Not executed within a shell. + The docker image''s ENTRYPOINT is used if this is not + provided. Variable references $(VAR_NAME) are expanded + using the container''s environment. If a variable cannot + be resolved, the reference in the input string will be + unchanged. Double $$ are reduced to a single $, which + allows for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" + will produce the string literal "$(VAR_NAME)". Escaped + references will never be expanded, regardless of whether + the variable exists or not. Cannot be updated. More info: + https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + items: + type: string + type: array + env: + description: List of environment variables to set in the + container. Cannot be updated. + items: + description: EnvVar represents an environment variable + present in a Container. + properties: + name: + description: Name of the environment variable. Must + be a C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) are + expanded using the previously defined environment + variables in the container and any service environment + variables. If a variable cannot be resolved, the + reference in the input string will be unchanged. + Double $$ are reduced to a single $, which allows + for escaping the $(VAR_NAME) syntax: i.e. "$$(VAR_NAME)" + will produce the string literal "$(VAR_NAME)". Escaped + references will never be expanded, regardless of + whether the variable exists or not. Defaults to + "".' + type: string + valueFrom: + description: Source for the environment variable's + value. Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap + or its key must be defined + type: boolean + required: + - key + type: object + fieldRef: + description: 'Selects a field of the pod: supports + metadata.name, metadata.namespace, `metadata.labels[''<KEY>'']`, + `metadata.annotations[''<KEY>'']`, spec.nodeName, + spec.serviceAccountName, status.hostIP, status.podIP, + status.podIPs.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in + the specified API version. + type: string + required: + - fieldPath + type: object + resourceFieldRef: + description: 'Selects a resource of the container: + only resources limits and requests (limits.cpu, + limits.memory, limits.ephemeral-storage, requests.cpu, + requests.memory and requests.ephemeral-storage) + are currently supported.' + properties: + containerName: + description: 'Container name: required for + volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of + the exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + secretKeyRef: + description: Selects a key of a secret in the + pod's namespace + properties: + key: + description: The key of the secret to select + from. Must be a valid secret key. + type: string + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the Secret or + its key must be defined + type: boolean + required: + - key + type: object + type: object + required: + - name + type: object + type: array + envFrom: + description: List of sources to populate environment variables + in the container. The keys defined within a source must + be a C_IDENTIFIER. All invalid keys will be reported as + an event when the container is starting. When a key exists + in multiple sources, the value associated with the last + source will take precedence. Values defined by an Env + with a duplicate key will take precedence. Cannot be updated. + items: + description: EnvFromSource represents the source of a + set of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap must + be defined + type: boolean + type: object + prefix: + description: An optional identifier to prepend to + each key in the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret must be + defined + type: boolean + type: object + type: object + type: array + image: + description: 'Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config management + to default or override container images in workload controllers + like Deployments and StatefulSets.' + type: string + imagePullPolicy: + description: 'Image pull policy. One of Always, Never, IfNotPresent. + Defaults to Always if :latest tag is specified, or IfNotPresent + otherwise. Cannot be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' + type: string + lifecycle: + description: Actions that the management system should take + in response to container lifecycle events. Cannot be updated. + properties: + postStart: + description: 'PostStart is called immediately after + a container is created. If the handler fails, the + container is terminated and restarted according to + its restart policy. Other management of the container + blocks until the hook completes. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to + execute inside the container, the working + directory for the command is root ('/') in + the container's filesystem. The command is + simply exec'd, it is not run inside a shell, + so traditional shell instructions ('|', etc) + won't work. To use a shell, you need to explicitly + call out to that shell. Exit status of 0 is + treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request + to perform. + properties: + host: + description: Host name to connect to, defaults + to the pod IP. You probably want to set "Host" + in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to + the host. Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: Deprecated. TCPSocket is NOT supported + as a LifecycleHandler and kept for the backward + compatibility. There are no validation of this + field and lifecycle hooks will fail in runtime + when tcp handler is specified. + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + description: 'PreStop is called immediately before a + container is terminated due to an API request or management + event such as liveness/startup probe failure, preemption, + resource contention, etc. The handler is not called + if the container crashes or exits. The Pod''s termination + grace period countdown begins before the PreStop hook + is executed. Regardless of the outcome of the handler, + the container will eventually terminate within the + Pod''s termination grace period (unless delayed by + finalizers). Other management of the container blocks + until the hook completes or until the termination + grace period is reached. More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to + execute inside the container, the working + directory for the command is root ('/') in + the container's filesystem. The command is + simply exec'd, it is not run inside a shell, + so traditional shell instructions ('|', etc) + won't work. To use a shell, you need to explicitly + call out to that shell. Exit status of 0 is + treated as live/healthy and non-zero is unhealthy. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http request + to perform. + properties: + host: + description: Host name to connect to, defaults + to the pod IP. You probably want to set "Host" + in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to + the host. Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: Deprecated. TCPSocket is NOT supported + as a LifecycleHandler and kept for the backward + compatibility. There are no validation of this + field and lifecycle hooks will fail in runtime + when tcp handler is specified. + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + description: 'Periodic probe of container liveness. Container + will be restarted if the probe fails. Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for + the command is root ('/') in the container's + filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, you need + to explicitly call out to that shell. Exit status + of 0 is treated as live/healthy and non-zero is + unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC + port. This is an alpha field and requires enabling + GRPCContainerProbe feature gate. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + description: "Service is the name of the service + to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + \n If this is not specified, the default behavior + is defined by gRPC." + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to + the pod IP. You probably want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the + host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container + has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum + value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving + a TCP port. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs + to terminate gracefully upon probe failure. The grace + period is the duration in seconds after the processes + running in the pod are sent a termination signal and + the time when the processes are forcibly halted with + a kill signal. Set this value longer than the expected + cleanup time for your process. If this value is nil, + the pod's terminationGracePeriodSeconds will be used. + Otherwise, this value overrides the value provided + by the pod spec. Value must be non-negative integer. + The value zero indicates stop immediately via the + kill signal (no opportunity to shut down). This is + a beta field and requires enabling ProbeTerminationGracePeriod + feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds + is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe + times out. Defaults to 1 second. Minimum value is + 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + name: + description: Name of the container specified as a DNS_LABEL. + Each container in a pod must have a unique name (DNS_LABEL). + Cannot be updated. + type: string + ports: + description: List of ports to expose from the container. + Exposing a port here gives the system additional information + about the network connections a container uses, but is + primarily informational. Not specifying a port here DOES + NOT prevent that port from being exposed. Any port which + is listening on the default "0.0.0.0" address inside a + container will be accessible from the network. Cannot + be updated. + items: + description: ContainerPort represents a network port in + a single container. + properties: + containerPort: + description: Number of port to expose on the pod's + IP address. This must be a valid port number, 0 + < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external port + to. + type: string + hostPort: + description: Number of port to expose on the host. + If specified, this must be a valid port number, + 0 < x < 65536. If HostNetwork is specified, this + must match ContainerPort. Most containers do not + need this. + format: int32 + type: integer + name: + description: If specified, this must be an IANA_SVC_NAME + and unique within the pod. Each named port in a + pod must have a unique name. Name for the port that + can be referred to by services. + type: string + protocol: + default: TCP + description: Protocol for port. Must be UDP, TCP, + or SCTP. Defaults to "TCP". + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + - protocol + x-kubernetes-list-type: map + readinessProbe: + description: 'Periodic probe of container service readiness. + Container will be removed from service endpoints if the + probe fails. Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for + the command is root ('/') in the container's + filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, you need + to explicitly call out to that shell. Exit status + of 0 is treated as live/healthy and non-zero is + unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC + port. This is an alpha field and requires enabling + GRPCContainerProbe feature gate. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + description: "Service is the name of the service + to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + \n If this is not specified, the default behavior + is defined by gRPC." + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to + the pod IP. You probably want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the + host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container + has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum + value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving + a TCP port. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs + to terminate gracefully upon probe failure. The grace + period is the duration in seconds after the processes + running in the pod are sent a termination signal and + the time when the processes are forcibly halted with + a kill signal. Set this value longer than the expected + cleanup time for your process. If this value is nil, + the pod's terminationGracePeriodSeconds will be used. + Otherwise, this value overrides the value provided + by the pod spec. Value must be non-negative integer. + The value zero indicates stop immediately via the + kill signal (no opportunity to shut down). This is + a beta field and requires enabling ProbeTerminationGracePeriod + feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds + is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe + times out. Defaults to 1 second. Minimum value is + 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + resources: + description: 'Compute Resources required by this container. + Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of + compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount + of compute resources required. If Requests is omitted + for a container, it defaults to Limits if that is + explicitly specified, otherwise to an implementation-defined + value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + securityContext: + description: 'SecurityContext defines the security options + the container should be run with. If set, the fields of + SecurityContext override the equivalent fields of PodSecurityContext. + More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/' + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation controls whether + a process can gain more privileges than its parent + process. This bool directly controls if the no_new_privs + flag will be set on the container process. AllowPrivilegeEscalation + is true always when the container is: 1) run as Privileged + 2) has CAP_SYS_ADMIN Note that this field cannot be + set when spec.os.name is windows.' + type: boolean + capabilities: + description: The capabilities to add/drop when running + containers. Defaults to the default set of capabilities + granted by the container runtime. Note that this field + cannot be set when spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + type: object + privileged: + description: Run container in privileged mode. Processes + in privileged containers are essentially equivalent + to root on the host. Defaults to false. Note that + this field cannot be set when spec.os.name is windows. + type: boolean + procMount: + description: procMount denotes the type of proc mount + to use for the containers. The default is DefaultProcMount + which uses the container runtime defaults for readonly + paths and masked paths. This requires the ProcMountType + feature flag to be enabled. Note that this field cannot + be set when spec.os.name is windows. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only + root filesystem. Default is false. Note that this + field cannot be set when spec.os.name is windows. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of the container + process. Uses runtime default if unset. May also be + set in PodSecurityContext. If set in both SecurityContext + and PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set + when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as + a non-root user. If true, the Kubelet will validate + the image at runtime to ensure that it does not run + as UID 0 (root) and fail to start the container if + it does. If unset or false, no such validation will + be performed. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container + process. Defaults to user specified in image metadata + if unspecified. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name + is windows. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to the + container. If unspecified, the container runtime will + allocate a random SELinux context for each container. May + also be set in PodSecurityContext. If set in both + SecurityContext and PodSecurityContext, the value + specified in SecurityContext takes precedence. Note + that this field cannot be set when spec.os.name is + windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by this container. + If seccomp options are provided at both the pod & + container level, the container options override the + pod options. Note that this field cannot be set when + spec.os.name is windows. + properties: + localhostProfile: + description: localhostProfile indicates a profile + defined in a file on the node should be used. + The profile must be preconfigured on the node + to work. Must be a descending path, relative to + the kubelet's configured seccomp profile location. + Must only be set if type is "Localhost". + type: string + type: + description: "type indicates which kind of seccomp + profile will be applied. Valid options are: \n + Localhost - a profile defined in a file on the + node should be used. RuntimeDefault - the container + runtime default profile should be used. Unconfined + - no profile should be applied." + type: string + required: + - type + type: object + windowsOptions: + description: The Windows specific settings applied to + all containers. If unspecified, the options from the + PodSecurityContext will be used. If set in both SecurityContext + and PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set + when spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA + admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec + named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name + of the GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container + should be run as a 'Host Process' container. This + field is alpha-level and will only be honored + by components that enable the WindowsHostProcessContainers + feature flag. Setting this field without the feature + flag will result in errors when validating the + Pod. All of a Pod's containers must have the same + effective HostProcess value (it is not allowed + to have a mix of HostProcess containers and non-HostProcess + containers). In addition, if HostProcess is true + then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: The UserName in Windows to run the + entrypoint of the container process. Defaults + to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set + in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence. + type: string + type: object + type: object + startupProbe: + description: 'StartupProbe indicates that the Pod has successfully + initialized. If specified, no other probes are executed + until this completes successfully. If this probe fails, + the Pod will be restarted, just as if the livenessProbe + failed. This can be used to provide different probe parameters + at the beginning of a Pod''s lifecycle, when it might + take a long time to load data or warm a cache, than during + steady-state operation. This cannot be updated. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute + inside the container, the working directory for + the command is root ('/') in the container's + filesystem. The command is simply exec'd, it is + not run inside a shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, you need + to explicitly call out to that shell. Exit status + of 0 is treated as live/healthy and non-zero is + unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe + to be considered failed after having succeeded. Defaults + to 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC + port. This is an alpha field and requires enabling + GRPCContainerProbe feature gate. + properties: + port: + description: Port number of the gRPC service. Number + must be in the range 1 to 65535. + format: int32 + type: integer + service: + description: "Service is the name of the service + to place in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + \n If this is not specified, the default behavior + is defined by gRPC." + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to + the pod IP. You probably want to set "Host" in + httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the request. + HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom header + to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the + host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container + has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe + to be considered successful after having failed. Defaults + to 1. Must be 1 for liveness and startup. Minimum + value is 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving + a TCP port. + properties: + host: + description: 'Optional: Host name to connect to, + defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access + on the container. Number must be in the range + 1 to 65535. Name must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs + to terminate gracefully upon probe failure. The grace + period is the duration in seconds after the processes + running in the pod are sent a termination signal and + the time when the processes are forcibly halted with + a kill signal. Set this value longer than the expected + cleanup time for your process. If this value is nil, + the pod's terminationGracePeriodSeconds will be used. + Otherwise, this value overrides the value provided + by the pod spec. Value must be non-negative integer. + The value zero indicates stop immediately via the + kill signal (no opportunity to shut down). This is + a beta field and requires enabling ProbeTerminationGracePeriod + feature gate. Minimum value is 1. spec.terminationGracePeriodSeconds + is used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe + times out. Defaults to 1 second. Minimum value is + 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + stdin: + description: Whether this container should allocate a buffer + for stdin in the container runtime. If this is not set, + reads from stdin in the container will always result in + EOF. Default is false. + type: boolean + stdinOnce: + description: Whether the container runtime should close + the stdin channel after it has been opened by a single + attach. When stdin is true the stdin stream will remain + open across multiple attach sessions. If stdinOnce is + set to true, stdin is opened on container start, is empty + until the first client attaches to stdin, and then remains + open and accepts data until the client disconnects, at + which time stdin is closed and remains closed until the + container is restarted. If this flag is false, a container + processes that reads from stdin will never receive an + EOF. Default is false + type: boolean + terminationMessagePath: + description: 'Optional: Path at which the file to which + the container''s termination message will be written is + mounted into the container''s filesystem. Message written + is intended to be brief final status, such as an assertion + failure message. Will be truncated by the node if greater + than 4096 bytes. The total message length across all containers + will be limited to 12kb. Defaults to /dev/termination-log. + Cannot be updated.' + type: string + terminationMessagePolicy: + description: Indicate how the termination message should + be populated. File will use the contents of terminationMessagePath + to populate the container status message on both success + and failure. FallbackToLogsOnError will use the last chunk + of container log output if the termination message file + is empty and the container exited with an error. The log + output is limited to 2048 bytes or 80 lines, whichever + is smaller. Defaults to File. Cannot be updated. + type: string + tty: + description: Whether this container should allocate a TTY + for itself, also requires 'stdin' to be true. Default + is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of block devices + to be used by the container. + items: + description: volumeDevice describes a mapping of a raw + block device within a container. + properties: + devicePath: + description: devicePath is the path inside of the + container that the device will be mapped to. + type: string + name: + description: name must match the name of a persistentVolumeClaim + in the pod + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + description: Pod volumes to mount into the container's filesystem. + Cannot be updated. + items: + description: VolumeMount describes a mounting of a Volume + within a container. + properties: + mountPath: + description: Path within the container at which the + volume should be mounted. Must not contain ':'. + type: string + mountPropagation: + description: mountPropagation determines how mounts + are propagated from the host to container and the + other way around. When not set, MountPropagationNone + is used. This field is beta in 1.10. + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: Mounted read-only if true, read-write + otherwise (false or unspecified). Defaults to false. + type: boolean + subPath: + description: Path within the volume from which the + container's volume should be mounted. Defaults to + "" (volume's root). + type: string + subPathExpr: + description: Expanded path within the volume from + which the container's volume should be mounted. + Behaves similarly to SubPath but environment variable + references $(VAR_NAME) are expanded using the container's + environment. Defaults to "" (volume's root). SubPathExpr + and SubPath are mutually exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + description: Container's working directory. If not specified, + the container runtime's default will be used, which might + be configured in the container image. Cannot be updated. + type: string + required: + - name + type: object + type: array + type: object + podTemplate: + description: PodTemplate defines configuration for the template for + Mattermost pods. + properties: + containerSecurityContext: + description: Defines the security context for the Mattermost app + server container. + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation controls whether a + process can gain more privileges than its parent process. + This bool directly controls if the no_new_privs flag will + be set on the container process. AllowPrivilegeEscalation + is true always when the container is: 1) run as Privileged + 2) has CAP_SYS_ADMIN Note that this field cannot be set + when spec.os.name is windows.' + type: boolean + capabilities: + description: The capabilities to add/drop when running containers. + Defaults to the default set of capabilities granted by the + container runtime. Note that this field cannot be set when + spec.os.name is windows. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent POSIX capabilities + type + type: string + type: array + type: object + privileged: + description: Run container in privileged mode. Processes in + privileged containers are essentially equivalent to root + on the host. Defaults to false. Note that this field cannot + be set when spec.os.name is windows. + type: boolean + procMount: + description: procMount denotes the type of proc mount to use + for the containers. The default is DefaultProcMount which + uses the container runtime defaults for readonly paths and + masked paths. This requires the ProcMountType feature flag + to be enabled. Note that this field cannot be set when spec.os.name + is windows. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only root filesystem. + Default is false. Note that this field cannot be set when + spec.os.name is windows. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of the container + process. Uses runtime default if unset. May also be set + in PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set when + spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a non-root + user. If true, the Kubelet will validate the image at runtime + to ensure that it does not run as UID 0 (root) and fail + to start the container if it does. If unset or false, no + such validation will be performed. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container + process. Defaults to user specified in image metadata if + unspecified. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. Note + that this field cannot be set when spec.os.name is windows. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to the container. + If unspecified, the container runtime will allocate a random + SELinux context for each container. May also be set in + PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set when + spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by this container. + If seccomp options are provided at both the pod & container + level, the container options override the pod options. Note + that this field cannot be set when spec.os.name is windows. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile must + be preconfigured on the node to work. Must be a descending + path, relative to the kubelet's configured seccomp profile + location. Must only be set if type is "Localhost". + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - a + profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile + should be used. Unconfined - no profile should be applied." + type: string + required: + - type + type: object + windowsOptions: + description: The Windows specific settings applied to all + containers. If unspecified, the options from the PodSecurityContext + will be used. If set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence. + Note that this field cannot be set when spec.os.name is + linux. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named + by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. This field is + alpha-level and will only be honored by components that + enable the WindowsHostProcessContainers feature flag. + Setting this field without the feature flag will result + in errors when validating the Pod. All of a Pod's containers + must have the same effective HostProcess value (it is + not allowed to have a mix of HostProcess containers + and non-HostProcess containers). In addition, if HostProcess + is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set in + PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. + type: string + type: object + type: object + extraAnnotations: + additionalProperties: + type: string + description: Defines annotations to add to the Mattermost app + server pods. Overrides of default prometheus annotations are + ignored. + type: object + extraLabels: + additionalProperties: + type: string + description: Defines labels to add to the Mattermost app server + pods. Overrides what is set in ResourceLabels, does not override + default labels (app and cluster labels). + type: object + securityContext: + description: Defines the security context for the Mattermost app + server pods. + properties: + fsGroup: + description: "A special supplemental group that applies to + all containers in a pod. Some volume types allow the Kubelet + to change the ownership of that volume to be owned by the + pod: \n 1. The owning GID will be the FSGroup 2. The setgid + bit is set (new files created in the volume will be owned + by FSGroup) 3. The permission bits are OR'd with rw-rw---- + \n If unset, the Kubelet will not modify the ownership and + permissions of any volume. Note that this field cannot be + set when spec.os.name is windows." + format: int64 + type: integer + fsGroupChangePolicy: + description: 'fsGroupChangePolicy defines behavior of changing + ownership and permission of the volume before being exposed + inside Pod. This field will only apply to volume types which + support fsGroup based ownership(and permissions). It will + have no effect on ephemeral volume types such as: secret, + configmaps and emptydir. Valid values are "OnRootMismatch" + and "Always". If not specified, "Always" is used. Note that + this field cannot be set when spec.os.name is windows.' + type: string + runAsGroup: + description: The GID to run the entrypoint of the container + process. Uses runtime default if unset. May also be set + in SecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence for that container. Note that this field + cannot be set when spec.os.name is windows. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run as a non-root + user. If true, the Kubelet will validate the image at runtime + to ensure that it does not run as UID 0 (root) and fail + to start the container if it does. If unset or false, no + such validation will be performed. May also be set in SecurityContext. If + set in both SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the container + process. Defaults to user specified in image metadata if + unspecified. May also be set in SecurityContext. If set + in both SecurityContext and PodSecurityContext, the value + specified in SecurityContext takes precedence for that container. + Note that this field cannot be set when spec.os.name is + windows. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to all containers. + If unspecified, the container runtime will allocate a random + SELinux context for each container. May also be set in + SecurityContext. If set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence + for that container. Note that this field cannot be set when + spec.os.name is windows. + properties: + level: + description: Level is SELinux level label that applies + to the container. + type: string + role: + description: Role is a SELinux role label that applies + to the container. + type: string + type: + description: Type is a SELinux type label that applies + to the container. + type: string + user: + description: User is a SELinux user label that applies + to the container. + type: string + type: object + seccompProfile: + description: The seccomp options to use by the containers + in this pod. Note that this field cannot be set when spec.os.name + is windows. + properties: + localhostProfile: + description: localhostProfile indicates a profile defined + in a file on the node should be used. The profile must + be preconfigured on the node to work. Must be a descending + path, relative to the kubelet's configured seccomp profile + location. Must only be set if type is "Localhost". + type: string + type: + description: "type indicates which kind of seccomp profile + will be applied. Valid options are: \n Localhost - a + profile defined in a file on the node should be used. + RuntimeDefault - the container runtime default profile + should be used. Unconfined - no profile should be applied." + type: string + required: + - type + type: object + supplementalGroups: + description: A list of groups applied to the first process + run in each container, in addition to the container's primary + GID. If unspecified, no groups will be added to any container. + Note that this field cannot be set when spec.os.name is + windows. + items: + format: int64 + type: integer + type: array + sysctls: + description: Sysctls hold a list of namespaced sysctls used + for the pod. Pods with unsupported sysctls (by the container + runtime) might fail to launch. Note that this field cannot + be set when spec.os.name is windows. + items: + description: Sysctl defines a kernel parameter to be set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + description: The Windows specific settings applied to all + containers. If unspecified, the options within a container's + SecurityContext will be used. If set in both SecurityContext + and PodSecurityContext, the value specified in SecurityContext + takes precedence. Note that this field cannot be set when + spec.os.name is linux. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA admission + webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential spec named + by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name of the + GMSA credential spec to use. + type: string + hostProcess: + description: HostProcess determines if a container should + be run as a 'Host Process' container. This field is + alpha-level and will only be honored by components that + enable the WindowsHostProcessContainers feature flag. + Setting this field without the feature flag will result + in errors when validating the Pod. All of a Pod's containers + must have the same effective HostProcess value (it is + not allowed to have a mix of HostProcess containers + and non-HostProcess containers). In addition, if HostProcess + is true then HostNetwork must also be set to true. + type: boolean + runAsUserName: + description: The UserName in Windows to run the entrypoint + of the container process. Defaults to the user specified + in image metadata if unspecified. May also be set in + PodSecurityContext. If set in both SecurityContext and + PodSecurityContext, the value specified in SecurityContext + takes precedence. + type: string + type: object + type: object + type: object + probes: + description: Probes defines configuration of liveness and readiness + probe for Mattermost pods. These settings generally don't need to + be changed. + properties: + livenessProbe: + description: Defines the probe to check if the application is + up and running. + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute inside + the container, the working directory for the command is + root ('/') in the container's filesystem. The command + is simply exec'd, it is not run inside a shell, so traditional + shell instructions ('|', etc) won't work. To use a shell, + you need to explicitly call out to that shell. Exit + status of 0 is treated as live/healthy and non-zero + is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe to + be considered failed after having succeeded. Defaults to + 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + This is an alpha field and requires enabling GRPCContainerProbe + feature gate. + properties: + port: + description: Port number of the gRPC service. Number must + be in the range 1 to 65535. + format: int32 + type: integer + service: + description: "Service is the name of the service to place + in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + \n If this is not specified, the default behavior is + defined by gRPC." + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header to + be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on the + container. Number must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has started + before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe to + be considered successful after having failed. Defaults to + 1. Must be 1 for liveness and startup. Minimum value is + 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on the + container. Number must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs to + terminate gracefully upon probe failure. The grace period + is the duration in seconds after the processes running in + the pod are sent a termination signal and the time when + the processes are forcibly halted with a kill signal. Set + this value longer than the expected cleanup time for your + process. If this value is nil, the pod's terminationGracePeriodSeconds + will be used. Otherwise, this value overrides the value + provided by the pod spec. Value must be non-negative integer. + The value zero indicates stop immediately via the kill signal + (no opportunity to shut down). This is a beta field and + requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is + used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times + out. Defaults to 1 second. Minimum value is 1. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + readinessProbe: + description: Defines the probe to check if the application is + ready to accept traffic. + properties: + exec: + description: Exec specifies the action to take. + properties: + command: + description: Command is the command line to execute inside + the container, the working directory for the command is + root ('/') in the container's filesystem. The command + is simply exec'd, it is not run inside a shell, so traditional + shell instructions ('|', etc) won't work. To use a shell, + you need to explicitly call out to that shell. Exit + status of 0 is treated as live/healthy and non-zero + is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for the probe to + be considered failed after having succeeded. Defaults to + 3. Minimum value is 1. + format: int32 + type: integer + grpc: + description: GRPC specifies an action involving a GRPC port. + This is an alpha field and requires enabling GRPCContainerProbe + feature gate. + properties: + port: + description: Port number of the gRPC service. Number must + be in the range 1 to 65535. + format: int32 + type: integer + service: + description: "Service is the name of the service to place + in the gRPC HealthCheckRequest (see https://github.com/grpc/grpc/blob/master/doc/health-checking.md). + \n If this is not specified, the default behavior is + defined by gRPC." + type: string + required: + - port + type: object + httpGet: + description: HTTPGet specifies the http request to perform. + properties: + host: + description: Host name to connect to, defaults to the + pod IP. You probably want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in the request. HTTP + allows repeated headers. + items: + description: HTTPHeader describes a custom header to + be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port to access on the + container. Number must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting to the host. + Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container has started + before liveness probes are initiated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform the probe. + Default to 10 seconds. Minimum value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for the probe to + be considered successful after having failed. Defaults to + 1. Must be 1 for liveness and startup. Minimum value is + 1. + format: int32 + type: integer + tcpSocket: + description: TCPSocket specifies an action involving a TCP + port. + properties: + host: + description: 'Optional: Host name to connect to, defaults + to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port to access on the + container. Number must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs to + terminate gracefully upon probe failure. The grace period + is the duration in seconds after the processes running in + the pod are sent a termination signal and the time when + the processes are forcibly halted with a kill signal. Set + this value longer than the expected cleanup time for your + process. If this value is nil, the pod's terminationGracePeriodSeconds + will be used. Otherwise, this value overrides the value + provided by the pod spec. Value must be non-negative integer. + The value zero indicates stop immediately via the kill signal + (no opportunity to shut down). This is a beta field and + requires enabling ProbeTerminationGracePeriod feature gate. + Minimum value is 1. spec.terminationGracePeriodSeconds is + used if unset. + format: int64 + type: integer + timeoutSeconds: + description: 'Number of seconds after which the probe times + out. Defaults to 1 second. Minimum value is 1. More info: + https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + type: object + replicas: + description: Replicas defines the number of replicas to use for the + Mattermost app servers. + format: int32 + type: integer + resourceLabels: + additionalProperties: + type: string + type: object + resourcePatch: + description: "ResourcePatch specifies JSON patches that can be applied + to resources created by Mattermost Operator. \n WARNING: ResourcePatch + is highly experimental and subject to change. Some patches may be + impossible to perform or may impact the stability of Mattermost + server. \n Use at your own risk when no other options are available." + properties: + deployment: + properties: + disable: + type: boolean + patch: + type: string + type: object + service: + properties: + disable: + type: boolean + patch: + type: string + type: object + type: object + scheduling: + description: Scheduling defines the configuration related to scheduling + of the Mattermost pods as well as resource constraints. These settings + generally don't need to be changed. + properties: + affinity: + description: If specified, affinity will define the pod's scheduling + constraints + properties: + nodeAffinity: + description: Describes node affinity scheduling rules for + the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods + to nodes that satisfy the affinity expressions specified + by this field, but it may choose a node that violates + one or more of the expressions. The node that is most + preferred is the one with the greatest sum of weights, + i.e. for each node that meets all of the scheduling + requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating + through the elements of this field and adding "weight" + to the sum if the node matches the corresponding matchExpressions; + the node(s) with the highest sum are the most preferred. + items: + description: An empty preferred scheduling term matches + all objects with implicit weight 0 (i.e. it's a no-op). + A null preferred scheduling term matches no objects + (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated with + the corresponding weight. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. If the + operator is Gt or Lt, the values array + must have a single element, which will + be interpreted as an integer. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. If the + operator is Gt or Lt, the values array + must have a single element, which will + be interpreted as an integer. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + description: Weight associated with matching the + corresponding nodeSelectorTerm, in the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by + this field are not met at scheduling time, the pod will + not be scheduled onto the node. If the affinity requirements + specified by this field cease to be met at some point + during pod execution (e.g. due to an update), the system + may or may not try to eventually evict the pod from + its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector terms. + The terms are ORed. + items: + description: A null or empty node selector term + matches no objects. The requirements of them are + ANDed. The TopologySelectorTerm type implements + a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector requirements + by node's labels. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. If the + operator is Gt or Lt, the values array + must have a single element, which will + be interpreted as an integer. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector requirements + by node's fields. + items: + description: A node selector requirement is + a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: The label key that the selector + applies to. + type: string + operator: + description: Represents a key's relationship + to a set of values. Valid operators + are In, NotIn, Exists, DoesNotExist. + Gt, and Lt. + type: string + values: + description: An array of string values. + If the operator is In or NotIn, the + values array must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be empty. If the + operator is Gt or Lt, the values array + must have a single element, which will + be interpreted as an integer. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + description: Describes pod affinity scheduling rules (e.g. + co-locate this pod in the same node, zone, etc. as some + other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods + to nodes that satisfy the affinity expressions specified + by this field, but it may choose a node that violates + one or more of the expressions. The node that is most + preferred is the one with the greatest sum of weights, + i.e. for each node that meets all of the scheduling + requirements (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by iterating + through the elements of this field and adding "weight" + to the sum if the node has pods which matches the corresponding + podAffinityTerm; the node(s) with the highest sum are + the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by + this field and the ones listed in the namespaces + field. null selector and null or empty namespaces + list means "this pod's namespace". An empty + selector ({}) matches all namespaces. This + field is beta-level and is only honored when + PodAffinityNamespaceSelector feature is enabled. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. + The term is applied to the union of the namespaces + listed in this field and the ones selected + by namespaceSelector. null or empty namespaces + list and null namespaceSelector means "this + pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the + pods matching the labelSelector in the specified + namespaces, where co-located is defined as + running on a node whose value of the label + with key topologyKey matches that of any node + on which any of the selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the + corresponding podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified by + this field are not met at scheduling time, the pod will + not be scheduled onto the node. If the affinity requirements + specified by this field cease to be met at some point + during pod execution (e.g. due to a pod label update), + the system may or may not try to eventually evict the + pod from its node. When there are multiple elements, + the lists of nodes corresponding to each podAffinityTerm + are intersected, i.e. all terms must be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not + co-located (anti-affinity) with, where co-located + is defined as running on a node whose value of the + label with key <topologyKey> matches that of any node + on which a pod of the set of pods is running + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by this + field and the ones listed in the namespaces field. + null selector and null or empty namespaces list + means "this pod's namespace". An empty selector + ({}) matches all namespaces. This field is beta-level + and is only honored when PodAffinityNamespaceSelector + feature is enabled. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. The + term is applied to the union of the namespaces + listed in this field and the ones selected by + namespaceSelector. null or empty namespaces list + and null namespaceSelector means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey + matches that of any node on which any of the selected + pods is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling rules + (e.g. avoid putting this pod in the same node, zone, etc. + as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule pods + to nodes that satisfy the anti-affinity expressions + specified by this field, but it may choose a node that + violates one or more of the expressions. The node that + is most preferred is the one with the greatest sum of + weights, i.e. for each node that meets all of the scheduling + requirements (resource request, requiredDuringScheduling + anti-affinity expressions, etc.), compute a sum by iterating + through the elements of this field and adding "weight" + to the sum if the node has pods which matches the corresponding + podAffinityTerm; the node(s) with the highest sum are + the most preferred. + items: + description: The weights of all of the matched WeightedPodAffinityTerm + fields are added per-node to find the most preferred + node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, associated + with the corresponding weight. + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by + this field and the ones listed in the namespaces + field. null selector and null or empty namespaces + list means "this pod's namespace". An empty + selector ({}) matches all namespaces. This + field is beta-level and is only honored when + PodAffinityNamespaceSelector feature is enabled. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The requirements + are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label key + that the selector applies to. + type: string + operator: + description: operator represents a + key's relationship to a set of values. + Valid operators are In, NotIn, Exists + and DoesNotExist. + type: string + values: + description: values is an array of + string values. If the operator is + In or NotIn, the values array must + be non-empty. If the operator is + Exists or DoesNotExist, the values + array must be empty. This array + is replaced during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. + The term is applied to the union of the namespaces + listed in this field and the ones selected + by namespaceSelector. null or empty namespaces + list and null namespaceSelector means "this + pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the + pods matching the labelSelector in the specified + namespaces, where co-located is defined as + running on a node whose value of the label + with key topologyKey matches that of any node + on which any of the selected pods is running. + Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching the + corresponding podAffinityTerm, in the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements specified + by this field are not met at scheduling time, the pod + will not be scheduled onto the node. If the anti-affinity + requirements specified by this field cease to be met + at some point during pod execution (e.g. due to a pod + label update), the system may or may not try to eventually + evict the pod from its node. When there are multiple + elements, the lists of nodes corresponding to each podAffinityTerm + are intersected, i.e. all terms must be satisfied. + items: + description: Defines a set of pods (namely those matching + the labelSelector relative to the given namespace(s)) + that this pod should be co-located (affinity) or not + co-located (anti-affinity) with, where co-located + is defined as running on a node whose value of the + label with key <topologyKey> matches that of any node + on which a pod of the set of pods is running + properties: + labelSelector: + description: A label query over a set of resources, + in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaceSelector: + description: A label query over the set of namespaces + that the term applies to. The term is applied + to the union of the namespaces selected by this + field and the ones listed in the namespaces field. + null selector and null or empty namespaces list + means "this pod's namespace". An empty selector + ({}) matches all namespaces. This field is beta-level + and is only honored when PodAffinityNamespaceSelector + feature is enabled. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies a static list + of namespace names that the term applies to. The + term is applied to the union of the namespaces + listed in this field and the ones selected by + namespaceSelector. null or empty namespaces list + and null namespaceSelector means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located (affinity) + or not co-located (anti-affinity) with the pods + matching the labelSelector in the specified namespaces, + where co-located is defined as running on a node + whose value of the label with key topologyKey + matches that of any node on which any of the selected + pods is running. Empty topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + nodeSelector: + additionalProperties: + type: string + description: 'NodeSelector is a selector which must be true for + the pod to fit on a node. Selector which must match a node''s + labels for the pod to be scheduled on that node. More info: + https://kubernetes.io/docs/concepts/configuration/assign-pod-node/' + type: object + resources: + description: Defines the resource requests and limits for the + Mattermost app server pods. + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount of compute + resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum amount of compute + resources required. If Requests is omitted for a container, + it defaults to Limits if that is explicitly specified, otherwise + to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + tolerations: + description: 'Defines tolerations for the Mattermost app server + pods More info: https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/' + items: + description: The pod this Toleration is attached to tolerates + any taint that matches the triple <key,value,effect> using + the matching operator <operator>. + properties: + effect: + description: Effect indicates the taint effect to match. + Empty means match all taint effects. When specified, allowed + values are NoSchedule, PreferNoSchedule and NoExecute. + type: string + key: + description: Key is the taint key that the toleration applies + to. Empty means match all taint keys. If the key is empty, + operator must be Exists; this combination means to match + all values and all keys. + type: string + operator: + description: Operator represents a key's relationship to + the value. Valid operators are Exists and Equal. Defaults + to Equal. Exists is equivalent to wildcard for value, + so that a pod can tolerate all taints of a particular + category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period of + time the toleration (which must be of effect NoExecute, + otherwise this field is ignored) tolerates the taint. + By default, it is not set, which means tolerate the taint + forever (do not evict). Zero and negative values will + be treated as 0 (evict immediately) by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration matches + to. If the operator is Exists, the value should be empty, + otherwise just a regular string. + type: string + type: object + type: array + type: object + serviceAnnotations: + additionalProperties: + type: string + type: object + size: + description: 'Size defines the size of the Mattermost. This is typically + specified in number of users. This will override replica and resource + requests/limits appropriately for the provided number of users. + This is a write-only field - its value is erased after setting appropriate + values of resources. Accepted values are: 100users, 1000users, 5000users, + 10000users, and 250000users. If replicas and resource requests/limits + are not specified, and Size is not provided the configuration for + 5000users will be applied. Setting ''Replicas'', ''Scheduling.Resources'', + ''FileStore.Replicas'', ''FileStore.Resource'', ''Database.Replicas'', + or ''Database.Resources'' will override the values set by Size. + Setting new Size will override previous values regardless if set + by Size or manually.' + type: string + updateJob: + description: UpdateJob defines configuration for the template for + the update job. + properties: + disabled: + description: Determines whether to disable the Operator's creation + of the update job. + type: boolean + extraAnnotations: + additionalProperties: + type: string + description: Defines annotations to add to the update job pod. + type: object + extraLabels: + additionalProperties: + type: string + description: Defines labels to add to the update job pod. Overrides + what is set in ResourceLabels, does not override default label + (app label). + type: object + type: object + useIngressTLS: + description: 'UseIngressTLS specifies whether TLS secret should be + configured for Ingress. Deprecated: Use Spec.Ingress.TLSSecret.' + type: boolean + useServiceLoadBalancer: + type: boolean + version: + description: Version defines the Mattermost Docker image version. + type: string + volumeMounts: + description: Defines additional volumeMounts to add to Mattermost + application pods. + items: + description: VolumeMount describes a mounting of a Volume within + a container. + properties: + mountPath: + description: Path within the container at which the volume should + be mounted. Must not contain ':'. + type: string + mountPropagation: + description: mountPropagation determines how mounts are propagated + from the host to container and the other way around. When + not set, MountPropagationNone is used. This field is beta + in 1.10. + type: string + name: + description: This must match the Name of a Volume. + type: string + readOnly: + description: Mounted read-only if true, read-write otherwise + (false or unspecified). Defaults to false. + type: boolean + subPath: + description: Path within the volume from which the container's + volume should be mounted. Defaults to "" (volume's root). + type: string + subPathExpr: + description: Expanded path within the volume from which the + container's volume should be mounted. Behaves similarly to + SubPath but environment variable references $(VAR_NAME) are + expanded using the container's environment. Defaults to "" + (volume's root). SubPathExpr and SubPath are mutually exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + volumes: + description: Volumes allows for mounting volumes from various sources + into the Mattermost application pods. + items: + description: Volume represents a named volume in a pod that may + be accessed by any container in the pod. + properties: + awsElasticBlockStore: + description: 'AWSElasticBlockStore represents an AWS Disk resource + that is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + properties: + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", + "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + partition: + description: 'The partition in the volume that you want + to mount. If omitted, the default is to mount by volume + name. Examples: For volume /dev/sda1, you specify the + partition as "1". Similarly, the volume partition for + /dev/sda is "0" (or you can leave the property empty).' + format: int32 + type: integer + readOnly: + description: 'Specify "true" to force and set the ReadOnly + property in VolumeMounts to "true". If omitted, the default + is "false". More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: boolean + volumeID: + description: 'Unique ID of the persistent disk resource + in AWS (Amazon EBS volume). More info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: string + required: + - volumeID + type: object + azureDisk: + description: AzureDisk represents an Azure Data Disk mount on + the host and bind mount to the pod. + properties: + cachingMode: + description: 'Host Caching mode: None, Read Only, Read Write.' + type: string + diskName: + description: The Name of the data disk in the blob storage + type: string + diskURI: + description: The URI the data disk in the blob storage + type: string + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + kind: + description: 'Expected values Shared: multiple blob disks + per storage account Dedicated: single blob disk per storage + account Managed: azure managed data disk (only in managed + availability set). defaults to shared' + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + description: AzureFile represents an Azure File Service mount + on the host and bind mount to the pod. + properties: + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + secretName: + description: the name of secret that contains Azure Storage + Account Name and Key + type: string + shareName: + description: Share Name + type: string + required: + - secretName + - shareName + type: object + cephfs: + description: CephFS represents a Ceph FS mount on the host that + shares a pod's lifetime + properties: + monitors: + description: 'Required: Monitors is a collection of Ceph + monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + items: + type: string + type: array + path: + description: 'Optional: Used as the mounted root, rather + than the full Ceph tree, default is /' + type: string + readOnly: + description: 'Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: boolean + secretFile: + description: 'Optional: SecretFile is the path to key ring + for User, default is /etc/ceph/user.secret More info: + https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + secretRef: + description: 'Optional: SecretRef is reference to the authentication + secret for User, default is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + user: + description: 'Optional: User is the rados user name, default + is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + required: + - monitors + type: object + cinder: + description: 'Cinder represents a cinder volume attached and + mounted on kubelets host machine. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + properties: + fsType: + description: 'Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Examples: + "ext4", "xfs", "ntfs". Implicitly inferred to be "ext4" + if unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + readOnly: + description: 'Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: boolean + secretRef: + description: 'Optional: points to a secret object containing + parameters used to connect to OpenStack.' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + volumeID: + description: 'volume id used to identify the volume in cinder. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + required: + - volumeID + type: object + configMap: + description: ConfigMap represents a configMap that should populate + this volume + properties: + defaultMode: + description: 'Optional: mode bits used to set permissions + on created files by default. Must be an octal value between + 0000 and 0777 or a decimal value between 0 and 511. YAML + accepts both octal and decimal values, JSON requires decimal + values for mode bits. Defaults to 0644. Directories within + the path are not affected by this setting. This might + be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits + set.' + format: int32 + type: integer + items: + description: If unspecified, each key-value pair in the + Data field of the referenced ConfigMap will be projected + into the volume as a file whose name is the key and content + is the value. If specified, the listed keys will be projected + into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in + the ConfigMap, the volume setup will error unless it is + marked optional. Paths must be relative and may not contain + the '..' path or start with '..'. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits used to set permissions + on this file. Must be an octal value between 0000 + and 0777 or a decimal value between 0 and 511. YAML + accepts both octal and decimal values, JSON requires + decimal values for mode bits. If not specified, + the volume defaultMode will be used. This might + be in conflict with other options that affect the + file mode, like fsGroup, and the result can be other + mode bits set.' + format: int32 + type: integer + path: + description: The relative path of the file to map + the key to. May not be an absolute path. May not + contain the path element '..'. May not start with + the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap or its keys must + be defined + type: boolean + type: object + csi: + description: CSI (Container Storage Interface) represents ephemeral + storage that is handled by certain external CSI drivers (Beta + feature). + properties: + driver: + description: Driver is the name of the CSI driver that handles + this volume. Consult with your admin for the correct name + as registered in the cluster. + type: string + fsType: + description: Filesystem type to mount. Ex. "ext4", "xfs", + "ntfs". If not provided, the empty value is passed to + the associated CSI driver which will determine the default + filesystem to apply. + type: string + nodePublishSecretRef: + description: NodePublishSecretRef is a reference to the + secret object containing sensitive information to pass + to the CSI driver to complete the CSI NodePublishVolume + and NodeUnpublishVolume calls. This field is optional, + and may be empty if no secret is required. If the secret + object contains more than one secret, all secret references + are passed. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + readOnly: + description: Specifies a read-only configuration for the + volume. Defaults to false (read/write). + type: boolean + volumeAttributes: + additionalProperties: + type: string + description: VolumeAttributes stores driver-specific properties + that are passed to the CSI driver. Consult your driver's + documentation for supported values. + type: object + required: + - driver + type: object + downwardAPI: + description: DownwardAPI represents downward API about the pod + that should populate this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on created files + by default. Must be a Optional: mode bits used to set + permissions on created files by default. Must be an octal + value between 0000 and 0777 or a decimal value between + 0 and 511. YAML accepts both octal and decimal values, + JSON requires decimal values for mode bits. Defaults to + 0644. Directories within the path are not affected by + this setting. This might be in conflict with other options + that affect the file mode, like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + items: + description: Items is a list of downward API volume file + items: + description: DownwardAPIVolumeFile represents information + to create the file containing the pod field + properties: + fieldRef: + description: 'Required: Selects a field of the pod: + only annotations, labels, name and namespace are + supported.' + properties: + apiVersion: + description: Version of the schema the FieldPath + is written in terms of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + mode: + description: 'Optional: mode bits used to set permissions + on this file, must be an octal value between 0000 + and 0777 or a decimal value between 0 and 511. YAML + accepts both octal and decimal values, JSON requires + decimal values for mode bits. If not specified, + the volume defaultMode will be used. This might + be in conflict with other options that affect the + file mode, like fsGroup, and the result can be other + mode bits set.' + format: int32 + type: integer + path: + description: 'Required: Path is the relative path + name of the file to be created. Must not be absolute + or contain the ''..'' path. Must be utf-8 encoded. + The first item of the relative path must not start + with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the container: + only resources limits and requests (limits.cpu, + limits.memory, requests.cpu and requests.memory) + are currently supported.' + properties: + containerName: + description: 'Container name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format of the + exposed resources, defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + emptyDir: + description: 'EmptyDir represents a temporary directory that + shares a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + properties: + medium: + description: 'What type of storage medium should back this + directory. The default is "" which means to use the node''s + default medium. Must be an empty string (default) or Memory. + More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + description: 'Total amount of local storage required for + this EmptyDir volume. The size limit is also applicable + for memory medium. The maximum usage on memory medium + EmptyDir would be the minimum value between the SizeLimit + specified here and the sum of memory limits of all containers + in a pod. The default is nil which means that the limit + is undefined. More info: http://kubernetes.io/docs/user-guide/volumes#emptydir' + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + ephemeral: + description: "Ephemeral represents a volume that is handled + by a cluster storage driver. The volume's lifecycle is tied + to the pod that defines it - it will be created before the + pod starts, and deleted when the pod is removed. \n Use this + if: a) the volume is only needed while the pod runs, b) features + of normal volumes like restoring from snapshot or capacity + \ tracking are needed, c) the storage driver is specified + through a storage class, and d) the storage driver supports + dynamic volume provisioning through a PersistentVolumeClaim + (see EphemeralVolumeSource for more information on the + connection between this volume type and PersistentVolumeClaim). + \n Use PersistentVolumeClaim or one of the vendor-specific + APIs for volumes that persist for longer than the lifecycle + of an individual pod. \n Use CSI for light-weight local ephemeral + volumes if the CSI driver is meant to be used that way - see + the documentation of the driver for more information. \n A + pod can use both types of ephemeral volumes and persistent + volumes at the same time." + properties: + volumeClaimTemplate: + description: "Will be used to create a stand-alone PVC to + provision the volume. The pod in which this EphemeralVolumeSource + is embedded will be the owner of the PVC, i.e. the PVC + will be deleted together with the pod. The name of the + PVC will be `<pod name>-<volume name>` where `<volume + name>` is the name from the `PodSpec.Volumes` array entry. + Pod validation will reject the pod if the concatenated + name is not valid for a PVC (for example, too long). \n + An existing PVC with that name that is not owned by the + pod will *not* be used for the pod to avoid using an unrelated + volume by mistake. Starting the pod is then blocked until + the unrelated PVC is removed. If such a pre-created PVC + is meant to be used by the pod, the PVC has to updated + with an owner reference to the pod once the pod exists. + Normally this should not be necessary, but it may be useful + when manually reconstructing a broken cluster. \n This + field is read-only and no changes will be made by Kubernetes + to the PVC after it has been created. \n Required, must + not be nil." + properties: + metadata: + description: May contain labels and annotations that + will be copied into the PVC when creating it. No other + fields are allowed and will be rejected during validation. + type: object + spec: + description: The specification for the PersistentVolumeClaim. + The entire content is copied unchanged into the PVC + that gets created from this template. The same fields + as in a PersistentVolumeClaim are also valid here. + properties: + accessModes: + description: 'AccessModes contains the desired access + modes the volume should have. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#access-modes-1' + items: + type: string + type: array + dataSource: + description: 'This field can be used to specify + either: * An existing VolumeSnapshot object (snapshot.storage.k8s.io/VolumeSnapshot) + * An existing PVC (PersistentVolumeClaim) If the + provisioner or an external controller can support + the specified data source, it will create a new + volume based on the contents of the specified + data source. If the AnyVolumeDataSource feature + gate is enabled, this field will always have the + same contents as the DataSourceRef field.' + properties: + apiGroup: + description: APIGroup is the group for the resource + being referenced. If APIGroup is not specified, + the specified Kind must be in the core API + group. For any other third-party types, APIGroup + is required. + type: string + kind: + description: Kind is the type of resource being + referenced + type: string + name: + description: Name is the name of resource being + referenced + type: string + required: + - kind + - name + type: object + dataSourceRef: + description: 'Specifies the object from which to + populate the volume with data, if a non-empty + volume is desired. This may be any local object + from a non-empty API group (non core object) or + a PersistentVolumeClaim object. When this field + is specified, volume binding will only succeed + if the type of the specified object matches some + installed volume populator or dynamic provisioner. + This field will replace the functionality of the + DataSource field and as such if both fields are + non-empty, they must have the same value. For + backwards compatibility, both fields (DataSource + and DataSourceRef) will be set to the same value + automatically if one of them is empty and the + other is non-empty. There are two important differences + between DataSource and DataSourceRef: * While + DataSource only allows two specific types of objects, + DataSourceRef allows any non-core object, as + well as PersistentVolumeClaim objects. * While + DataSource ignores disallowed values (dropping + them), DataSourceRef preserves all values, and + generates an error if a disallowed value is specified. + (Alpha) Using this field requires the AnyVolumeDataSource + feature gate to be enabled.' + properties: + apiGroup: + description: APIGroup is the group for the resource + being referenced. If APIGroup is not specified, + the specified Kind must be in the core API + group. For any other third-party types, APIGroup + is required. + type: string + kind: + description: Kind is the type of resource being + referenced + type: string + name: + description: Name is the name of resource being + referenced + type: string + required: + - kind + - name + type: object + resources: + description: 'Resources represents the minimum resources + the volume should have. If RecoverVolumeExpansionFailure + feature is enabled users are allowed to specify + resource requirements that are lower than previous + value but must still be higher than capacity recorded + in the status field of the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#resources' + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount + of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum + amount of compute resources required. If Requests + is omitted for a container, it defaults to + Limits if that is explicitly specified, otherwise + to an implementation-defined value. More info: + https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' + type: object + type: object + selector: + description: A label query over volumes to consider + for binding. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + storageClassName: + description: 'Name of the StorageClass required + by the claim. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#class-1' + type: string + volumeMode: + description: volumeMode defines what type of volume + is required by the claim. Value of Filesystem + is implied when not included in claim spec. + type: string + volumeName: + description: VolumeName is the binding reference + to the PersistentVolume backing this claim. + type: string + type: object + required: + - spec + type: object + type: object + fc: + description: FC represents a Fibre Channel resource that is + attached to a kubelet's host machine and then exposed to the + pod. + properties: + fsType: + description: 'Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + lun: + description: 'Optional: FC target lun number' + format: int32 + type: integer + readOnly: + description: 'Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts.' + type: boolean + targetWWNs: + description: 'Optional: FC target worldwide names (WWNs)' + items: + type: string + type: array + wwids: + description: 'Optional: FC volume world wide identifiers + (wwids) Either wwids or combination of targetWWNs and + lun must be set, but not both simultaneously.' + items: + type: string + type: array + type: object + flexVolume: + description: FlexVolume represents a generic volume resource + that is provisioned/attached using an exec based plugin. + properties: + driver: + description: Driver is the name of the driver to use for + this volume. + type: string + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". The default filesystem depends on FlexVolume + script. + type: string + options: + additionalProperties: + type: string + description: 'Optional: Extra command options if any.' + type: object + readOnly: + description: 'Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting in VolumeMounts.' + type: boolean + secretRef: + description: 'Optional: SecretRef is reference to the secret + object containing sensitive information to pass to the + plugin scripts. This may be empty if no secret object + is specified. If the secret object contains more than + one secret, all secrets are passed to the plugin scripts.' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + required: + - driver + type: object + flocker: + description: Flocker represents a Flocker volume attached to + a kubelet's host machine. This depends on the Flocker control + service being running + properties: + datasetName: + description: Name of the dataset stored as metadata -> name + on the dataset for Flocker should be considered as deprecated + type: string + datasetUUID: + description: UUID of the dataset. This is unique identifier + of a Flocker dataset + type: string + type: object + gcePersistentDisk: + description: 'GCEPersistentDisk represents a GCE Disk resource + that is attached to a kubelet''s host machine and then exposed + to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + properties: + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", + "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + partition: + description: 'The partition in the volume that you want + to mount. If omitted, the default is to mount by volume + name. Examples: For volume /dev/sda1, you specify the + partition as "1". Similarly, the volume partition for + /dev/sda is "0" (or you can leave the property empty). + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + format: int32 + type: integer + pdName: + description: 'Unique name of the PD resource in GCE. Used + to identify the disk in GCE. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: string + readOnly: + description: 'ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: boolean + required: + - pdName + type: object + gitRepo: + description: 'GitRepo represents a git repository at a particular + revision. DEPRECATED: GitRepo is deprecated. To provision + a container with a git repo, mount an EmptyDir into an InitContainer + that clones the repo using git, then mount the EmptyDir into + the Pod''s container.' + properties: + directory: + description: Target directory name. Must not contain or + start with '..'. If '.' is supplied, the volume directory + will be the git repository. Otherwise, if specified, + the volume will contain the git repository in the subdirectory + with the given name. + type: string + repository: + description: Repository URL + type: string + revision: + description: Commit hash for the specified revision. + type: string + required: + - repository + type: object + glusterfs: + description: 'Glusterfs represents a Glusterfs mount on the + host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/glusterfs/README.md' + properties: + endpoints: + description: 'EndpointsName is the endpoint name that details + Glusterfs topology. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + path: + description: 'Path is the Glusterfs volume path. More info: + https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + readOnly: + description: 'ReadOnly here will force the Glusterfs volume + to be mounted with read-only permissions. Defaults to + false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: boolean + required: + - endpoints + - path + type: object + hostPath: + description: 'HostPath represents a pre-existing file or directory + on the host machine that is directly exposed to the container. + This is generally used for system agents or other privileged + things that are allowed to see the host machine. Most containers + will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + --- TODO(jonesdl) We need to restrict who can use host directory + mounts and who can/can not mount host directories as read/write.' + properties: + path: + description: 'Path of the directory on the host. If the + path is a symlink, it will follow the link to the real + path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + type: + description: 'Type for HostPath Volume Defaults to "" More + info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + required: + - path + type: object + iscsi: + description: 'ISCSI represents an ISCSI Disk resource that is + attached to a kubelet''s host machine and then exposed to + the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' + properties: + chapAuthDiscovery: + description: whether support iSCSI Discovery CHAP authentication + type: boolean + chapAuthSession: + description: whether support iSCSI Session CHAP authentication + type: boolean + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", + "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + initiatorName: + description: Custom iSCSI Initiator Name. If initiatorName + is specified with iscsiInterface simultaneously, new iSCSI + interface <target portal>:<volume name> will be created + for the connection. + type: string + iqn: + description: Target iSCSI Qualified Name. + type: string + iscsiInterface: + description: iSCSI Interface Name that uses an iSCSI transport. + Defaults to 'default' (tcp). + type: string + lun: + description: iSCSI Target Lun number. + format: int32 + type: integer + portals: + description: iSCSI Target Portal List. The portal is either + an IP or ip_addr:port if the port is other than default + (typically TCP ports 860 and 3260). + items: + type: string + type: array + readOnly: + description: ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. + type: boolean + secretRef: + description: CHAP Secret for iSCSI target and initiator + authentication + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + targetPortal: + description: iSCSI Target Portal. The Portal is either an + IP or ip_addr:port if the port is other than default (typically + TCP ports 860 and 3260). + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + description: 'Volume''s name. Must be a DNS_LABEL and unique + within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + nfs: + description: 'NFS represents an NFS mount on the host that shares + a pod''s lifetime More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + properties: + path: + description: 'Path that is exported by the NFS server. More + info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + readOnly: + description: 'ReadOnly here will force the NFS export to + be mounted with read-only permissions. Defaults to false. + More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: boolean + server: + description: 'Server is the hostname or IP address of the + NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + description: 'PersistentVolumeClaimVolumeSource represents a + reference to a PersistentVolumeClaim in the same namespace. + More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + properties: + claimName: + description: 'ClaimName is the name of a PersistentVolumeClaim + in the same namespace as the pod using this volume. More + info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: string + readOnly: + description: Will force the ReadOnly setting in VolumeMounts. + Default false. + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + description: PhotonPersistentDisk represents a PhotonController + persistent disk attached and mounted on kubelets host machine + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + pdID: + description: ID that identifies Photon Controller persistent + disk + type: string + required: + - pdID + type: object + portworxVolume: + description: PortworxVolume represents a portworx volume attached + and mounted on kubelets host machine + properties: + fsType: + description: FSType represents the filesystem type to mount + Must be a filesystem type supported by the host operating + system. Ex. "ext4", "xfs". Implicitly inferred to be "ext4" + if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + volumeID: + description: VolumeID uniquely identifies a Portworx volume + type: string + required: + - volumeID + type: object + projected: + description: Items for all in one resources secrets, configmaps, + and downward API + properties: + defaultMode: + description: Mode bits used to set permissions on created + files by default. Must be an octal value between 0000 + and 0777 or a decimal value between 0 and 511. YAML accepts + both octal and decimal values, JSON requires decimal values + for mode bits. Directories within the path are not affected + by this setting. This might be in conflict with other + options that affect the file mode, like fsGroup, and the + result can be other mode bits set. + format: int32 + type: integer + sources: + description: list of volume projections + items: + description: Projection that may be projected along with + other supported volume types + properties: + configMap: + description: information about the configMap data + to project + properties: + items: + description: If unspecified, each key-value pair + in the Data field of the referenced ConfigMap + will be projected into the volume as a file + whose name is the key and content is the value. + If specified, the listed keys will be projected + into the specified paths, and unlisted keys + will not be present. If a key is specified which + is not present in the ConfigMap, the volume + setup will error unless it is marked optional. + Paths must be relative and may not contain the + '..' path or start with '..'. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits used to + set permissions on this file. Must be + an octal value between 0000 and 0777 or + a decimal value between 0 and 511. YAML + accepts both octal and decimal values, + JSON requires decimal values for mode + bits. If not specified, the volume defaultMode + will be used. This might be in conflict + with other options that affect the file + mode, like fsGroup, and the result can + be other mode bits set.' + format: int32 + type: integer + path: + description: The relative path of the file + to map the key to. May not be an absolute + path. May not contain the path element + '..'. May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the ConfigMap or + its keys must be defined + type: boolean + type: object + downwardAPI: + description: information about the downwardAPI data + to project + properties: + items: + description: Items is a list of DownwardAPIVolume + file + items: + description: DownwardAPIVolumeFile represents + information to create the file containing + the pod field + properties: + fieldRef: + description: 'Required: Selects a field + of the pod: only annotations, labels, + name and namespace are supported.' + properties: + apiVersion: + description: Version of the schema the + FieldPath is written in terms of, + defaults to "v1". + type: string + fieldPath: + description: Path of the field to select + in the specified API version. + type: string + required: + - fieldPath + type: object + mode: + description: 'Optional: mode bits used to + set permissions on this file, must be + an octal value between 0000 and 0777 or + a decimal value between 0 and 511. YAML + accepts both octal and decimal values, + JSON requires decimal values for mode + bits. If not specified, the volume defaultMode + will be used. This might be in conflict + with other options that affect the file + mode, like fsGroup, and the result can + be other mode bits set.' + format: int32 + type: integer + path: + description: 'Required: Path is the relative + path name of the file to be created. Must + not be absolute or contain the ''..'' + path. Must be utf-8 encoded. The first + item of the relative path must not start + with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the + container: only resources limits and requests + (limits.cpu, limits.memory, requests.cpu + and requests.memory) are currently supported.' + properties: + containerName: + description: 'Container name: required + for volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output format + of the exposed resources, defaults + to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to + select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + secret: + description: information about the secret data to + project + properties: + items: + description: If unspecified, each key-value pair + in the Data field of the referenced Secret will + be projected into the volume as a file whose + name is the key and content is the value. If + specified, the listed keys will be projected + into the specified paths, and unlisted keys + will not be present. If a key is specified which + is not present in the Secret, the volume setup + will error unless it is marked optional. Paths + must be relative and may not contain the '..' + path or start with '..'. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits used to + set permissions on this file. Must be + an octal value between 0000 and 0777 or + a decimal value between 0 and 511. YAML + accepts both octal and decimal values, + JSON requires decimal values for mode + bits. If not specified, the volume defaultMode + will be used. This might be in conflict + with other options that affect the file + mode, like fsGroup, and the result can + be other mode bits set.' + format: int32 + type: integer + path: + description: The relative path of the file + to map the key to. May not be an absolute + path. May not contain the path element + '..'. May not start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + optional: + description: Specify whether the Secret or its + key must be defined + type: boolean + type: object + serviceAccountToken: + description: information about the serviceAccountToken + data to project + properties: + audience: + description: Audience is the intended audience + of the token. A recipient of a token must identify + itself with an identifier specified in the audience + of the token, and otherwise should reject the + token. The audience defaults to the identifier + of the apiserver. + type: string + expirationSeconds: + description: ExpirationSeconds is the requested + duration of validity of the service account + token. As the token approaches expiration, the + kubelet volume plugin will proactively rotate + the service account token. The kubelet will + start trying to rotate the token if the token + is older than 80 percent of its time to live + or if the token is older than 24 hours.Defaults + to 1 hour and must be at least 10 minutes. + format: int64 + type: integer + path: + description: Path is the path relative to the + mount point of the file to project the token + into. + type: string + required: + - path + type: object + type: object + type: array + type: object + quobyte: + description: Quobyte represents a Quobyte mount on the host + that shares a pod's lifetime + properties: + group: + description: Group to map volume access to Default is no + group + type: string + readOnly: + description: ReadOnly here will force the Quobyte volume + to be mounted with read-only permissions. Defaults to + false. + type: boolean + registry: + description: Registry represents a single or multiple Quobyte + Registry services specified as a string as host:port pair + (multiple entries are separated with commas) which acts + as the central registry for volumes + type: string + tenant: + description: Tenant owning the given Quobyte volume in the + Backend Used with dynamically provisioned Quobyte volumes, + value is set by the plugin + type: string + user: + description: User to map volume access to Defaults to serivceaccount + user + type: string + volume: + description: Volume is a string that references an already + created Quobyte volume by name. + type: string + required: + - registry + - volume + type: object + rbd: + description: 'RBD represents a Rados Block Device mount on the + host that shares a pod''s lifetime. More info: https://examples.k8s.io/volumes/rbd/README.md' + properties: + fsType: + description: 'Filesystem type of the volume that you want + to mount. Tip: Ensure that the filesystem type is supported + by the host operating system. Examples: "ext4", "xfs", + "ntfs". Implicitly inferred to be "ext4" if unspecified. + More info: https://kubernetes.io/docs/concepts/storage/volumes#rbd + TODO: how do we prevent errors in the filesystem from + compromising the machine' + type: string + image: + description: 'The rados image name. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + keyring: + description: 'Keyring is the path to key ring for RBDUser. + Default is /etc/ceph/keyring. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + monitors: + description: 'A collection of Ceph monitors. More info: + https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + items: + type: string + type: array + pool: + description: 'The rados pool name. Default is rbd. More + info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + readOnly: + description: 'ReadOnly here will force the ReadOnly setting + in VolumeMounts. Defaults to false. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: boolean + secretRef: + description: 'SecretRef is name of the authentication secret + for RBDUser. If provided overrides keyring. Default is + nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + user: + description: 'The rados user name. Default is admin. More + info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + required: + - image + - monitors + type: object + scaleIO: + description: ScaleIO represents a ScaleIO persistent volume + attached and mounted on Kubernetes nodes. + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Default is "xfs". + type: string + gateway: + description: The host address of the ScaleIO API Gateway. + type: string + protectionDomain: + description: The name of the ScaleIO Protection Domain for + the configured storage. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef references to the secret for ScaleIO + user and other sensitive information. If this is not provided, + Login operation will fail. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + sslEnabled: + description: Flag to enable/disable SSL communication with + Gateway, default false + type: boolean + storageMode: + description: Indicates whether the storage for a volume + should be ThickProvisioned or ThinProvisioned. Default + is ThinProvisioned. + type: string + storagePool: + description: The ScaleIO Storage Pool associated with the + protection domain. + type: string + system: + description: The name of the storage system as configured + in ScaleIO. + type: string + volumeName: + description: The name of a volume already created in the + ScaleIO system that is associated with this volume source. + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + description: 'Secret represents a secret that should populate + this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + properties: + defaultMode: + description: 'Optional: mode bits used to set permissions + on created files by default. Must be an octal value between + 0000 and 0777 or a decimal value between 0 and 511. YAML + accepts both octal and decimal values, JSON requires decimal + values for mode bits. Defaults to 0644. Directories within + the path are not affected by this setting. This might + be in conflict with other options that affect the file + mode, like fsGroup, and the result can be other mode bits + set.' + format: int32 + type: integer + items: + description: If unspecified, each key-value pair in the + Data field of the referenced Secret will be projected + into the volume as a file whose name is the key and content + is the value. If specified, the listed keys will be projected + into the specified paths, and unlisted keys will not be + present. If a key is specified which is not present in + the Secret, the volume setup will error unless it is marked + optional. Paths must be relative and may not contain the + '..' path or start with '..'. + items: + description: Maps a string key to a path within a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits used to set permissions + on this file. Must be an octal value between 0000 + and 0777 or a decimal value between 0 and 511. YAML + accepts both octal and decimal values, JSON requires + decimal values for mode bits. If not specified, + the volume defaultMode will be used. This might + be in conflict with other options that affect the + file mode, like fsGroup, and the result can be other + mode bits set.' + format: int32 + type: integer + path: + description: The relative path of the file to map + the key to. May not be an absolute path. May not + contain the path element '..'. May not start with + the string '..'. + type: string + required: + - key + - path + type: object + type: array + optional: + description: Specify whether the Secret or its keys must + be defined + type: boolean + secretName: + description: 'Name of the secret in the pod''s namespace + to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + type: object + storageos: + description: StorageOS represents a StorageOS volume attached + and mounted on Kubernetes nodes. + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). ReadOnly here + will force the ReadOnly setting in VolumeMounts. + type: boolean + secretRef: + description: SecretRef specifies the secret to use for obtaining + the StorageOS API credentials. If not specified, default + values will be attempted. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, uid?' + type: string + type: object + volumeName: + description: VolumeName is the human-readable name of the + StorageOS volume. Volume names are only unique within + a namespace. + type: string + volumeNamespace: + description: VolumeNamespace specifies the scope of the + volume within StorageOS. If no namespace is specified + then the Pod's namespace will be used. This allows the + Kubernetes name scoping to be mirrored within StorageOS + for tighter integration. Set VolumeName to any name to + override the default behaviour. Set to "default" if you + are not using namespaces within StorageOS. Namespaces + that do not pre-exist within StorageOS will be created. + type: string + type: object + vsphereVolume: + description: VsphereVolume represents a vSphere volume attached + and mounted on kubelets host machine + properties: + fsType: + description: Filesystem type to mount. Must be a filesystem + type supported by the host operating system. Ex. "ext4", + "xfs", "ntfs". Implicitly inferred to be "ext4" if unspecified. + type: string + storagePolicyID: + description: Storage Policy Based Management (SPBM) profile + ID associated with the StoragePolicyName. + type: string + storagePolicyName: + description: Storage Policy Based Management (SPBM) profile + name. + type: string + volumePath: + description: Path that identifies vSphere volume vmdk + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + type: object + status: + description: MattermostStatus defines the observed state of Mattermost + properties: + endpoint: + description: The endpoint to access the Mattermost instance + type: string + error: + description: The last observed error in the deployment of this Mattermost + instance + type: string + image: + description: The image running on the pods in the Mattermost instance + type: string + observedGeneration: + description: The last observed Generation of the Mattermost resource + that was acted on. + format: int64 + type: integer + replicas: + description: Total number of non-terminated pods targeted by this + Mattermost deployment + format: int32 + type: integer + resourcePatch: + description: Status of specified resource patches. + properties: + deploymentPatch: + description: PatchStatus represents status of particular patch. + properties: + applied: + type: boolean + error: + type: string + type: object + servicePatch: + description: PatchStatus represents status of particular patch. + properties: + applied: + type: boolean + error: + type: string + type: object + type: object + state: + description: Represents the running state of the Mattermost instance + type: string + updatedReplicas: + description: Total number of non-terminated pods targeted by this + Mattermost deployment that are running with the desired image. + format: int32 + type: integer + version: + description: The version currently running in the Mattermost instance + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} +status: + acceptedNames: + kind: "" + plural: "" + conditions: [] + storedVersions: [] +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: mattermost-operator + namespace: mattermost-operator +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + name: mattermost-operator +rules: +- apiGroups: + - "" + resources: + - pods + - services + - configmaps + - secrets + - serviceaccounts + verbs: + - '*' +- apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - '*' +- apiGroups: + - "" + resources: + - namespaces + verbs: + - get +- apiGroups: + - apps + resources: + - deployments + verbs: + - '*' +- apiGroups: + - apps + resourceNames: + - mattermost-operator + resources: + - deployments/finalizers + verbs: + - update +- apiGroups: + - apps + resources: + - replicasets + verbs: + - get + - list + - watch + - delete +- apiGroups: + - batch + resources: + - jobs + verbs: + - get + - create + - list + - delete + - watch + - update +- apiGroups: + - networking.k8s.io + resources: + - ingresses + verbs: + - '*' +- apiGroups: + - rbac.authorization.k8s.io + resources: + - rolebindings + - roles + verbs: + - get + - create + - list + - delete + - watch + - update +- apiGroups: + - mattermost.com + resources: + - '*' + verbs: + - '*' +- apiGroups: + - installation.mattermost.com + resources: + - '*' + verbs: + - '*' +- apiGroups: + - mysql.presslabs.org + resources: + - mysqlbackups + - mysqlclusters + - mysqlclusters/status + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +- apiGroups: + - miniocontroller.min.io + resources: + - minioinstances + - minioinstances/status + verbs: + - get + - list + - watch + - create + - update + - patch + - delete +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: mattermost-operator +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: mattermost-operator +subjects: +- kind: ServiceAccount + name: mattermost-operator + namespace: mattermost-operator +--- +apiVersion: v1 +kind: Service +metadata: + creationTimestamp: null + labels: + app: mattermost-operator + name: mattermost-operator + name: mattermost-operator + namespace: mattermost-operator +spec: + ports: + - name: metrics + port: 8383 + protocol: TCP + targetPort: metrics + selector: + name: mattermost-operator + type: ClusterIP +status: + loadBalancer: {} +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mattermost-operator + namespace: mattermost-operator +spec: + replicas: 1 + selector: + matchLabels: + name: mattermost-operator + template: + metadata: + labels: + name: mattermost-operator + spec: + containers: + - args: + - --enable-leader-election + - --metrics-addr=0.0.0.0:8383 + command: + - /mattermost-operator + env: + - name: MAX_RECONCILING_INSTALLATIONS + value: "20" + - name: REQUEUE_ON_LIMIT_DELAY + value: 20s + - name: MAX_RECONCILE_CONCURRENCY + value: "10" + image: mattermost/mattermost-operator:1.19.0 + imagePullPolicy: IfNotPresent + name: mattermost-operator + ports: + - containerPort: 8383 + name: metrics + serviceAccountName: mattermost-operator diff --git a/renovate.json b/renovate.json index d7f57a1bc..f191aafa7 100644 --- a/renovate.json +++ b/renovate.json @@ -2,12 +2,10 @@ "baseBranches": ["main"], "configWarningReuseIssue": false, "dependencyDashboard": "true", - "dependencyDashboardHeader": "- [ ] Follow steps in [package update docs](./docs/PACKAGE_UPDATES.md) to upgrade the package.", - "dependencyDashboardTitle": "Renovate: Upgrade Mattermost Package Dependencies", + "dependencyDashboardHeader": "- [ ] Sync upstream helm chart version with updated dependencies.", + "dependencyDashboardTitle": "Renovate: Upgrade Mattermost-Operator Package Dependencies", "draftPR": true, - "enabledManagers": ["regex"], - "ignorePaths": ["chart/charts/**"], - "labels": ["mattermost","Package Sustainment","kind::maintenance"], + "labels": ["mattermostoperator","Package Sustainment","kind::maintenance"], "packageRules": [ { "matchDatasources": ["docker"], @@ -17,29 +15,28 @@ "commands": [ "match-chart-yaml-appversion", "regenerate-helm-docs", - "bump-changelog '- {{{replace 'registry1.dso.mil/' '' depName}}} updated from {{{currentVersion}}} to {{{newVersion}}}'" + "bump-changelog '- {{{replace 'registry1.dso.mil/' '' depName}}} updated from {{{replace 'v' '' currentVersion}}} to {{{replace 'v' '' newVersion}}}'" ], "fileFilters": ["chart/Chart.yaml", "README.md", "CHANGELOG.md"] } } ], - "separateMajorMinor": false, "regexManagers": [ { "fileMatch": ["^chart/values\\.yaml$"], "matchStrings": [ - "image:\\s+name:\\s+[^/]+/(?<depName>.+)\\s+tag:\\s+(?<currentValue>.+)" + "image:\\s+repository:\\s+[^/]+/(?<depName>.+)\\s+tag:\\s+(?<currentValue>.+)" ], - "depNameTemplate": "registry1.dso.mil/ironbank/opensource/mattermost/mattermost", "datasourceTemplate": "docker" }, { "fileMatch": ["^chart/Chart\\.yaml$"], "matchStrings": [ - "- Mattermost:\\s+(?<currentValue>.+)", + "- Mattermost Operator:\\s+(?<currentValue>.+)", "appVersion:\\s+(?<currentValue>.+)" ], - "depNameTemplate": "registry1.dso.mil/ironbank/opensource/mattermost/mattermost", + "extractVersionTemplate": "^v(?<version>.*)$", + "depNameTemplate": "registry1.dso.mil/ironbank/opensource/mattermost/mattermost-operator", "datasourceTemplate": "docker" }, { @@ -51,4 +48,3 @@ } ] } - diff --git a/tests/dependencies.yaml b/tests/dependencies.yaml deleted file mode 100644 index de33714de..000000000 --- a/tests/dependencies.yaml +++ /dev/null @@ -1,7 +0,0 @@ -mattermostoperator: - git: "https://repo1.dso.mil/platform-one/big-bang/apps/collaboration-tools/mattermost-operator.git" - namespace: "mattermost-operator" - -miniooperator: - git: "https://repo1.dso.mil/platform-one/big-bang/apps/application-utilities/minio-operator.git" - namespace: "minio-operator" diff --git a/tests/policy/mattermost-minio-ib.rego b/tests/policy/mattermost-minio-ib.rego deleted file mode 100644 index 63d7c4435..000000000 --- a/tests/policy/mattermost-minio-ib.rego +++ /dev/null @@ -1,35 +0,0 @@ -package main - -name = input.metadata.name - -registries_allow := ["registry1.dsop.io/ironbank", "registry1.dso.mil/ironbank", "registry.dsop.io", "registry.dso.mil"] -registries_warn := ["registry.dsop.io", "registry.dso.mil"] - -in_allowed_registries(image) { - startswith(image, registries_allow[i]) -} - -in_warning_registries(image) { - startswith(image, registries_warn[i]) -} - -# Deny non-approved registries -deny[msg] { - image := input.spec.image - not in_allowed_registries(image) - msg := sprintf("Image '%v' in '%v' is not from approved registries", [image, name]) -} - -# Warn about non-Ironbank Images -warn[msg] { - image := input.spec.image - in_warning_registries(image) - msg := sprintf("Image '%v' in '%v' is not from Ironbank", [image, name]) -} - -# Warn about DSOP Images -warn[msg] { - image := input.spec.image - contains(image, ".dsop.io") - msg := sprintf("Update 'dsop.io' to 'dso.mil' for image '%v' in '%v'", [image, name]) -} diff --git a/tests/policy/postgres-ib.rego b/tests/policy/postgres-ib.rego deleted file mode 100644 index df2e9a1b5..000000000 --- a/tests/policy/postgres-ib.rego +++ /dev/null @@ -1,35 +0,0 @@ -package main - -name = input.metadata.name - -registries_allow_pg := ["registry1.dsop.io/ironbank", "registry1.dso.mil/ironbank", "registry.dsop.io", "registry.dso.mil"] -registries_warn_pg := ["registry.dsop.io", "registry.dso.mil"] - -in_allowed_registries_pg(image) { - startswith(image, registries_allow[i]) -} - -in_warning_registries_pg(image) { - startswith(image, registries_warn[i]) -} - -# Deny non-approved registries -deny[msg] { - image := input.spec.dockerImage - not in_allowed_registries_pg(image) - msg := sprintf("Image '%v' in '%v' is not from approved registries", [image, name]) -} - -# Warn about non-Ironbank Images -warn[msg] { - image := input.spec.dockerImage - in_warning_registries_pg(image) - msg := sprintf("Image '%v' in '%v' is not from Ironbank", [image, name]) -} - -# Warn about DSOP Images -warn[msg] { - image := input.spec.dockerImage - contains(image, ".dsop.io") - msg := sprintf("Update 'dsop.io' to 'dso.mil' for image '%v' in '%v'", [image, name]) -} diff --git a/tests/post-install-packages.yaml b/tests/post-install-packages.yaml new file mode 100644 index 000000000..0ef4b878e --- /dev/null +++ b/tests/post-install-packages.yaml @@ -0,0 +1,6 @@ +minio-operator: + git: + repo: https://repo1.dso.mil/platform-one/big-bang/apps/application-utilities/minio-operator +mattermost: + git: + repo: "https://repo1.dso.mil/platform-one/big-bang/apps/collaboration-tools/mattermost.git" diff --git a/tests/test-values.yml b/tests/test-values.yml index d2eb3a2d5..117128371 100644 --- a/tests/test-values.yml +++ b/tests/test-values.yml @@ -1,22 +1,3 @@ -minio: - install: true - tenant: - pools: - - servers: 1 - volumesPerServer: 4 - size: 256Mi - securityContext: - runAsUser: 1001 - runAsGroup: 1001 - fsGroup: 1001 - -postgresql: - install: true - networkPolicies: enabled: true - -bbtests: - enabled: true - cypress: - image: registry1.dso.mil/bigbang-ci/cypress-kubectl:8.3.1 + controlPlaneCidr: 172.16.0.0/12 diff --git a/tests/wait.sh b/tests/wait.sh deleted file mode 100755 index bafe4a44b..000000000 --- a/tests/wait.sh +++ /dev/null @@ -1,27 +0,0 @@ -#!/bin/bash -wait_project() { - # interval and timeout are in seconds - interval=5 - timeout=600 - # - resourcename=mattermost - counter=0 - # need to remove the default "set -e" to allow commands to return nonzero exit codes without the script failing - set +e - while true; do - if kubectl get $resourcename --namespace=$resourcename -o jsonpath='{.items[0].status.state}' | \ - grep "^stable" 1>/dev/null - then - echo "$resourcename custom resource creation finished" - break - fi - sleep $interval - let counter++ - if [[ $(($counter * $interval)) -ge $timeout ]]; then - echo "$resourcename timeout waiting $timeout seconds for resource creation, running describe..." 1>&2 - kubectl describe $resourcename --namespace=$resourcename 1>&2 - exit 1 - fi - done - set -e -} -- GitLab From 53191af3b188de9d5d4c19114d81bc6f4cce1197 Mon Sep 17 00:00:00 2001 From: Micah Nagel <micah.nagel@defenseunicorns.com> Date: Tue, 7 Feb 2023 14:14:57 -0700 Subject: [PATCH 4/5] add --- CHANGELOG.md | 16 ---------------- chart/Chart.yaml | 10 +++++----- chart/values.yaml | 2 +- 3 files changed, 6 insertions(+), 22 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 39f50dba9..1e0d7abb7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,22 +3,6 @@ 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). --- -## [1.19.0-bb.0] - 2022-12-06 -### Changed -- ironbank/opensource/mattermost/mattermost-operator updated from 1.18.1 to 1.19.0 - -## [1.18.1-bb.1] - 2022-09-08 -### Added -- Added default securitycontext to container (drop capabilities, non-privileged, read only fs) -- Added post install package to validate MM successful install - -## [1.18.1-bb.0] - 2022-06-23 -### Changed -- Updated to latest 1.18.1 image/manifests - -## [1.18.0-bb.1] - 2022-06-14 -### Changed -- Adding securityContext section to deployment template and chart values. ## [1.18.0-bb.0] - 2022-04-20 ### Changed diff --git a/chart/Chart.yaml b/chart/Chart.yaml index 563b86485..9addf07da 100644 --- a/chart/Chart.yaml +++ b/chart/Chart.yaml @@ -3,8 +3,8 @@ name: mattermost-operator description: "Deployment of mattermost operator using Helm" home: https://github.com/mattermost/mattermost-operator type: application -version: 1.19.0-bb.0 -appVersion: 1.19.0 +version: 1.18.0-bb.0 +appVersion: 1.18.0 kubeVersion: ">=1.12.0-0" keywords: - Mattermost @@ -14,12 +14,12 @@ maintainers: email: info@mattermost.com dependencies: - name: mattermost-operator-crds - version: 1.19.0 + version: 1.18.0 condition: installCRDs repository: "file://mattermost-operator-crds" annotations: bigbang.dev/applicationVersions: | - - Mattermost Operator: 1.19.0 + - Mattermost Operator: 1.18.0 helm.sh/images: | - name: mattermost-operator - image: registry1.dso.mil/ironbank/opensource/mattermost/mattermost-operator:v1.19.0 + image: registry1.dso.mil/ironbank/opensource/mattermost/mattermost-operator:v1.18.0 diff --git a/chart/values.yaml b/chart/values.yaml index cbfb318f6..b58157aac 100644 --- a/chart/values.yaml +++ b/chart/values.yaml @@ -4,7 +4,7 @@ image: # -- Full image name repository: "registry1.dso.mil/ironbank/opensource/mattermost/mattermost-operator" # -- Image tag - tag: v1.19.0 + tag: v1.18.0 replicas: # -- Mattermost operator desired replicas -- GitLab From 6b59c766748d25876f991209a6dc90e826ca7bbe Mon Sep 17 00:00:00 2001 From: Micah Nagel <micah.nagel@defenseunicorns.com> Date: Tue, 7 Feb 2023 21:15:33 +0000 Subject: [PATCH 5/5] Update Ironbank --- CHANGELOG.md | 4 ++++ chart/Chart.yaml | 8 ++++---- chart/values.yaml | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1e0d7abb7..a8987682f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), --- +## [1.19.0-bb.0] - 2023-02-07 +### Changed +- ironbank/opensource/mattermost/mattermost-operator updated from 1.18.0 to 1.19.0 + ## [1.18.0-bb.0] - 2022-04-20 ### Changed - Updated to latest IB image 1.18.0 diff --git a/chart/Chart.yaml b/chart/Chart.yaml index 9addf07da..0f3fbbc87 100644 --- a/chart/Chart.yaml +++ b/chart/Chart.yaml @@ -3,8 +3,8 @@ name: mattermost-operator description: "Deployment of mattermost operator using Helm" home: https://github.com/mattermost/mattermost-operator type: application -version: 1.18.0-bb.0 -appVersion: 1.18.0 +version: 1.19.0-bb.0 +appVersion: 1.19.0 kubeVersion: ">=1.12.0-0" keywords: - Mattermost @@ -19,7 +19,7 @@ dependencies: repository: "file://mattermost-operator-crds" annotations: bigbang.dev/applicationVersions: | - - Mattermost Operator: 1.18.0 + - Mattermost Operator: 1.19.0 helm.sh/images: | - name: mattermost-operator - image: registry1.dso.mil/ironbank/opensource/mattermost/mattermost-operator:v1.18.0 + image: registry1.dso.mil/ironbank/opensource/mattermost/mattermost-operator:v1.19.0 diff --git a/chart/values.yaml b/chart/values.yaml index b58157aac..cbfb318f6 100644 --- a/chart/values.yaml +++ b/chart/values.yaml @@ -4,7 +4,7 @@ image: # -- Full image name repository: "registry1.dso.mil/ironbank/opensource/mattermost/mattermost-operator" # -- Image tag - tag: v1.18.0 + tag: v1.19.0 replicas: # -- Mattermost operator desired replicas -- GitLab